blob: 2ffd4902c3cf79811c2a0e36d0cb7208b2940b84 [file] [log] [blame]
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +01001/*
2 * network range to IP+mask converter
3 *
4 * Copyright 2011-2012 Willy Tarreau <w@1wt.eu>
5 *
6 * This program reads lines starting by two IP addresses and outputs them with
7 * the two IP addresses replaced by a netmask covering the range between these
8 * IPs (inclusive). When multiple ranges are needed, as many lines are emitted.
9 * The IP addresses may be delimited by spaces, tabs or commas. Quotes are
10 * stripped, and lines beginning with a sharp character ('#') are ignored. The
11 * IP addresses may be either in the dotted format or represented as a 32-bit
12 * integer value in network byte order.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version
17 * 2 of the License, or (at your option) any later version.
18 */
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <arpa/inet.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#define MAXLINE 1024
28
29static inline void in6_bswap(struct in6_addr *a)
30{
Willy Tarreau0034cd92016-11-22 11:50:51 +010031 a->s6_addr32[0] = ntohl(a->s6_addr32[0]);
32 a->s6_addr32[1] = ntohl(a->s6_addr32[1]);
33 a->s6_addr32[2] = ntohl(a->s6_addr32[2]);
34 a->s6_addr32[3] = ntohl(a->s6_addr32[3]);
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +010035}
36
37/* returns a string version of an IPv6 address in host order */
38static const char *get_ipv6_addr(struct in6_addr *addr)
39{
40 struct in6_addr a;
41 static char out[INET6_ADDRSTRLEN + 1];
42
43 memcpy(&a, addr, sizeof(struct in6_addr));
44 in6_bswap(&a);
45 return inet_ntop(AF_INET6, &a, out, INET6_ADDRSTRLEN + 1);
46}
47
48static const char *get_addr(struct in6_addr *addr)
49{
50 static char out[50];
51 snprintf(out, 50, "%08x:%08x:%08x:%08x",
Willy Tarreau0034cd92016-11-22 11:50:51 +010052 addr->s6_addr32[0],
53 addr->s6_addr32[1],
54 addr->s6_addr32[2],
55 addr->s6_addr32[3]);
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +010056 return out;
57}
58
59/* a <= b */
60static inline int a_le_b(struct in6_addr *a, struct in6_addr *b)
61{
Willy Tarreau0034cd92016-11-22 11:50:51 +010062 if (a->s6_addr32[0] < b->s6_addr32[0]) return 1;
63 if (a->s6_addr32[0] > b->s6_addr32[0]) return 0;
64 if (a->s6_addr32[1] < b->s6_addr32[1]) return 1;
65 if (a->s6_addr32[1] > b->s6_addr32[1]) return 0;
66 if (a->s6_addr32[2] < b->s6_addr32[2]) return 1;
67 if (a->s6_addr32[2] > b->s6_addr32[2]) return 0;
68 if (a->s6_addr32[3] < b->s6_addr32[3]) return 1;
69 if (a->s6_addr32[3] > b->s6_addr32[3]) return 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +010070 return 1;
71}
72
73/* a == b */
74static inline int a_eq_b(struct in6_addr *a, struct in6_addr *b)
75{
Willy Tarreau0034cd92016-11-22 11:50:51 +010076 if (a->s6_addr32[0] != b->s6_addr32[0]) return 0;
77 if (a->s6_addr32[1] != b->s6_addr32[1]) return 0;
78 if (a->s6_addr32[2] != b->s6_addr32[2]) return 0;
79 if (a->s6_addr32[3] != b->s6_addr32[3]) return 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +010080 return 1;
81}
82
83/* a > b */
84static inline int a_gt_b(struct in6_addr *a, struct in6_addr *b)
85{
Willy Tarreau0034cd92016-11-22 11:50:51 +010086 if (a->s6_addr32[0] > b->s6_addr32[0]) return 1;
87 if (a->s6_addr32[0] < b->s6_addr32[0]) return 0;
88 if (a->s6_addr32[1] > b->s6_addr32[1]) return 1;
89 if (a->s6_addr32[1] < b->s6_addr32[1]) return 0;
90 if (a->s6_addr32[2] > b->s6_addr32[2]) return 1;
91 if (a->s6_addr32[2] < b->s6_addr32[2]) return 0;
92 if (a->s6_addr32[3] > b->s6_addr32[3]) return 1;
93 if (a->s6_addr32[3] < b->s6_addr32[3]) return 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +010094 return 0;
95}
96
97/* ( 1 << m ) - 1 -> r */
98static inline struct in6_addr *hmask(unsigned int b, struct in6_addr *r)
99{
100
101 if (b < 32) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100102 r->s6_addr32[3] = (1 << b) - 1;
103 r->s6_addr32[2] = 0;
104 r->s6_addr32[1] = 0;
105 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100106 }
107 else if (b < 64) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100108 r->s6_addr32[3] = 0xffffffff;
109 r->s6_addr32[2] = (1 << (b - 32)) - 1;
110 r->s6_addr32[1] = 0;
111 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100112 }
113 else if (b < 96) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100114 r->s6_addr32[3] = 0xffffffff;
115 r->s6_addr32[2] = 0xffffffff;
116 r->s6_addr32[1] = (1 << (b - 64)) - 1;
117 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100118 }
119 else if (b < 128) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100120 r->s6_addr32[3] = 0xffffffff;
121 r->s6_addr32[2] = 0xffffffff;
122 r->s6_addr32[1] = 0xffffffff;
123 r->s6_addr32[0] = (1 << (b - 96)) - 1;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100124 }
125 else {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100126 r->s6_addr32[3] = 0xffffffff;
127 r->s6_addr32[2] = 0xffffffff;
128 r->s6_addr32[1] = 0xffffffff;
129 r->s6_addr32[0] = 0xffffffff;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100130 }
131 return r;
132}
133
134/* 1 << b -> r */
135static inline struct in6_addr *one_ls_b(unsigned int b, struct in6_addr *r)
136{
137 if (b < 32) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100138 r->s6_addr32[3] = 1 << b;
139 r->s6_addr32[2] = 0;
140 r->s6_addr32[1] = 0;
141 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100142 }
143 else if (b < 64) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100144 r->s6_addr32[3] = 0;
145 r->s6_addr32[2] = 1 << (b - 32);
146 r->s6_addr32[1] = 0;
147 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100148 }
149 else if (b < 96) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100150 r->s6_addr32[3] = 0;
151 r->s6_addr32[2] = 0;
152 r->s6_addr32[1] = 1 << (b - 64);
153 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100154 }
155 else if (b < 128) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100156 r->s6_addr32[3] = 0;
157 r->s6_addr32[2] = 0;
158 r->s6_addr32[1] = 0;
159 r->s6_addr32[0] = 1 << (b - 96);
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100160 }
161 else {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100162 r->s6_addr32[3] = 0;
163 r->s6_addr32[2] = 0;
164 r->s6_addr32[1] = 0;
165 r->s6_addr32[0] = 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100166 }
167 return r;
168}
169
170/* a + b -> r */
171static inline struct in6_addr *a_plus_b(struct in6_addr *a, struct in6_addr *b, struct in6_addr *r)
172{
173 unsigned long long int c = 0;
174 int i;
175
176 for (i=3; i>=0; i--) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100177 c = (unsigned long long int)a->s6_addr32[i] +
178 (unsigned long long int)b->s6_addr32[i] + c;
179 r->s6_addr32[i] = c;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100180 c >>= 32;
181 }
182
183 return r;
184}
185
186/* a - b -> r */
187static inline struct in6_addr *a_minus_b(struct in6_addr *a, struct in6_addr *b, struct in6_addr *r)
188{
189 signed long long int c = 0;
190 signed long long int d;
191 int i;
192
193 /* Check sign. Return 0xff..ff (-1) if the result is less than 0. */
194 if (a_gt_b(b, a)) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100195 r->s6_addr32[3] = 0xffffffff;
196 r->s6_addr32[2] = 0xffffffff;
197 r->s6_addr32[1] = 0xffffffff;
198 r->s6_addr32[0] = 0xffffffff;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100199 return r;
200 }
201
202 for (i=3; i>=0; i--) {
Willy Tarreau0034cd92016-11-22 11:50:51 +0100203 d = (unsigned long long int)b->s6_addr32[i] + c;
204 c = (unsigned long long int)a->s6_addr32[i];
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100205 if (c < d)
206 c += 0x100000000ULL;
207 c -= d;
Willy Tarreau0034cd92016-11-22 11:50:51 +0100208 r->s6_addr32[i] = c;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100209 c >>= 32;
210 }
211
212 return r;
213}
214
215/* a & b -> r */
216static inline struct in6_addr *a_and_b(struct in6_addr *a, struct in6_addr *b, struct in6_addr *r)
217{
Willy Tarreau0034cd92016-11-22 11:50:51 +0100218 r->s6_addr32[0] = a->s6_addr32[0] & b->s6_addr32[0];
219 r->s6_addr32[1] = a->s6_addr32[1] & b->s6_addr32[1];
220 r->s6_addr32[2] = a->s6_addr32[2] & b->s6_addr32[2];
221 r->s6_addr32[3] = a->s6_addr32[3] & b->s6_addr32[3];
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100222 return r;
223}
224
225/* a != 0 */
226int is_set(struct in6_addr *a)
227{
Willy Tarreau0034cd92016-11-22 11:50:51 +0100228 return a->s6_addr32[0] ||
229 a->s6_addr32[1] ||
230 a->s6_addr32[2] ||
231 a->s6_addr32[3];
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100232}
233
234/* 1 */
Willy Tarreau0034cd92016-11-22 11:50:51 +0100235static struct in6_addr one = { .s6_addr32 = {0, 0, 0, 1} };
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100236
237/* print all networks present between address <low> and address <high> in
238 * cidr format, followed by <eol>.
239 */
240static void convert_range(struct in6_addr *low, struct in6_addr *high, const char *eol, const char *pfx)
241{
242 int bit;
243 struct in6_addr r0;
244 struct in6_addr r1;
245
246 if (a_eq_b(low, high)) {
247 /* single value */
248 printf("%s%s%s%s\n", pfx?pfx:"", pfx?" ":"", get_ipv6_addr(low), eol);
249 return;
250 }
251 else if (a_gt_b(low, high)) {
252 struct in6_addr *swap = low;
253 low = high;
254 high = swap;
255 }
256
257 if (a_eq_b(low, a_plus_b(high, &one, &r0))) {
258 /* full range */
259 printf("%s%s::/0%s\n", pfx?pfx:"", pfx?" ":"", eol);
260 return;
261 }
262 //printf("low=%08x high=%08x\n", low, high);
263
264 bit = 0;
265 while (bit < 128 && a_le_b(a_plus_b(low, hmask(bit, &r0), &r0), high)) {
266
267 /* enlarge mask */
268 if (is_set(a_and_b(low, one_ls_b(bit, &r0), &r0))) {
269 /* can't aggregate anymore, dump and retry from the same bit */
270 printf("%s%s%s/%d%s\n", pfx?pfx:"", pfx?" ":"", get_ipv6_addr(low), 128-bit, eol);
271 a_plus_b(low, one_ls_b(bit, &r0), low);
272 }
273 else {
274 /* try to enlarge the mask as much as possible first */
275 bit++;
276 //printf(" ++bit=%d\n", bit);
277 }
278 }
279 //printf("stopped 1 at low=%08x, bit=%d\n", low, bit);
280
281 bit = 127;
282 while (bit >= 0 && is_set(a_plus_b(a_minus_b(high, low, &r0), &one, &r0))) {
283
284 /* shrink mask */
285 if (is_set(a_and_b(a_plus_b(a_minus_b(high, low, &r0), &one, &r0), one_ls_b(bit, &r1), &r1))) {
286 /* large bit accepted, dump and go on from the same bit */
287 //printf("max: %08x/%d\n", low, 32-bit);
288 printf("%s%s%s/%d%s\n", pfx?pfx:"", pfx?" ":"", get_ipv6_addr(low), 128-bit, eol);
289 a_plus_b(low, one_ls_b(bit, &r0), low);
290 }
291 else {
292 bit--;
293 //printf(" --bit=%d, low=%08x\n", bit, low);
294 }
295 }
296 //printf("stopped at low=%08x\n", low);
297}
298
299static void usage(const char *argv0)
300{
301 fprintf(stderr,
302 "Usage: %s [<addr> ...] < iplist.csv\n"
303 "\n"
304 "This program reads lines starting by two IP addresses and outputs them with\n"
305 "the two IP addresses replaced by a netmask covering the range between these\n"
306 "IPs (inclusive). When multiple ranges are needed, as many lines are emitted.\n"
307 "The IP addresses may be delimited by spaces, tabs or commas. Quotes are\n"
308 "stripped, and lines beginning with a sharp character ('#') are ignored. The\n"
309 "IP addresses may be either in the dotted format or represented as a 32-bit\n"
310 "integer value in network byte order.\n"
311 "\n"
312 "For each optional <addr> specified, only the network it belongs to is returned,\n"
313 "prefixed with the <addr> value.\n"
314 "\n", argv0);
315}
316
Willy Tarreau6a6dabe2021-04-02 14:33:40 +0200317int main(int argc, char **argv)
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100318{
319 char line[MAXLINE];
320 int l, lnum;
321 char *lb, *le, *hb, *he, *err;
322 struct in6_addr sa, da, ta;
323
324 if (argc > 1 && *argv[1] == '-') {
325 usage(argv[0]);
326 exit(1);
327 }
328
329 lnum = 0;
330 while (fgets(line, sizeof(line), stdin) != NULL) {
331 l = strlen(line);
332 if (l && line[l - 1] == '\n')
333 line[--l] = '\0';
334
335 lnum++;
336 /* look for the first field which must be the low address of a range,
337 * in dotted IPv4 format or as an integer. spaces and commas are
338 * considered as delimiters, quotes are removed.
339 */
340 for (lb = line; *lb == ' ' || *lb == '\t' || *lb == ',' || *lb == '"'; lb++);
341 if (!*lb || *lb == '#')
342 continue;
343 for (le = lb + 1; *le != ' ' && *le != '\t' && *le != ',' && *le != '"' && *le; le++);
344 if (!*le)
345 continue;
346 /* we have the low address between lb(included) and le(excluded) */
347 *(le++) = 0;
348
349 for (hb = le; *hb == ' ' || *hb == '\t' || *hb == ',' || *hb == '"'; hb++);
350 if (!*hb || *hb == '#')
351 continue;
352 for (he = hb + 1; *he != ' ' && *he != '\t' && *he != ',' && *he != '"' && *he; he++);
353 if (!*he)
354 continue;
355 /* we have the high address between hb(included) and he(excluded) */
356 *(he++) = 0;
357
358 /* we want to remove a possible ending quote and a possible comma,
359 * not more.
360 */
361 while (*he == '"')
362 *(he++) = ' ';
363 while (*he == ',' || *he == ' ' || *he == '\t')
364 *(he++) = ' ';
365
366 /* if the trailing string is not empty, prefix it with a space */
367 if (*(he-1) == ' ')
368 he--;
369
370 if (inet_pton(AF_INET6, lb, &sa) <= 0) {
371 fprintf(stderr, "Failed to parse source address <%s> at line %d, skipping line\n", lb, lnum);
372 continue;
373 }
374
375 if (inet_pton(AF_INET6, hb, &da) <= 0) {
376 fprintf(stderr, "Failed to parse destination address <%s> at line %d, skipping line\n", hb, lnum);
377 continue;
378 }
379
380 in6_bswap(&sa);
381 in6_bswap(&da);
382
383 if (argc > 1) {
384 for (l = 1; l < argc; l++) {
385 if (inet_pton(AF_INET6, argv[l], &da) <= 0)
386 continue;
387 in6_bswap(&ta);
388 if ((a_le_b(&sa, &ta) && a_le_b(&ta, &da)) || (a_le_b(&da, &ta) && a_le_b(&ta, &sa)))
389 convert_range(&sa, &da, he, argv[l]);
390 }
391 }
392 else {
393 convert_range(&sa, &da, he, NULL);
394 }
395 }
Willy Tarreau6a6dabe2021-04-02 14:33:40 +0200396 return 0;
Thierry FOURNIER1a0fb5d2013-12-17 15:04:01 +0100397}