[Answers Posted] Relevance Challenge - IP Address is in which Subnet - February 2020

Welcome to the February 2020 Relevance Challenge!

This one is a little bit different from most, in that I don’t have a working answer for this one yet. But I’m sure at least one exists, so I hope we can all work through this, and once we have some solutions we can open it up to the community at large for crowdsourced improvement.

The substance of this challenge is, given a list of IP address and IP Subnets in CIDR form, can we map each IP address to its appropriate subnet?

For instance, given IP addresses

192.168.1.50
192.168.2.16
192.168.2.220
192.168.3.10
192.168.3.110
192.168.3.224

And Subnet ranges

192.168.1.0/24
192.168.2.0/25
192.168.2.128/25
192.168.3.0/25
192.168.3.128/26
192.168.3.192/26

Can we provide a listing mapping each address to the correct subnet, such as

192.168.1.50, 192.168.1.0/24
192.168.2.16, 192.168.2.0/25
192.168.2.220, 192.168.2.128/25
192.168.3.10, 192.168.3.0/25
192.168.3.110, 192.168.3.128/26
192.168.3.224, 192.168.3.192/26

Note that because we are using “Classless Internet Domain Routing” (CIDR), these are not all Class-C addresses. We cannot rely simply on an address that “starts with” the Subnet definition, because 192.168.3.224 is not on the 192.168.3.0/25 subnet.

I expect this problem has a number of applications, such as in defining Sites, Relay Affiliations, etc.; I plan to use this in a report for Unmanaged Asset Scan results in particular.

Please send me your solutions by private message. I’ll keep this challenge open for at least two weeks. Everyone who provides a working solution will be awarded with community recognition and accolades, and your usernames inscribed here to be seen for generations to come!

Previous Challenges:

4 Likes

A kind of sub challenge, that you might want to tackle first is, given CIDR notation, figure out the start/end IP ranges.

For example, given 192.168.2.0/25 return 192.168.2.0 - 192.168.2.127

I feel like this is one of those things I avoid trying to wrap my head around and just use an online calculator whenever it comes up. I just read this, now my head hurts: https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking

1 Like

I find the following is also useful to understand subnetting, and are a bit more practical

I just found these helpful youtube videos

And this one especially

My relevance solution is based pretty heavily on knowledge contained in that second video.

1 Like

There are inspectors for IP addresses. There are inspectors for CIDR ranges. Why isn’t there an inspector to test whether an IP address is inside a CIDR range?

(I’m riffing on a George Carlin bit that is not appropriate for a family newspaper. :wink: )

1 Like

There haven’t been any responses on this challenge yet, so I think it’s worth dropping a few hints.

The way the IP stack handles addresses, subnet masks, and routing is via bitwise comparison. If (IP Address) AND (Subnet Mask) = (Subnet address), then the address is “in” the subnet.

Consider that the IP Address 192.168.0.127 can be represented as the following bit set:

11000000 = 192
10101000 = 168
00000000 = 0
01111111 = 127

And a subnet mask 255.255.255.128 (or, /25) can be represented as

11111111 = 255
11111111 = 255
11111111 = 255
10000000 = 128

To determine whether an address is in a subnet, the computer performs an AND operation. A given subnet address is the IP address AND’d with the subnet mask. Let’s check to see if a given IP address is in the 192.168.0.0/25 network:

11000000 10101000 00000000 01111111  (192.168.0.127) AND
11111111 11111111 11111111 10000000  (subnet mask 255.255.255.128)
 =================
11000000 10101000 00000000 00000000 (192.168.0.0)

Hence, this IP address 192.168.0.127 is in the 192.168.0.0/25 network.

We can contrast that with 192.168.0.200:

11000000 10101000 00000000 11001000(192.168.0.200) AND
11111111 11111111 11111111 10000000   (subnet mask 255.255.255.128)
=================
11000000 10101000 00000000 10000000 (192.168.0.128)

The 192.168.0.200 IP address, AND’d with the 255.255.255.128 subnet mask, yields the 192.168.0.128 network - so this address is not in the 192.168.0.0/25 network, instead it would be in the 192.168.0.128/25 network.

And for one final hint, we do have some bit operators in Relevance:

// Create bit set from an Integer
q: 127 as bits
A: 1111111
T: 0.093 ms
I: singular bit set

// Or create a bit set from a String
q: bit set "101010"
A: 101010
T: 0.071 ms
I: singular bit set

// Pad a bit set by prefixing it with 0 values
q: padded string of (8 as bits)
A: 0000000000000000000000000000000000000000000000000000000000001000
T: 0.045 ms
I: singular string

In relevance, bit sets have several operators.
* is the AND operator
+ is the OR operator

q: 10 as bits
A: 1010
T: 0.070 ms
I: singular bit set

q: 2 as bits
A: 10
T: 0.048 ms
I: singular bit set

q: (10 as bits * 2 as bits)
A: 10
T: 0.026 ms
I: singular bit set

We can also perform left shift and right shift operations on a bit set.

q: left shift 2 of (3 as bits)
A: 1100
T: 0.034 ms
I: singular bit set
1 Like
6 Likes

Yeah, I know it’s a lot to take in if you haven’t worked in subnetting or binary logic. The youtube videos I linked do a better job of explaining it, but I wanted to have something here in the Forum (and to nudge the challenge a bit).

There are real applications for this, I promise! I built a dashboard comparing which subnets were scanned with the Unmanaged Asset scanner, to how many Unmanaged Assets were found on the subnet. Our Scanner populates an Analysis with which subnets it scanned (in CIDR form, like 192.168.0.128/25), and our Unmanaged Assets report their IP address (but not their Subnet). If the scanner finds no assets on a given subnet, it’s likely getting blocked by firewalls, and we need to pick another machine from which to scan.

2 Likes

@JasonWalker,
This is a great topic. I have the network knowledge but nothing near the relevance skill for this. I will be paying attention to this one.

1 Like

Answers, Part 1

Today I learned the Forum has a limit to the number of characters in a post, so I'll have to split this into a couple of replies.

Welcome back for the answers to the IP / Subnet Relevance Challenge. Sadly we didn’t get any correct answers on this one, but hopefully now with answers posted we can start a discussion on improving what I came up with.

Strap in everyone, this is going to be a bit of a journey. The key points to consider here are

  1. Representing an IP Address in Binary Form
  2. Splitting an IP Address by Octet
  3. Converting an Subnet CIDR Address and Mask into Bit Sets
  4. Converting a Subnet CIDR string into a Bit Set
  5. Use of Bitwise operators left shift, OR (+) and AND (*)
  6. Novel use of concatenation 1 of (values) to produce 111 instead of concatenation ", " of values to produce “value, value, value”
  7. Efficiency of iterating String Sets rather than repeated lookups

