Initial implementation of draft-ietf-6man-ipv6only-flag.

This change defines the RA "6" (IPv6-Only) flag which routers
may advertise, kernel logic to check if all routers on a link
have the flag set and accordingly update a per-interface flag.

If all routers agree that it is an IPv6-only link, ether_output_frame(),
based on the interface flag, will filter out all ETHERTYPE_IP/ARP
frames, drop them, and return EAFNOSUPPORT to upper layers.

The change also updates ndp to show the "6" flag, ifconfig to
display the IPV6_ONLY nd6 flag if set, and rtadvd to allow
announcing the flag.

Further changes to tcpdump (contrib code) are availble and will
be upstreamed.

Tested the code (slightly earlier version) with 2 FreeBSD
IPv6 routers, a FreeBSD laptop on ethernet as well as wifi,
and with Win10 and OSX clients (which did not fall over with
the "6" flag set but not understood).

We may also want to (a) implement and RX filter, and (b) over
time enahnce user space to, say, stop dhclient from running
when the interface flag is set.  Also we might want to start
IPv6 before IPv4 in the future.

All the code is hidden under the EXPERIMENTAL option and not
compiled by default as the draft is a work-in-progress and
we cannot rely on the fact that IANA will assign the bits
as requested by the draft and hence they may change.

Dear 6man, you have running code.

Discussed with:	Bob Hinden, Brian E Carpenter
This commit is contained in:
Bjoern A. Zeeb 2018-10-30 20:08:48 +00:00
parent 9978bd996b
commit 201100c58b
12 changed files with 120 additions and 0 deletions

View File

@ -51,6 +51,9 @@ SRCS+= ifpfsync.c # pfsync(4) support
SRCS+= ifbridge.c # bridge support
SRCS+= iflagg.c # lagg support
.if ${MK_EXPERIMENTAL} != "no"
CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
.endif
.if ${MK_INET6_SUPPORT} != "no"
CFLAGS+= -DINET6
.endif

View File

@ -57,9 +57,17 @@ static const char rcsid[] =
#include "ifconfig.h"
#define MAX_SYSCTL_TRY 5
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \
"\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \
"\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD" \
"\012IPV6_ONLY" \
"\020DEFAULTIF"
#else
#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \
"\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \
"\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD\020DEFAULTIF"
#endif
static int isnd6defif(int);
void setnd6flags(const char *, int, int, const struct afswtch *);

View File

