bb2d391712fb0208347ffc0b88abe146df14dc0a galt Fri Jun 21 18:21:50 2019 -0700 Adding Support for CIDR specification of subnets, e.g. 192.168.1.255/31. It still supports the older subnet format too, e.g. 192.168 diff --git src/lib/internet.c src/lib/internet.c index 6a4c673..81cbc51 100644 --- src/lib/internet.c +++ src/lib/internet.c @@ -133,30 +133,132 @@ } } void internetUnpackIp(bits32 packed, unsigned char unpacked[4]) /* Convert from 32 bit to 4-byte format with most significant * byte first. */ { int i; for (i=3; i>=0; --i) { unpacked[i] = (packed&0xff); packed >>= 8; } } -boolean internetIpInSubnet(unsigned char unpackedIp[4], unsigned char subnet[4]) -/* Return true if unpacked IP address is in subnet. */ +bits32 internetPackIp(unsigned char unpacked[4]) +/* Convert from 4-byte format with most significant + * byte first to native 32-bit format. */ { int i; -for (i=0; i<4; ++i) +bits32 packed = 0; +for (i=0; i<=3; ++i) + { + packed <<= 8; + packed |= unpacked[i]; + } +return packed; +} + +void internetCidrRange(struct cidr *cidr, bits32 *pStartIp, bits32 *pEndIp) +/* get range of CIDR formatted subnet as start and end IPs. */ { - unsigned char c = subnet[i]; - if (c == 255) +bits32 packedIp = cidr->ip; +int bits = cidr->subnetLength; +int r = 32 - bits; +bits32 start = packedIp & (((unsigned int) 0xFFFFFFFF) << r); +bits32 end; +// shr or shl 32 of a 32-bit value +// does nothing at all rather than turning it to zeroes. +if (bits == 32) + end = packedIp; +else + end = packedIp | (((unsigned int) 0xFFFFFFFF) >> bits); + +*pStartIp = start; +*pEndIp = end; + +} + +boolean internetIpInSubnetCidr(unsigned char unpackedIp[4], struct cidr *cidr) +/* Return true if unpacked IP address is in subnet cidr. */ +{ +bits32 subnetIp = cidr->ip; +//printf("packed32 bits=%u %08x\n", subnetIp, subnetIp); // DEBUG REMOVE +int r = 32 - cidr->subnetLength; +bits32 caremask = subnetIp & (((unsigned int) 0xFFFFFFFF) << r); + +bits32 packedIp = internetPackIp(unpackedIp); + +if ((subnetIp & caremask) == (packedIp & caremask)) return TRUE; - if (c != unpackedIp[i]) + return FALSE; } -return TRUE; + +static void notGoodSubnetCidr(char *sns) +/* Complain about subnet format. */ +{ +errAbort("'%s' is not a properly formatted subnet. Subnets must consist of\n" + "one to four dot-separated numbers between 0 and 255 \n" + "optionally followed by an slash and subnet bit length integer between 1 and 32.\n" + "A trailing dot on the subnet IP address is not allowed.", sns); +} + +struct cidr *internetParseSubnetCidr(char *cidr) +/* parse input CIDR format IP for range or subnet */ +{ +if (!cidr) + return NULL; +char *s = cloneString(cidr); +char *c = strchr(s, '/'); +char *ip = s; +int bits = -1; +if (c) // has slash + { + *c++ = 0; + bits = atoi(c); + } +if (ip[strlen(ip)-1] == '.') // trailing dot not allowed in subnet ip + notGoodSubnetCidr(cidr); +char *snsCopy = cloneString(ip); +char expIp[17]; +char *words[5]; +int wordCount, i; +expIp[0] = 0; +wordCount = chopByChar(snsCopy, '.', words, ArraySize(words)); +if (wordCount > 4 || wordCount < 1) + notGoodSubnetCidr(cidr); +for (i=0; i<4; ++i) + { + int x = 0; // does not matter what these bits are, it is just filler. + char *s = "0"; + if (i 255) + notGoodSubnetCidr(cidr); + } + safecat(expIp, sizeof expIp, s); + if (i < 3) + safecat(expIp, sizeof expIp, "."); + } +if (bits == -1) + bits = 8 * wordCount; +if ((bits > 32) || (bits < 1)) + notGoodSubnetCidr(cidr); +ip = expIp; + +unsigned char quadIp[4]; +internetParseDottedQuad(ip, quadIp); +bits32 packedIp = 0; +packedIp = internetPackIp(quadIp); +struct cidr *result; +AllocVar(result); +result->ip = packedIp; +result->subnetLength = bits; +return result; }