2
* network range to IP+mask converter
4
* Copyright 2011-2012 Willy Tarreau <w@1wt.eu>
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.
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.
20
#include <sys/types.h>
21
#include <sys/socket.h>
22
#include <arpa/inet.h>
29
static inline void in6_bswap(struct in6_addr *a)
31
a->in6_u.u6_addr32[0] = ntohl(a->in6_u.u6_addr32[0]);
32
a->in6_u.u6_addr32[1] = ntohl(a->in6_u.u6_addr32[1]);
33
a->in6_u.u6_addr32[2] = ntohl(a->in6_u.u6_addr32[2]);
34
a->in6_u.u6_addr32[3] = ntohl(a->in6_u.u6_addr32[3]);
37
/* returns a string version of an IPv6 address in host order */
38
static const char *get_ipv6_addr(struct in6_addr *addr)
41
static char out[INET6_ADDRSTRLEN + 1];
43
memcpy(&a, addr, sizeof(struct in6_addr));
45
return inet_ntop(AF_INET6, &a, out, INET6_ADDRSTRLEN + 1);
48
static const char *get_addr(struct in6_addr *addr)
51
snprintf(out, 50, "%08x:%08x:%08x:%08x",
52
addr->in6_u.u6_addr32[0],
53
addr->in6_u.u6_addr32[1],
54
addr->in6_u.u6_addr32[2],
55
addr->in6_u.u6_addr32[3]);
60
static inline int a_le_b(struct in6_addr *a, struct in6_addr *b)
62
if (a->in6_u.u6_addr32[0] < b->in6_u.u6_addr32[0]) return 1;
63
if (a->in6_u.u6_addr32[0] > b->in6_u.u6_addr32[0]) return 0;
64
if (a->in6_u.u6_addr32[1] < b->in6_u.u6_addr32[1]) return 1;
65
if (a->in6_u.u6_addr32[1] > b->in6_u.u6_addr32[1]) return 0;
66
if (a->in6_u.u6_addr32[2] < b->in6_u.u6_addr32[2]) return 1;
67
if (a->in6_u.u6_addr32[2] > b->in6_u.u6_addr32[2]) return 0;
68
if (a->in6_u.u6_addr32[3] < b->in6_u.u6_addr32[3]) return 1;
69
if (a->in6_u.u6_addr32[3] > b->in6_u.u6_addr32[3]) return 0;
74
static inline int a_eq_b(struct in6_addr *a, struct in6_addr *b)
76
if (a->in6_u.u6_addr32[0] != b->in6_u.u6_addr32[0]) return 0;
77
if (a->in6_u.u6_addr32[1] != b->in6_u.u6_addr32[1]) return 0;
78
if (a->in6_u.u6_addr32[2] != b->in6_u.u6_addr32[2]) return 0;
79
if (a->in6_u.u6_addr32[3] != b->in6_u.u6_addr32[3]) return 0;
84
static inline int a_gt_b(struct in6_addr *a, struct in6_addr *b)
86
if (a->in6_u.u6_addr32[0] > b->in6_u.u6_addr32[0]) return 1;
87
if (a->in6_u.u6_addr32[0] < b->in6_u.u6_addr32[0]) return 0;
88
if (a->in6_u.u6_addr32[1] > b->in6_u.u6_addr32[1]) return 1;
89
if (a->in6_u.u6_addr32[1] < b->in6_u.u6_addr32[1]) return 0;
90
if (a->in6_u.u6_addr32[2] > b->in6_u.u6_addr32[2]) return 1;
91
if (a->in6_u.u6_addr32[2] < b->in6_u.u6_addr32[2]) return 0;
92
if (a->in6_u.u6_addr32[3] > b->in6_u.u6_addr32[3]) return 1;
93
if (a->in6_u.u6_addr32[3] < b->in6_u.u6_addr32[3]) return 0;
97
/* ( 1 << m ) - 1 -> r */
98
static inline struct in6_addr *hmask(unsigned int b, struct in6_addr *r)
102
r->in6_u.u6_addr32[3] = (1 << b) - 1;
103
r->in6_u.u6_addr32[2] = 0;
104
r->in6_u.u6_addr32[1] = 0;
105
r->in6_u.u6_addr32[0] = 0;
108
r->in6_u.u6_addr32[3] = 0xffffffff;
109
r->in6_u.u6_addr32[2] = (1 << (b - 32)) - 1;
110
r->in6_u.u6_addr32[1] = 0;
111
r->in6_u.u6_addr32[0] = 0;
114
r->in6_u.u6_addr32[3] = 0xffffffff;
115
r->in6_u.u6_addr32[2] = 0xffffffff;
116
r->in6_u.u6_addr32[1] = (1 << (b - 64)) - 1;
117
r->in6_u.u6_addr32[0] = 0;
120
r->in6_u.u6_addr32[3] = 0xffffffff;
121
r->in6_u.u6_addr32[2] = 0xffffffff;
122
r->in6_u.u6_addr32[1] = 0xffffffff;
123
r->in6_u.u6_addr32[0] = (1 << (b - 96)) - 1;
126
r->in6_u.u6_addr32[3] = 0xffffffff;
127
r->in6_u.u6_addr32[2] = 0xffffffff;
128
r->in6_u.u6_addr32[1] = 0xffffffff;
129
r->in6_u.u6_addr32[0] = 0xffffffff;
135
static inline struct in6_addr *one_ls_b(unsigned int b, struct in6_addr *r)
138
r->in6_u.u6_addr32[3] = 1 << b;
139
r->in6_u.u6_addr32[2] = 0;
140
r->in6_u.u6_addr32[1] = 0;
141
r->in6_u.u6_addr32[0] = 0;
144
r->in6_u.u6_addr32[3] = 0;
145
r->in6_u.u6_addr32[2] = 1 << (b - 32);
146
r->in6_u.u6_addr32[1] = 0;
147
r->in6_u.u6_addr32[0] = 0;
150
r->in6_u.u6_addr32[3] = 0;
151
r->in6_u.u6_addr32[2] = 0;
152
r->in6_u.u6_addr32[1] = 1 << (b - 64);
153
r->in6_u.u6_addr32[0] = 0;
156
r->in6_u.u6_addr32[3] = 0;
157
r->in6_u.u6_addr32[2] = 0;
158
r->in6_u.u6_addr32[1] = 0;
159
r->in6_u.u6_addr32[0] = 1 << (b - 96);
162
r->in6_u.u6_addr32[3] = 0;
163
r->in6_u.u6_addr32[2] = 0;
164
r->in6_u.u6_addr32[1] = 0;
165
r->in6_u.u6_addr32[0] = 0;
171
static inline struct in6_addr *a_plus_b(struct in6_addr *a, struct in6_addr *b, struct in6_addr *r)
173
unsigned long long int c = 0;
176
for (i=3; i>=0; i--) {
177
c = (unsigned long long int)a->in6_u.u6_addr32[i] +
178
(unsigned long long int)b->in6_u.u6_addr32[i] + c;
179
r->in6_u.u6_addr32[i] = c;
187
static inline struct in6_addr *a_minus_b(struct in6_addr *a, struct in6_addr *b, struct in6_addr *r)
189
signed long long int c = 0;
190
signed long long int d;
193
/* Check sign. Return 0xff..ff (-1) if the result is less than 0. */
195
r->in6_u.u6_addr32[3] = 0xffffffff;
196
r->in6_u.u6_addr32[2] = 0xffffffff;
197
r->in6_u.u6_addr32[1] = 0xffffffff;
198
r->in6_u.u6_addr32[0] = 0xffffffff;
202
for (i=3; i>=0; i--) {
203
d = (unsigned long long int)b->in6_u.u6_addr32[i] + c;
204
c = (unsigned long long int)a->in6_u.u6_addr32[i];
208
r->in6_u.u6_addr32[i] = c;
216
static inline struct in6_addr *a_and_b(struct in6_addr *a, struct in6_addr *b, struct in6_addr *r)
218
r->in6_u.u6_addr32[0] = a->in6_u.u6_addr32[0] & b->in6_u.u6_addr32[0];
219
r->in6_u.u6_addr32[1] = a->in6_u.u6_addr32[1] & b->in6_u.u6_addr32[1];
220
r->in6_u.u6_addr32[2] = a->in6_u.u6_addr32[2] & b->in6_u.u6_addr32[2];
221
r->in6_u.u6_addr32[3] = a->in6_u.u6_addr32[3] & b->in6_u.u6_addr32[3];
226
int is_set(struct in6_addr *a)
228
return a->in6_u.u6_addr32[0] ||
229
a->in6_u.u6_addr32[1] ||
230
a->in6_u.u6_addr32[2] ||
231
a->in6_u.u6_addr32[3];
235
static struct in6_addr one = { .in6_u.u6_addr32 = {0, 0, 0, 1} };
237
/* print all networks present between address <low> and address <high> in
238
* cidr format, followed by <eol>.
240
static void convert_range(struct in6_addr *low, struct in6_addr *high, const char *eol, const char *pfx)
246
if (a_eq_b(low, high)) {
248
printf("%s%s%s%s\n", pfx?pfx:"", pfx?" ":"", get_ipv6_addr(low), eol);
251
else if (a_gt_b(low, high)) {
252
struct in6_addr *swap = low;
257
if (a_eq_b(low, a_plus_b(high, &one, &r0))) {
259
printf("%s%s::/0%s\n", pfx?pfx:"", pfx?" ":"", eol);
262
//printf("low=%08x high=%08x\n", low, high);
265
while (bit < 128 && a_le_b(a_plus_b(low, hmask(bit, &r0), &r0), high)) {
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);
274
/* try to enlarge the mask as much as possible first */
276
//printf(" ++bit=%d\n", bit);
279
//printf("stopped 1 at low=%08x, bit=%d\n", low, bit);
282
while (bit >= 0 && is_set(a_plus_b(a_minus_b(high, low, &r0), &one, &r0))) {
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);
293
//printf(" --bit=%d, low=%08x\n", bit, low);
296
//printf("stopped at low=%08x\n", low);
299
static void usage(const char *argv0)
302
"Usage: %s [<addr> ...] < iplist.csv\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"
312
"For each optional <addr> specified, only the network it belongs to is returned,\n"
313
"prefixed with the <addr> value.\n"
317
main(int argc, char **argv)
321
char *lb, *le, *hb, *he, *err;
322
struct in6_addr sa, da, ta;
324
if (argc > 1 && *argv[1] == '-') {
330
while (fgets(line, sizeof(line), stdin) != NULL) {
332
if (l && line[l - 1] == '\n')
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.
340
for (lb = line; *lb == ' ' || *lb == '\t' || *lb == ',' || *lb == '"'; lb++);
341
if (!*lb || *lb == '#')
343
for (le = lb + 1; *le != ' ' && *le != '\t' && *le != ',' && *le != '"' && *le; le++);
346
/* we have the low address between lb(included) and le(excluded) */
349
for (hb = le; *hb == ' ' || *hb == '\t' || *hb == ',' || *hb == '"'; hb++);
350
if (!*hb || *hb == '#')
352
for (he = hb + 1; *he != ' ' && *he != '\t' && *he != ',' && *he != '"' && *he; he++);
355
/* we have the high address between hb(included) and he(excluded) */
358
/* we want to remove a possible ending quote and a possible comma,
363
while (*he == ',' || *he == ' ' || *he == '\t')
366
/* if the trailing string is not empty, prefix it with a space */
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);
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);
384
for (l = 1; l < argc; l++) {
385
if (inet_pton(AF_INET6, argv[l], &da) <= 0)
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]);
393
convert_range(&sa, &da, he, NULL);