freebsd-dev/usr.sbin/rtadvd/config.c
Yoshinobu Inoue 1aa540eb03 Forbid include of soem inet6 header files from wrong place
KAME put INET6 related stuff into sys/netinet6 dir, but IPv6
  standard API(RFC2553) require following files to be under sys/netinet.
    netinet/ip6.h
    netinet/icmp6.h
  Now those header files just include each following files.
    netinet6/ip6.h
    netinet6/icmp6.h

  Also KAME has netinet6/in6.h for easy INET6 common defs
  sharing between different BSDs, but RFC2553 requires only
  netinet/in.h should be included from userland.
  So netinet/in.h also includes netinet6/in6.h inside.

  To keep apps portability, apps should not directly include
  above files from netinet6 dir.
  Ideally, all contents of,
    netinet6/ip6.h
    netinet6/icmp6.h
    netinet6/in6.h
  should be moved into
    netinet/ip6.h
    netinet/icmp6.h
    netinet/in.h
  but to avoid big changes in this stage, add some hack, that
    -Put some special macro define into those files under neitnet
    -Let files under netinet6 cause error if it is included
     from some apps, and, if the specifal macro define is not
     defined.
     (which should have been defined if files under netinet is
     included)
    -And let them print an error message which tells the
     correct name of the include file to be included.

  Also fix apps which includes invalid header files.

Approved by: jkh

Obtained from: KAME project
2000-02-10 19:33:58 +00:00

640 lines
17 KiB
C

