Hello, I have ported support for advertising route information, from FreeBSD's rtadvd. This is derived from FreeBSD's svn revision 78064. That was from KAME originally.
------------------------------------------------------------------------ UMEZAWA Takeshi (FAMILY Given) <[email protected]> Internet Initiative Japan Inc. diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 58476b6..432113c 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -200,6 +200,13 @@ struct mld_hdr { * Neighbor Discovery */ +#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ + +#define ND_RA_FLAG_RTPREF_HIGH 0x08 /* 00001000 */ +#define ND_RA_FLAG_RTPREF_MEDIUM 0x00 /* 00000000 */ +#define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */ +#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ + struct nd_router_solicit { /* router solicitation */ struct icmp6_hdr nd_rs_hdr; /* could be followed by options */ @@ -282,6 +289,7 @@ struct nd_opt_hdr { /* Neighbor discovery option header */ #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 +#define ND_OPT_ROUTE_INFO 24 #define ND_OPT_RDNSS 25 #define ND_OPT_DNSSL 31 @@ -314,6 +322,14 @@ struct nd_opt_mtu { /* MTU option */ u_int32_t nd_opt_mtu_mtu; } __packed; +struct nd_opt_route_info { /* route info */ + u_int8_t nd_opt_rti_type; + u_int8_t nd_opt_rti_len; + u_int8_t nd_opt_rti_prefixlen; + u_int8_t nd_opt_rti_flags; + u_int32_t nd_opt_rti_lifetime; +} __packed; + struct nd_opt_rdnss { /* RDNSS option */ u_int8_t nd_opt_rdnss_type; u_int8_t nd_opt_rdnss_len; diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index 53719ec..f38b69d 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -111,6 +111,7 @@ getconfig(intface) TAILQ_INIT(&tmp->prefixes); TAILQ_INIT(&tmp->rdnsss); TAILQ_INIT(&tmp->dnssls); + TAILQ_INIT(&tmp->rtinfos); SLIST_INIT(&tmp->soliciters); /* check if we are allowed to forward packets (if not determined) */ @@ -325,6 +326,77 @@ getconfig(intface) if (tmp->pfxs == 0 && !agetflag("noifprefix")) get_prefix(tmp); + tmp->rtinfocnt = 0; + for (i = -1; i < MAXRTINFO; i++) { + struct rtinfo *rti; + char entbuf[256]; + const char *flagstr; + + makeentry(entbuf, sizeof(entbuf), i, "rtprefix"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + rti = malloc(sizeof(struct rtinfo)); + if (rti == NULL) + fatal("malloc"); + + if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { + log_warn("inet_pton failed for %s", addr); + exit(1); + } + + makeentry(entbuf, sizeof(entbuf), i, "rtplen"); + MAYHAVE(val, entbuf, 64); + if (val < 0 || val > 128) { + log_warnx("route prefixlen (%ld) for %s " + "on %s out of range", + val, addr, intface); + exit(1); + } + rti->prefixlen = (int)val; + + makeentry(entbuf, sizeof(entbuf), i, "rtflags"); + if ((flagstr = (char *)agetstr(entbuf, &bp))) { + val = 0; + if (strchr(flagstr, 'h')) + val |= ND_RA_FLAG_RTPREF_HIGH; + if (strchr(flagstr, 'l')) { + if (val & ND_RA_FLAG_RTPREF_HIGH) { + log_warnx("the \'h\' and \'l\'" + " route preferences are" + " exclusive"); + exit(1); + } + val |= ND_RA_FLAG_RTPREF_LOW; + } + } else { + MAYHAVE(val, entbuf, 0); + } + rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { + log_warnx("invalid route preference (%02x)" + " for %s/%d on %s", + rti->rtpref, addr, rti->prefixlen, intface); + exit(1); + } + + makeentry(entbuf, sizeof(entbuf), i, "rtltime"); + MAYHAVE(val64, entbuf, -1); + if (val64 == -1) + val = tmp->lifetime; + if (val64 < 0 || val64 >= 0xffffffff) { + log_warnx("route lifetime (%d) " + " for %s/%d on %s out of range", + rti->rtpref, addr, rti->prefixlen, intface); + exit(1); + } + rti->lifetime = (uint32_t)val64; + + TAILQ_INSERT_TAIL(&tmp->rtinfos, rti, entry); + tmp->rtinfocnt++; + } + tmp->rdnsscnt = 0; for (i = -1; i < MAXRDNSS; ++i) { struct rdnss *rds; @@ -698,9 +770,11 @@ make_packet(struct rainfo *rainfo) struct nd_router_advert *ra; struct nd_opt_prefix_info *ndopt_pi; struct nd_opt_mtu *ndopt_mtu; + struct nd_opt_route_info *ndopt_rti; struct nd_opt_rdnss *ndopt_rdnss; struct nd_opt_dnssl *ndopt_dnssl; struct prefix *pfx; + struct rtinfo *rti; struct rdnss *rds; struct dnssl *dsl; struct dnssldom *dnsd; @@ -720,6 +794,9 @@ make_packet(struct rainfo *rainfo) packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; if (rainfo->linkmtu) packlen += sizeof(struct nd_opt_mtu); + TAILQ_FOREACH(rti, &rainfo->rtinfos, entry) + packlen += sizeof(struct nd_opt_route_info) + + ((rti->prefixlen + 0x3f) >> 6) * 8; TAILQ_FOREACH(rds, &rainfo->rdnsss, entry) packlen += sizeof(struct nd_opt_rdnss) + 16 * rds->servercnt; TAILQ_FOREACH(dsl, &rainfo->dnssls, entry) { @@ -828,6 +905,19 @@ make_packet(struct rainfo *rainfo) buf += sizeof(struct nd_opt_prefix_info); } + TAILQ_FOREACH(rti, &rainfo->rtinfos, entry) { + uint8_t psize = (rti->prefixlen + 0x3f) >> 6; + + ndopt_rti = (struct nd_opt_route_info *)buf; + ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; + ndopt_rti->nd_opt_rti_len = 1 + psize; + ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen; + ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref; + ndopt_rti->nd_opt_rti_lifetime = htonl(rti->lifetime); + memcpy(ndopt_rti + 1, &rti->prefix, psize * 8); + buf += sizeof(struct nd_opt_route_info) + psize * 8; + } + TAILQ_FOREACH(rds, &rainfo->rdnsss, entry) { ndopt_rdnss = (struct nd_opt_rdnss *)buf; ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h index 53b97bc..495af88 100644 --- a/usr.sbin/rtadvd/config.h +++ b/usr.sbin/rtadvd/config.h @@ -44,3 +44,4 @@ extern void get_prefix __P((struct rainfo *)); #define MAXPREFIX 100 #define MAXRDNSS 100 #define MAXDNSSL 100 +#define MAXRTINFO 100 diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index 37757b4..48b2ada 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -121,6 +121,7 @@ union nd_opts { #define NDOPT_FLAG_MTU (1 << 4) #define NDOPT_FLAG_RDNSS (1 << 5) #define NDOPT_FLAG_DNSSL (1 << 6) +#define NDOPT_FLAG_RTINFO (1 << 7) u_int32_t ndopt_flags[] = { [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, @@ -128,6 +129,7 @@ u_int32_t ndopt_flags[] = { [ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO, [ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR, [ND_OPT_MTU] = NDOPT_FLAG_MTU, + [ND_OPT_ROUTE_INFO] = NDOPT_FLAG_RTINFO, [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, }; @@ -812,7 +814,8 @@ ra_input(int len, struct nd_router_advert *ra, if (nd6_options((struct nd_opt_hdr *)(ra + 1), len - sizeof(struct nd_router_advert), &ndopts, NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO - | NDOPT_FLAG_MTU | NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL)) { + | NDOPT_FLAG_MTU | NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL + | NDOPT_FLAG_RTINFO)) { log_warnx("ND option check failed for an RA from %s on %s", inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, INET6_ADDRSTRLEN), @@ -1112,6 +1115,7 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, } if (hdr->nd_opt_type > ND_OPT_MTU && + hdr->nd_opt_type != ND_OPT_ROUTE_INFO && hdr->nd_opt_type != ND_OPT_RDNSS && hdr->nd_opt_type != ND_OPT_DNSSL) { diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf index 04725a4..d4bdaab 100644 --- a/usr.sbin/rtadvd/rtadvd.conf +++ b/usr.sbin/rtadvd/rtadvd.conf @@ -19,4 +19,5 @@ #ef0:\ # :addr="2001:db8:ffff:1000::":prefixlen#64:\ +# :rtprefix="2001:db8:ffff:1001::":\ # :rdnss="2001:db8:ffff:1000::1":dnssl="example.com": diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5 index 185ed56..3de5c34 100644 --- a/usr.sbin/rtadvd/rtadvd.conf.5 +++ b/usr.sbin/rtadvd/rtadvd.conf.5 @@ -231,6 +231,45 @@ is specified for this item, MTU option will be included and its value will be set to the interface MTU automatically. .El .Pp +The following items are for ICMPv6 route information option, +which will be attached to router advertisement header. +These items are optional. +Each items can be augmented with number, like +.Dq Li rtplen2 , +to specify multiple routes. +.Bl -tag -width indent +.It Cm \&rtprefix +(str) The prefix filled into the Prefix field of route information option. +Since +.Dq \&: +is used for +.Xr termcap 5 +file format as well as IPv6 numeric address, the field MUST be quoted by +doublequote character. +.It Cm \&rtplen +(num) Prefix length field in route information option. +The default value is 64. +.It Cm \&rtflags +(str or num) A 8-bit flags field in route information option. +Currently only the preference values are defined. +The notation is same as that of the raflags field. +Bit 4 +.Po +.Li 0x10 +.Pc +and +Bit 3 +.Po +.Li 0x08 +.Pc +are used to encode the route preference for the route. +The default value is 0x00, i.e., medium preference. +.It Cm \&rtltime +(num) route lifetime field in route information option. +.Pq unit: seconds . +The default value is same as router lifetime. +.El +.Pp The following items are for ICMPv6 RDNSS option, used to give a list of recursive DNS servers to hosts. If this item is omitted, no information about DNS servers will be advertised. @@ -349,6 +388,13 @@ ef0:\e .%T Neighbor Discovery for IP version 6 (IPv6) .Re .Rs +.%A R. Draves +.%A D. Thaler +.%D 2005 +.%R RFC 4191 +.%T Default Router Preferences and More-Specific Routes +.Re +.Rs .%A J. Jeong .%A S. Park .%A L. Beloeil diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 34ffbae..5c2ccec 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -82,6 +82,15 @@ struct prefix { struct in6_addr prefix; }; +struct rtinfo { + TAILQ_ENTRY(rtinfo) entry; + + uint32_t lifetime; + int rtpref; + int prefixlen; + struct in6_addr prefix; +}; + struct rdnss { TAILQ_ENTRY(rdnss) entry; @@ -143,6 +152,8 @@ struct rainfo { int rdnsscnt; /* number of rdnss entries */ TAILQ_HEAD(dnssllist, dnssl) dnssls; int dnsslcnt; + TAILQ_HEAD(rtinfolist, rtinfo) rtinfos; + int rtinfocnt; long clockskew; /* used for consisitency check of lifetimes */
