From 201100c58b7ad34aab387ddecb3c96c68cf203c5 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Tue, 30 Oct 2018 20:08:48 +0000 Subject: [PATCH] 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 --- sbin/ifconfig/Makefile | 3 +++ sbin/ifconfig/af_nd6.c | 8 ++++++++ sys/net/if_ethersubr.c | 20 ++++++++++++++++++++ sys/netinet/icmp6.h | 4 ++++ sys/netinet6/nd6.h | 3 +++ sys/netinet6/nd6_rtr.c | 38 ++++++++++++++++++++++++++++++++++++++ usr.sbin/ndp/Makefile | 7 +++++++ usr.sbin/ndp/ndp.c | 3 +++ usr.sbin/rtadvd/Makefile | 7 +++++++ usr.sbin/rtadvd/config.c | 11 +++++++++++ usr.sbin/rtadvd/rtadvd.c | 13 +++++++++++++ usr.sbin/rtadvd/rtadvd.h | 3 +++ 12 files changed, 120 insertions(+) diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index ae5b0168240..9e0f73bba24 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -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 diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c index 8c576117e22..2ce97394c70 100644 --- a/sbin/ifconfig/af_nd6.c +++ b/sbin/ifconfig/af_nd6.c @@ -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 *); diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 28dac295e07..628e32b1764 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -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. diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 310684b8b47..d4da06d0ff5 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -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 */ diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index cabfeec0dba..7544d23c666 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -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) \ diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index ec1aa13730c..e00a0d857b5 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -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); diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile index c2ff3090b49..52d8304a436 100644 --- a/usr.sbin/ndp/Makefile +++ b/usr.sbin/ndp/Makefile @@ -13,6 +13,8 @@ # A PARTICULAR PURPOSE. # $FreeBSD$ +.include + .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 diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c index 10e186d1d9f..a006006aab3 100644 --- a/usr.sbin/ndp/ndp.c +++ b/usr.sbin/ndp/ndp.c @@ -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]); diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile index 5d627e55b26..920c6d966af 100644 --- a/usr.sbin/rtadvd/Makefile +++ b/usr.sbin/rtadvd/Makefile @@ -14,11 +14,18 @@ # # $FreeBSD$ +.include + 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 diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index cd7134cecae..f537ace0c25 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -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); diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index d4618587644..d7237f06c22 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -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 && diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 005a42c2038..885d2fbb3c8 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -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 */