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