@ -475,6 +475,26 @@ ether_output_frame(struct ifnet *ifp, struct mbuf *m)
return (0);
}
#ifdef EXPERIMENTAL
#if defined(INET6) && defined(INET)
/* draft-ietf-6man-ipv6only-flag */
/* Catch ETHERTYPE_IP, and ETHERTYPE_ARP if we are v6-only. */
if ((ND_IFINFO(ifp)->flags & ND6_IFF_IPV6_ONLY) != 0) {
struct ether_header *eh;
eh = mtod(m, struct ether_header *);
switch (ntohs(eh->ether_type)) {
case ETHERTYPE_IP:
case ETHERTYPE_ARP:
m_freem(m);
return (EAFNOSUPPORT);
/* NOTREACHED */
break;
};
}
#endif
#endif
/*
* Queue message on interface, update output statistics if
* successful, and start output if interface not yet active.

View File

@ -244,6 +244,10 @@ struct nd_router_advert { /* router advertisement */
#define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */
#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
#ifdef EXPERIMENTAL
#define ND_RA_FLAG_IPV6_ONLY 0x02 /* draft-ietf-6man-ipv6only-flag */
#endif
#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
struct nd_neighbor_solicit { /* neighbor solicitation */

View File

@ -90,6 +90,9 @@ struct nd_ifinfo {
#define ND6_IFF_NO_RADR 0x40
#define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */
#define ND6_IFF_NO_DAD 0x100
#ifdef EXPERIMENTAL
#define ND6_IFF_IPV6_ONLY 0x200 /* draft-ietf-6man-ipv6only-flag */
#endif
#ifdef _KERNEL
#define ND_IFINFO(ifp) \

View File

@ -204,6 +204,37 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len)
m_freem(m);
}
#ifdef EXPERIMENTAL
/*
* An initial update routine for draft-ietf-6man-ipv6only-flag.
* We need to iterate over all default routers for the given
* interface to see whether they are all advertising the "6"
* (IPv6-Only) flag. If they do set, otherwise unset, the
* interface flag we later use to filter on.
*/
static void
defrtr_ipv6_only_ifp(struct ifnet *ifp)
{
struct nd_defrouter *dr;
bool ipv6_only;
ipv6_only = true;
ND6_RLOCK();
TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry)
if (dr->ifp == ifp &&
(dr->raflags & ND_RA_FLAG_IPV6_ONLY) == 0)
ipv6_only = false;
ND6_RUNLOCK();
IF_AFDATA_WLOCK(ifp);
if (ipv6_only)
ND_IFINFO(ifp)->flags |= ND6_IFF_IPV6_ONLY;
else
ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
IF_AFDATA_WUNLOCK(ifp);
}
#endif
/*
* Receive Router Advertisement Message.
*
@ -319,6 +350,9 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len)
}
}
dr = defrtrlist_update(&dr0);
#ifdef EXPERIMENTAL
defrtr_ipv6_only_ifp(ifp);
#endif
}
/*
@ -692,6 +726,10 @@ defrouter_del(struct nd_defrouter *dr)
if (ND_IFINFO(dr->ifp)->flags & ND6_IFF_ACCEPT_RTADV)
rt6_flush(&dr->rtaddr, dr->ifp);
#ifdef EXPERIMENTAL
defrtr_ipv6_only_ifp(dr->ifp);
#endif
if (dr->installed) {
deldr = dr;
defrouter_delreq(dr);

View File

@ -13,6 +13,8 @@
# A PARTICULAR PURPOSE.
# $FreeBSD$
.include <src.opts.mk>
.PATH: ${SRCTOP}/contrib/tcpdump
PROG= ndp
@ -22,6 +24,11 @@ SRCS= ndp.c gmt2local.c
CFLAGS+= -I. -I${.CURDIR} -I${SRCTOP}/contrib/tcpdump
CFLAGS+= -D_U_=""
.if ${MK_EXPERIMENTAL} != "no"
CFLAGS+= -DEXPERIMENTAL
CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
.endif
WARNS?= 3
.include <bsd.prog.mk>

View File

@ -1096,6 +1096,9 @@ rtrlist()
printf(", flags=%s%s",
p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
p->flags & ND_RA_FLAG_OTHER ? "O" : "");
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
printf("%s", p->flags & ND_RA_FLAG_IPV6_ONLY ? "6" : "");
#endif
rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
printf(", pref=%s", rtpref_str[rtpref]);

View File

@ -14,11 +14,18 @@
#
# $FreeBSD$
.include <src.opts.mk>
PROG= rtadvd
MAN= rtadvd.conf.5 rtadvd.8
SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c timer_subr.c \
control.c control_server.c
.if ${MK_EXPERIMENTAL} != "no"
CFLAGS+= -DEXPERIMENTAL
CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
.endif
LIBADD= util
WARNS?= 1

View File

@ -437,6 +437,10 @@ getconfig(struct ifinfo *ifi)
}
val |= ND_RA_FLAG_RTPREF_LOW;
}
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
if (strchr(flagstr, '6'))
val |= ND_RA_FLAG_IPV6_ONLY;
#endif
} else
MAYHAVE(val, "raflags", 0);
@ -452,6 +456,9 @@ getconfig(struct ifinfo *ifi)
__func__, rai->rai_rtpref, ifi->ifi_ifname);
goto getconfig_free_rai;
}
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
rai->rai_ipv6onlyflg = val & ND_RA_FLAG_IPV6_ONLY;
#endif
MAYHAVE(val, "rltime", rai->rai_maxinterval * 3);
if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval ||
@ -1406,6 +1413,10 @@ make_packet(struct rainfo *rai)
rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0;
ra->nd_ra_flags_reserved |=
rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0;
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
ra->nd_ra_flags_reserved |=
rai->rai_ipv6onlyflg ? ND_RA_FLAG_IPV6_ONLY : 0;
#endif
ra->nd_ra_router_lifetime = htons(rai->rai_lifetime);
ra->nd_ra_reachable = htonl(rai->rai_reachabletime);
ra->nd_ra_retransmit = htonl(rai->rai_retranstimer);

View File

@ -1160,6 +1160,19 @@ ra_input(int len, struct nd_router_advert *nra,
sizeof(ntopbuf)), on_off[rai->rai_otherflg]);
inconsistent++;
}
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
/* 6 flag */
if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_IPV6_ONLY) !=
rai->rai_ipv6onlyflg) {
syslog(LOG_NOTICE,
"6 flag inconsistent on %s:"
" %s from %s, %s from us",
ifi->ifi_ifname, on_off[!rai->rai_ipv6onlyflg],
inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
sizeof(ntopbuf)), on_off[rai->rai_ipv6onlyflg]);
inconsistent++;
}
#endif
/* Reachable Time */
reachabletime = ntohl(nra->nd_ra_reachable);
if (reachabletime && rai->rai_reachabletime &&

View File

@ -196,6 +196,9 @@ struct rainfo {
uint16_t rai_mininterval; /* MinRtrAdvInterval */
int rai_managedflg; /* AdvManagedFlag */
int rai_otherflg; /* AdvOtherConfigFlag */
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
int rai_ipv6onlyflg; /* AdvIPv6OnlyFlag */
#endif
int rai_rtpref; /* router preference */
uint32_t rai_linkmtu; /* AdvLinkMTU */