Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146780891
D55449.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D55449.diff
View Options
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -452,6 +452,7 @@
nhop_free(nh);
return (error);
}
+ set_nhop_expire_from_info(nh, info);
*pnh = nhop_get_nhop(nh, &error);
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
--- a/sys/netinet/icmp6.h
+++ b/sys/netinet/icmp6.h
@@ -360,6 +360,8 @@
/* prefix follows */
} __packed;
+#define ND_OPT_RTI_FLAG_PRF_MASK 0x18 /* 00011000 */
+
struct nd_opt_rdnss { /* RDNSS option (RFC 6106) */
u_int8_t nd_opt_rdnss_type;
u_int8_t nd_opt_rdnss_len;
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -282,7 +282,7 @@
#define V_ip6_temp_regen_advance VNET(ip6_temp_regen_advance)
union nd_opts {
- struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */
+ struct nd_opt_hdr *nd_opt_array[24]; /* max = ND_OPT_NONCE */
struct {
struct nd_opt_hdr *zero;
struct nd_opt_hdr *src_lladdr;
@@ -300,10 +300,20 @@
struct nd_opt_hdr *__res13;
struct nd_opt_nonce *nonce;
struct nd_opt_hdr *__res15;
+ struct nd_opt_hdr *__res16;
+ struct nd_opt_hdr *__res17;
+ struct nd_opt_hdr *__res18;
+ struct nd_opt_hdr *__res19;
+ struct nd_opt_hdr *__res20;
+ struct nd_opt_hdr *__res21;
+ struct nd_opt_hdr *__res22;
+ struct nd_opt_hdr *__res23;
+ struct nd_opt_route_info *rti_beg;
struct nd_opt_hdr *search; /* multiple opts */
struct nd_opt_hdr *last; /* multiple opts */
int done;
struct nd_opt_prefix_info *pi_end;/* multiple opts, end */
+ struct nd_opt_route_info *rti_end;/* multiple opts, end */
} nd_opt_each;
};
#define nd_opts_src_lladdr nd_opt_each.src_lladdr
@@ -313,6 +323,8 @@
#define nd_opts_rh nd_opt_each.rh
#define nd_opts_mtu nd_opt_each.mtu
#define nd_opts_nonce nd_opt_each.nonce
+#define nd_opts_rti nd_opt_each.rti_beg
+#define nd_opts_rti_end nd_opt_each.rti_end
#define nd_opts_search nd_opt_each.search
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -490,6 +490,14 @@
ndopts->nd_opts_pi_end =
(struct nd_opt_prefix_info *)nd_opt;
break;
+ case ND_OPT_ROUTE_INFO:
+ if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
+ ndopts->nd_opt_array[nd_opt->nd_opt_type]
+ = nd_opt;
+ }
+ ndopts->nd_opts_rti_end =
+ (struct nd_opt_route_info *)nd_opt;
+ break;
/* What about ND_OPT_ROUTE_INFO? RFC 4191 */
case ND_OPT_RDNSS: /* RFC 6106 */
case ND_OPT_DNSSL: /* RFC 6106 */
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -60,6 +60,7 @@
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/route/route_ctl.h>
+#include <net/route/route_var.h>
#include <net/radix.h>
#include <net/vnet.h>
@@ -77,9 +78,20 @@
MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery");
+struct nd_routectl {
+ struct ifnet *ndrt_ifp; /* nexthop interface */
+ struct sockaddr_in6 ndrt_gateway; /* gateway address */
+ struct sockaddr_in6 ndrt_prefix; /* route prefix */
+ struct sockaddr_in6 ndrt_mask; /* route prefix mask */
+ uint8_t ndrt_plen; /* route prefix len */
+ uint8_t ndrt_flags; /* route info flags */
+ uint32_t ndrt_lifetime; /* route info lifetime (sec) */
+};
+
static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *,
bool, int);
+static int nd6_routelist_update(struct nd_routectl *);
static int nd6_prefix_onlink(struct nd_prefix *);
static int in6_get_tmp_ifid(struct in6_aliasreq *);
@@ -409,6 +421,108 @@
(void)prelist_update(&pr, dr, auth, mcast);
}
+static void
+nd6_ra_opt_rti(struct nd_opt_hdr *hdr, struct ifnet *ifp,
+ struct in6_addr saddr6, struct nd_defrouter **dr0)
+{
+ struct nd_opt_route_info *rti = NULL;
+ struct nd_defrouter *dr;
+ struct nd_routectl rt;
+ struct in6_addr prefix, netmask;
+ char ip6bufs[INET6_ADDRSTRLEN];
+ int olen;
+ uint8_t pref;
+
+ if (hdr->nd_opt_type != ND_OPT_ROUTE_INFO)
+ return;
+
+ /* RFC 4191 section 2.3, Field validation */
+ rti = (struct nd_opt_route_info *)hdr;
+ if (rti->nd_opt_rti_len == 0 || rti->nd_opt_rti_len > 3) {
+ nd6log((LOG_INFO,
+ "%s: invalid option len %d for route "
+ "information option, ignored\n", __func__,
+ rti->nd_opt_rti_len));
+ return;
+ }
+ if (rti->nd_opt_rti_prefixlen > 128 ||
+ (rti->nd_opt_rti_prefixlen > 64 && rti->nd_opt_rti_len != 3) ||
+ (rti->nd_opt_rti_prefixlen > 0 && rti->nd_opt_rti_len < 2)) {
+ nd6log((LOG_INFO,
+ "%s: invalid prefix len %d with option len %d for route "
+ "information option, ignored\n", __func__,
+ rti->nd_opt_rti_prefixlen, rti->nd_opt_rti_len));
+ return;
+ }
+ pref = rti->nd_opt_rti_flags & ND_OPT_RTI_FLAG_PRF_MASK;
+ if ((pref & ND_RA_FLAG_RTPREF_RSV) != 0) {
+ nd6log((LOG_INFO,
+ "%s: reserved preference %d for route "
+ "information option, ignored\n", __func__,
+ pref));
+ return;
+ }
+
+ /*
+ * RFC 4191 section 2.3: The bits in the prefix after
+ * the prefix length (if any) are reserved and MUST be
+ * initialized to zero by the sender and ignored by the receiver.
+ */
+ memset(&prefix, 0, sizeof(prefix));
+ /*
+ * Calculate the variable length of the option and copy the remaining
+ * length into the prefix.
+ * The resulting value cannot exceed the size of struct in6_addr
+ * since the option length is limited to 3 (24 bytes).
+ */
+ olen = (rti->nd_opt_rti_len << 3) - sizeof(struct nd_opt_route_info);
+ memcpy(&prefix, (caddr_t)rti + sizeof(struct nd_opt_route_info), olen);
+ in6_prefixlen2mask(&netmask, rti->nd_opt_rti_prefixlen);
+ IN6_MASK_ADDR(&prefix, &netmask);
+ if (IN6_IS_ADDR_MULTICAST(&prefix)
+ || IN6_IS_ADDR_LINKLOCAL(&prefix)) {
+ nd6log((LOG_INFO,
+ "%s: invalid prefix %s, ignored\n",
+ __func__, ip6_sprintf(ip6bufs, &prefix)));
+ return;
+ }
+
+ /*
+ * RFC 4191 section 3.1: The Router Preference and Lifetime
+ * values in a ::/0 Route Information Option override the
+ * preference and lifetime values in the Router Advertisement header.
+ */
+ dr = *dr0;
+ if (rti->nd_opt_rti_prefixlen == 0 && dr != NULL) {
+#ifdef notyet
+ dr->rtlifetime = ntohl(rti->nd_opt_rti_lifetime);
+#endif
+ dr->raflags &= ~ND_OPT_RTI_FLAG_PRF_MASK;
+ dr->raflags |= pref;
+ *dr0 = defrtrlist_update(dr);
+ nd6log((LOG_INFO,
+ "%s: override default route with route "
+ "information option\n", __func__));
+ return;
+ }
+
+ memset(&rt, 0, sizeof(rt));
+ rt.ndrt_ifp = ifp;
+ rt.ndrt_prefix.sin6_len = sizeof(struct sockaddr_in6);
+ rt.ndrt_prefix.sin6_family = AF_INET6;
+ rt.ndrt_prefix.sin6_addr = prefix;
+ rt.ndrt_gateway.sin6_len = sizeof(struct sockaddr_in6);
+ rt.ndrt_gateway.sin6_family = AF_INET6;
+ rt.ndrt_gateway.sin6_addr = saddr6;
+ rt.ndrt_mask.sin6_len = sizeof(struct sockaddr_in6);
+ rt.ndrt_mask.sin6_family = AF_INET6;
+ rt.ndrt_mask.sin6_addr = netmask;
+ rt.ndrt_plen = rti->nd_opt_rti_prefixlen;
+ rt.ndrt_flags = rti->nd_opt_rti_flags;
+ rt.ndrt_lifetime = ntohl(rti->nd_opt_rti_lifetime);
+ (void)nd6_routelist_update(&rt);
+}
+
static void
nd6_ra_opt_mtu(struct nd_opt_mtu *optmtu, struct ifnet *ifp,
struct in6_addr saddr6)
@@ -490,7 +604,7 @@
struct in6_ifextra *ndi;
struct ip6_hdr *ip6;
struct nd_router_advert *nd_ra;
- struct nd_opt_hdr *pt;
+ struct nd_opt_hdr *pt, *rti;
struct in6_addr saddr6;
struct nd_defrouter dr0, *dr;
union nd_opts ndopts;
@@ -602,13 +716,23 @@
#ifdef EXPERIMENTAL
defrtr_ipv6_only_ifp(ifp);
#endif
+ /*
+ * Authenticity for NA consists authentication for
+ * both IP header and IP datagrams, doesn't it ?
+ */
+ auth = ((m->m_flags & M_AUTHIPHDR) && (m->m_flags & M_AUTHIPDGM));
+
+ /* Route Information */
+ if (ndopts.nd_opts_rti != NULL) {
+ for (rti = (struct nd_opt_hdr *)ndopts.nd_opts_rti;
+ rti <= (struct nd_opt_hdr *)ndopts.nd_opts_rti_end;
+ rti = (struct nd_opt_hdr *)((caddr_t)rti +
+ (rti->nd_opt_len << 3))) {
+ nd6_ra_opt_rti(rti, ifp, saddr6, &dr);
+ }
+ }
/* Prefix Information */
if (ndopts.nd_opts_pi != NULL) {
- /*
- * Authenticity for NA consists authentication for
- * both IP header and IP datagrams, doesn't it ?
- */
- auth = ((m->m_flags & M_AUTHIPHDR) && (m->m_flags & M_AUTHIPDGM));
for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
pt = (struct nd_opt_hdr *)((caddr_t)pt +
@@ -2279,6 +2403,212 @@
return (error);
}
+/*
+ * Install advertised route via default router.
+ */
+static int
+nd6_route_rtrequest(struct nd_routectl *key)
+{
+ struct rib_cmd_info rc;
+ struct rib_head *rnh;
+ struct nhop_object *nh;
+ struct ifaddr *ifa;
+ struct ifnet *ifp;
+ struct sockaddr *dst, *gw, *mask;
+ int flags, error;
+ time_t expire;
+
+ NET_EPOCH_ASSERT();
+
+ ifp = key->ndrt_ifp;
+ dst = (struct sockaddr *)&key->ndrt_prefix;
+ gw = (struct sockaddr *)&key->ndrt_gateway;
+ mask = (struct sockaddr *)&key->ndrt_mask;
+ flags = RTF_DYNAMIC | RTF_GATEWAY;
+ if (key->ndrt_plen == 128) {
+ flags |= RTF_HOST;
+ mask = NULL;
+ }
+
+ rnh = rt_tables_get_rnh_safe(key->ndrt_ifp->if_fib, AF_INET6);
+ if (rnh == NULL)
+ return (EINVAL);
+
+ /* Get the best ifa for the given interface and gateway. */
+ if ((ifa = ifaof_ifpforaddr(gw, ifp)) == NULL)
+ return (ENETUNREACH);
+
+ expire = time_second + key->ndrt_lifetime;
+ struct rt_metrics rmx = {
+ .rmx_expire = expire,
+ };
+ struct rt_addrinfo info = {
+ .rti_flags = flags,
+ .rti_info = {
+ [RTAX_DST] = dst,
+ [RTAX_GATEWAY] = gw,
+ [RTAX_NETMASK] = mask,
+ [RTAX_AUTHOR] = gw,
+ },
+ .rti_ifa = ifa,
+ .rti_ifp = ifp,
+ .rti_mflags = RTV_EXPIRE,
+ .rti_rmx = &rmx,
+ };
+ /* XXX: route preference */
+ struct route_nhop_data rnd = {
+ .rnd_weight = RT_DEFAULT_WEIGHT,
+ };
+
+ error = nhop_create_from_info(rnh, &info, &nh);
+ if (error == 0) {
+ nhop_set_origin(nh, NH_ORIGIN_REDIRECT);
+ rnd.rnd_nhop = nh;
+ error = rib_add_route_px(ifp->if_fib, dst, key->ndrt_plen, &rnd, RTM_F_CREATE, &rc);
+ }
+ if (error != 0)
+ return (error);
+ RTSTAT_INC(rts_dynamic);
+
+ /* Send notification of a route addition to userland. */
+ rt_missmsg_fib(RTM_REDIRECT, &info, flags | RTF_UP, error, ifp->if_fib);
+
+ return (error);
+}
+
+/*
+ * Update lifetime of advertised route.
+ */
+static int
+nd6_route_rtupdate(struct nd_routectl *key)
+{
+ struct rib_cmd_info rc;
+ time_t expire;
+ int error;
+
+ expire = time_second + key->ndrt_lifetime;
+ struct rt_metrics rmx = {
+ .rmx_expire = expire,
+ };
+ struct rt_addrinfo info = {
+ .rti_info[RTAX_DST] = (struct sockaddr *)&key->ndrt_prefix,
+ .rti_mflags = RTV_EXPIRE,
+ .rti_rmx = &rmx,
+ };
+ if (key->ndrt_plen != 128)
+ info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&key->ndrt_mask;
+ error = rib_change_route(key->ndrt_ifp->if_fib, &info, &rc);
+
+ return (error);
+}
+
+/*
+ * Delete advertised route.
+ */
+static int
+nd6_route_rtdelete(struct nd_routectl *key)
+{
+ struct sockaddr *dst, *gw;
+ struct rib_cmd_info rc;
+ struct nhop_object *nh;
+ struct ifnet *ifp;
+ int error;
+
+ ifp = key->ndrt_ifp;
+ dst = (struct sockaddr *)&key->ndrt_prefix;
+ gw = (struct sockaddr *)&key->ndrt_gateway;
+ error = rib_del_route_px(ifp->if_fib, dst, key->ndrt_plen,
+ rib_match_gw, gw, 0, &rc);
+ if (error == 0) {
+ nh = nhop_select_func(rc.rc_nh_old, 0);
+ rt_routemsg(RTM_DELETE, rc.rc_rt, nh, ifp->if_fib);
+ }
+
+ return (error);
+}
+
+/*
+ * Lookup for exact match of advertised route with gateway
+ * as its nexthop address.
+ */
+static bool
+nd6_route_rtlookup(struct nd_routectl *key)
+{
+ RIB_RLOCK_TRACKER;
+ struct rib_head *rnh;
+ struct route_nhop_data rnd;
+ struct sockaddr *dst, *gw;
+
+ dst = (struct sockaddr *)&key->ndrt_prefix;
+ gw = (struct sockaddr *)&key->ndrt_gateway;
+ rnh = rt_tables_get_rnh_safe(key->ndrt_ifp->if_fib, AF_INET6);
+ if (rnh == NULL)
+ return (false);
+
+ RIB_RLOCK(rnh);
+ rib_lookup_prefix_plen(rnh, dst, key->ndrt_plen, &rnd);
+ RIB_RUNLOCK(rnh);
+ if (rnd.rnd_nhop == NULL)
+ return (false);
+
+ return (match_nhop_gw(rnd.rnd_nhop, gw));
+}
+
+static int
+nd6_routelist_update(struct nd_routectl *new)
+{
+ char ip6buf[INET6_ADDRSTRLEN];
+ int error = 0;
+
+ NET_EPOCH_ASSERT();
+
+ /*
+ * RFC 4191 section 3.1: If the received route's
+ * lifetime is zero, the route is removed from the Routing
+ * Table if present.
+ */
+ if (new->ndrt_lifetime == 0) {
+ error = nd6_route_rtdelete(new);
+ if (error != 0) {
+ nd6log((LOG_DEBUG,
+ "%s: failed to delete the route %s/%d on %s (errno=%d)\n",
+ __func__, ip6_sprintf(ip6buf, &new->ndrt_prefix.sin6_addr),
+ new->ndrt_plen, if_name(new->ndrt_ifp), error));
+ }
+ return (error);
+ }
+
+ if (nd6_route_rtlookup(new)) {
+ /*
+ * RFC 4191 section 3.1: the route's lifetime and
+ * preference is updated if the route is already present.
+ * XXX: preference on routing table? (ndrt_flags)
+ */
+ error = nd6_route_rtupdate(new);
+ if (error != 0) {
+ nd6log((LOG_DEBUG,
+ "%s: failed to update route lifetime of "
+ "%s/%d from RA on %s (errno=%d)\n",
+ __func__, ip6_sprintf(ip6buf, &new->ndrt_prefix.sin6_addr),
+ new->ndrt_plen, if_name(new->ndrt_ifp), error));
+ }
+ } else {
+ /*
+ * RFC 4191 section 3.1: If a route's lifetime is non-zero,
+ * the route is added to the Routing Table if not present.
+ */
+ error = nd6_route_rtrequest(new);
+ if (error != 0) {
+ nd6log((LOG_NOTICE,
+ "%s: failed to add route %s/%d from RA on %s (errno=%d)\n",
+ __func__, ip6_sprintf(ip6buf, &new->ndrt_prefix.sin6_addr),
+ new->ndrt_plen, if_name(new->ndrt_ifp), error));
+ }
+ }
+
+ return (error);
+}
+
/*
* Get a randomized interface identifier for a temporary address
* Based on RFC 8981, Section 3.3.1.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 6, 1:21 PM (5 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29327061
Default Alt Text
D55449.diff (13 KB)
Attached To
Mode
D55449: ndp: Add support for route information (RFC 4191)
Attached
Detach File
Event Timeline
Log In to Comment