How to parse IP range in CIDR format in C

Hello everybody,

I'm coding a network program and i need it to "understand" ip ranges, but i don't know how to make to parse an IP CIDR range, let's say "172.16.10.0/24" to work with the specified IP range.

I've found a program which does it, but i don't understand the code. Here is the function:

static int parse_iprange(char *p)
{
    char *e;
    u32 ip=0;
    int sh;

    for(sh = 24;; sh -= 8) {
        unsigned long v;

        v = strtoul(p, &e, 10);
        if(p == e || v > 255)
            return 0;

        ip |= v << sh;

        p = e + 1;
        if(*e == '/') {
            v = strtoul(p, &e, 10);
            if(p == e || *e || v > 32)
                return 0;
            if(v) {
                v = 32 - v;
                if(sh > v)
                    return 0;
mask:
                v = ~0 << v;
            }
            scan.start = ip & v;
            scan.end = scan.start - v;
            return 1;
        }

        if(!sh) break;

        v = sh;
        if(!*e)
            goto mask;

        if(*e != '.')
            return 0;

        if(!*p || *p == '*' && !p[1])
            goto mask;
    }

    scan.start = ip;
    scan.end = ip + 1;

    if(*e == '-') {
        u32 end = 0, m = ~0;

        do {
            unsigned long v = strtoul(p, &e, 10);
            if(p == e || v > 255)
                return 0;
            p = e + 1;
            end = end<<8 | v;
            m <<= 8;
        } while(m && *e);

        if(*e)
            return 0;

        end |= ip & m;
        if(end < ip)
            return 0;

        scan.end = end + 1;
        return 1;
    }
    return *e == 0;
}

So, please, can somebody explain me how to do this or at least, what does this function do?

Thanks!

I was bored, so I tried it myself. Here's the complete program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

void fatal()
{
	fprintf(stderr, "Usage: cidr2range ip/netmask\n");
	exit(EXIT_FAILURE);
}

unsigned int ip2ui(char *ip)
{
	/* An IP consists of four ranges. */
	long ipAsUInt = 0;
	/* Deal with first range. */
	char *cPtr = strtok(ip, ".");
	if(cPtr) ipAsUInt += atoi(cPtr) * pow(256, 3);

	/* Proceed with the remaining ones. */
	int exponent = 2;
	while(cPtr && exponent >= 0)
	{
		cPtr = strtok(NULL, ".\0");
		if(cPtr) ipAsUInt += atoi(cPtr) * pow(256, exponent--);
	}

	return ipAsUInt;
}

char *ui2ip(unsigned int ipAsUInt)
{
	char *ip = malloc(16*sizeof(char));
	int exponent;
	for(exponent = 3; exponent >= 0; --exponent)
	{
		int r = ipAsUInt / pow(256, exponent);
		char buf[4];
		sprintf(buf, "%d", r);
		strcat(ip, buf);
		strcat(ip, ".");
		ipAsUInt -= r*pow(256, exponent);
	}
	/* Replace last dot with '\0'. */
	ip[strlen(ip)-1] = 0;
	return ip;
}

unsigned int createBitmask(const char *bitmask)
{
	unsigned int times = (unsigned int)atol(bitmask)-1, i, bitmaskAsUInt = 1;
	/* Fill in set bits (1) from the right. */
	for(i=0; i<times; ++i)
	{
		bitmaskAsUInt <<= 1;
		bitmaskAsUInt |= 1;
	}
	/* Shift in unset bits from the right. */
	for(i=0; i<32-times-1; ++i)
		bitmaskAsUInt <<= 1;
	return bitmaskAsUInt;
}

int main(int argc, char **argv)
{
	/* Correct call? */
	if(argc!=2) fatal();
	
	/* Split arguments and terminate application if wrong format. */
	char *ip, *bitmask;
	ip = strtok(argv[1], "/");
	if(!ip) fatal();
	bitmask = strtok(NULL, "\0");
	if(!bitmask) fatal();
	
	/* Convert the ASCII strings to workable integers.
	 * The inet_addr() function cannot be used because
	 * the resulting integer is in NBO.
	*/
	unsigned int ipAsUInt = ip2ui(ip);
	unsigned int bitmaskAsUInt = createBitmask(bitmask);

	char *networkAddress = ui2ip(ipAsUInt & bitmaskAsUInt),
		 *broadcastAddress = ui2ip(ipAsUInt | ~bitmaskAsUInt);
	printf("IP range spans from %s to %s (Network and broadcast addresses inclusive)\n", networkAddress, broadcastAddress);
	free(networkAddress);
	free(broadcastAddress);
	return 0;
}

Hello,
Thanks for your time, i've been testing your code and it has a bug, it works perfectly with any address starting with "128.x.x.x" and lower, but, when you try and address greater than 128, you'll always get the same result, for example, if you input "192.168.1.1/24", the program will show you up "128.168.1.0".

Might it be the power functions incremented by three?

I just tried and here's the (correct) result:

Can you take a look again?