Because both this post, and some of the individual Relevance queries, are quite long, I’ve hidden the explanation for each section in a “Details” tag. Click each “Details” header below to reveal the explanation for each section.

Representing an IP address in Binary Form

In native binary form, an IP address is simply a large integer, made up of 32 bits. In human-readable form, it is split into four "octets", with each octet ranging from 0 to 255 and separated with dots.

We can convert an individual octet into its binary representation pretty easily. For example, consider IP address 192.168.10.33

q: 192 as bits
A: 11000000
T: 14.234 ms
I: singular bit set

q: 168 as bits
A: 10101000
T: 14.211 ms
I: singular bit set

q: 10 as bits
A: 1
T: 14.187 ms
I: singular bit set

q: 33 as bits
A: 110010
T: 14.164 ms
I: singular bit set

So we convert an integer into a bit set; however, when we want to combine them into a single large value, we have to consider how these bits should be “shifted”. For a final value, the bits that make up the “192” value need to be in the first 8 bits of the 32-bit value, so we have to “left shift” this value by 24 bits. The bits that make up “168” need to occupy the next 8 bits, so this one must be “left-shifted” by 16 bits. The bits that make up the “1” value need to occupy the next set of 8 bits, so they must be “left-shifted” by 8 bits. The value that makes up “50” will occupy the lowest 8 bits, so it doesn’t need to be shifted at all.

We can handle the bit-shifting in native Relevance via

q: left shift 24 of (192 as bits)
A: 11000000000000000000000000000000
T: 14.140 ms
I: singular bit set

q: left shift 16 of (168 as bits)
A: 101010000000000000000000
T: 14.112 ms
I: singular bit set

q: left shift 1 of (10 as bits)
A: 10100
T: 14.084 ms
I: singular bit set

q: 33 as bits
A: 100001
T: 14.055 ms
I: singular bit set

For display purposes, the leading “0” values are trimmed off of the display, but they are still there internally; if we want a better visual of how these are stored, we can display each of them as a “padded string”, similar to how the “version” inspector has a padded property:

q: padded string of left shift 24 of (192 as bits)
A: 0000000000000000000000000000000011000000000000000000000000000000
T: 14.031 ms
I: singular string

q: padded string of left shift 16 of (168 as bits)
A: 0000000000000000000000000000000000000000101010000000000000000000
T: 13.998 ms
I: singular string

q: padded string of left shift 8 of (10 as bits)
A: 0000000000000000000000000000000000000000000000000000101000000000
T: 13.966 ms
I: singular string

q: padded string of (33 as bits)
A: 0000000000000000000000000000000000000000000000000000000000100001
T: 13.936 ms
I: singular string

This helps us to visually align where each group of bits contributes to the final value. It also shows us that internally, BigFix is storing each bit set as a group of 64 bits (but we’ll only be interested in the lowest-order 32 bits of that when dealing with IP addresses).

To combine them all together into a single value representing the complete IP address, we use the bitwise “OR” operation. This is represented in Relevance with the “+” symbol on bit sets. NOTE that when dealing with Bit Sets, the OR operation of “+” is slightly different from the “Addition” operation of the “+” symbol. A bitwise OR takes two bit sets, compares the bit value in each position, and returns a “1” for that bit position if either bit set had a 1 in that position; returning a “0” for that position if neither bit set had a “1” in that position.

    010   (2)
OR  011   (3)
    ---
    011   (3)
So, we can represent the IP address "192.168.10.33" in one bit set by OR'ing the component bit sets for each octet:
q: left shift 24 of (192 as bits) + left shift 16 of (168 as bits) + left shift 8 of (10 as bits) + (33 as bits)
A: 11000000101010000000101000100001
T: 13.907 ms
I: singular bit set

Splitting an IP address by Octet

So, now we understand how to take each octet of an IP address and combine them into a single bit set. But first we need to split an IP address into its component octets. We chould do that via a series of preceding texts of "." and "following text of ".", but I find it slightly more readable to split the IP address into components separated by ", ", so we can treat it as a Tuple String and refer totuple string item 0 of it,tuple string item 1 of it`, etc.:

q: concatenation ", " of substrings separated by "." of "192.168.10.33"
A: 192, 168, 10, 33
T: 13.870 ms
I: singular string

[Much of the remaining relevance gets a bit longer, so I’m showing the view from the “Single Clause” window of the Fixlet Debugger, with intended relevance]

(
   tuple string item 0 of it
 ; tuple string item 1 of it
 ; tuple string item 2 of it
 ; tuple string item 3 of it
) of concatenation ", " of substrings separated by "." of "192.168.10.33"

192
168
10
33


Evaluation time: 0.203 ms
Evaluates to plural object of type string

Now that we have each octet in its own string, we take step to convert each tuple string back into an integer, then to a bit set

( 
  (
    tuple string item 0 of it as integer as bit set
  , tuple string item 1 of it as integer as bit set
  , tuple string item 2 of it as integer as bit set
  , tuple string item 3 of it as integer as bit set
  ) of concatenation ", " of substrings separated by "." of it
) of "192.168.10.33"

11000000, 10101000, 1010, 100001


Evaluation time: 0.266 ms
Evaluates to singular object of type ( bit set, bit set, bit set, bit set )

And finally, we can handle the left-shifting and OR them back into one, single bit set value

(
 (
   left shift 24 of item 0 of it
 + left shift 16 of item 1 of it 
 + left shift 8 of item 2 of it 
 + item 3 of it
 ) of 
 (
   tuple string item 0 of it as integer as bit set
 , tuple string item 1 of it as integer as bit set
 , tuple string item 2 of it as integer as bit set
 , tuple string item 3 of it as integer as bit set
 ) of concatenation ", " of substrings separated by "." of it
) of "192.168.10.33"

11000000101010000000101000100001


Evaluation time: 0.125 ms
Evaluates to singular object of type bit set

Converting Subnet CIDR Address and Mask into Bit Sets

