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
This commit is contained in:
Hajimu UMEMOTO 2003-10-30 17:46:40 +00:00
parent 4c6867a867
commit f71d0e1192
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=121748
5 changed files with 602 additions and 0 deletions

View File

@ -44,6 +44,7 @@ SUBDIR= IPXrouted \
ifmcstat \
inetd \
iostat \
ip6addrctl \
jail \
jexec \
jls \

View File

@ -0,0 +1,6 @@
# $FreeBSD$
PROG= ip6addrctl
MAN= ip6addrctl.8
.include <bsd.prog.mk>

View File

@ -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)

View File

@ -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 <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 <net/if_var.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);
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 "
"<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 -c path_to_this_file
#
# $FreeBSD$
#
#Format:
#Prefix Precedence Label
::1/128 50 0
::/0 40 1
2002::/16 30 2
::/96 20 3
::ffff:0:0/96 10 4