Add ip6addrctl from FreeBSD

This commit is contained in:
christos 2015-12-12 23:35:56 +00:00
parent 5b5956f338
commit b649613962
4 changed files with 604 additions and 0 deletions

View File

@ -0,0 +1,7 @@
# $NetBSD: Makefile,v 1.1 2015/12/12 23:35:56 christos Exp $
# $FreeBSD: head/usr.sbin/ip6addrctl/Makefile 241134 2012-10-02 14:48:03Z eadler $
PROG= ip6addrctl
MAN= ip6addrctl.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,127 @@
.\" $NetBSD: ip6addrctl.8,v 1.1 2015/12/12 23:35:56 christos Exp $
.\" $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: head/usr.sbin/ip6addrctl/ip6addrctl.8 140368 2005-01-17 07:44:44Z ru $
.\"
.Dd September 25, 2001
.Dt IP6ADDRCTL 8
.Os
.\"
.Sh NAME
.Nm ip6addrctl
.Nd configure address selection policy for IPv6 and IPv4
.\"
.Sh SYNOPSIS
.Nm
.Op Cm show
.Nm
.Cm add
.Ar prefix precedence label
.Nm
.Cm delete
.Ar prefix
.Nm
.Cm flush
.Nm
.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 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.
.El
.\"
.Sh EXIT STATUS
.Ex -std
.\"
.Sh SEE ALSO
.Rs
.%A "Richard Draves"
.%T "Default Address Selection for IPv6"
.%N RFC 3484
.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)

View File

@ -0,0 +1,458 @@
/* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz 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: head/usr.sbin/ip6addrctl/ip6addrctl.c 281143 2015-04-06 09:42:23Z glebius $
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: ip6addrctl.c,v 1.1 2015/12/12 23:35:56 christos Exp $");
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <stdlib.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <err.h>
static char *configfile;
struct policyqueue {
TAILQ_ENTRY(policyqueue) pc_entry;
struct in6_addrpolicy pc_policy;
};
TAILQ_HEAD(policyhead, policyqueue);
static struct policyhead policyhead;
static void usage(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[])
{
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[1], "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(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, sizeof(mib) / sizeof(mib[0]), 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, sizeof(mib) / sizeof(mib[0]), 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 "
"<prefix> <precedence> <label>\n");
fprintf(stderr, " ip6addrctl delete <prefix>\n");
fprintf(stderr, " ip6addrctl flush\n");
fprintf(stderr, " ip6addrctl install <configfile>\n");
exit(1);
}

View File

@ -0,0 +1,12 @@
# default policy table based on RFC 3484.
# usage: ip6addrctl install path_to_this_file
#
# $FreeBSD: head/usr.sbin/ip6addrctl/ip6addrctl.conf.sample 123872 2003-12-26 17:10:58Z ume $
#
#Format:
#Prefix Precedence Label
::1/128 50 0
::/0 40 1
2002::/16 30 2
::/96 20 3
::ffff:0:0/96 10 4