Now we understand how we can convert an IP address to a bit set; how about a subnet mask in CIDR form? To review, a subnet displayed in CIDR form takes the format of “192.168.1.0/27”. The number after the slash indicates “how many of the bits in the subnet mask are a 1 value”. In the case of “27”, this means that the subnet mask is “27 ones, followed by 5 zeroes” to make up the total of 32-bits in the mask. So /27 is represented in binary as
11111111 11111111 11111111 11100000
(when displayed in octet form rather than CIDR form, this would be “255.255.255.224”

Conveniently, we also happen to be able to create a Bit Set in relevance by starting with a string representing the bits.

q: bit set "1110"
A: 1110
T: 13.429 ms
I: singular bit set

To derive the bit set from a /27 form, I find it easier to use a bit of a trick in concatenation. The first step is to basically count “how many integers are there between 1 and our CIDR value”, by simply counting from 1 to 27:

q: (integers in (1,it)) of 27
A: 1
A: 2
A: 3
...
A: 26
A: 27
T: 13.403 ms
I: plural integer

Now, “concatenation” is a really interesting operation. Normally when we do a concatenation we take the original plural input and combine the values with some kind of delimiter, like how we concatenated the octets of an IP address with ", " to make a tuple string earlier. However it’s also possible to use the “concatenation” operation to completely ignore the original input, and just create a repeating string. Instead of concatenation "X" of ("v1";"v2"), this form is concatenation of "X" of ("v1";"v2"). This is easy to miss, it’s the difference between concatenation "X" of and concatenation of "X" of. For example

q: concatenation "1" of ("a";"b";"c")
A: a1b1c
T: 13.309 ms
I: singular string

q: concatenation of "1" of ("a";"b";"c")
A: 111
T: 13.252 ms
I: singular string

So to create a string of 27 “1” values for the CIDR subnet above, we can use

q: (concatenation of  "1" of integers in (1,(it))) of 27
A: 111111111111111111111111111
T: 13.169 ms
I: singular string

To get the remaining “0” bits, we can do the same thing, except counting backwards from 31 to the CIDR value:

q: (concatenation of  "1" of integers in (1,(it)) & concatenation of "0" of integers in (31, it)) of 27
A: 11111111111111111111111111100000
T: 13.101 ms
I: singular string

To determine both the subnet address and the subnet mask for a given subnet in CIDR form, we combine the IP address and the subnet mask logic. We split the CIDR subnet form on the “/” symbol, treat the preceding text of the “/” as an IP address, the following text of the “/” as a CIDR subnet, and show the bitset values of the IP address and the subnet mask:

(
 it
 , (
     (
       item 0 of it
     + item 1 of it
     + item 2 of it
     + item 3 of it
     ) of (
        left shift 24 of item 0 of it
      , left shift 16 of item 1 of it
      , left shift 8 of item 2 of it
      , item 3 of it
      ) of (
        tuple string item 0 of it as integer as bit set
      , tuple string item 1 of it as integer as bit set
      , tuple string item 2 of it as integer as bit set
      , tuple string item 3 of it as integer as bit set
      ) of concatenation ", " of substrings separated by "." of preceding text of it
           , (bit set (it)
     ) of  (
     concatenation of  "1" of integers in (1,(it))
     & concatenation of "0" of integers in (31, it)
 ) of (it as integer) of following text of it) of first "/" of it
) of "192.168.10.32/27"

192.168.10.32/27, ( 11000000101010000000101000100000, 11111111111111111111111111100000 )


Evaluation time: 0.439 ms
Evaluates to singular object of type ( string, ( bit set, bit set ) )
1 Like

Answers, Part 2

Now we are ready to perform some comparisons.

Comparing IP Addresses to Subnets

To recap, we can retrieve an IP address in bitset form via

Convert IP Address to Bitset

(
    it
 , (
      left shift 24 of item 0 of it
    + left shift 16 of item 1 of it 
    + left shift 8 of item 2 of it 
    + item 3 of it
   ) of (
       tuple string item 0 of it as integer as bit set
     , tuple string item 1 of it as integer as bit set
     , tuple string item 2 of it as integer as bit set
     , tuple string item 3 of it as integer as bit set
     ) of concatenation ", " of substrings separated by "." of it
) of "192.168.10.33"

192.168.10.33, 11000000101010000000101000100001


Evaluation time: 0.140 ms
Evaluates to singular object of type ( string, bit set )

And we can retrieve a Subnet and Subnet Mask in bitset forms via

Convert Subnet CIDR to Bitsets

(
    it
 , (
    (
      item 0 of it
    + item 1 of it
    + item 2 of it
    + item 3 of it
    ) of (
      left shift 24 of item 0 of it
    , left shift 16 of item 1 of it
    , left shift 8 of item 2 of it
    , item 3 of it
    ) of (
      tuple string item 0 of it as integer as bit set
    , tuple string item 1 of it as integer as bit set
    , tuple string item 2 of it as integer as bit set
    , tuple string item 3 of it as integer as bit set
    ) of concatenation ", " of substrings separated by "." of preceding text of it
      , (bit set (it)) of  (
         concatenation of "1" of integers in (1,(it)) 
       & concatenation of "0" of integers in (31, it)
       ) of (it as integer) of following text of it
   ) of first "/" of it
) of "192.168.10.32/27"

192.168.10.32/27, ( 11000000101010000000101000100000, 11111111111111111111111111100000 )


Evaluation time: 0.230 ms
Evaluates to singular object of type ( string, ( bit set, bit set ) )

Now we wish to compare an IP address to a Subnet.

For this we will use the bitwise "AND" operator, which in Relevance is represented by the "*" symbol. In a bitwise AND, each bit position from the two values is compared. If there is a "1" in that position of both bitsets, the result for that bit is a "1". If either of the bitsets has a "0" in that position, the result is a "0". For example
     010   (2)
AND  011   (3)
     ---
     010   (2)

When comparing an IP address to a subnet, we AND every bit of the IP address against the Subnet Mask. If the result value is equal to the Subnet Address, then the IP is “in” that Subnet.

     11000000 10101000 00001010 00100001   (IP address  192.168.10.33)
AND  11111111 11111111 11111111 11100000   (Subnet Mask 255.255.255.248)
     -------- -------- -------- --------
     11000000 10101000 00001010 00100000   (Subnet Address 192.168.10.32)

Because 192.168.10.33 AND 255.255.255.248 = the subnet address 192.168.10.32, this IP address is “in” the Subnet.

Combining all of that logic together, we can compare a list of subnet CIDR addresses to IP addresses via the following:

(
   item 0 of it  /* Subnet CIDR address */
 , item 1 of it  /* Readable IP address*/
 , item 2 of it  /* Subnet as bit set*/
 , item 3 of it  /* subnet mask as bit set */
 , item 4 of it  /* ip address as bit set */
 ,  item 2 of it = item 3 of it * item 4 of it   /* bool: subnet = IP address * subnet mask */
) of 
(
   item 0 of it
 , item 1 of it
 , (
    ( item 0 of it + item 1 of it + item 2 of it + item 3 of it) of 
    (
      left shift 24 of item 0 of it
    , left shift 16 of item 1 of it
    , left shift 8 of item 2 of it
    , item 3 of it
    ) of  
   (
      tuple string item 0 of it as integer as bit set
    , tuple string item 1 of it as integer as bit set
    , tuple string item 2 of it as integer as bit set
    , tuple string item 3 of it as integer as bit set
   ) of concatenation ", " of substrings separated by "." of preceding text of first "/" of item 0 of it 
   )
 , (
    (bit set (it)) of  (concatenation of  "1" of integers in (1,(it)) & concatenation of "0" of integers in (31, it)) of (it as integer) of following text of it
   ) of first "/" of item 0 of it /* Subnet CIDR list */
  , (
     (
       item 0 of it
     + item 1 of it
     + item 2 of it
     + item 3 of it
     ) of 
     (
       left shift 24 of item 0 of it
     , left shift 16 of item 1 of it
     , left shift 8 of item 2 of it
     , item 3 of it
     ) of (
         tuple string item 0 of it as integer as bit set
       , tuple string item 1 of it as integer as bit set
       , tuple string item 2 of it as integer as bit set
       , tuple string item 3 of it as integer as bit set
       ) of concatenation ", " of substrings separated by "." of item 1 of it) /* Address list */
     ) of 
  (     /* subnet list */ 
      ("192.168.10.32/27"; "192.168.128.0/27"; "192.168.128.64/27"; "192.168.128.128/27"; "192.168.128.192/26")
        /* IP address list */ 
    , ("192.168.10.33";"192.168.10.34";"192.168.10.35";"192.168.128.1")
  )

192.168.10.32/27, 192.168.10.33, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010000000101000100001, True
192.168.10.32/27, 192.168.10.34, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010000000101000100010, True
192.168.10.32/27, 192.168.10.35, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010000000101000100011, True
192.168.10.32/27, 192.168.128.1, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010001000000000000001, False
192.168.128.0/27, 192.168.10.33, 11000000101010001000000000000000, 11111111111111111111111111100000, 11000000101010000000101000100001, False
192.168.128.0/27, 192.168.10.34, 11000000101010001000000000000000, 11111111111111111111111111100000, 11000000101010000000101000100010, False
192.168.128.0/27, 192.168.10.35, 11000000101010001000000000000000, 11111111111111111111111111100000, 11000000101010000000101000100011, False
192.168.128.0/27, 192.168.128.1, 11000000101010001000000000000000, 11111111111111111111111111100000, 11000000101010001000000000000001, True
192.168.128.64/27, 192.168.10.33, 11000000101010001000000001000000, 11111111111111111111111111100000, 11000000101010000000101000100001, False
192.168.128.64/27, 192.168.10.34, 11000000101010001000000001000000, 11111111111111111111111111100000, 11000000101010000000101000100010, False
192.168.128.64/27, 192.168.10.35, 11000000101010001000000001000000, 11111111111111111111111111100000, 11000000101010000000101000100011, False
192.168.128.64/27, 192.168.128.1, 11000000101010001000000001000000, 11111111111111111111111111100000, 11000000101010001000000000000001, False
192.168.128.128/27, 192.168.10.33, 11000000101010001000000010000000, 11111111111111111111111111100000, 11000000101010000000101000100001, False
192.168.128.128/27, 192.168.10.34, 11000000101010001000000010000000, 11111111111111111111111111100000, 11000000101010000000101000100010, False
192.168.128.128/27, 192.168.10.35, 11000000101010001000000010000000, 11111111111111111111111111100000, 11000000101010000000101000100011, False
192.168.128.128/27, 192.168.128.1, 11000000101010001000000010000000, 11111111111111111111111111100000, 11000000101010001000000000000001, False
192.168.128.192/26, 192.168.10.33, 11000000101010001000000011000000, 11111111111111111111111111000000, 11000000101010000000101000100001, False
192.168.128.192/26, 192.168.10.34, 11000000101010001000000011000000, 11111111111111111111111111000000, 11000000101010000000101000100010, False
192.168.128.192/26, 192.168.10.35, 11000000101010001000000011000000, 11111111111111111111111111000000, 11000000101010000000101000100011, False
192.168.128.192/26, 192.168.128.1, 11000000101010001000000011000000, 11111111111111111111111111000000, 11000000101010001000000000000001, False


Evaluation time: 3.328 ms
Evaluates to plural object of type ( string, string, bit set, bit set, bit set, boolean )

We can also add filtering, to retrieve the list of only the matching subnet & IP addresses via the following query.

For illustration purposes, I'm displaying the original subnet CIDR address, the original readable IP address, and the bit set representations for the Subnet, Subnet Mask, and IP address.
(
   item 0 of it  /* Subnet CIDR address */
 , item 1 of it  /* Readable IP address*/
 , item 2 of it  /* Subnet as bit set*/
 , item 3 of it  /* subnet mask as bit set */
 , item 4 of it  /* ip address as bit set */
) whose 
 (
  item 2 of it = item 3 of it * item 4 of it /* bool: subnet = IP address * subnet mask */
 ) of 
(
   item 0 of it
 , item 1 of it
 , (
    (
      item 0 of it
    + item 1 of it 
    + item 2 of it 
    + item 3 of it
    ) of 
 (
   left shift 24 of item 0 of it
 , left shift 16 of item 1 of it
 , left shift 8 of item 2 of it
 , item 3 of it
 ) of 
 (
    tuple string item 0 of it as integer as bit set
  , tuple string item 1 of it as integer as bit set
  , tuple string item 2 of it as integer as bit set
  , tuple string item 3 of it as integer as bit set
 ) of concatenation ", " of substrings separated by "." of preceding text of first "/" of item 0 of it) /* Subnet CIDR List */
 , (
     (bit set (it)) of  
     (
       concatenation of  "1" of integers in (1,(it)) 
       & concatenation of "0" of integers in (31, it)
     ) of (it as integer) of following text of it) of first "/" of item 0 of it
    , (
        (
          item 0 of it
        + item 1 of it 
        + item 2 of it 
        + item 3 of it
        ) of (
            left shift 24 of item 0 of it
          , left shift 16 of item 1 of it
          , left shift 8 of item 2 of it
          , item 3 of it
          ) of (
            tuple string item 0 of it as integer as bit set
          , tuple string item 1 of it as integer as bit set
          , tuple string item 2 of it as integer as bit set
          , tuple string item 3 of it as integer as bit set
          ) of concatenation ", " of substrings separated by "." of item 1 of it  /* IP Address list */
       )
) of 
 (        /* subnet list */ 
     (
        "192.168.10.32/27"
      ; "192.168.128.0/27"
      ; "192.168.128.64/27"
      ; "192.168.128.128/27"
      ; "192.168.128.192/26"
     )
          /* IP address list */ 
    , (
        "192.168.10.33"
      ; "192.168.10.34"
      ; "192.168.10.35"
      ; "192.168.128.1"
      )
)

192.168.10.32/27, 192.168.10.33, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010000000101000100001
192.168.10.32/27, 192.168.10.34, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010000000101000100010
192.168.10.32/27, 192.168.10.35, 11000000101010000000101000100000, 11111111111111111111111111100000, 11000000101010000000101000100011
192.168.128.0/27, 192.168.128.1, 11000000101010001000000000000000, 11111111111111111111111111100000, 11000000101010001000000000000001


Evaluation time: 2.826 ms
Evaluates to plural object of type ( string, string, bit set, bit set, bit set )

Challenge Solution

Solving the original Relevance challenge is a matter of only returning the Subnet and IP address values where Address AND Subnet Mask = Subnet Address:

(
    item 0 of it   /* Subnet CIDR address in readable form */
  , item 1 of it  /* IP Address in readable form */
) of  
(
    item 0 of it  /* Subnet CIDR address */
  , item 1 of it  /* Readable IP address*/
  ,   item 2 of it  /* Subnet as bit set*/
  ,  item 3 of it  /* subnet mask as bit set */
  ,  item 4 of it  /* ip address as bit set */
) whose 
  (
        /* bool: subnet = IP address * subnet mask */
    item 2 of it = item 3 of it * item 4 of it 
  ) of 
(
   item 0 of it  /* Subnet CIDR address in readable form */
 , item 1 of it  /*  IP Address in readable form */
 , (
     (
       item 0 of it   /* Combine the octets of the Subnet Address bitsets into one bitset */
     + item 1 of it
     + item 2 of it
     + item 3 of it
     ) of 
     (
            /* Shift the octet bitsets of the Subnet Address into their correct positions */
       left shift 24 of item 0 of it
     , left shift 16 of item 1 of it
     , left shift 8 of item 2 of it
     , item 3 of it
     ) of (
              /* Split the octets of the Subnet Address into bitsets */
        tuple string item 0 of it as integer as bit set
      , tuple string item 1 of it as integer as bit set
      , tuple string item 2 of it as integer as bit set
      , tuple string item 3 of it as integer as bit set
      ) of concatenation ", " of substrings separated by "." of preceding text of first "/" of item 0 of it)
      , (
                /* Create a bitset of the Subnet Mask portion */
           (bit set (it)) of  (
                  concatenation of  "1" of integers in (1,(it)) 
              & concatenation of "0" of integers in (31, it)) of (it as integer) of following text of it
                   /* Split the Subnet CIDR Address on "/" - Address is before, Mask is after */
         ) of first "/" of item 0 of it
         , (
            (
                   /* Merge the octet bitsets for the IP Address via bitwise "OR" operator */
              item 0 of it 
            + item 1 of it 
            + item 2 of it 
            + item 3 of it
            ) of (
                      /* Shift the octet bitsets for IP Address into their correct positions */
                left shift 24 of item 0 of it
              , left shift 16 of item 1 of it
              , left shift 8 of item 2 of it
              , item 3 of it
              ) of (
                    /* Split the octets of the IP Address, and make a bitset from each octet */
                 tuple string item 0 of it as integer as bit set
               , tuple string item 1 of it as integer as bit set
               , tuple string item 2 of it as integer as bit set
               , tuple string item 3 of it as integer as bit set
               ) of concatenation ", " of substrings separated by "." of item 1 of it)
           ) of 
(           /* subnet list */ 
   (
      "192.168.1.0/24"
    ; "192.168.2.0/25"
    ; "192.168.2.128/25"
    ; "192.168.3.0/25"
    ; "192.168.3.128/26"
    ; "192.168.3.192/26"
   )
      /* IP address list */ 
 , (
    "192.168.1.50"
  ; "192.168.2.16"
  ; "192.168.2.220"
  ; "192.168.3.10"
  ; "192.168.3.110"
  ; "192.168.3.224"
   )
)

192.168.1.0/24, 192.168.1.50
192.168.2.0/25, 192.168.2.16
192.168.2.128/25, 192.168.2.220
192.168.3.0/25, 192.168.3.10
192.168.3.0/25, 192.168.3.110
192.168.3.192/26, 192.168.3.224


Evaluation time: 7.466 ms
Evaluates to plural object of type ( string, string )

Efficiency and Scaling

Those comparisons are nice, but do have a couple of limitations.

  • Only the subnets that actually have a matching IP address are included
  • The way we are creating cross-product of ( Every Subnet CIDR X Every IP Address) can be slow when run at scale

For every additional Subnet, we have to re-calculate the IP address. If there is one subnet and 10 IP addresses, I have to run the IP address calculation 10 times. If there are 2 subnets, double the IP address calculation to run 20 times. Add a third subnet and the IP address is calculated 30 times. In my use case, there is added overhead in that I’m retrieving the list of IP addresses via session relevance as well, so those lookups add overhead of their own.

For a primer on how this cross-product efficiency works, I’d recommend the excellent blog & post by @brolly33 at Efficient Session Relevance Query for Computer Properties

In the real data set, I'll be dealing with up to 14 thousand Subnets and up to a half million IP addresses. Each Subnet address is read from an Analysis in the "BES Asset Management" site, and each IP address is read from the properties of a BES Unmanaged Asset. Looking up these values repeatedly and re-calculating their bit set values can gets expensive quickly. Also in my use-case I need to display each Subnet Address, and the number of IP addresses that are matched on that subnet, so I need to preserve the Subnet Addresses that had 0 matches.

The way I try to make this more efficient is to lookup and calculate all of the subnet addresses, one time, and store the results in a string set. Then I lookup and calculate all of the IP addresses, one time, and store it in another string set. Then I loop through each Subnet in the set, compare each Subnet to each element of the IP Address set, and preserve the matching values.

To store these as Sets, we have to convert back-and-forth between Bit Sets and Strings, which is a bit more difficult to read. For the Subnets (which I’ll want to display back), I keep the original Subnet CIDR string, the subnet address in bit set format, and the original subnet mask in bit set format, all delimited by colons. For the IP addresses (which I don’t care to display back), I only keep the IP address in bit set format.

(  
    item 0 of it
  , elements of item 1 of it
) of 
(
   elements of item 0 of it
 , item 1 of it
) of 
(
   set of (
      item 0 of it 
    & ":"
    & /* Subnet as bit set*/  item 1 of it as string
    & ":"
    & /* subnet mask as bit set */ item 2 of it as string
    ) of 
      (
        it
      , (
         (
           item 0 of it
         + item 1 of it
         + item 2 of it
         + item 3 of it
         ) of 
        (
          left shift 24 of item 0 of it
        , left shift 16 of item 1 of it
        , left shift 8 of item 2 of it
        , item 3 of it
        ) of 
        (
          tuple string item 0 of it as integer as bit set
        , tuple string item 1 of it as integer as bit set
        , tuple string item 2 of it as integer as bit set
        , tuple string item 3 of it as integer as bit set
        ) of concatenation ", " of substrings separated by "." of preceding text of first "/" of it
      )
    , (
       (bit set (it)) of  
        (
          concatenation of  "1" of integers in (1,(it)) 
        & concatenation of "0" of integers in (31, it)
        ) of (it as integer) of following text of it) of first "/" of it
      ) of 
 (
   (    /* Subnet List */
      "192.168.10.32/27"
    ; "192.168.128.0/27"
    ; "192.168.128.64/27"
    ; "192.168.128.128/27"
    ; "192.168.128.192/26"
   )
 )
  , set of (it as string) of 
  (
    (
      item 0 of it
    + item 1 of it
    + item 2 of it
    + item 3 of it
    ) of 
    (
       left shift 24 of item 0 of it
     , left shift 16 of item 1 of it
     , left shift 8 of item 2 of it
     , item 3 of it
     ) of 
     (
         tuple string item 0 of it as integer as bit set
       , tuple string item 1 of it as integer as bit set
       , tuple string item 2 of it as integer as bit set
       , tuple string item 3 of it as integer as bit set
      ) of concatenation ", " of substrings separated by "." of it
  ) of 
     (
         "192.168.10.33"
       ; "192.168.10.34"
       ; "192.168.10.35" 
       ; "192.168.128.1"
     )
)

192.168.10.32/27:11000000101010000000101000100000:11111111111111111111111111100000, 11000000101010000000101000100001
192.168.10.32/27:11000000101010000000101000100000:11111111111111111111111111100000, 11000000101010000000101000100010
192.168.10.32/27:11000000101010000000101000100000:11111111111111111111111111100000, 11000000101010000000101000100011
192.168.10.32/27:11000000101010000000101000100000:11111111111111111111111111100000, 11000000101010001000000000000001
192.168.128.0/27:11000000101010001000000000000000:11111111111111111111111111100000, 11000000101010000000101000100001
192.168.128.0/27:11000000101010001000000000000000:11111111111111111111111111100000, 11000000101010000000101000100010
192.168.128.0/27:11000000101010001000000000000000:11111111111111111111111111100000, 11000000101010000000101000100011
192.168.128.0/27:11000000101010001000000000000000:11111111111111111111111111100000, 11000000101010001000000000000001
192.168.128.128/27:11000000101010001000000010000000:11111111111111111111111111100000, 11000000101010000000101000100001
192.168.128.128/27:11000000101010001000000010000000:11111111111111111111111111100000, 11000000101010000000101000100010
192.168.128.128/27:11000000101010001000000010000000:11111111111111111111111111100000, 11000000101010000000101000100011
192.168.128.128/27:11000000101010001000000010000000:11111111111111111111111111100000, 11000000101010001000000000000001
192.168.128.192/26:11000000101010001000000011000000:11111111111111111111111111000000, 11000000101010000000101000100001
192.168.128.192/26:11000000101010001000000011000000:11111111111111111111111111000000, 11000000101010000000101000100010
192.168.128.192/26:11000000101010001000000011000000:11111111111111111111111111000000, 11000000101010000000101000100011
192.168.128.192/26:11000000101010001000000011000000:11111111111111111111111111000000, 11000000101010001000000000000001
192.168.128.64/27:11000000101010001000000001000000:11111111111111111111111111100000, 11000000101010000000101000100001
192.168.128.64/27:11000000101010001000000001000000:11111111111111111111111111100000, 11000000101010000000101000100010
192.168.128.64/27:11000000101010001000000001000000:11111111111111111111111111100000, 11000000101010000000101000100011
192.168.128.64/27:11000000101010001000000001000000:11111111111111111111111111100000, 11000000101010001000000000000001


Evaluation time: 3.528 ms
Evaluates to plural object of type ( string, string )

The comparison for my scalable query is as follows. For each Subnet element, I split the element on colons (":"), convert the subnet address and masks back from a string to a bit set, and compare it to each element of the IP address set (also converting that back from a string to a bit set). Then I keep a count of the number of IP addresses that matched the bit set.

(
    preceding text of first ":" of item 0 of it  /* Subnet Address in CIDR readable form */
   , number of 
     (
              /* For this Subnet, count the number of matching IP addresses in the IP Address Bitset string set */
         item 1 of it  /* Subnet Address Bitset */
       , item 2 of it /* Subnet Mask Bitset */
       , elements of item 3 of it  /* Loop through each IP Address bitset string */
     ) whose (
                   /* Compare this IP Address to the Subnet */
                   /* IP Address Bitset AND Subnet Mask Bitset = Subnet Address Bitset */
                bit set (item 2 of it) * item 1 of it = item 0 of it
             )
) of 
(
         /* For each element of the Subnet Address Bitset, split out the Subnet CIDR Address, the Subnet Address Bitset, and the Subnet Mask Bitset */
    item 0 of it
  , bit set (preceding text of first ":" of following text of first ":" of item 0 of it)
  , bit set (following text of first ":" of following text of first ":" of item 0 of it)
  , item 1 of it   /* Set of IP Address Bitset strings */
) of 
(
       /* Iterate through each element of the Subnet String Set */
    elements of item 0 of it
  , item 1 of it
) of 
(
    set of (
            /* Create a delimited string set of Subnet CIDR:Subnet Address Bitset:Subnet Mask Bitset */
      item 0 of it 
    & ":" 
    & /* Subnet as bit set*/  item 1 of it as string 
    & ":" 
    & /* subnet mask as bit set */ item 2 of it as string
    ) of 
    (
      it  /* Preserve the original subnet CIDR address in readable form */
    , (
       (
             /* Combine the subnet octet bitsets into one subnet address bitset */
         item 0 of it 
       + item 1 of it 
       + item 2 of it 
       + item 3 of it
       ) of 
       (
             /* Shift each subnet address octet bitset into their positions */
         left shift 24 of item 0 of it
       , left shift 16 of item 1 of it
       , left shift 8 of item 2 of it
       , item 3 of it
       ) of 
        (
                 /* Split the Subnet Address octets and convert each octet to a bitset */
          tuple string item 0 of it as integer as bit set
        , tuple string item 1 of it as integer as bit set
        , tuple string item 2 of it as integer as bit set
        , tuple string item 3 of it as integer as bit set
        ) of concatenation ", " of substrings separated by "." of preceding text of first "/" of it  /* Subnet Addresses */
      )
    , (
              /* Create a bitset of the subnet mask */
        (bit set (it)) of  
          (
             /* Create a string representing the subnet mask bits */
            concatenation of  "1" of integers in (1,(it)) 
          & concatenation of "0" of integers in (31, it)
          ) of (it as integer) of following text of it /* Subnet CIDR mask */
      ) of first "/" of it
    ) of (
      (   /* Subnet CIDR Addresses */
          "192.168.1.0/24"
        ; "192.168.2.0/25"
        ; "192.168.2.128/25"
        ; "192.168.3.0/25"
        ; "192.168.3.128/26"
        ; "192.168.3.192/26"      )
      )
             /* Create a set of IP Address Bitset Strings - so we only retrieve/convert IP addresses one time */
    , set of (it as string) of 
      (
       
         (
           (
                 /* Combine the IP Address octet bitsets via bitwise OR */
              item 0 of it
            + item 1 of it
            + item 2 of it
            + item 3 of it
           ) of 
           (
                 /* Shift each IP address octet bitset into its correct position */
             left shift 24 of item 0 of it
           , left shift 16 of item 1 of it
           , left shift 8 of item 2 of it
           , item 3 of it
           ) of 
           (
                   /* Separate the octets of the IP address, and make a bitset of each octet */
             tuple string item 0 of it as integer as bit set
           , tuple string item 1 of it as integer as bit set
           , tuple string item 2 of it as integer as bit set
           , tuple string item 3 of it as integer as bit set
           ) of concatenation ", " of substrings separated by "." of it /* IP Addresses */
         )
) of (
              /* IP Address list */
        "192.168.1.50"
      ; "192.168.2.16"
      ; "192.168.2.220"
      ; "192.168.3.10"
      ; "192.168.3.110"
      ; "192.168.3.224"         
     )
)

192.168.1.0/24, 1
192.168.2.0/25, 1
192.168.2.128/25, 1
192.168.3.0/25, 2
192.168.3.128/26, 0
192.168.3.192/26, 1


Evaluation time: 1.701 ms
Evaluates to plural object of type ( substring, bit set, bit set, integer )

Even at this very small scale, this query is much more efficient to execute.

I hope this challenge is interesting or useful to you at some point. Comparing IP addresses is itself a real-world use case, but other use cases come to mind; such as evaluating the UserAccountControl value of a Windows user account (where each bit of the value represents an attribute such as “Account Disabled”, “Password Change Required”, etc.) or in Registry values such as Meltdown / Spectre mitigations, where each Bit of the registry value represents a mitigation to enable or disable.

4 Likes

As soon as it seemed like the answer was going to require use of a Bit Set, I gave up :slight_smile:

I got much further working on decoding a bit set in Windows Security Center, but even then I ended up giving up before getting the answer. I couldn’t find a better answer than this anywhere on the web because to get the real docs from Microsoft you have to be under NDA: https://stackoverflow.com/a/23641299/861745

Alternatively, you could convert those bits to strings and use Starts With to match your supernetting. (you do have to 0 pad to insure 8 places in your octets…)

q: (concatenation of ((last 8 of padded string of (it as integer as bit set)) as string) of  substrings separated by "." of it) of ("192.168.0.1";"192.168.0.2";"0.0.0.1")
A: 11000000101010000000000000000001
A: 11000000101010000000000000000010
A: 00000000000000000000000000000001

for a total solution of

q: (elements of item 0 of it, elements of item 1 of it) whose (concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of item 0 of it starts with (first ((it as integer) of following text of first "/" of it)of it) of (concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of preceding text of first "/" of it & "/" & following text of first "/" of it) of item 1 of it)  of (set of ("192.168.1.50";"192.168.2.16";"192.168.2.220";"192.168.3.10";"192.168.3.110";"192.168.3.224"), set of("192.168.1.0/24";"192.168.2.0/25";"192.168.2.128/25";"192.168.3.0/25";"192.168.3.128/26";"192.168.3.192/26"))
A: 192.168.1.50, 192.168.1.0/24
A: 192.168.2.16, 192.168.2.0/25
A: 192.168.2.220, 192.168.2.128/25
A: 192.168.3.10, 192.168.3.0/25
A: 192.168.3.110, 192.168.3.0/25
A: 192.168.3.224, 192.168.3.192/26

And then to count them

q: (it, multiplicity of it) of unique values of items 1 of (elements of item 0 of it, elements of item 1 of it) whose (concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of item 0 of it starts with (first ((it as integer) of following text of first "/" of it)of it) of (concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of preceding text of first "/" of it & "/" & following text of first "/" of it) of item 1 of it)  of (set of ("192.168.1.50";"192.168.2.16";"192.168.2.220";"192.168.3.10";"192.168.3.110";"192.168.3.224"), set of("192.168.1.0/24";"192.168.2.0/25";"192.168.2.128/25";"192.168.3.0/25";"192.168.3.128/26";"192.168.3.192/26"))
A: 192.168.1.0/24, 1
A: 192.168.2.0/25, 1
A: 192.168.2.128/25, 1
A: 192.168.3.0/25, 2
A: 192.168.3.192/26, 1

And since I realized that I was dropping the 0 rows, I went back and used Jason’s technique to break the sets apart into elements in 2 phases.

q: (item 0 of it, number of (item 0 of it , elements of item 1 of it) whose ((concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of item 1 of it) starts with ((first ((it as integer) of following text of first "/" of it) of it) of (concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of preceding text of first "/" of it & "/" & following text of first "/" of it) of item 0 of it))) of (elements of item 0 of it , item 1 of it) of (set of("192.168.1.0/24";"192.168.2.0/25";"192.168.2.128/25";"192.168.3.0/25";"192.168.3.128/26";"192.168.3.192/26") , set of ("192.168.1.50";"192.168.2.16";"192.168.2.220";"192.168.3.10";"192.168.3.110";"192.168.3.224")) 
A: 192.168.1.0/24, 1
A: 192.168.2.0/25, 1
A: 192.168.2.128/25, 1
A: 192.168.3.0/25, 2
A: 192.168.3.128/26, 0
A: 192.168.3.192/26, 1
2 Likes

Based on @brolly33’s improvement in converting from IP address to bitset string, I have refined my version further as well. Mine still uses bitset comparisons rather than string comparison, just so we have some different options here. I also don’t bother packing the subnet calculations into a string set and then unpacking them again, which I think makes it at least a little bit more readable.

(
	    item 0 of it  /* Subnet CIDR Address */
	           /* count how many IP address bitset string elements match this subnet */
	           /* IP Address AND Subnet Mask = Subnet Address bitset */
	  , number of (
	         item 1 of it  /* subnet address bitset string */
	       , item 2 of it  /* subnet mask bitset string */
	       , elements of item 3 of it  /* ip address bitset strings */
	       ) whose (bit set (item 2 of it) * item 1 of it = item 0 of it)
) of

	(
	  /* Derive bitsets for subnets */
	  item 0 of it  /* subnet CIDR address */
	  ,  bit set (concatenation of ((last 8 of padded string of (it as integer as bit set)) as string) of substrings separated by "." of preceding text of first "/" of item 0 of it) 
	  ,  bit set ((concatenation of  "1" of integers in (1,(it)) & concatenation of "0" of integers in (31, it)) of (following text of first "/" of item 0 of it as integer))
	  , item 1 of it /* preserve IP address set of bitset strings */
	) of
	
	(
	   /* unwind subnet CIDR strings */
	   elements of item 0 of it
	   , item 1 of it
	) of
	
	(
	  item 0 of it   /* preserve subnet CIDR strings */
	         /* Convert set of IP addresses into a set of IP Address Bitsets */
	  ,  set of (it as string) of 
	       (concatenation of ((last 8 of padded string of (it as integer as bit set)) as string) of substrings separated by "." of it)  of elements of item 1 of it
	)
	of 
	
	(
	  set of (   /* Subnet CIDR Addresses */
	          "192.168.1.0/24"
	        ; "192.168.2.0/25"
	        ; "192.168.2.128/25"
	        ; "192.168.3.0/25"
	        ; "192.168.3.128/26"
	        ; "192.168.3.192/26"
	        ; "0.1.0.0/27"
	      )
	
	 , set of (  /* IP Addresses */
	        "192.168.1.50"
	      ; "192.168.2.16"
	      ; "192.168.2.220"
	      ; "192.168.3.10"
	      ; "192.168.3.110"
	      ; "192.168.3.224"
	      ; "0.1.0.1"
	      )
)

edit: One minor change to the relevance on this one - now when I calculate the Subnet Address and Subnet Mask, I cost those as a bit set. This is so that when I’m making the comparison to each IP address element, I don’t have to repeatedly convert the subnet values into a bitset for every IP address comparison. This is slightly more efficient, but admittedly I had to scale my sample data up to 255 subnets and 255 IP addresses to see a 14 ms improvement.

2 Likes

@jgstew

Here is one solve for the "given a CIDR range, find the start/end IP"
I used string parsing, but I bet @JasonWalker could do it with bit rotations…

q:  ((concatenation "." of (bit sets (it as string) as integer as string) of firsts 8 of following texts of positions whose (it mod 8 = 0) of item 0 of it & " - " & concatenation "." of (bit sets (it as string) as integer as string) of firsts 8 of following texts of positions whose (it mod 8 = 0) of item 1 of it) of  (first 32 of (it & concatenation of "0" of (integers in (31,0))), first 32 of (it & concatenation of "1" of (integers in (31,0)))) of (first ((it as integer) of following text of first "/" of it)of it) of (concatenation of (last 8 of padded string of (it as integer as bit set)) of substrings separated by "." of preceding text of first "/" of it & "/" & following text of first "/" of it))  of "192.168.2.0/25"
A: 192.168.2.0 - 192.168.2.127
T: 0.562 ms
2 Likes

I’ll leave that one as an exercise for the reader :slight_smile:

I’m confident it’s possible though, and a good start might be to inverse the subnet mask. Given we know how to calculate a subnet mask bitset already, we can assume that 255.255.255.0 (or /24) is equal to

q: bit set ("11111111111111111111111100000000") 
A: 11111111111111111111111100000000
T: 0.130 ms
I: singular bit set

So the Network part of the address are those bits with “1” and the Subnet part of the network are those with “0”. We can negate the bit set to find just the local network address range with the minus operator. First we build a bit set of 1’s for the full 32-bit range, and subtract our subnet mask from that.

q: (bit set (concatenation of "1" of integers in (1, 32)) - bit set ("11111111111111111111111100000000") )
A: 11111111

And we can find the integer value of that resulting bit set

q: (it as integer) of (bit set (concatenation of "1" of integers in (1, 32)) - bit set ("11111111111111111111111100000000") )
A: 255
T: 0.103 ms
I: singular integer

This tells us that a /24 network has 255 possible IP addresses on it. If we repeat that all again with a /25 subnet, we’d find 127 available addresses, and so on.

1 Like

Ok so I couldn’t leave it alone. Here’s a quick calculation for every possible subnet mask, showing the CIDR notation, the number of possible hosts on the subnet, and the subnet mask bits:

q: ("/" & item 0 of it as string, (it as integer) of (bit set (concatenation of "1" of integers in (1, 32)) - bit set (item 1 of it)), item 1 of it) of (it, (concatenation of  "1" of integers in (1,(it)) & concatenation of "0" of integers in (31, it))) of (integers in (31, 1))
A: /31, 1, 11111111111111111111111111111110
A: /30, 3, 11111111111111111111111111111100
A: /29, 7, 11111111111111111111111111111000
A: /28, 15, 11111111111111111111111111110000
A: /27, 31, 11111111111111111111111111100000
A: /26, 63, 11111111111111111111111111000000
A: /25, 127, 11111111111111111111111110000000
A: /24, 255, 11111111111111111111111100000000
A: /23, 511, 11111111111111111111111000000000
A: /22, 1023, 11111111111111111111110000000000
A: /21, 2047, 11111111111111111111100000000000
A: /20, 4095, 11111111111111111111000000000000
A: /19, 8191, 11111111111111111110000000000000
A: /18, 16383, 11111111111111111100000000000000
A: /17, 32767, 11111111111111111000000000000000
A: /16, 65535, 11111111111111110000000000000000
A: /15, 131071, 11111111111111100000000000000000
A: /14, 262143, 11111111111111000000000000000000
A: /13, 524287, 11111111111110000000000000000000
A: /12, 1048575, 11111111111100000000000000000000
A: /11, 2097151, 11111111111000000000000000000000
A: /10, 4194303, 11111111110000000000000000000000
A: /9, 8388607, 11111111100000000000000000000000
A: /8, 16777215, 11111111000000000000000000000000
A: /7, 33554431, 11111110000000000000000000000000
A: /6, 67108863, 11111100000000000000000000000000
A: /5, 134217727, 11111000000000000000000000000000
A: /4, 268435455, 11110000000000000000000000000000
A: /3, 536870911, 11100000000000000000000000000000
A: /2, 1073741823, 11000000000000000000000000000000
A: /1, 2147483647, 10000000000000000000000000000000
6 Likes

/31 the loneliest CIDR

2 Likes

Indeed …I don’t think /31 is actually valid as a network mask, but I was captivated by the pretty ASCII pattern and didn’t want to cut off the first row.

1 Like

Hi all,

Anyone have a variant of this that could function as
exists THIS ip address within THAT cidr range ?

or, exists ip address within THESE cidr ranges?

The remoteworkpocalypse has rather altered the priority of shaping prefetch downloads around/through different VPNs.

If I understand the case correctly…I think we have a direct inspector to return the CIDR subnet addresses of the client.