/* This program contains a set of procedures to determine if an ip address belongs to an ip networks. It's not intended to be the most efficient, but to demonstrate in a very detailed way the algorithm that is used. */ #include //An 'unsigned char' in C is just an integer with values from 0 to 255. //We use arrays of four unsigned chars to represent 32-bit ip addresses. //We could have also used a single 32-bit 'unsigned int', but the code //would be less clear. In general, this program could be more efficient //but I tried to avoid code that would look too confusing. typedef unsigned char IPADDR[4]; // 4 8-bit values = 32 bits. // The 'typedef' sets IPADDR = array of 4 unsigned bytes, so declaring // IPADDR m; is the same as declaring unsigned char m[4]; // To determine if a host ip address belongs to a certain network, we // need 3 IPADDR structures (3 arrays of 4 unsigned chars): the host // address, the network address, and the network mask. The network // mask is also a 32 bit value with a number of leading 1's followed // by trailing 0's. We sometimes use the notation /masksize to // represent number of leading 1's. For example, /24 represents // 24 leading 1's followed by 8 0's, which is written 255.255.255.0. // You should know that 255 as an 8-bit binary integer is 11111111. // So a network address such as 147.4.180.0/19 is represented by // the pair of 32-bit values 147.4.180.0 and 255.255.224.0. 224 in // binary is 11100000, so 255.255.224.0 represents 19 leading 1's followed // by 13 trailing 0's. /////////// CRITICAL: ************ // To determine if an ip host address belongs to a certain network, // we test if // hostip & networkmask == networkip // So 147.4.194.2 does NOT belong to 147.4.180.0/19 because if we do // a bitwise & (and) between 147.4.194.2 and 255.255.224.0, we do NOT // get exactly 147.4.180.0: instead we get 147.4.192.0. This is because // 224 = 11100000 // &&&&&&&& (bitwise and) // 194 = 11000010 // = 11000000 = 192 // But 147.4.194.2 does belong to 147.4.192.0/19, as well as /20, /21, /22. // But it does not belong to 147.4.192.0/23, because /23 represents the // mask 255.255.11111110.0 (= 255.255.254.0) Do you get // 147.4.192.0 if you & this mask with 147.4.194.2? Work out the // answer on paper, then verify it by adding a test case to main below. // Procedure to perform a bit-wise and of two IPADDR structures, // writing the result to a third IPADDR structure void bitwiseand(IPADDR A, IPADDR B, IPADDR RESULT) { int i; for (i=0;i<4;i++) RESULT[i] = A[i] & B[i]; // & is bitwise and } // debug procedure to print a 4-unsigned char array to stdout: void printip(IPADDR addr) // prints ip address for debugging { for(int i=0;i<3;i++) printf("%d.",addr[i]); printf("%d\n",addr[3]); } // Procedure to determine if an address belongs to a network: // returns 1 (true) iff hostIP belongs to the network as defined by // the pair networkIP and networkMASK int in_network(IPADDR hostIP, IPADDR networkIP, IPADDR networkMASK) { printf("host address "); printip(hostIP); // print to trace printf("network address "); printip(networkIP); printf("network mask "); printip(networkMASK); IPADDR result; bitwiseand(hostIP,networkMASK,result); // result = hostip & netmask printf("host & mask: "); printip(result); for (int i=0;i<4;i++) if (result[i] != networkIP[i]) return 0; //0=false return 1; // returns true if result of bitwise and == networkIP } // Construct a network mask given masksize (numerber of leading 1's): int makemask(int masksize, IPADDR mask) // outputs to mask { int i=1; int nborder = *((char*)&i) == 0; // true if native network byte ordering if (masksize<0 || masksize>32) return 1; int x = -1; // start with all 1's in binary if (masksize==0) x = 0; // special case, 32 bit signed shift doesn't get 0 else x = x << (32-masksize); // left shift to add the zero bits if (nborder) {for(i=0;i<4;i++) mask[i] = *(((unsigned char*)&x)+i); } else // this is more likely as x86/x64 processors reverse the byte ordering {for(i=0;i<4;i++) mask[i] = *(((unsigned char*)&x)+3-i);} return 0; // 0 means success, 1 means error, C has no exception handling }//makemask // This procedure uses the fact that -1 in two's complement is represented // in binary by all 1's. We use a 32 bit signed int x = -1, then left shift // it by (32-masksize) bits, adding that many 0 bits to the end. Finally, // we use type-casting and pointer arithmetic to transfer the bytes of x to // the 4-byte array in mask. We also need to check the native byte ordering // of the underlying machine in order to know if we need the switch the bytes // of x. int main(int argc, char* argv[]) { /* IPADDR host1 = {147,4,180,108}; // IP address "147.4.180.108" as array IPADDR host2 = {147,4,192,1}; // IP address 147.4.192.1 IPADDR net1 = {147,4,180,0}; IPADDR net2 = {147,4,180,64}; IPADDR mask1 = {255,255,255,0}; // equivalent to saying /24 IPADDR mask2 = {255,255,255,192}; // equivalent to /26 (192=11000000) IPADDR mask3 = {255,255,224,0}; // equivalent to /19 (224=11100000) IPADDR defaultnet = {0,0,0,0}; IPADDR defaultmask = {0,0,0,0}; // A network is defined by a network ip address and a network mask. // For example, net1+mask1 represents "147.4.180.0/24", // net1+mask2 represents "147.4.180.0/26", etc. // determine if host 147.4.180.108 belongs to 147.4.180.0/24: printf("%d\n", in_network(host1,net1,mask1)); // true // determine if host 147.4.180.108 belongs to 147.4.180.0/26 printf("%d\n", in_network(host1,net1,mask2)); // false // determine if host 147.4.192.1 belongs to 147.4.180.0/19 printf("%d\n", in_network(host2,net1,mask3)); // false // determine if host 147.4.192.1 belongs to default 0.0.0.0/0 printf("%d\n", in_network(host2,defaultnet,defaultmask)); // true IPADDR cmask; makemask(16,cmask); printf("constructed mask: "); printip(cmask); makemask(19,cmask); printf("constructed mask: "); printip(cmask); makemask(26,cmask); printf("constructed mask: "); printip(cmask); makemask(0,cmask); printf("constructed mask: "); printip(cmask); */ IPADDR host = {147,4,180,102}; IPADDR net = {147,4,180,64}; IPADDR mask; int masksize = 26; int answer, i; int slot[4]; // for help with scanf if (argc==3) { sscanf(argv[1],"%u.%u.%u.%u",slot,slot+1,slot+2,slot+3); for(i=0;i<4;i++) if (slot[i]<0) return 1; else host[i] = (unsigned char)(slot[i]%256); sscanf(argv[2],"%u.%u.%u.%u/%u",slot,slot+1,slot+2,slot+3,&masksize); for(i=0;i<4;i++) if (slot[i]<0) return 1; else net[i] = (unsigned char)(slot[i]%256); if (masksize<0 || masksize>32) return 1; } makemask(masksize,mask); answer = in_network(host,net,mask); if (answer) printf("yes\n"); else printf("no\n"); return 0; }//main /* Side-note on use of C programming language: Unlike languages such as Java, which has a 'garbage collector', in C it is generally safer to statically allocate memory instead of allocating it dynamically with 'malloc' or 'new'. This is why bitwiseand writes the result to a buffer that's allocated externally (in main). This style of programming helps to avoid memory errors (dangling pointers and memory leaks). If I wrote this program in Java, I might have bitwiseand return the array. But Java is less suited for low-level programming than C. For example, there are no unsigned types in Java: 255 as an 8-bit value must be represented by -1. The resulting program would therefore be harder to read. */ /* Output of this program: host address 147.4.180.108 network address 147.4.180.0 network mask 255.255.255.0 host & mask: 147.4.180.0 1 host address 147.4.180.108 network address 147.4.180.0 network mask 255.255.255.192 host & mask: 147.4.180.64 0 host address 147.4.192.1 network address 147.4.180.0 network mask 255.255.224.0 host & mask: 147.4.192.0 0 host address 147.4.192.1 network address 0.0.0.0 network mask 0.0.0.0 host & mask: 0.0.0.0 1 */