]> git.ipfire.org Git - network.git/blob - src/inetcalc.c
netcalc: Fix comparing IP addresses
[network.git] / src / inetcalc.c
1 /*#############################################################################
2 # #
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2015 IPFire Network Development Team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <assert.h>
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <getopt.h>
25 #include <netinet/in.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/socket.h>
30
31 typedef struct ip_address {
32 int family;
33 struct in6_addr addr;
34 int prefix;
35 } ip_address_t;
36
37 static struct in6_addr prefix_to_bitmask(int prefix) {
38 assert(prefix <= 128);
39
40 struct in6_addr bitmask;
41
42 for (int i = 0; i < 16; i++)
43 bitmask.s6_addr[i] = 0;
44
45 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
46 if (i >= 8)
47 bitmask.s6_addr[j] = 0xff;
48 else
49 bitmask.s6_addr[j] = 0xff << (8 - i);
50 }
51
52 return bitmask;
53 }
54
55 static int bitmask_to_prefix(uint32_t bits) {
56 int prefix = 0;
57
58 // Count all ones until we find the first zero
59 while (bits & (1 << 31)) {
60 bits <<= 1;
61 prefix++;
62 }
63
64 // The remaining bits must all be zero
65 if (bits)
66 return -1;
67
68 return prefix;
69 }
70
71 static int ip_address_parse_subnet_mask(ip_address_t* ip, const char* prefix) {
72 struct in_addr mask;
73
74 int r = inet_pton(AF_INET, prefix, &mask.s_addr);
75 if (r != 1)
76 return 1;
77
78 uint32_t bits = ntohl(mask.s_addr);
79 ip->prefix = bitmask_to_prefix(bits);
80
81 return (ip->prefix < 0 || ip->prefix > 32);
82 }
83
84 static int ip_address_parse_prefix_cidr(ip_address_t* ip, const int family, const char* prefix) {
85 ip->prefix = 0;
86 while (*prefix) {
87 char p = *prefix++;
88
89 if (p >= '0' && p <= '9') {
90 ip->prefix *= 10;
91 ip->prefix += p - '0';
92 } else {
93 return 1;
94 }
95 }
96
97 switch (family) {
98 case AF_INET6:
99 return (ip->prefix < 0 || ip->prefix > 128);
100
101 case AF_INET:
102 return (ip->prefix < 0 || ip->prefix > 32);
103
104 default:
105 return 1;
106 }
107 }
108
109 static int ip_address_parse_prefix(ip_address_t* ip, const int family, const char* prefix) {
110 int r = ip_address_parse_prefix_cidr(ip, family, prefix);
111
112 if (r && family == AF_INET) {
113 r = ip_address_parse_subnet_mask(ip, prefix);
114 }
115
116 return r;
117 }
118
119 static int ip_address_parse_simple(ip_address_t* ip, const int family, const char* address) {
120 assert(family == AF_INET || family == AF_INET6);
121
122 size_t address_length = strlen(address);
123 char buffer[address_length + 1];
124 strncpy(buffer, address, sizeof(buffer));
125
126 // Search for a prefix or subnet mask
127 char* prefix = strchr(buffer, '/');
128 if (prefix) {
129 buffer[prefix - buffer] = '\0';
130 prefix++;
131 }
132
133 memset(&ip->addr, 0, sizeof(ip->addr));
134 int r = inet_pton(family, buffer, &ip->addr);
135
136 switch (r) {
137 // If parsing the IP address failed, we will return false
138 case 0:
139 return 1;
140
141 // If the IP address could be successfully parsed, we will
142 // save the address family and return true
143 case 1:
144 ip->family = family;
145 r = 0;
146 break;
147
148 default:
149 return r;
150 }
151
152 if (prefix)
153 r = ip_address_parse_prefix(ip, family, prefix);
154 else
155 ip->prefix = -1;
156
157 return r;
158 }
159
160 static int ip_address_parse(ip_address_t* ip, const int family, const char* address) {
161 static int families[] = { AF_INET, AF_INET6, 0 };
162
163 int r = 1;
164 int* f = families;
165 while (*f) {
166 if (family == AF_UNSPEC || family == *f) {
167 r = ip_address_parse_simple(ip, *f, address);
168
169 if (r == 0)
170 break;
171 }
172
173 f++;
174 }
175
176 return r;
177 }
178
179 static int ip_address_eq(const ip_address_t* a1, const ip_address_t* a2) {
180 if (a1->family != a2->family)
181 return 1;
182
183 if (!IN6_ARE_ADDR_EQUAL(&a1->addr, &a2->addr))
184 return 1;
185
186 if (a1->prefix != a2->prefix)
187 return 1;
188
189 return 0;
190 }
191
192 static int ip_address_gt(const ip_address_t* a1, const ip_address_t* a2) {
193 if (a1->family != a2->family || a1->prefix != a2->prefix)
194 return -1;
195
196 for (unsigned int i = 0; i < 4; i++) {
197 if (a1->addr.s6_addr[i] > a2->addr.s6_addr[i])
198 return 0;
199 }
200
201 return 1;
202 }
203
204 static int ip_address_format_string(char* buffer, size_t size, const ip_address_t* ip) {
205 assert(ip->family == AF_INET || ip->family == AF_INET6);
206
207 const char* p = inet_ntop(ip->family, &ip->addr.s6_addr, buffer, size);
208 if (!p)
209 return errno;
210
211 return 0;
212 }
213
214 static void ip_address_print(const ip_address_t* ip) {
215 char buffer[INET6_ADDRSTRLEN+4];
216
217 int r = ip_address_format_string(buffer, sizeof(buffer), ip);
218 if (r)
219 return;
220
221 if (ip->prefix >= 0) {
222 size_t len = strlen(buffer);
223 snprintf(buffer + len, sizeof(buffer) - len, "/%d", ip->prefix);
224 }
225
226 printf("%s\n", buffer);
227 }
228
229 static void ip_address_make_network(ip_address_t* net, const ip_address_t* ip) {
230 assert(ip->prefix >= 0);
231
232 struct in6_addr mask = prefix_to_bitmask(ip->prefix);
233
234 net->family = ip->family;
235 net->prefix = ip->prefix;
236
237 for (int i = 0; i < 16; i++)
238 net->addr.s6_addr[i] = ip->addr.s6_addr[i] & mask.s6_addr[i];
239 }
240
241 static void ip_address_make_broadcast(ip_address_t* broadcast, const ip_address_t* ip) {
242 assert(ip->family == AF_INET && ip->prefix >= 0);
243
244 struct in6_addr mask = prefix_to_bitmask(ip->prefix);
245
246 broadcast->family = ip->family;
247 broadcast->prefix = ip->prefix;
248
249 for (int i = 0; i < 16; i++)
250 broadcast->addr.s6_addr[i] = ip->addr.s6_addr[i] | ~mask.s6_addr[i];
251 }
252
253 static int action_check(const int family, const char* address) {
254 ip_address_t ip;
255
256 int r = ip_address_parse(&ip, family, address);
257 if (r)
258 return r;
259
260 // No prefix allowed
261 return (ip.prefix >= 0);
262 }
263
264 static int action_equal(const int family, const char* addr1, const char* addr2) {
265 ip_address_t a1;
266 ip_address_t a2;
267 int r;
268
269 r = ip_address_parse(&a1, family, addr1);
270 if (r)
271 return 2;
272
273 r = ip_address_parse(&a2, family, addr2);
274 if (r)
275 return 2;
276
277 return ip_address_eq(&a1, &a2);
278 }
279
280 static int action_greater(const int family, const char* addr1, const char* addr2) {
281 ip_address_t a1;
282 ip_address_t a2;
283 int r;
284
285 r = ip_address_parse(&a1, family, addr1);
286 if (r)
287 return 2;
288
289 r = ip_address_parse(&a2, family, addr2);
290 if (r)
291 return 2;
292
293 return ip_address_gt(&a1, &a2);
294 }
295
296 static int action_format(const int family, const char* address) {
297 ip_address_t ip;
298
299 int r = ip_address_parse(&ip, family, address);
300 if (r)
301 return r;
302
303 ip_address_print(&ip);
304 return 0;
305 }
306
307 static int action_broadcast(const int family, const char* address) {
308 ip_address_t ip;
309 int r = ip_address_parse(&ip, family, address);
310 if (r) {
311 fprintf(stderr, "Invalid IP address: %s\n", address);
312 return r;
313 }
314
315 if (ip.family != AF_INET) {
316 fprintf(stderr, "This is only possible for IPv4\n");
317 return 1;
318 }
319
320 ip_address_t broadcast;
321 ip_address_make_broadcast(&broadcast, &ip);
322
323 ip_address_print(&broadcast);
324 return 0;
325 }
326
327 static int action_network(const int family, const char* address) {
328 ip_address_t ip;
329
330 int r = ip_address_parse(&ip, family, address);
331 if (r) {
332 fprintf(stderr, "Invalid IP address: %s\n", address);
333 return r;
334 }
335
336 ip_address_t network;
337 ip_address_make_network(&network, &ip);
338
339 ip_address_print(&network);
340 return 0;
341 }
342
343 static int action_prefix(const int family, const char* addr1, const char* addr2) {
344 int r;
345
346 ip_address_t network;
347 r = ip_address_parse(&network, family, addr1);
348 if (r)
349 return r;
350
351 ip_address_t broadcast;
352 r = ip_address_parse(&broadcast, family, addr2);
353 if (r)
354 return r;
355
356 r = ip_address_gt(&broadcast, &network);
357 if (r)
358 return r;
359
360 struct in6_addr netmask;
361 for (int i = 0; i < 16; i++)
362 netmask.s6_addr[i] = network.addr.s6_addr[i] ^ broadcast.addr.s6_addr[i];
363
364 uint32_t mask = netmask.s6_addr[0] << 24 | netmask.s6_addr[1] << 16 |
365 netmask.s6_addr[2] << 8 | netmask.s6_addr[3];
366
367 int prefix = bitmask_to_prefix(~mask);
368 if (prefix < 0)
369 return 1;
370
371 printf("%d\n", prefix);
372 return 0;
373 }
374
375 enum actions {
376 AC_UNSPEC = 0,
377 AC_BROADCAST,
378 AC_CHECK,
379 AC_EQUAL,
380 AC_FORMAT,
381 AC_GREATER,
382 AC_NETWORK,
383 AC_PREFIX,
384 };
385
386 static void set_action(int* action, int what) {
387 if (*action != AC_UNSPEC) {
388 printf("Another action has already been selected\n");
389 exit(1);
390 }
391
392 *action = what;
393 }
394
395 static struct option long_options[] = {
396 {"broadcast", no_argument, 0, 'b'},
397 {"check", no_argument, 0, 'c'},
398 {"equal", no_argument, 0, 'e'},
399 {"format", no_argument, 0, 'f'},
400 {"greater", no_argument, 0, 'g'},
401 {"ipv4-only", no_argument, 0, '4'},
402 {"ipv6-only", no_argument, 0, '6'},
403 {"network", no_argument, 0, 'n'},
404 {"prefix", no_argument, 0, 'p'},
405 {"verbose", no_argument, 0, 'v'},
406 {0, 0, 0, 0}
407 };
408
409 int main(int argc, char** argv) {
410 int option_index = 0;
411 int required_arguments = 0;
412
413 int verbose = 0;
414 int action = AC_UNSPEC;
415 int family = AF_UNSPEC;
416
417 while (1) {
418 int c = getopt_long(argc, argv, "46bcefgnpv", long_options, &option_index);
419 if (c == -1)
420 break;
421
422 switch (c) {
423 case 0:
424 if (long_options[option_index].flag != 0)
425 break;
426
427 printf("option: %s", long_options[option_index].name);
428 if (optarg)
429 printf(" with arg %s", optarg);
430 printf("\n");
431 break;
432
433 case '4':
434 family = AF_INET;
435 break;
436
437 case '6':
438 family = AF_INET6;
439 break;
440
441 case 'b':
442 set_action(&action, AC_BROADCAST);
443 required_arguments = 1;
444 break;
445
446 case 'c':
447 set_action(&action, AC_CHECK);
448 required_arguments = 1;
449 break;
450
451 case 'e':
452 set_action(&action, AC_EQUAL);
453 required_arguments = 2;
454 break;
455
456 case 'f':
457 set_action(&action, AC_FORMAT);
458 required_arguments = 1;
459 break;
460
461 case 'g':
462 set_action(&action, AC_GREATER);
463 required_arguments = 2;
464 break;
465
466 case 'n':
467 set_action(&action, AC_NETWORK);
468 required_arguments = 1;
469 break;
470
471 case 'p':
472 set_action(&action, AC_PREFIX);
473 required_arguments = 2;
474 break;
475
476 case 'v':
477 verbose = 1;
478 break;
479
480 case '?':
481 break;
482
483 default:
484 abort();
485 }
486 }
487
488 while (optind--) {
489 argc--;
490 argv++;
491 }
492
493 if (argc != required_arguments) {
494 fprintf(stderr, "Invalid number of arguments. Got %d, required %d.\n",
495 argc, required_arguments);
496 return 1;
497 }
498
499 if (verbose && family != AF_UNSPEC)
500 printf("Address family = %d\n", family);
501
502 int r = 0;
503
504 switch (action) {
505 case AC_UNSPEC:
506 printf("No action specified\n");
507 r = 1;
508 break;
509
510 case AC_BROADCAST:
511 r = action_broadcast(family, argv[0]);
512 break;
513
514 case AC_CHECK:
515 r = action_check(family, argv[0]);
516
517 if (verbose) {
518 if (r == 0)
519 printf("%s is a valid IP address\n", argv[0]);
520 else
521 printf("%s is not a valid IP address\n", argv[0]);
522 }
523 break;
524
525 case AC_EQUAL:
526 r = action_equal(family, argv[0], argv[1]);
527
528 if (verbose) {
529 if (r == 0)
530 printf("%s equals %s\n", argv[0], argv[1]);
531 else if (r == 2)
532 printf("Invalid IP address provided\n");
533 else
534 printf("%s does not equal %s\n", argv[0], argv[1]);
535 }
536 break;
537
538 case AC_FORMAT:
539 r = action_format(family, argv[0]);
540
541 if (verbose && r)
542 printf("Invalid IP address given\n");
543
544 break;
545
546 case AC_GREATER:
547 r = action_greater(family, argv[0], argv[1]);
548
549 if (verbose) {
550 if (r == 0)
551 printf("%s is greater than %s\n", argv[0], argv[1]);
552 else if (r == 2)
553 printf("Invalid IP address provided\n");
554 else
555 printf("%s is not greater than %s\n", argv[0], argv[1]);
556 }
557 break;
558
559 case AC_NETWORK:
560 r = action_network(family, argv[0]);
561 break;
562
563 case AC_PREFIX:
564 r = action_prefix(family, argv[0], argv[1]);
565 break;
566 }
567
568 return r;
569 }