@@ -52,6 +52,8 @@ public final class IpPrefixFunctions
52
52
{
53
53
private static final BigInteger TWO = BigInteger .valueOf (2 );
54
54
55
+ private static final Block EMPTY_BLOCK = IPPREFIX .createBlockBuilder (null , 0 ).build ();
56
+
55
57
/**
56
58
* Our definitions for what IANA considers not "globally reachable" are taken from the docs at
57
59
* https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml and
@@ -290,6 +292,71 @@ public static boolean isPrivateIpAddress(@SqlType(StandardTypes.IPADDRESS) Slice
290
292
return false ;
291
293
}
292
294
295
+ @ Description ("Split the input prefix into subnets the size of the new prefix length." )
296
+ @ ScalarFunction ("ip_prefix_subnets" )
297
+ @ SqlType ("array(IPPREFIX)" )
298
+ public static Block ipPrefixSubnets (@ SqlType (StandardTypes .IPPREFIX ) Slice prefix , @ SqlType (StandardTypes .BIGINT ) long newPrefixLength )
299
+ {
300
+ boolean inputIsIpV4 = isIpv4 (prefix );
301
+
302
+ if (newPrefixLength < 0 || (inputIsIpV4 && newPrefixLength > 32 ) || (!inputIsIpV4 && newPrefixLength > 128 )) {
303
+ throw new PrestoException (INVALID_FUNCTION_ARGUMENT , "Invalid prefix length for IPv" + (inputIsIpV4 ? "4" : "6" ) + ": " + newPrefixLength );
304
+ }
305
+
306
+ int inputPrefixLength = getPrefixLength (prefix );
307
+ // An IP prefix is a 'network', or group of contiguous IP addresses. The common format for describing IP prefixes is
308
+ // uses 2 parts separated by a '/': (1) the IP address part and the (2) prefix length part (also called subnet size or CIDR).
309
+ // For example, in 9.255.255.0/24, 9.255.255.0 is the IP address part and 24 is the prefix length.
310
+ // The prefix length describes how many IP addresses the prefix contains in terms of the leading number of bits required. A higher number of bits
311
+ // means smaller number of IP addresses. Subnets inherently mean smaller groups of IP addresses.
312
+ // We can only disaggregate a prefix if the prefix length is the same length or longer (more-specific) than the length of the input prefix.
313
+ // E.g., if the input prefix is 9.255.255.0/24, the prefix length can be /24, /25, /26, etc... but not 23 or larger value than 24.
314
+
315
+ int newPrefixCount = 0 ; // if inputPrefixLength > newPrefixLength, there are no new prefixes and we will return an empty array.
316
+ if (inputPrefixLength <= newPrefixLength ) {
317
+ // Next, count how many new prefixes we will generate. In general, every difference in prefix length doubles the number new prefixes.
318
+ // For example if we start with 9.255.255.0/24, and want to split into /25s, we would have 2 new prefixes. If we wanted to split into /26s,
319
+ // we would have 4 new prefixes, and /27 would have 8 prefixes etc....
320
+ newPrefixCount = 1 << (newPrefixLength - inputPrefixLength ); // 2^N
321
+ }
322
+
323
+ if (newPrefixCount == 0 ) {
324
+ return EMPTY_BLOCK ;
325
+ }
326
+
327
+ BlockBuilder blockBuilder = IPPREFIX .createBlockBuilder (null , newPrefixCount );
328
+
329
+ if (newPrefixCount == 1 ) {
330
+ IPPREFIX .writeSlice (blockBuilder , prefix ); // just return the original prefix in an array
331
+ return blockBuilder .build (); // returns empty or single entry
332
+ }
333
+
334
+ int ipVersionMaxBits = inputIsIpV4 ? 32 : 128 ;
335
+ BigInteger newPrefixIpCount = TWO .pow (ipVersionMaxBits - (int ) newPrefixLength );
336
+
337
+ Slice startingIpAddressAsSlice = ipSubnetMin (prefix );
338
+ BigInteger currentIpAddress = toBigInteger (startingIpAddressAsSlice );
339
+
340
+ try {
341
+ for (int i = 0 ; i < newPrefixCount ; i ++) {
342
+ InetAddress asInetAddress = bigIntegerToIpAddress (currentIpAddress );
343
+ Slice ipPrefixAsSlice = castFromVarcharToIpPrefix (utf8Slice (InetAddresses .toAddrString (asInetAddress ) + "/" + newPrefixLength ));
344
+ IPPREFIX .writeSlice (blockBuilder , ipPrefixAsSlice );
345
+ currentIpAddress = currentIpAddress .add (newPrefixIpCount ); // increment to start of next new prefix
346
+ }
347
+ }
348
+ catch (UnknownHostException ex ) {
349
+ throw new PrestoException (GENERIC_INTERNAL_ERROR , "Unable to convert " + currentIpAddress + " to IP prefix" , ex );
350
+ }
351
+
352
+ return blockBuilder .build ();
353
+ }
354
+
355
+ private static int getPrefixLength (Slice ipPrefix )
356
+ {
357
+ return ipPrefix .getByte (IPPREFIX .getFixedSize () - 1 ) & 0xFF ;
358
+ }
359
+
293
360
private static List <Slice > generateMinIpPrefixes (BigInteger firstIpAddress , BigInteger lastIpAddress , int ipVersionMaxBits )
294
361
{
295
362
List <Slice > ipPrefixSlices = new ArrayList <>();
0 commit comments