nrelease: Reorder and clean up the "srcs" target
[dragonfly.git] / usr.sbin / ip6addrctl / ip6addrctl.c
1 /*      $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */
2
3 /*-
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Copyright (C) 2001 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: head/usr.sbin/ip6addrctl/ip6addrctl.c 326025 2017-11-20 19:49:47Z pfg $
34  */
35
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <sys/queue.h>
39 #include <sys/param.h>
40 #include <sys/ioctl.h>
41 #include <sys/sysctl.h>
42
43 #include <net/if.h>
44
45 #include <netinet/in.h>
46 #include <netinet6/in6_var.h>
47
48 #include <stdlib.h>
49 #include <netdb.h>
50 #include <stdio.h>
51 #include <unistd.h>
52 #include <limits.h>
53 #include <string.h>
54 #include <err.h>
55
56 static char *configfile;
57
58 struct policyqueue {
59         TAILQ_ENTRY(policyqueue) pc_entry;
60         struct in6_addrpolicy pc_policy;
61 };
62 TAILQ_HEAD(policyhead, policyqueue);
63 static struct policyhead policyhead;
64
65 static void usage(void);
66 static void get_policy(void);
67 static void dump_policy(void);
68 static int mask2plen(struct sockaddr_in6 *);
69 static int parse_prefix(const char *, struct in6_addrpolicy *);
70 static void make_policy_fromfile(char *);
71 static void plen2mask(struct sockaddr_in6 *, int);
72 static void set_policy(void);
73 static void add_policy(char *, char *, char *);
74 static void delete_policy(char *);
75 static void flush_policy(void);
76
77 int
78 main(int argc, char *argv[])
79 {
80         TAILQ_INIT(&policyhead);
81
82         if (argc == 1 || strcasecmp(argv[1], "show") == 0) {
83                 get_policy();
84                 dump_policy();
85         } else if (strcasecmp(argv[1], "add") == 0) {
86                 if (argc < 5)
87                         usage();
88                 add_policy(argv[2], argv[3], argv[4]);
89         } else if (strcasecmp(argv[1], "delete") == 0) {
90                 if (argc < 3)
91                         usage();
92                 delete_policy(argv[2]);
93         } else if (strcasecmp(argv[1], "flush") == 0) {
94                 get_policy();
95                 flush_policy();
96         } else if (strcasecmp(argv[1], "install") == 0) {
97                 if (argc < 3)
98                         usage();
99                 configfile = argv[2];
100                 make_policy_fromfile(configfile);
101                 set_policy();
102         } else
103                 usage();
104
105         exit(0);
106 }
107
108 static void
109 get_policy(void)
110 {
111         int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY };
112         size_t l;
113         struct in6_addrpolicy *buf;
114         struct in6_addrpolicy *pol, *ep;
115
116         if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) {
117                 err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)");
118                 /* NOTREACHED */
119         }
120         if (l == 0) {
121                 printf("no source-address-selection policy is installed\n");
122                 return;
123         }
124         if ((buf = malloc(l)) == NULL) {
125                 errx(1, "malloc failed");
126                 /* NOTREACHED */
127         }
128         if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) {
129                 err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)");
130                 /* NOTREACHED */
131         }
132
133         ep = buf + l/sizeof(*buf);
134         for (pol = buf; pol + 1 <= ep; pol++) {
135                 struct policyqueue *new;
136
137                 if ((new = malloc(sizeof(*new))) == NULL)
138                         errx(1, "malloc failed\n");
139                 new->pc_policy = *pol;
140                 TAILQ_INSERT_TAIL(&policyhead, new, pc_entry);
141         }
142
143         free(buf);
144 }
145
146 static void
147 dump_policy(void)
148 {
149         size_t addrlen;
150         char addrbuf[NI_MAXHOST];
151         struct in6_addrpolicy *pol;
152         struct policyqueue *ent;
153         int plen, first = 1;
154
155         for (ent = TAILQ_FIRST(&policyhead); ent;
156              ent = TAILQ_NEXT(ent, pc_entry)) {
157                 pol = &ent->pc_policy;
158                 if (first) {
159                         printf("%-30s %5s %5s %8s\n",
160                                "Prefix", "Prec", "Label", "Use");
161                         first = 0;
162                 }
163
164                 if ((getnameinfo((struct sockaddr *)&pol->addr,
165                                  sizeof(pol->addr), addrbuf, sizeof(addrbuf),
166                                  NULL, 0, NI_NUMERICHOST))) {
167                         warnx("getnameinfo for prefix address failed");
168                         continue;
169                 }
170                 if ((plen = mask2plen(&pol->addrmask)) < 0) {
171                         warnx("invalid address mask");
172                         continue;
173                 }
174                 addrlen = strlen(addrbuf);
175                 if (addrlen + sizeof("/128") < sizeof(addrbuf)) {
176                         snprintf(&addrbuf[addrlen],
177                                  sizeof(addrbuf) - addrlen - 1,
178                                  "/%d", plen);
179                         printf("%-30s", addrbuf);
180                 } else          /* XXX */
181                         printf("%s/%d", addrbuf, plen);
182                 printf(" %5d %5d %8llu\n", pol->preced, pol->label,
183                     (unsigned long long)pol->use);
184         }
185 }
186
187 #define SKIP_WHITE(p, emptyok) \
188         do { \
189                 while((*(p) == ' ' || *(p) == '\t')) \
190                         (p)++; \
191                 if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \
192                         goto bad; \
193         } while (0);
194 #define SKIP_WORD(p) \
195         do { \
196                 while(*(p) != ' ' && *(p) != '\t') \
197                         (p)++; \
198                 if (*(p) == '\0' || *(p) == '\n') \
199                         goto bad; \
200         } while (0);
201
202 static void
203 make_policy_fromfile(char *conf)
204 {
205         char line[_POSIX2_LINE_MAX], *cp;
206         char *addrstr;
207         FILE *fp;
208         int count = 0;
209         struct in6_addrpolicy pol0;
210         struct policyqueue *new;
211
212         if ((fp = fopen(conf, "r")) == NULL)
213                 err(1, "fopen: %s", conf);
214
215         while(fgets(line, sizeof(line), fp)) {
216                 count++;
217                 cp = line;
218
219                 memset(&pol0, 0, sizeof(pol0));
220
221                 /* get prefix */
222                 SKIP_WHITE(cp, 1);
223                 if (*cp == '\n') /* empty line */
224                         continue;
225                 if (*cp == '#')
226                         continue;
227                 addrstr = cp;
228                 if (parse_prefix((const char *)addrstr, &pol0))
229                         goto bad;
230
231                 /* get precedence value */
232                 SKIP_WORD(cp);
233                 SKIP_WHITE(cp, 0);
234                 pol0.preced = atoi(cp);
235
236                 /* get label */
237                 SKIP_WORD(cp);
238                 SKIP_WHITE(cp, 0);
239                 pol0.label = atoi(cp);
240
241                 /* parse succeeded.  make a control buffer entry. */
242                 if ((new = malloc(sizeof(*new))) == NULL)
243                         errx(1, "malloc failed\n");
244                 memset(new, 0, sizeof(*new));
245                 new->pc_policy = pol0;
246                 TAILQ_INSERT_TAIL(&policyhead, new, pc_entry);
247         }
248
249         fclose(fp);
250         return;
251
252   bad:
253         errx(1, "parse failed at line %d", count);
254         /* NOTREACHED */
255 }
256
257 static int
258 parse_prefix(const char *prefix0, struct in6_addrpolicy *pol)
259 {
260         int e = 0, plen;
261         char *prefix, *plenstr;
262         struct addrinfo hints, *res;
263
264         if ((prefix = strdup(prefix0)) == NULL)
265                 errx(1, "strdup failed");
266
267         if ((plenstr = strchr(prefix, '/')) == NULL) {
268                 e = -1;
269                 goto end;
270         }
271         *plenstr = '\0';
272
273         memset(&hints, 0, sizeof(hints));
274         hints.ai_flags = AI_NUMERICHOST;
275         hints.ai_family = AF_INET6;
276
277         if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) {
278                 warnx("getaddrinfo failed for %s: %s", prefix,
279                       gai_strerror(e));
280                 goto end;
281         }
282         memcpy(&pol->addr, res->ai_addr, res->ai_addrlen);
283         freeaddrinfo(res);
284         plen = atoi(plenstr + 1);
285         if (plen < 0 || plen > 128) {
286                 warnx("invalid prefix length: %d", plen);
287                 e = -1;
288                 goto end;
289         }
290         plen2mask(&pol->addrmask, plen);
291
292   end:
293         free(prefix);
294         return(e);
295 }
296
297 static void
298 plen2mask(struct sockaddr_in6 *mask, int plen)
299 {
300         u_char *cp = (unsigned char *)&mask->sin6_addr;
301
302         memset(mask, 0, sizeof(*mask));
303         mask->sin6_family = AF_INET6; /* just in case */
304         mask->sin6_len = sizeof(*mask);
305
306         for(; plen >= 8; plen -= 8)
307                 *cp++ = 0xff;
308         if (plen > 0)
309                 *cp = (0xff << (8 - plen));
310 }
311
312 static void
313 set_policy(void)
314 {
315         struct policyqueue *ent;
316         int s;
317
318         if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
319                 err(1, "socket(UDP)");
320
321         for (ent = TAILQ_FIRST(&policyhead); ent;
322              ent = TAILQ_NEXT(ent, pc_entry)) {
323                 if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy))
324                         warn("ioctl(SIOCAADDRCTL_POLICY)");
325         }
326
327         close(s);
328 }
329
330 static int
331 mask2plen(struct sockaddr_in6 *mask)
332 {
333         int masklen, final = 0;
334         u_char *p, *lim;
335
336         masklen = 0;
337         lim = (u_char *)(mask + 1);
338         for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) {
339                 if (final && *p) {
340                         goto bad;
341                 }
342
343                 switch (*p & 0xff) {
344                 case 0xff:
345                         masklen += 8;
346                         break;
347                 case 0xfe:
348                         masklen += 7;
349                         final++;
350                         break;
351                 case 0xfc:
352                         masklen += 6;
353                         final++;
354                         break;
355                 case 0xf8:
356                         masklen += 5;
357                         final++;
358                         break;
359                 case 0xf0:
360                         masklen += 4;
361                         final++;
362                         break;
363                 case 0xe0:
364                         masklen += 3;
365                         final++;
366                         break;
367                 case 0xc0:
368                         masklen += 2;
369                         final++;
370                         break;
371                 case 0x80:
372                         masklen += 1;
373                         final++;
374                         break;
375                 case 0x00:
376                         final++;
377                         break;
378                 default:
379                         goto bad;
380                         break;
381                 }
382         }
383         return(masklen);
384
385   bad:
386         return(-1);
387 }
388
389 static void
390 add_policy(char *prefix, char *prec, char *label)
391 {
392         struct in6_addrpolicy p;
393         int s;
394
395         memset(&p, 0, sizeof(p));
396
397         if (parse_prefix((const char *)prefix, &p))
398                 errx(1, "bad prefix: %s", prefix);
399         p.preced = atoi(prec);
400         p.label = atoi(label);
401
402         if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
403                 err(1, "socket(UDP)");
404         if (ioctl(s, SIOCAADDRCTL_POLICY, &p))
405                 err(1, "ioctl(SIOCAADDRCTL_POLICY)");
406
407         close(s);
408 }
409
410 static void
411 delete_policy(char *prefix)
412 {
413         struct in6_addrpolicy p;
414         int s;
415
416         memset(&p, 0, sizeof(p));
417
418         if (parse_prefix((const char *)prefix, &p))
419                 errx(1, "bad prefix: %s", prefix);
420
421         if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
422                 err(1, "socket(UDP)");
423         if (ioctl(s, SIOCDADDRCTL_POLICY, &p))
424                 err(1, "ioctl(SIOCDADDRCTL_POLICY)");
425
426         close(s);
427 }
428
429 static void
430 flush_policy(void)
431 {
432         struct policyqueue *ent;
433         int s;
434
435         if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
436                 err(1, "socket(UDP)");
437
438         for (ent = TAILQ_FIRST(&policyhead); ent;
439              ent = TAILQ_NEXT(ent, pc_entry)) {
440                 if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy))
441                         warn("ioctl(SIOCDADDRCTL_POLICY)");
442         }
443
444         close(s);
445 }
446
447 static void
448 usage(void)
449 {
450         fprintf(stderr, "usage: ip6addrctl [show]\n");
451         fprintf(stderr, "       ip6addrctl add "
452                 "<prefix> <precedence> <label>\n");
453         fprintf(stderr, "       ip6addrctl delete <prefix>\n");
454         fprintf(stderr, "       ip6addrctl flush\n");
455         fprintf(stderr, "       ip6addrctl install <configfile>\n");
456
457         exit(1);
458 }