/*
* Copyright (C) 1998 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/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "rtadvd.h"
#include "advcap.h"
#include "timer.h"
#include "if.h"
#include "config.h"
static void makeentry __P((char *, int, char *, int));
static void make_packet __P((struct rainfo *));
static void get_prefix __P((struct rainfo *));
extern struct rainfo *ralist;
void
getconfig(intface)
char *intface;
{
int stat, pfxs, i;
char tbuf[BUFSIZ];
struct rainfo *tmp;
long val;
char buf[BUFSIZ];
char *bp = buf;
char *addr;
#define MUSTHAVE(var, cap) \
{ \
int t; \
if ((t = agetnum(cap)) < 0) { \
fprintf(stderr, "rtadvd: need %s for interface %s\n", \
cap, intface); \
exit(1); \
} \
var = t; \
}
#define MAYHAVE(var, cap, def) \
{ \
if ((var = agetnum(cap)) < 0) \
var = def; \
}
if ((stat = agetent(tbuf, intface)) <= 0) {
memset(tbuf, 0, sizeof(tbuf));
syslog(LOG_INFO,
"<%s> %s isn't defined in the configuration file"
" or the configuration file doesn't exist."
" Treat it as default",
__FUNCTION__, intface);
}
tmp = (struct rainfo *)malloc(sizeof(*ralist));
memset(tmp, 0, sizeof(*tmp));
tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
/* get interface information */
if (agetflag("nolladdr"))
tmp->advlinkopt = 0;
else
tmp->advlinkopt = 1;
if (tmp->advlinkopt) {
if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
syslog(LOG_ERR,
"<%s> can't get information of %s",
__FUNCTION__, intface);
exit(1);
}
tmp->ifindex = tmp->sdl->sdl_index;
} else
tmp->ifindex = if_nametoindex(intface);
strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
if ((tmp->phymtu = if_getmtu(intface)) == 0) {
tmp->phymtu = IPV6_MMTU;
syslog(LOG_WARNING,
"<%s> can't get interface mtu of %s. Treat as %d",
__FUNCTION__, intface, IPV6_MMTU);
}
/*
* set router configuration variables.
*/
MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
syslog(LOG_ERR,
"<%s> maxinterval must be between %d and %d",
__FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
exit(1);
}
tmp->maxinterval = (u_int)val;
MAYHAVE(val, "mininterval", tmp->maxinterval/3);
if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
syslog(LOG_ERR,
"<%s> mininterval must be between %d and %d",
__FUNCTION__,
MIN_MININTERVAL,
(tmp->maxinterval * 3) / 4);
exit(1);
}
tmp->mininterval = (u_int)val;
MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
tmp->hoplimit = val & 0xff;
MAYHAVE(val, "raflags", 0);
tmp->managedflg= val & ND_RA_FLAG_MANAGED;
tmp->otherflg = val & ND_RA_FLAG_OTHER;
MAYHAVE(val, "rltime", tmp->maxinterval * 3);
if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
syslog(LOG_ERR,
"<%s> router lifetime on %s must be 0 or"
" between %d and %d",
__FUNCTION__, intface,
tmp->maxinterval, MAXROUTERLIFETIME);
exit(1);
}
tmp->lifetime = val & 0xffff;
MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
if (val > MAXREACHABLETIME) {
syslog(LOG_ERR,
"<%s> reachable time must be no greater than %d",
__FUNCTION__, MAXREACHABLETIME);
exit(1);
}
tmp->reachabletime = (u_int32_t)val;
MAYHAVE(val, "retrans", DEF_ADVRETRANSTIMER);
if (val < 0 || val > 0xffffffff) {
syslog(LOG_ERR,
"<%s> retrans time out of range", __FUNCTION__);
exit(1);
}
tmp->retranstimer = (u_int32_t)val;
/* prefix information */
if ((pfxs = agetnum("addrs")) < 0) {
/* auto configure prefix information */
if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
syslog(LOG_ERR,
"<%s> conflicting prefix configuration for %s: "
"automatic and manual config at the same time",
__FUNCTION__, intface);
exit(1);
}
get_prefix(tmp);
}
else {
tmp->pfxs = pfxs;
for (i = 0; i < pfxs; i++) {
struct prefix *pfx;
char entbuf[256];
int added = (pfxs > 1) ? 1 : 0;
/* allocate memory to store prefix information */
if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
syslog(LOG_ERR,
"<%s> can't allocate enough memory",
__FUNCTION__);
exit(1);
}
/* link into chain */
insque(pfx, &tmp->prefix);
makeentry(entbuf, i, "prefixlen", added);
MAYHAVE(val, entbuf, 64);
if (val < 0 || val > 128) {
syslog(LOG_ERR,
"<%s> prefixlen out of range",
__FUNCTION__);
exit(1);
}
pfx->prefixlen = (int)val;
makeentry(entbuf, i, "pinfoflags", added);
MAYHAVE(val, entbuf,
(ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
makeentry(entbuf, i, "vltime", added);
MAYHAVE(val, entbuf, DEF_ADVVALIDLIFETIME);
if (val < 0 || val > 0xffffffff) {
syslog(LOG_ERR,
"<%s> vltime out of range",
__FUNCTION__);
exit(1);
}
pfx->validlifetime = (u_int32_t)val;
makeentry(entbuf, i, "pltime", added);
MAYHAVE(val, entbuf, DEF_ADVPREFERREDLIFETIME);
if (val < 0 || val > 0xffffffff) {
syslog(LOG_ERR,
"<%s> pltime out of range",
__FUNCTION__);
exit(1);
}
pfx->preflifetime = (u_int32_t)val;
makeentry(entbuf, i, "addr", added);
addr = (char *)agetstr(entbuf, &bp);
if (addr == NULL) {
syslog(LOG_ERR,
"<%s> need %s as an prefix for "
"interface %s",
__FUNCTION__, entbuf, intface);
exit(1);
}
if (inet_pton(AF_INET6, addr,
&pfx->prefix) != 1) {
syslog(LOG_ERR,
"<%s> inet_pton failed for %s",
__FUNCTION__, addr);
exit(1);
}
if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
syslog(LOG_ERR,
"<%s> multicast prefix(%s) must "
"not be advertised (IF=%s)",
__FUNCTION__, addr, intface);
exit(1);
}
if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
syslog(LOG_NOTICE,
"<%s> link-local prefix(%s) will be"
" advertised on %s",
__FUNCTION__, addr, intface);
}
}
MAYHAVE(val, "mtu", 0);
if (val < 0 || val > 0xffffffff) {
syslog(LOG_ERR,
"<%s> mtu out of range", __FUNCTION__);
exit(1);
}
tmp->linkmtu = (u_int32_t)val;
if (tmp->linkmtu == 0) {
char *mtustr;
if ((mtustr = (char *)agetstr("mtu", &bp)) &&
strcmp(mtustr, "auto") == 0)
tmp->linkmtu = tmp->phymtu;
}
else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
syslog(LOG_ERR,
"<%s> advertised link mtu must be between"
" least MTU and physical link MTU",
__FUNCTION__);
exit(1);
}
/* okey */
tmp->next = ralist;
ralist = tmp;
/* construct the sending packet */
make_packet(tmp);
/* set timer */
tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
tmp, tmp);
ra_timer_update((void *)tmp, &tmp->timer->tm);
rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
}
static void
get_prefix(struct rainfo *rai)
{
size_t len;
u_char *buf, *lim, *next;
u_char ntopbuf[INET6_ADDRSTRLEN];
if ((len = rtbuf_len()) < 0) {
syslog(LOG_ERR,
"<%s> can't get buffer length for routing info",
__FUNCTION__);
exit(1);
}
if ((buf = malloc(len)) == NULL) {
syslog(LOG_ERR,
"<%s> can't allocate buffer", __FUNCTION__);
exit(1);
}
if (get_rtinfo(buf, &len) < 0) {
syslog(LOG_ERR,
"<%s> can't get routing inforamtion", __FUNCTION__);
exit(1);
}
lim = buf + len;
next = get_next_msg(buf, lim, rai->ifindex, &len,
RTADV_TYPE2BITMASK(RTM_GET));
while (next < lim) {
struct prefix *pp;
struct in6_addr *a;
/* allocate memory to store prefix info. */
if ((pp = malloc(sizeof(*pp))) == NULL) {
syslog(LOG_ERR,
"<%s> can't get allocate buffer for prefix",
__FUNCTION__);
exit(1);
}
memset(pp, 0, sizeof(*pp));
/* set prefix and its length */
a = get_addr(next);
memcpy(&pp->prefix, a, sizeof(*a));
if ((pp->prefixlen = get_prefixlen(next)) < 0) {
syslog(LOG_ERR,
"<%s> failed to get prefixlen "
"or prefixl is invalid",
__FUNCTION__);
exit(1);
}
syslog(LOG_DEBUG,
"<%s> add %s/%d to prefix list on %s",
__FUNCTION__,
inet_ntop(AF_INET6, a, ntopbuf, INET6_ADDRSTRLEN),
pp->prefixlen, rai->ifname);
/* set other fields with protocol defaults */
pp->validlifetime = DEF_ADVVALIDLIFETIME;
pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
pp->onlinkflg = 1;
pp->autoconfflg = 1;
/* link into chain */
insque(pp, &rai->prefix);
/* counter increment */
rai->pfxs++;
/* forward pointer and get next prefix(if any) */
next += len;
next = get_next_msg(next, lim, rai->ifindex,
&len, RTADV_TYPE2BITMASK(RTM_GET));
}
free(buf);
}
static void
makeentry(buf, id, string, add)
char *buf, *string;
int id, add;
{
strcpy(buf, string);
if (add) {
char *cp;
cp = (char *)index(buf, '\0');
cp += sprintf(cp, "%d", id);
*cp = '\0';
}
}
/*
* Add a prefix to the list of specified interface and reconstruct
* the outgoing packet.
* The prefix must not be in the list.
* XXX: other parameter of the prefix(e.g. lifetime) shoule be
* able to be specified.
*/
static void
add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
{
struct prefix *prefix;
u_char ntopbuf[INET6_ADDRSTRLEN];
if ((prefix = malloc(sizeof(*prefix))) == NULL) {
syslog(LOG_ERR, "<%s> memory allocation failed",
__FUNCTION__);
return; /* XXX: error or exit? */
}
prefix->prefix = ipr->ipr_prefix.sin6_addr;
prefix->prefixlen = ipr->ipr_plen;
prefix->validlifetime = ipr->ipr_vltime;
prefix->preflifetime = ipr->ipr_pltime;
prefix->onlinkflg = ipr->ipr_raf_onlink;
prefix->autoconfflg = ipr->ipr_raf_auto;
insque(prefix, &rai->prefix);
syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
__FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
ntopbuf, INET6_ADDRSTRLEN),
ipr->ipr_plen, rai->ifname);
/* free the previous packet */
free(rai->ra_data);
rai->ra_data = 0;
/* reconstruct the packet */
rai->pfxs++;
make_packet(rai);
/*
* reset the timer so that the new prefix will be advertised quickly.
*/
rai->initcounter = 0;
ra_timer_update((void *)rai, &rai->timer->tm);
rtadvd_set_timer(&rai->timer->tm, rai->timer);
}
/*
* Delete a prefix to the list of specified interface and reconstruct
* the outgoing packet.
* The prefix must be in the list
*/
void
delete_prefix(struct rainfo *rai, struct prefix *prefix)
{
u_char ntopbuf[INET6_ADDRSTRLEN];
remque(prefix);
syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
__FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
ntopbuf, INET6_ADDRSTRLEN),
prefix->prefixlen, rai->ifname);
free(prefix);
rai->pfxs--;
make_packet(rai);
}
/*
* Try to get an in6_prefixreq contents for a prefix which matches
* ipr->ipr_prefix and ipr->ipr_plen and belongs to
* the interface whose name is ipr->ipr_name[].
*/
static int
init_prefix(struct in6_prefixreq *ipr)
{
int s;
if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
strerror(errno));
exit(1);
}
if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
strerror(errno));
ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
ipr->ipr_raf_onlink = 1;
ipr->ipr_raf_auto = 1;
/* omit other field initialization */
}
else if (ipr->ipr_origin < PR_ORIG_RR) {
u_char ntopbuf[INET6_ADDRSTRLEN];
syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
"lower than PR_ORIG_RR(router renumbering)."
"This should not happen if I am router", __FUNCTION__,
inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
sizeof(ntopbuf)), ipr->ipr_origin);
return 1;
}
close(s);
return 0;
}
void
make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
{
struct in6_prefixreq ipr;
memset(&ipr, 0, sizeof(ipr));
if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
"exist. This should not happen! %s", __FUNCTION__,
ifindex, strerror(errno));
exit(1);
}
ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
ipr.ipr_prefix.sin6_family = AF_INET6;
ipr.ipr_prefix.sin6_addr = *addr;
ipr.ipr_plen = plen;
if (init_prefix(&ipr))
return; /* init failed by some error */
add_prefix(rai, &ipr);
}
static void
make_packet(struct rainfo *rainfo)
{
size_t packlen, lladdroptlen = 0;
char *buf;
struct nd_router_advert *ra;
struct nd_opt_prefix_info *ndopt_pi;
struct nd_opt_mtu *ndopt_mtu;
struct prefix *pfx;
/* calculate total length */
packlen = sizeof(struct nd_router_advert);
if (rainfo->advlinkopt) {
if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
syslog(LOG_INFO,
"<%s> link-layer address option has"
" null length on %s."
" Treat as not included.",
__FUNCTION__, rainfo->ifname);
rainfo->advlinkopt = 0;
}
packlen += lladdroptlen;
}
if (rainfo->pfxs)
packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
if (rainfo->linkmtu)
packlen += sizeof(struct nd_opt_mtu);
/* allocate memory for the packet */
if ((buf = malloc(packlen)) == NULL) {
syslog(LOG_ERR,
"<%s> can't get enough memory for an RA packet",
__FUNCTION__);
exit(1);
}
rainfo->ra_data = buf;
/* XXX: what if packlen > 576? */
rainfo->ra_datalen = packlen;
/*
* construct the packet
*/
ra = (struct nd_router_advert *)buf;
ra->nd_ra_type = ND_ROUTER_ADVERT;
ra->nd_ra_code = 0;
ra->nd_ra_cksum = 0;
ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
ra->nd_ra_flags_reserved = 0;
ra->nd_ra_flags_reserved |=
rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
ra->nd_ra_flags_reserved |=
rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
ra->nd_ra_reachable = htonl(rainfo->reachabletime);
ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
buf += sizeof(*ra);
if (rainfo->advlinkopt) {
lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
buf += lladdroptlen;
}
if (rainfo->linkmtu) {
ndopt_mtu = (struct nd_opt_mtu *)buf;
ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
ndopt_mtu->nd_opt_mtu_len = 1;
ndopt_mtu->nd_opt_mtu_reserved = 0;
ndopt_mtu->nd_opt_mtu_mtu = ntohl(rainfo->linkmtu);
buf += sizeof(struct nd_opt_mtu);
}
for (pfx = rainfo->prefix.next;
pfx != &rainfo->prefix; pfx = pfx->next) {
ndopt_pi = (struct nd_opt_prefix_info *)buf;
ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
ndopt_pi->nd_opt_pi_len = 4;
ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
ndopt_pi->nd_opt_pi_flags_reserved = 0;
if (pfx->onlinkflg)
ndopt_pi->nd_opt_pi_flags_reserved |=
ND_OPT_PI_FLAG_ONLINK;
if (pfx->autoconfflg)
ndopt_pi->nd_opt_pi_flags_reserved |=
ND_OPT_PI_FLAG_AUTO;
ndopt_pi->nd_opt_pi_valid_time = ntohl(pfx->validlifetime);
ndopt_pi->nd_opt_pi_preferred_time =
ntohl(pfx->preflifetime);
ndopt_pi->nd_opt_pi_reserved2 = 0;
ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
buf += sizeof(struct nd_opt_prefix_info);
}
return;
}