From 0a0369dedddf2869ccc77443d949e3f2d227b8a2 Mon Sep 17 00:00:00 2001 From: ume Date: Thu, 30 Oct 2003 17:46:40 +0000 Subject: add ip6addrctl(8) which is a command to configure address selection policy for IPv6 and IPv4 described in RFC3484. source address selection part of RFC3484 is not merged from KAME, yet. Obtained from: KAME --- usr.sbin/ip6addrctl/Makefile | 6 + usr.sbin/ip6addrctl/ip6addrctl.8 | 120 ++++++++ usr.sbin/ip6addrctl/ip6addrctl.c | 463 +++++++++++++++++++++++++++++ usr.sbin/ip6addrctl/ip6addrctl.conf.sample | 12 + 4 files changed, 601 insertions(+) create mode 100644 usr.sbin/ip6addrctl/Makefile create mode 100644 usr.sbin/ip6addrctl/ip6addrctl.8 create mode 100644 usr.sbin/ip6addrctl/ip6addrctl.c create mode 100644 usr.sbin/ip6addrctl/ip6addrctl.conf.sample (limited to 'usr.sbin/ip6addrctl') diff --git a/usr.sbin/ip6addrctl/Makefile b/usr.sbin/ip6addrctl/Makefile new file mode 100644 index 0000000..cd2510e --- /dev/null +++ b/usr.sbin/ip6addrctl/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= ip6addrctl +MAN= ip6addrctl.8 + +.include diff --git a/usr.sbin/ip6addrctl/ip6addrctl.8 b/usr.sbin/ip6addrctl/ip6addrctl.8 new file mode 100644 index 0000000..0ba942d --- /dev/null +++ b/usr.sbin/ip6addrctl/ip6addrctl.8 @@ -0,0 +1,120 @@ +.\" $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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 25, 2001 +.Dt IP6ADDRCTL 8 +.Os +.\" +.Sh NAME +.Nm ip6addrctl +.Nd configure address selection policy for IPv6 and IPv4 +.\" +.Sh SYNOPSIS +.Nm ip6addrctl +.Op Cm show +.Nm ip6addrctl +.Cm add +.Ar prefix precedence label +.Nm ip6addrctl +.Cm delete +.Ar prefix +.Nm ip6addrctl +.Cm flush +.Nm ip6addrctl +.Cm install +.Ar configfile +.\" +.Sh DESCRIPTION +The +.Nm +command 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 modify the table, the following operations are available: +.Bl -tag -width Ds +.It Cm add Ar prefix precedence label +add a policy entry. +.Ar Prefix +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. +.Ar Precedence +and +.Ar label +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 (#) are +comments and are ignored. +.El +.\" +.Sh RETURN VALUES +The program exits with 0 on success, non-zero on failures. +.\" +.Sh SEE ALSO +Richard Draves, +.Do +Default Address Selection for IPv6 +.Dc , +RFC 3484. +.\" +.Sh HISTORY +The +.Nm +command 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 new file mode 100644 index 0000000..eeee542 --- /dev/null +++ b/usr.sbin/ip6addrctl/ip6addrctl.c @@ -0,0 +1,463 @@ +/* $KAME: ip6addrctl.c,v 1.1 2001/12/27 12:45:24 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static char *configfile; + +struct policyqueue { + TAILQ_ENTRY(policyqueue) pc_entry; + struct in6_addrpolicy pc_policy; +}; +TAILQ_HEAD(policyhead, policyqueue); +struct policyhead policyhead; + +static void usage __P((void)); +static void get_policy __P((void)); +static void dump_policy __P((void)); +static int mask2plen __P((struct sockaddr_in6 *)); +static int parse_prefix __P((const char *, struct in6_addrpolicy *)); +static void make_policy_fromfile __P((char *)); +static void plen2mask __P((struct sockaddr_in6 *, int)); +static void set_policy __P((void)); +static void add_policy __P((char *, char *, char *)); +static void delete_policy __P((char *)); +static void flush_policy __P(()); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + TAILQ_INIT(&policyhead); + + if (argc == 1 || strcasecmp(argv[1], "show") == 0) { + get_policy(); + dump_policy(); + } else if (strcasecmp(argv[1], "add") == 0) { + if (argc < 5) + usage(); + add_policy(argv[2], argv[3], argv[4]); + } else if (strcasecmp(argv[0], "delete") == 0) { + if (argc < 3) + usage(); + delete_policy(argv[2]); + } else if (strcasecmp(argv[1], "flush") == 0) { + get_policy(); + flush_policy(); + } else if (strcasecmp(argv[1], "install") == 0) { + if (argc < 3) + usage(); + configfile = argv[2]; + make_policy_fromfile(configfile); + set_policy(); + } else + usage(); + + exit(0); +} + +static void +get_policy() +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; + size_t l; + char *buf; + struct in6_addrpolicy *pol, *ep; + + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { + err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); + /* NOTREACHED */ + } + if ((buf = malloc(l)) == NULL) { + errx(1, "malloc failed"); + /* NOTREACHED */ + } + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { + err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); + /* NOTREACHED */ + } + + ep = (struct in6_addrpolicy *)(buf + l); + for (pol = (struct in6_addrpolicy *)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() +{ + 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(conf) + 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(prefix0, pol) + 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(mask, plen) + struct sockaddr_in6 *mask; + int plen; +{ + u_char *cp = (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() +{ + 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(mask) + 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(prefix, prec, label) + char *prefix, *prec, *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(prefix) + 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() +{ + 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() +{ + fprintf(stderr, "usage: ip6addrctl [show]\n"); + fprintf(stderr, " ip6addrctl add " + "