diff --git a/usr.sbin/ip6addrctl/Makefile b/usr.sbin/ip6addrctl/Makefile index ba38dc1162d9..356c524629d8 100644 --- a/usr.sbin/ip6addrctl/Makefile +++ b/usr.sbin/ip6addrctl/Makefile @@ -1,5 +1,12 @@ +.include + PACKAGE= runtime PROG= ip6addrctl MAN= ip6addrctl.8 +.if ${MK_JAIL} != "no" +CFLAGS+= -DJAIL +LIBADD+= jail +.endif + .include diff --git a/usr.sbin/ip6addrctl/ip6addrctl.8 b/usr.sbin/ip6addrctl/ip6addrctl.8 index 50245cef91ea..8d3b1a229859 100644 --- a/usr.sbin/ip6addrctl/ip6addrctl.8 +++ b/usr.sbin/ip6addrctl/ip6addrctl.8 @@ -1,129 +1,139 @@ .\" $KAME: ip6addrctl.8,v 1.3 2003/03/22 05:56:41 jinmei Exp $ .\" .\" Copyright (C) 2001 WIDE Project. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the project nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd August 10, 2024 +.Dd January 29, 2025 .Dt IP6ADDRCTL 8 .Os .\" .Sh NAME .Nm ip6addrctl .Nd configure address selection policy for IPv6 and IPv4 .\" .Sh SYNOPSIS .Nm +.Op Fl j Ar jail .Op Cm show .Nm +.Op Fl j Ar jail .Cm add .Ar prefix precedence label .Nm +.Op Fl j Ar jail .Cm delete .Ar prefix .Nm +.Op Fl j Ar jail .Cm flush .Nm +.Op Fl j Ar jail .Cm install .Ar configfile .\" .Sh DESCRIPTION The .Nm utility manages the policy table of source and destination address selection for outgoing IPv4 and IPv6 packets. When .Nm is invoked without an argument or with a single argument .Cm show , it prints the content of the policy table currently installed in the kernel. .Pp +To operate inside a jail, the option +.Op Fl j Ar jail +should precede other options. +.Pp To modify the table, the following operations are available: .Bl -tag -width indent .It Cm add Ar prefix precedence label Add a policy entry. The .Ar prefix argument is an IPv6 prefix, which is a key for the entry. An IPv4 prefix should be specified with an IPv6 prefix using an IPv4-mapped IPv6 address. The .Ar precedence and .Ar label arguments are decimal numbers, which specify the precedence and label values for the entry, respectively. This operation should be performed without an existing entry for the prefix. .It Cm delete Ar prefix Delete a policy entry specified by .Ar prefix , which should be an IPv6 prefix. A corresponding entry for the prefix should have already been installed. .It Cm flush Delete all existing policy entries in the kernel. .It Cm install Ar configfile Install policy entries from a configuration file named .Ar configfile . The configuration file should contain a set of policy entries. Each entry is specified in a single line which contains an IPv6 prefix, a decimal precedence value, and a decimal label value, separated with white space or tab characters. In the configuration file, lines beginning with the pound-sign .Pq Ql # are comments and are ignored. +Note that the configuration file will be processed before attaching to a jail. .El .\" .Sh EXIT STATUS .Ex -std .\" .Sh SEE ALSO .Xr getaddrinfo 1 , .Xr getaddrinfo 3 .Rs .%A "Dave Thaler" .%A "Richard Draves" .%A "Arifumi Matsumoto" .%A "Tim Chown" .%T "Default Address Selection for Internet Protocol Version 6 (IPv6)" .%R RFC 6724 .Re .\" .Sh HISTORY The .Nm utility first appeared in the KAME IPv6 protocol stack kit. The original command name was .Nm addrselect , but it was then renamed to the current one so that the name would describe its function well. .\" .Sh BUGS .\" (to be written) diff --git a/usr.sbin/ip6addrctl/ip6addrctl.c b/usr.sbin/ip6addrctl/ip6addrctl.c index dd5df48048aa..b42a839c62eb 100644 --- a/usr.sbin/ip6addrctl/ip6addrctl.c +++ b/usr.sbin/ip6addrctl/ip6addrctl.c @@ -1,456 +1,507 @@ /* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2001 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include +#ifdef JAIL +#include +#endif #include #include #include #include #include #include +#ifdef JAIL +#include +#endif #include #include #include #include #include +#ifdef JAIL +static char *jailname; +#endif static char *configfile; struct policyqueue { TAILQ_ENTRY(policyqueue) pc_entry; struct in6_addrpolicy pc_policy; }; TAILQ_HEAD(policyhead, policyqueue); static struct policyhead policyhead = TAILQ_HEAD_INITIALIZER(policyhead); static void usage(void) __dead2; +static void attach_jail(void); static void get_policy(void); static void dump_policy(void); static int mask2plen(struct sockaddr_in6 *); static int parse_prefix(const char *, struct in6_addrpolicy *); static void make_policy_fromfile(char *); static void plen2mask(struct sockaddr_in6 *, int); static void set_policy(void); static void add_policy(char *, char *, char *); static void delete_policy(char *); static void flush_policy(void); int main(int argc, char *argv[]) { - if (argc == 1 || (argc == 2 && strcasecmp(argv[1], "show") == 0)) { + int ch; + + while ((ch = getopt(argc, argv, "j:")) != -1) { + switch (ch) { + case 'j': +#ifdef JAIL + if ((jailname = optarg) == NULL) + usage(); +#else + errx(1, "not built with jail support"); +#endif + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0 || (argc == 1 && strcasecmp(argv[0], "show") == 0)) { + attach_jail(); get_policy(); dump_policy(); - } else if (strcasecmp(argv[1], "add") == 0) { - if (argc != 5) + } else if (strcasecmp(argv[0], "add") == 0) { + if (argc != 4) usage(); - add_policy(argv[2], argv[3], argv[4]); - } else if (strcasecmp(argv[1], "delete") == 0) { - if (argc != 3) - usage(); - delete_policy(argv[2]); - } else if (strcasecmp(argv[1], "flush") == 0) { + attach_jail(); + add_policy(argv[1], argv[2], argv[3]); + } else if (strcasecmp(argv[0], "delete") == 0) { if (argc != 2) usage(); + attach_jail(); + delete_policy(argv[1]); + } else if (strcasecmp(argv[0], "flush") == 0) { + if (argc != 1) + usage(); + attach_jail(); get_policy(); flush_policy(); - } else if (strcasecmp(argv[1], "install") == 0) { - if (argc != 3) + } else if (strcasecmp(argv[0], "install") == 0) { + if (argc != 2) usage(); - configfile = argv[2]; + configfile = argv[1]; make_policy_fromfile(configfile); + attach_jail(); set_policy(); } else usage(); exit(0); } +static void +attach_jail(void) +{ +#ifdef JAIL + int jid; + + if (jailname == NULL) + return; + + jid = jail_getid(jailname); + if (jid == -1) + errx(1, "jail not found"); + if (jail_attach(jid) != 0) + errx(1, "cannot attach to jail"); +#endif +} + static void get_policy(void) { int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; size_t l; struct in6_addrpolicy *buf; struct in6_addrpolicy *pol, *ep; if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); /* NOTREACHED */ } if (l == 0) { printf("no source-address-selection policy is installed\n"); return; } if ((buf = malloc(l)) == NULL) { errx(1, "malloc failed"); /* NOTREACHED */ } if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); /* NOTREACHED */ } ep = buf + l/sizeof(*buf); for (pol = buf; pol + 1 <= ep; pol++) { struct policyqueue *new; if ((new = malloc(sizeof(*new))) == NULL) errx(1, "malloc failed\n"); new->pc_policy = *pol; TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); } free(buf); } static void dump_policy(void) { size_t addrlen; char addrbuf[NI_MAXHOST]; struct in6_addrpolicy *pol; struct policyqueue *ent; int plen, first = 1; for (ent = TAILQ_FIRST(&policyhead); ent; ent = TAILQ_NEXT(ent, pc_entry)) { pol = &ent->pc_policy; if (first) { printf("%-30s %5s %5s %8s\n", "Prefix", "Prec", "Label", "Use"); first = 0; } if ((getnameinfo((struct sockaddr *)&pol->addr, sizeof(pol->addr), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST))) { warnx("getnameinfo for prefix address failed"); continue; } if ((plen = mask2plen(&pol->addrmask)) < 0) { warnx("invalid address mask"); continue; } addrlen = strlen(addrbuf); if (addrlen + sizeof("/128") < sizeof(addrbuf)) { snprintf(&addrbuf[addrlen], sizeof(addrbuf) - addrlen - 1, "/%d", plen); printf("%-30s", addrbuf); } else /* XXX */ printf("%s/%d", addrbuf, plen); printf(" %5d %5d %8llu\n", pol->preced, pol->label, (unsigned long long)pol->use); } } #define SKIP_WHITE(p, emptyok) \ do { \ while((*(p) == ' ' || *(p) == '\t')) \ (p)++; \ if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \ goto bad; \ } while (0); #define SKIP_WORD(p) \ do { \ while(*(p) != ' ' && *(p) != '\t') \ (p)++; \ if (*(p) == '\0' || *(p) == '\n') \ goto bad; \ } while (0); static void make_policy_fromfile(char *conf) { char line[_POSIX2_LINE_MAX], *cp; char *addrstr; FILE *fp; int count = 0; struct in6_addrpolicy pol0; struct policyqueue *new; if ((fp = fopen(conf, "r")) == NULL) err(1, "fopen: %s", conf); while(fgets(line, sizeof(line), fp)) { count++; cp = line; memset(&pol0, 0, sizeof(pol0)); /* get prefix */ SKIP_WHITE(cp, 1); if (*cp == '\n') /* empty line */ continue; if (*cp == '#') continue; addrstr = cp; if (parse_prefix((const char *)addrstr, &pol0)) goto bad; /* get precedence value */ SKIP_WORD(cp); SKIP_WHITE(cp, 0); pol0.preced = atoi(cp); /* get label */ SKIP_WORD(cp); SKIP_WHITE(cp, 0); pol0.label = atoi(cp); /* parse succeeded. make a control buffer entry. */ if ((new = malloc(sizeof(*new))) == NULL) errx(1, "malloc failed\n"); memset(new, 0, sizeof(*new)); new->pc_policy = pol0; TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); } fclose(fp); return; bad: errx(1, "parse failed at line %d", count); /* NOTREACHED */ } static int parse_prefix(const char *prefix0, struct in6_addrpolicy *pol) { int e = 0, plen; char *prefix, *plenstr; struct addrinfo hints, *res; if ((prefix = strdup(prefix0)) == NULL) errx(1, "strdup failed"); if ((plenstr = strchr(prefix, '/')) == NULL) { e = -1; goto end; } *plenstr = '\0'; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = AF_INET6; if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) { warnx("getaddrinfo failed for %s: %s", prefix, gai_strerror(e)); goto end; } memcpy(&pol->addr, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); plen = atoi(plenstr + 1); if (plen < 0 || plen > 128) { warnx("invalid prefix length: %d", plen); e = -1; goto end; } plen2mask(&pol->addrmask, plen); end: free(prefix); return(e); } static void plen2mask(struct sockaddr_in6 *mask, int plen) { u_char *cp = (unsigned char *)&mask->sin6_addr; memset(mask, 0, sizeof(*mask)); mask->sin6_family = AF_INET6; /* just in case */ mask->sin6_len = sizeof(*mask); for(; plen >= 8; plen -= 8) *cp++ = 0xff; if (plen > 0) *cp = (0xff << (8 - plen)); } static void set_policy(void) { struct policyqueue *ent; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) err(1, "socket(UDP)"); for (ent = TAILQ_FIRST(&policyhead); ent; ent = TAILQ_NEXT(ent, pc_entry)) { if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy)) warn("ioctl(SIOCAADDRCTL_POLICY)"); } close(s); } static int mask2plen(struct sockaddr_in6 *mask) { int masklen, final = 0; u_char *p, *lim; masklen = 0; lim = (u_char *)(mask + 1); for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) { if (final && *p) { goto bad; } switch (*p & 0xff) { case 0xff: masklen += 8; break; case 0xfe: masklen += 7; final++; break; case 0xfc: masklen += 6; final++; break; case 0xf8: masklen += 5; final++; break; case 0xf0: masklen += 4; final++; break; case 0xe0: masklen += 3; final++; break; case 0xc0: masklen += 2; final++; break; case 0x80: masklen += 1; final++; break; case 0x00: final++; break; default: goto bad; break; } } return(masklen); bad: return(-1); } static void add_policy(char *prefix, char *prec, char *label) { struct in6_addrpolicy p; int s; memset(&p, 0, sizeof(p)); if (parse_prefix((const char *)prefix, &p)) errx(1, "bad prefix: %s", prefix); p.preced = atoi(prec); p.label = atoi(label); if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) err(1, "socket(UDP)"); if (ioctl(s, SIOCAADDRCTL_POLICY, &p)) err(1, "ioctl(SIOCAADDRCTL_POLICY)"); close(s); } static void delete_policy(char *prefix) { struct in6_addrpolicy p; int s; memset(&p, 0, sizeof(p)); if (parse_prefix((const char *)prefix, &p)) errx(1, "bad prefix: %s", prefix); if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) err(1, "socket(UDP)"); if (ioctl(s, SIOCDADDRCTL_POLICY, &p)) err(1, "ioctl(SIOCDADDRCTL_POLICY)"); close(s); } static void flush_policy(void) { struct policyqueue *ent; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) err(1, "socket(UDP)"); for (ent = TAILQ_FIRST(&policyhead); ent; ent = TAILQ_NEXT(ent, pc_entry)) { if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy)) warn("ioctl(SIOCDADDRCTL_POLICY)"); } close(s); } static void usage(void) { - fprintf(stderr, "usage: ip6addrctl [show]\n"); - fprintf(stderr, " ip6addrctl add " + fprintf(stderr, "usage: ip6addrctl [-j jail] [show]\n"); + fprintf(stderr, " ip6addrctl [-j jail] add " "