• CIDR Calculator

    From Joe Makowiec@makowiec@invalid.invalid to comp.os.linux.misc on Mon Apr 27 19:22:04 2026
    From Newsgroup: comp.os.linux.misc

    I know - there are a bunch of 'em out there, including one I use
    regularly. (For that matter, just figure it out on your fingers...)
    What I'm looking for, though, is one where you can feed it a low IP
    address and a high IP address, and it will figure out the (singular)
    range which covers all the IP addresses. So, for example, you feed it 192.168.0.0 and 192.168.1.255, and it gives you 192.168.0.0/23. The one
    I currently use can do that with no problem. However, if you feed it
    something like 192.168.0.8 to 192.168.5.200, it gives back a bunch of
    ranges with a number of /29s and /26s, a couple of /24s and a total of
    a dozen ranges. What i want is a calculator which will look at the high
    and the low, and figure out that 192.168.0.0/21 will cover the whole
    thing.

    Added useless information: what prompts this request is that my
    mailserver has been getting hit with a bunch of sasl hack attempts,
    many of which come from Bharti Airtel, which I assume is an ISP or a
    wireless / cellphone provider. I have to admire them for the beauty of
    their distributed attack, but I still want to swat them, and they're
    mostly smart enough to evade fail2ban. So I would be more than happy to
    just ban them at the firewall level, and this would make figuring out
    the range to ban easier.
    --
    Joe Makowiec
    https://makowiec.org/
    Email: https://makowiec.org/contact/?Joe
    Usenet Improvement Project: https://web.archive.org/web/20070914150106/http://improve-usenet.org/
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From John Ames@commodorejohn@gmail.com to comp.os.linux.misc on Mon Apr 27 13:31:11 2026
    From Newsgroup: comp.os.linux.misc

    On Mon, 27 Apr 2026 19:22:04 -0000 (UTC)
    Joe Makowiec <makowiec@invalid.invalid> wrote:

    However, if you feed it something like 192.168.0.8 to 192.168.5.200,
    it gives back a bunch of ranges with a number of /29s and /26s, a
    couple of /24s and a total of a dozen ranges. What i want is a
    calculator which will look at the high and the low, and figure out
    that 192.168.0.0/21 will cover the whole thing.

    Added useless information: what prompts this request is that my
    mailserver has been getting hit with a bunch of sasl hack attempts,
    many of which come from Bharti Airtel, which I assume is an ISP or a wireless / cellphone provider.

    Funnily enough, I was just having to do this the other way 'round some
    months ago - whitelisting StarLink and its nineteen billion /8s for
    certain customers as we try to block wardialing on our RDP servers. If
    it helps, this is the utility I came up with - not *exactly* what you specified, but it'll sort a list of CIDRs and combine contiguous ranges
    into single entries.

    Written in FreeBasic. Usage: melonballer [in-file] [out-file]

    ### melonballer.bas ###

    type cidr
    ip as ulong
    r as ubyte
    end type

    function parseCider(s as string) as cidr
    dim as string z
    dim as integer i, j
    dim as ubyte q(5)
    dim as cidr c
    z = ""
    j = 0
    for i = 1 to len(s)
    if asc(mid(s,i,1)) >= asc("0") and asc(mid(s,i,1)) <= asc("9") then
    z = z & mid(s,i,1)
    else
    q(j) = val(z)
    j += 1
    z = ""
    endif
    next
    if z <> "" then
    q(j) = val(z)
    j += 1
    endif
    if j < 5 or q(4) > 31 then
    c.ip = 0
    c.r = 32
    else
    c.ip = q(0) shl 24
    c.ip = c.ip or (q(1) shl 16)
    c.ip = c.ip or (q(2) shl 8)
    c.ip = c.ip or q(3)
    c.r = q(4)
    endif
    parseCider = c
    end function

    function squeezeCider(ipBase as ulong, ipBound as ulong) as cidr
    ' Returns a CIDR covering the given range.
    dim as cidr c
    dim as ulong i
    c.ip = ipBase
    i = ipBound - ipBase
    c.r = 0
    do while i > 1
    i = i shr 1
    c.r += 1
    loop
    c.r = 32 - c.r
    squeezeCider = c
    end function

    function ipMin(c as cidr) as ulong
    dim as long i
    i = -1 shl (32 - c.r)
    ipMin = c.ip and i
    end function

    function ipMax(c as cidr) as ulong
    ipMax = c.ip + (1 shl (32 - c.r)) - 1
    end function

    function contiguous(foo as cidr, bar as cidr) as Boolean
    ' Returns true if foo comes after bar.
    contiguous = false
    if ipMax(bar) + 1 = foo.ip then contiguous = true
    end function

    function contained(foo as cidr, bar as cidr) as Boolean
    ' Returns true if foo falls within bar.
    contained = false
    if foo.ip >= bar.ip and ipMax(foo) <= ipMax(bar) then contained = true
    end function

    function powerOfTwo(i as ulong) as Boolean
    ' Returns true if i is a power of two.
    dim as ulong j
    powerOfTwo = false
    if i = 1 then powerOfTwo = true
    if i <= 1 then exit function
    j = 1 shl 31
    do while ((i and j) = 0) and j <> 0
    j = j shr 1
    loop
    j -= 1
    if ((i and j) = 0) and j <> 0 then powerOfTwo = true
    end function

    function printCider(c as cidr) as string
    dim as string s
    s = ""
    s &= str(c.ip shr 24) & "." & str((c.ip shr 16) and 255) & "." & str((c.ip shr 8) and 255) & "." & str(c.ip and 255) & "/" & str(c.r)
    printCider = s
    end function

    function printIp(ip as ulong) as string
    dim as string s
    s = ""
    s &= str(ip shr 24) & "." & str((ip shr 16) and 255) & "." & str((ip shr 8) and 255) & "." & str(ip and 255)
    printIp = s
    end function


    open command(1) for input as 1

    dim as integer i, j, k, bookmark
    dim as cidr cList(), oList(), c
    dim as string s

    ' Build the input list.

    do while not eof(1)
    line input #1, s
    c = parseCider(trim(s))
    if ubound(cList) < 0 then
    redim cList(0)
    cList(0) = c
    continue do
    endif
    redim preserve cList(ubound(cList) + 1)
    cList(ubound(cList)) = c
    loop

    close 1

    ' Build the output list.

    redim oList(0)
    oList(0) = cList(0)

    for i = 1 to ubound(cList)
    for j = 0 to uBound(oList)
    ' Have we already covered this range?
    if contained(cList(i), oList(j)) then goto skipEntry
    if contiguous(cList(i), oList(j)) then
    ' We've hit a block of contiguous addresses - see how far it goes before we can't combine them.
    bookmark = i
    k = i + 1
    do while k <= ubound(cList)
    ' If we're no longer contiguous; stop searching.
    if not contiguous(cList(k), cList(k - 1)) then exit do
    ' Is the range we're covering a power of two?
    if powerOfTwo((ipMax(cList(k)) + 1) - oList(j).ip) then bookmark = k
    k += 1
    loop
    ' The range from oList(j) to cList(bookmark) is contiguous and power-of-two-sized; combine it.
    s = printCider(oList(j))
    for k = i to bookmark
    s &= ", " & printCider(cList(k))
    next
    c = squeezeCider(oList(j).ip, ipMax(cList(bookmark)) + 1)
    if ipMin(c) = oList(j).ip and ipMax(c) = ipMax(cList(bookmark)) then
    oList(j) = c
    s &= " -> " & printCider(c)
    print s
    print "Min: "; printIp(ipMin(cList(i))), "Max: "; printIp(ipMax(cList(bookmark))), printCider(c); ": "; printIp(ipMin(c)); " - "; printIp(ipMax(c))
    print
    ' Advance past cList(bookmark).
    i = bookmark
    endif
    goto skipEntry
    endif
    next j
    ' If we've gotten this far and have neither skipped nor combined, add it to the output list.
    redim preserve oList(ubound(oList) + 1)
    oList(ubound(oList)) = cList(i)
    skipEntry:
    next i

    ' Write the output list.

    open command(2) for output as 1

    for i = 0 to ubound(oList)
    print #1, printCider(oList(i))
    next

    close 1

    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.os.linux.misc on Mon Apr 27 22:35:21 2026
    From Newsgroup: comp.os.linux.misc

    On Mon, 27 Apr 2026 19:22:04 -0000 (UTC), Joe Makowiec wrote:

    What I'm looking for, though, is one where you can feed it a low IP
    address and a high IP address, and it will figure out the (singular)
    range which covers all the IP addresses. So, for example, you feed it 192.168.0.0 and 192.168.1.255, and it gives you 192.168.0.0/23. The one
    I currently use can do that with no problem. However, if you feed it something like 192.168.0.8 to 192.168.5.200, it gives back a bunch of
    ranges with a number of /29s and /26s, a couple of /24s and a total of
    a dozen ranges. What i want is a calculator which will look at the high
    and the low, and figure out that 192.168.0.0/21 will cover the whole
    thing.

    #+
    # Given a pair of IPv4 addresses in dotted-quad form, works out the
    # minimal single subnet that covers both addresses.
    #
    # Usage:
    #
    # min_subnet «a1» «a2»
    #
    # where «a1» and «a2» are two IPv4 addresses in the usual dotted-quad
    # form. The output will show the subnet mask and the complete range
    # of covered addresses.
    #
    # Written in response to a question on comp.os.linux.misc
    # from Joe Makowiec on 2026 April 28.
    #-

    import sys

    def dotted_to_int(s) :
    # converts (assumed) string a.b.c.d address form to unsigned 32-bit integer.
    # quick-and-dirty function with no real validation.
    return sum \
    (
    n
    for i, c in enumerate(reversed(s.split(".")))
    for n in (int(c, 10) << i * 8,)
    )
    #end dotted_to_int

    def int_to_dotted(i) :
    # converts (assumed) unsigned 32-bit integer to string a.b.c.d address form.
    return ".".join \
    (
    "%d" % (i >> s * 8 & 255)
    for s in range(3, -1, -1)
    )
    #end int_to_dotted

    def find_common_mask(i, j) :
    # returns the number of topmost bits that the 32-bit integers i
    # and j have in common.
    s = 32
    while True :
    if s == 0 :
    break
    s -= 1
    m = 1 << s
    if i & m != j & m :
    s += 1
    break
    #end if
    #end while
    return 32 - s
    #end find_common_mask

    #+
    # Mainline
    #-

    a1, a2 = sys.argv[1:]
    ia1 = dotted_to_int(a1)
    ia2 = dotted_to_int(a2)
    shift = find_common_mask(ia1, ia2)
    lo = ia1 & (1 << 32) - (1 << 32 - shift)
    hi = lo | (1 << 32 - shift) - 1
    sys.stdout.write \
    (
    "%s .. %s => %s .. %s => %s/%d\n"
    %
    (
    int_to_dotted(ia1),
    int_to_dotted(ia2),
    int_to_dotted(lo),
    int_to_dotted(hi),
    int_to_dotted(lo),
    shift,
    )
    )
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Marc Haber@mh+usenetspam2616@zugschl.us to comp.os.linux.misc on Tue Apr 28 07:49:48 2026
    From Newsgroup: comp.os.linux.misc

    Joe Makowiec <makowiec@invalid.invalid> wrote:
    I know - there are a bunch of 'em out there, including one I use
    regularly. (For that matter, just figure it out on your fingers...)
    What I'm looking for, though, is one where you can feed it a low IP
    address and a high IP address, and it will figure out the (singular)
    range which covers all the IP addresses. So, for example, you feed it >192.168.0.0 and 192.168.1.255, and it gives you 192.168.0.0/23. The one
    I currently use can do that with no problem. However, if you feed it >something like 192.168.0.8 to 192.168.5.200, it gives back a bunch of
    ranges with a number of /29s and /26s, a couple of /24s and a total of
    a dozen ranges. What i want is a calculator which will look at the high
    and the low, and figure out that 192.168.0.0/21 will cover the whole
    thing.

    so, echo "0.0.0.0/0" will do the job?

    Added useless information: what prompts this request is that my
    mailserver has been getting hit with a bunch of sasl hack attempts,
    many of which come from Bharti Airtel, which I assume is an ISP or a >wireless / cellphone provider. I have to admire them for the beauty of
    their distributed attack, but I still want to swat them, and they're
    mostly smart enough to evade fail2ban. So I would be more than happy to
    just ban them at the firewall level, and this would make figuring out
    the range to ban easier.

    Use whois to find out their AS number, and then a public looking glass
    to find out the prefixes they're announcing.

    Greetings
    Marc
    -- ---------------------------------------------------------------------------- Marc Haber | " Questions are the | Mailadresse im Header Rhein-Neckar, DE | Beginning of Wisdom " |
    Nordisch by Nature | Lt. Worf, TNG "Rightful Heir" | Fon: *49 6224 1600402
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Fritz Wuehler@fritz@spamexpire-202604.rodent.frell.theremailer.net to comp.os.linux.misc on Wed Apr 29 00:08:34 2026
    From Newsgroup: comp.os.linux.misc

    Joe Makowiec <makow...@invalid.invalid> [JM]:
    What I'm looking for, though, is one where you can feed it a
    low IP address and a high IP address, and it will figure out
    the (singular) range which covers all the IP addresses.

    Here is a LLM-generated plain C and javascript/nodejs such
    function. Take your pick.


    // 8<---------------------------- C -------------------------

    /*
    * findMinimumCIDR.c
    *
    * Build the smallest single CIDR block that contains two IPv4 addresses.
    *
    * Example:
    * 192.168.0.8 and 192.168.5.200 -> 192.168.0.0/21
    *
    * Build:
    * cc -O2 -Wall -Wextra -std=c11 findMinimumCIDR.c -o findMinimumCIDR
    *
    * Run:
    * ./findMinimumCIDR 192.168.0.8 192.168.5.200
    */

    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>

    static int parse_ipv4(const char *s, uint32_t *out)
    {
    unsigned a, b, c, d;
    char tail;

    /* Ensure full-string match (no trailing junk) */
    if (sscanf(s, "%u.%u.%u.%u%c", &a, &b, &c, &d, &tail) != 4)
    return 0;
    if (a > 255 || b > 255 || c > 255 || d > 255)
    return 0;

    *out = (a << 24) | (b << 16) | (c << 8) | d;
    return 1;
    }

    static void ipv4_to_str(uint32_t ip, char buf[16])
    {
    snprintf(buf, 16, "%u.%u.%u.%u",
    (ip >> 24) & 255u, (ip >> 16) & 255u, (ip >> 8) & 255u, ip & 255u);
    }

    static unsigned prefix_len_for_range(uint32_t a, uint32_t b)
    {
    uint32_t x = a ^ b; /* differing bits */
    unsigned common = 32;

    if (x != 0) {
    /* count leading zeros in x (portable loop) */
    common = 0;
    for (int i = 31; i >= 0; --i) {
    if ((x >> i) & 1u) break;
    common++;
    }
    }
    return common; /* number of equal leading bits => CIDR prefix length */
    }

    int main(int argc, char **argv)
    {
    if (argc != 3) {
    fprintf(stderr, "Usage: %s <ipv4_1> <ipv4_2>\n", argv[0]);
    return 2;
    }

    uint32_t ip1, ip2;
    if (!parse_ipv4(argv[1], &ip1) || !parse_ipv4(argv[2], &ip2)) {
    fprintf(stderr, "Invalid IPv4 address.\n");
    return 2;
    }

    unsigned prefix = prefix_len_for_range(ip1, ip2);

    uint32_t mask = (prefix == 0) ? 0u : (0xFFFFFFFFu << (32 - prefix));
    uint32_t network = ip1 & mask; /* ip1 and ip2 share these prefix bits */

    char netstr[16];
    ipv4_to_str(network, netstr);
    printf("%s/%u\n", netstr, prefix);

    return 0;
    }



    // 8<--------------------- javascript/nodejs ----------------

    /* How it works:

    1. IP to Integer Conversion: Converts each IPv4 address to a 32-bit integer for bitwise operations.

    2. XOR Operation: Uses XOR (`^`) to find where the two IP addresses differ in their binary representation.

    3. Prefix Length Calculation: Counts the number of leading bits that are the same in both addresses by examining the XOR result.

    4. Network Address Calculation: Creates a network mask and applies it to find the network address.

    5. CIDR Format: Returns the result in CIDR notation (network/prefix-length).

    Example walkthrough with `192.168.0.8` and `192.168.5.200`:

    - 192.168.0.8 = 11000000.10101000.00000000.00001000
    - 192.168.5.200 = 11000000.10101000.00000101.11001000
    - XOR result shows differences starting at bit 21
    - First 21 bits are common, so prefix length is 21
    - Network address: `192.168.0.0/21`

    The function handles the parameters in any order and will always return the same CIDR range regardless of which IP is passed first.
    */

    function findMinimumCIDR(ip1, ip2) {
    // Convert IP address string to 32-bit integer
    function ipToInt(ip) {
    return ip.split('.').reduce((acc, octet, index) => {
    return acc + (parseInt(octet) << (8 * (3 - index)));
    }, 0) >>> 0; // Use unsigned right shift to ensure positive integer
    }

    // Convert 32-bit integer back to IP address string
    function intToIp(int) {
    return [
    (int >>> 24) & 255,
    (int >>> 16) & 255,
    (int >>> 8) & 255,
    int & 255
    ].join('.');
    }

    // Convert both IPs to integers
    const int1 = ipToInt(ip1);
    const int2 = ipToInt(ip2);

    // Find the XOR to see where bits differ
    const xor = int1 ^ int2;

    // Count leading zeros in the XOR result to find common prefix length
    let prefixLength = 0;
    let mask = 0x80000000; // Start with leftmost bit

    for (let i = 0; i < 32; i++) {
    if ((xor & mask) === 0) {
    prefixLength++;
    mask >>>= 1;
    } else {
    break;
    }
    }

    // Create network mask
    const networkMask = (0xFFFFFFFF << (32 - prefixLength)) >>> 0;

    // Calculate network address (use the smaller IP for consistency)
    const networkAddress = Math.min(int1, int2) & networkMask;

    return `${intToIp(networkAddress)}/${prefixLength}`;
    }

    // Testing
    console.log(findMinimumCIDR('192.168.0.8', '192.168.5.200')); // 192.168.0.0/21
    console.log(findMinimumCIDR('192.168.5.200', '192.168.0.8')); // 192.168.0.0/21

    // Additional test cases
    console.log(findMinimumCIDR('10.0.0.1', '10.0.0.2')); // 10.0.0.0/30 console.log(findMinimumCIDR('192.168.1.1', '192.168.1.1')); // 192.168.1.1/32
    console.log(findMinimumCIDR('172.16.0.1', '172.17.255.254')); // 172.16.0.0/15

    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.os.linux.misc on Wed Apr 29 01:59:21 2026
    From Newsgroup: comp.os.linux.misc

    On Mon, 27 Apr 2026 13:31:11 -0700, John Ames wrote:

    function powerOfTwo(i as ulong) as Boolean
    ' Returns true if i is a power of two.

    power_of_two = lambda n : n & n - 1 == 0

    list(enumerate(power_of_two(i) for i in range(17)))

    [(0, True),
    (1, True),
    (2, True),
    (3, False),
    (4, True),
    (5, False),
    (6, False),
    (7, False),
    (8, True),
    (9, False),
    (10, False),
    (11, False),
    (12, False),
    (13, False),
    (14, False),
    (15, False),
    (16, True)]
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From jmj@jmj@energokod.gda.pl to comp.os.linux.misc on Wed Apr 29 07:10:26 2026
    From Newsgroup: comp.os.linux.misc

    W dniu 27.04.2026 o 21:22, Joe Makowiec pisze:
    What I'm looking for, though, is one where you can feed it a low IP
    address and a high IP address, and it will figure out the (singular)
    range which covers all the IP addresses.

    It seems to be some easy algorithm exercise:

    Input: IP1, IP2 in the quad-dotted 192.168.xxx.xxx format.
    Data: IPmax = 0; lResult = 16; lMask = 0x10000;
    1. Start: Convert IP1 and IP2 to int;
    2. IPmax = max(IP1, IP2);
    3. lMask >>= 1;
    4. if(lMask) goto 5. else goto 6.;
    5. if((IPmax & lMask) == 0) { ++lResult; goto 3.; } else goto 6.;
    6. print lResult;
    7. Stop.
    --
    Jacek Marcin Jaworski, Pruszcz Gd., woj. Pomorskie, Polska 🇵🇱, EU 🇪🇺;
    tel.: +48-609-170-742, najlepiej w godz.: 5:00-5:55 lub 16:00-17:25; <jmj@energokod.gda.pl>, gpg: 4A541AA7A6E872318B85D7F6A651CC39244B0BFA;
    Domowa s. WWW: <https://energokod.gda.pl>;
    Mini Netykieta: <https://energokod.gda.pl/MiniNetykieta.html>;
    Mailowa Samoobrona: <https://emailselfdefense.fsf.org/pl>.
    UWAGA:
    NIE ZACIĄGAJ "UKRYTEGO DŁUGU"! PŁAĆ ZA PROG. FOSS I INFO. INTERNETOWE! CZYTAJ DARMOWY: "17. Raport Totaliztyczny - Patroni Kontra Bankierzy": <https://energokod.gda.pl/raporty-totaliztyczne/17.%20Patroni%20Kontra%20Bankierzy.pdf>

    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Rene Kita@mail@rkta.de to comp.os.linux.misc on Wed Apr 29 07:09:21 2026
    From Newsgroup: comp.os.linux.misc

    Marc Haber <mh+usenetspam2616@zugschl.us> wrote:
    Joe Makowiec <makowiec@invalid.invalid> wrote:
    [...]
    Use whois to find out their AS number, and then a public looking glass
    to find out the prefixes they're announcing.

    What would be a trustworthy looking glass for this? Ideally one that can
    be used in a scriptable way.
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.os.linux.misc on Wed Apr 29 08:05:01 2026
    From Newsgroup: comp.os.linux.misc

    On Wed, 29 Apr 2026 07:09:21 -0000 (UTC), Rene Kita wrote:

    Marc Haber <mh+usenetspam2616@zugschl.us> wrote:

    Use whois to find out their AS number, and then a public looking
    glass to find out the prefixes they're announcing.

    What would be a trustworthy looking glass for this? Ideally one that
    can be used in a scriptable way.

    Some scriptability, and pointers to suitable data sources, here <https://github.com/hadiasghari/pyasn>.
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From jmj@jmj@energokod.gda.pl to comp.os.linux.misc on Wed Apr 29 23:52:58 2026
    From Newsgroup: comp.os.linux.misc

    W dniu 29.04.2026 o 07:10, 🇵🇱Jacek Marcin Jaworski🇵🇱 pisze:
    1. Start: Convert IP1 and IP2 to int;

    Should be:
    1. Start: Convert IP1 and IP2 to uint;

    NOTE: uint.
    --
    Jacek Marcin Jaworski, Pruszcz Gd., woj. Pomorskie, Polska 🇵🇱, EU 🇪🇺;
    tel.: +48-609-170-742, najlepiej w godz.: 5:00-5:55 lub 16:00-17:25; <jmj@energokod.gda.pl>, gpg: 4A541AA7A6E872318B85D7F6A651CC39244B0BFA;
    Domowa s. WWW: <https://energokod.gda.pl>;
    Mini Netykieta: <https://energokod.gda.pl/MiniNetykieta.html>;
    Mailowa Samoobrona: <https://emailselfdefense.fsf.org/pl>.
    UWAGA:
    NIE ZACIĄGAJ "UKRYTEGO DŁUGU"! PŁAĆ ZA PROG. FOSS I INFO. INTERNETOWE! CZYTAJ DARMOWY: "17. Raport Totaliztyczny - Patroni Kontra Bankierzy": <https://energokod.gda.pl/raporty-totaliztyczne/17.%20Patroni%20Kontra%20Bankierzy.pdf>

    --- Synchronet 3.21f-Linux NewsLink 1.2