Page MenuHomeFreeBSD

D23075.id67073.diff
No OneTemporary

D23075.id67073.diff

Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -4102,6 +4102,7 @@
net/raw_cb.c standard
net/raw_usrreq.c standard
net/route.c standard
+net/route_temporal.c standard
net/rss_config.c optional inet rss | inet6 rss
net/rtsock.c standard
net/slcompress.c optional netgraph_vjc | sppp | \
Index: sys/net/route.h
===================================================================
--- sys/net/route.h
+++ sys/net/route.h
@@ -478,6 +478,8 @@
typedef int rt_walktree_f_t(struct rtentry *, void *);
typedef void rt_setwarg_t(struct rib_head *, uint32_t, int, void *);
+void rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f,
+ void *arg, bool report);
void rt_foreach_fib_walk(int af, rt_setwarg_t *, rt_walktree_f_t *, void *);
void rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg);
void rt_flushifroutes_af(struct ifnet *, int);
@@ -495,14 +497,15 @@
void rtalloc_ign_fib(struct route *ro, u_long ignflags, u_int fibnum);
struct rtentry *rtalloc1_fib(struct sockaddr *, int, u_long, u_int);
int rtioctl_fib(u_long, caddr_t, u_int);
-void rtredirect_fib(struct sockaddr *, struct sockaddr *,
- struct sockaddr *, int, struct sockaddr *, u_int);
int rtrequest_fib(int, struct sockaddr *,
struct sockaddr *, struct sockaddr *, int, struct rtentry **, u_int);
int rtrequest1_fib(int, struct rt_addrinfo *, struct rtentry **, u_int);
int rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t,
struct rt_addrinfo *);
void rib_free_info(struct rt_addrinfo *info);
+int rib_add_redirect(u_int fibnum, struct sockaddr *dst,
+ struct sockaddr *gateway, struct sockaddr *author, struct ifnet *ifp,
+ int flags, int expire_sec);
#endif
Index: sys/net/route.c
===================================================================
--- sys/net/route.c
+++ sys/net/route.c
@@ -187,10 +187,11 @@
{
struct rib_head **rnh;
- KASSERT(table >= 0 && table < rt_numfibs, ("%s: table out of bounds.",
- __func__));
- KASSERT(fam >= 0 && fam < (AF_MAX+1), ("%s: fam out of bounds.",
- __func__));
+ KASSERT(table >= 0 && table < rt_numfibs,
+ ("%s: table out of bounds (0 <= %d < %d)", __func__, table,
+ rt_numfibs));
+ KASSERT(fam >= 0 && fam < (AF_MAX + 1),
+ ("%s: fam out of bounds (0 <= %d < %d)", __func__, fam, AF_MAX+1));
/* rnh is [fib=0][af=0]. */
rnh = (struct rib_head **)V_rt_tables;
@@ -364,6 +365,8 @@
rh->rib_vnet = curvnet;
#endif
+ tmproutes_init(rh);
+
/* Init locks */
RIB_LOCK_INIT(rh);
@@ -394,6 +397,8 @@
rt_table_destroy(struct rib_head *rh)
{
+ tmproutes_destroy(rh);
+
rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head);
/* Assume table is already empty */
@@ -584,132 +589,78 @@
RT_UNLOCK(rt);
}
-
/*
- * Force a routing table entry to the specified
- * destination to go through the given gateway.
- * Normally called as a result of a routing redirect
- * message from the network layer.
+ * Adds a temporal redirect entry to the routing table.
+ * @fibnum: fib number
+ * @dst: destination to install redirect to
+ * @gateway: gateway to go via
+ * @author: sockaddr of originating router, can be NULL
+ * @ifp: interface to use for the redirected route
+ * @flags: set of flags to add. Allowed: RTF_GATEWAY
+ * @lifetime_sec: time in seconds to expire this redirect.
+ *
+ * Retuns 0 on success, errno otherwise.
*/
-void
-rtredirect_fib(struct sockaddr *dst,
- struct sockaddr *gateway,
- struct sockaddr *netmask,
- int flags,
- struct sockaddr *src,
- u_int fibnum)
+int
+rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway,
+ struct sockaddr *author, struct ifnet *ifp, int flags, int lifetime_sec)
{
struct rtentry *rt;
- int error = 0;
+ int error;
struct rt_addrinfo info;
+ struct rt_metrics rti_rmx;
struct ifaddr *ifa;
- struct rib_head *rnh;
NET_EPOCH_ASSERT();
- ifa = NULL;
- rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
- if (rnh == NULL) {
- error = EAFNOSUPPORT;
- goto out;
+ if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL)
+ return (EAFNOSUPPORT);
+
+ /* Verify the allowed flag mask. */
+ KASSERT(((flags & ~(RTF_GATEWAY)) == 0),
+ ("invalid redirect flags: %x", flags));
+
+ /* Get the best ifa for the given interface and gateway. */
+ if ((ifa = ifaof_ifpforaddr(gateway, ifp)) == NULL)
+ return (ENETUNREACH);
+ ifa_ref(ifa);
+
+ bzero(&info, sizeof(info));
+ info.rti_info[RTAX_DST] = dst;
+ info.rti_info[RTAX_GATEWAY] = gateway;
+ info.rti_ifa = ifa;
+ info.rti_ifp = ifp;
+ info.rti_flags = flags | RTF_DYNAMIC;
+
+ /* Setup route metrics to define expire time. */
+ bzero(&rti_rmx, sizeof(rti_rmx));
+ /* Set expire time as absolute. */
+ rti_rmx.rmx_expire = lifetime_sec + time_second;
+ info.rti_mflags |= RTV_EXPIRE;
+ info.rti_rmx = &rti_rmx;
+
+ error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum);
+ ifa_free(ifa);
+
+ if (error != 0) {
+ /* TODO: add per-fib redirect stats. */
+ return (error);
}
- /* verify the gateway is directly reachable */
- if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) {
- error = ENETUNREACH;
- goto out;
- }
- rt = rtalloc1_fib(dst, 0, 0UL, fibnum); /* NB: rt is locked */
- /*
- * If the redirect isn't from our current router for this dst,
- * it's either old or wrong. If it redirects us to ourselves,
- * we have a routing loop, perhaps as a result of an interface
- * going down recently.
- */
- if (!(flags & RTF_DONE) && rt) {
- if (!sa_equal(src, rt->rt_gateway)) {
- error = EINVAL;
- goto done;
- }
- if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
- error = EINVAL;
- goto done;
- }
- }
- if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
- error = EHOSTUNREACH;
- goto done;
- }
- /*
- * Create a new entry if we just got back a wildcard entry
- * or the lookup failed. This is necessary for hosts
- * which use routing redirects generated by smart gateways
- * to dynamically build the routing tables.
- */
- if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
- goto create;
- /*
- * Don't listen to the redirect if it's
- * for a route to an interface.
- */
- if (rt->rt_flags & RTF_GATEWAY) {
- if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
- /*
- * Changing from route to net => route to host.
- * Create new route, rather than smashing route to net.
- */
- create:
- if (rt != NULL)
- RTFREE_LOCKED(rt);
-
- flags |= RTF_DYNAMIC;
- bzero((caddr_t)&info, sizeof(info));
- info.rti_info[RTAX_DST] = dst;
- info.rti_info[RTAX_GATEWAY] = gateway;
- info.rti_info[RTAX_NETMASK] = netmask;
- ifa_ref(ifa);
- info.rti_ifa = ifa;
- info.rti_flags = flags;
- error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum);
- if (rt != NULL) {
- RT_LOCK(rt);
- flags = rt->rt_flags;
- }
- if (error == 0)
- RTSTAT_INC(rts_dynamic);
- } else {
- /*
- * Smash the current notion of the gateway to
- * this destination. Should check about netmask!!!
- */
- if ((flags & RTF_GATEWAY) == 0)
- rt->rt_flags &= ~RTF_GATEWAY;
- rt->rt_flags |= RTF_MODIFIED;
- flags |= RTF_MODIFIED;
- RTSTAT_INC(rts_newgateway);
- /*
- * add the key and gateway (in one malloc'd chunk).
- */
- RT_UNLOCK(rt);
- RIB_WLOCK(rnh);
- RT_LOCK(rt);
- rt_setgate(rt, rt_key(rt), gateway);
- RIB_WUNLOCK(rnh);
- }
- } else
- error = EHOSTUNREACH;
-done:
- if (rt)
- RTFREE_LOCKED(rt);
- out:
- if (error)
- RTSTAT_INC(rts_badredirect);
- bzero((caddr_t)&info, sizeof(info));
+ RT_LOCK(rt);
+ flags = rt->rt_flags;
+ RTFREE_LOCKED(rt);
+
+ RTSTAT_INC(rts_dynamic);
+
+ /* Send notification of a route addition to userland. */
+ bzero(&info, sizeof(info));
info.rti_info[RTAX_DST] = dst;
info.rti_info[RTAX_GATEWAY] = gateway;
- info.rti_info[RTAX_NETMASK] = netmask;
- info.rti_info[RTAX_AUTHOR] = src;
+ info.rti_info[RTAX_AUTHOR] = author;
rt_missmsg_fib(RTM_REDIRECT, &info, flags, error, fibnum);
+
+ return (0);
}
/*
@@ -1059,62 +1010,82 @@
}
/*
- * Iterates over all existing fibs in system.
- * Deletes each element for which @filter_f function returned
- * non-zero value.
- * If @af is not AF_UNSPEC, iterates over fibs in particular
- * address family.
+ * Iterates over a routing table specified by @fibnum and @family and
+ * deletes elements marked by @filter_f.
+ * @fibnum: rtable id
+ * @family: AF_ address family
+ * @filter_f: function returning non-zero value for items to delete
+ * @arg: data to pass to the @filter_f function
+ * @report: true if rtsock notification is needed.
*/
void
-rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg)
+rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report)
{
struct rib_head *rnh;
struct rt_delinfo di;
struct rtentry *rt;
- uint32_t fibnum;
- int i, start, end;
+ rnh = rt_tables_get_rnh(fibnum, family);
+ if (rnh == NULL)
+ return;
+
bzero(&di, sizeof(di));
di.info.rti_filter = filter_f;
di.info.rti_filterdata = arg;
+ di.rnh = rnh;
+ RIB_WLOCK(rnh);
+ rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
+ RIB_WUNLOCK(rnh);
+
+ if (di.head == NULL)
+ return;
+
+ /* We might have something to reclaim. */
+ while (di.head != NULL) {
+ rt = di.head;
+ di.head = rt->rt_chain;
+ rt->rt_chain = NULL;
+
+ /* TODO std rt -> rt_addrinfo export */
+ di.info.rti_info[RTAX_DST] = rt_key(rt);
+ di.info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+
+ rt_notifydelete(rt, &di.info);
+
+ if (report)
+ rt_routemsg(RTM_DELETE, rt, rt->rt_ifp, 0, fibnum);
+ RTFREE_LOCKED(rt);
+ }
+}
+
+/*
+ * Iterates over all existing fibs in system and deletes each element
+ * for which @filter_f function returns non-zero value.
+ * If @family is not AF_UNSPEC, iterates over fibs in particular
+ * address family.
+ */
+void
+rt_foreach_fib_walk_del(int family, rt_filter_f_t *filter_f, void *arg)
+{
+ u_int fibnum;
+ int i, start, end;
+
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
/* Do we want some specific family? */
- if (af != AF_UNSPEC) {
- start = af;
- end = af;
+ if (family != AF_UNSPEC) {
+ start = family;
+ end = family;
} else {
start = 1;
end = AF_MAX;
}
for (i = start; i <= end; i++) {
- rnh = rt_tables_get_rnh(fibnum, i);
- if (rnh == NULL)
+ if (rt_tables_get_rnh(fibnum, i) == NULL)
continue;
- di.rnh = rnh;
- RIB_WLOCK(rnh);
- rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
- RIB_WUNLOCK(rnh);
-
- if (di.head == NULL)
- continue;
-
- /* We might have something to reclaim */
- while (di.head != NULL) {
- rt = di.head;
- di.head = rt->rt_chain;
- rt->rt_chain = NULL;
-
- /* TODO std rt -> rt_addrinfo export */
- di.info.rti_info[RTAX_DST] = rt_key(rt);
- di.info.rti_info[RTAX_NETMASK] = rt_mask(rt);
-
- rt_notifydelete(rt, &di.info);
- RTFREE_LOCKED(rt);
- }
-
+ rib_walk_del(fibnum, i, filter_f, arg, 0);
}
}
}
@@ -1699,6 +1670,9 @@
/* XXX mtu manipulation will be done in rnh_addaddr -- itojun */
rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes);
+
+ if (rn != NULL && rt->rt_expire > 0)
+ tmproutes_update(rnh, rt);
rt_old = NULL;
if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) {
Index: sys/net/route_temporal.c
===================================================================
--- /dev/null
+++ sys/net/route_temporal.c
@@ -0,0 +1,160 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Alexander V. Chernikov
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * This file contains code responsible for expiring temporal routes
+ * (typically, redirect-originated) from the route tables.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/rmlock.h>
+#include <sys/callout.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/route_var.h>
+#include <net/vnet.h>
+
+/*
+ * Callback returning 1 for the expired routes.
+ * Updates time of the next nearest route expiration as a side effect.
+ */
+static int
+expire_route(const struct rtentry *rt, void *arg)
+{
+ time_t *next_callout;
+
+ if (rt->rt_expire == 0)
+ return (0);
+
+ if (rt->rt_expire <= time_uptime)
+ return (1);
+
+ next_callout = (time_t *)arg;
+
+ /*
+ * Update next_callout to determine the next ts to
+ * run the callback at.
+ */
+ if (*next_callout == 0 || *next_callout > rt->rt_expire)
+ *next_callout = rt->rt_expire;
+
+ return (0);
+}
+
+/*
+ * Per-rnh callout function traversing the tree and deleting
+ * expired routes. Calculates next callout run by looking at
+ * the rt_expire time for the remaining temporal routes.
+ */
+static void
+expire_callout(void *arg)
+{
+ struct rib_head *rnh;
+ time_t next_expire = 0;
+ int seconds;
+
+ rnh = (struct rib_head *)arg;
+
+ CURVNET_SET(rnh->rib_vnet);
+
+ rib_walk_del(rnh->rib_fibnum, rnh->rib_family, expire_route,
+ (void *)&next_expire, 1);
+
+ RIB_WLOCK(rnh);
+ if (next_expire > 0) {
+ seconds = (next_expire - time_uptime);
+ if (seconds < 0)
+ seconds = 0;
+ callout_reset_sbt(&rnh->expire_callout, SBT_1S * seconds,
+ SBT_1MS * 500, expire_callout, rnh, 0);
+ rnh->next_expire = next_expire;
+ } else {
+ /*
+ * Before resetting next_expire, check that tmproutes_update()
+ * has not kicked in and scheduled another invocation.
+ */
+ if (callout_pending(&rnh->expire_callout) == 0)
+ rnh->next_expire = 0;
+ }
+ RIB_WUNLOCK(rnh);
+ CURVNET_RESTORE();
+}
+
+/*
+ * Function responsible for updating the time of the next calllout
+ * w.r.t. new temporal routes insertion.
+ *
+ * Called by the routing code upon adding new temporal route
+ * to the tree. RIB_WLOCK must be held.
+ */
+void
+tmproutes_update(struct rib_head *rnh, struct rtentry *rt)
+{
+ int seconds;
+
+ RIB_WLOCK_ASSERT(rnh);
+
+ if (rnh->next_expire == 0 || rnh->next_expire > rt->rt_expire) {
+ /*
+ * Callback is not scheduled, is executing,
+ * or is scheduled for a later time than we need.
+ *
+ * Schedule the one for the current @rt expiration time.
+ */
+ seconds = (rt->rt_expire - time_uptime);
+ if (seconds < 0)
+ seconds = 0;
+ callout_reset_sbt(&rnh->expire_callout, SBT_1S * seconds,
+ SBT_1MS * 500, expire_callout, rnh, 0);
+
+ rnh->next_expire = rt->rt_expire;
+ }
+}
+
+void
+tmproutes_init(struct rib_head *rh)
+{
+
+ callout_init(&rh->expire_callout, 1);
+}
+
+
+void
+tmproutes_destroy(struct rib_head *rh)
+{
+
+ callout_drain(&rh->expire_callout);
+}
+
Index: sys/net/route_var.h
===================================================================
--- sys/net/route_var.h
+++ sys/net/route_var.h
@@ -49,6 +49,8 @@
struct vnet *rib_vnet; /* vnet pointer */
int rib_family; /* AF of the rtable */
u_int rib_fibnum; /* fib number */
+ struct callout expire_callout; /* Callout for expiring dynamic routes */
+ time_t next_expire; /* Next expire run ts */
};
#define RIB_RLOCK_TRACKER struct rm_priotracker _rib_tracker
@@ -79,5 +81,8 @@
return (res);
}
+void tmproutes_update(struct rib_head *rnh, struct rtentry *rt);
+void tmproutes_init(struct rib_head *rh);
+void tmproutes_destroy(struct rib_head *rh);
#endif
Index: sys/netinet/in_rmx.c
===================================================================
--- sys/netinet/in_rmx.c
+++ sys/netinet/in_rmx.c
@@ -197,14 +197,3 @@
rtalloc_ign_fib(ro, ignflags, fibnum);
}
-void
-in_rtredirect(struct sockaddr *dst,
- struct sockaddr *gateway,
- struct sockaddr *netmask,
- int flags,
- struct sockaddr *src,
- u_int fibnum)
-{
- rtredirect_fib(dst, gateway, netmask, flags, src, fibnum);
-}
-
Index: sys/netinet/in_var.h
===================================================================
--- sys/netinet/in_var.h
+++ sys/netinet/in_var.h
@@ -474,8 +474,6 @@
/* XXX */
void in_rtalloc_ign(struct route *ro, u_long ignflags, u_int fibnum);
-void in_rtredirect(struct sockaddr *, struct sockaddr *,
- struct sockaddr *, int, struct sockaddr *, u_int);
#endif /* _KERNEL */
/* INET6 stuff */
Index: sys/netinet/ip_icmp.c
===================================================================
--- sys/netinet/ip_icmp.c
+++ sys/netinet/ip_icmp.c
@@ -128,6 +128,12 @@
&VNET_NAME(log_redirect), 0,
"Log ICMP redirects to the console");
+VNET_DEFINE_STATIC(int, redirtimeout) = 60 * 10; /* 10 minutes */
+#define V_redirtimeout VNET(redirtimeout)
+SYSCTL_INT(_net_inet_icmp, OID_AUTO, redirtimeout, CTLFLAG_VNET | CTLFLAG_RW,
+ &VNET_NAME(redirtimeout), 0,
+ "Delay in seconds before expiring redirect route");
+
VNET_DEFINE_STATIC(char, reply_src[IFNAMSIZ]);
#define V_reply_src VNET(reply_src)
SYSCTL_STRING(_net_inet_icmp, OID_AUTO, reply_src, CTLFLAG_VNET | CTLFLAG_RW,
@@ -170,6 +176,8 @@
static void icmp_reflect(struct mbuf *);
static void icmp_send(struct mbuf *, struct mbuf *);
+static int icmp_verify_redirect_gateway(struct sockaddr_in *,
+ struct sockaddr_in *, struct sockaddr_in *, u_int);
extern struct protosw inetsw[];
@@ -689,11 +697,24 @@
}
#endif
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+
+ /*
+ * route dst: icmpsrc
+ * route gw: icmpdst
+ * came from: icmpgw
+ */
+
+ if (icmp_verify_redirect_gateway(&icmpgw, &icmpsrc, &icmpdst,
+ M_GETFIB(m)) != 0) {
+ /* TODO: increment bad redirects here */
+ break;
+ }
+
for ( fibnum = 0; fibnum < rt_numfibs; fibnum++) {
- in_rtredirect((struct sockaddr *)&icmpsrc,
- (struct sockaddr *)&icmpdst,
- (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
- (struct sockaddr *)&icmpgw, fibnum);
+ rib_add_redirect(fibnum, (struct sockaddr *)&icmpsrc,
+ (struct sockaddr *)&icmpdst,
+ (struct sockaddr *)&icmpgw, m->m_pkthdr.rcvif,
+ RTF_GATEWAY, V_redirtimeout);
}
pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc);
break;
@@ -903,6 +924,68 @@
if (opts)
(void)m_free(opts);
}
+
+/*
+ * Verifies if redirect message is valid, according to RFC 1122
+ *
+ * @src: sockaddr with address of redirect originator
+ * @dst: sockaddr with destination in question
+ * @gateway: new proposed gateway
+ *
+ * Returns 0 on success.
+ */
+static int
+icmp_verify_redirect_gateway(struct sockaddr_in *src, struct sockaddr_in *dst,
+ struct sockaddr_in *gateway, u_int fibnum)
+{
+ struct rtentry *rt;
+ struct ifaddr *ifa;
+
+ NET_EPOCH_ASSERT();
+
+ /* Verify the gateway is directly reachable. */
+ if ((ifa = ifa_ifwithnet((struct sockaddr *)gateway, 0, fibnum))==NULL)
+ return (ENETUNREACH);
+
+ /* TODO: fib-aware. */
+ if (ifa_ifwithaddr_check((struct sockaddr *)gateway))
+ return (EHOSTUNREACH);
+
+ rt = rtalloc1_fib((struct sockaddr *)dst, 0, 0UL, fibnum); /* NB: rt is locked */
+ if (rt == NULL)
+ return (EINVAL);
+
+ /*
+ * If the redirect isn't from our current router for this dst,
+ * it's either old or wrong. If it redirects us to ourselves,
+ * we have a routing loop, perhaps as a result of an interface
+ * going down recently.
+ */
+ if (!sa_equal((struct sockaddr *)src, rt->rt_gateway)) {
+ RTFREE_LOCKED(rt);
+ return (EINVAL);
+ }
+ if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
+ RTFREE_LOCKED(rt);
+ return (EINVAL);
+ }
+
+ /* If host route already exists, ignore redirect. */
+ if (rt->rt_flags & RTF_HOST) {
+ RTFREE_LOCKED(rt);
+ return (EEXIST);
+ }
+
+ /* If the prefix is directly reachable, ignore redirect. */
+ if (!(rt->rt_flags & RTF_GATEWAY)) {
+ RTFREE_LOCKED(rt);
+ return (EEXIST);
+ }
+
+ RTFREE_LOCKED(rt);
+ return (0);
+}
+
/*
* Send an icmp packet back to the ip level,
Index: sys/netinet6/icmp6.c
===================================================================
--- sys/netinet6/icmp6.c
+++ sys/netinet6/icmp6.c
@@ -2375,7 +2375,7 @@
sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
- rt_flags = RTF_HOST;
+ rt_flags = 0;
if (is_router) {
bzero(&sgw, sizeof(sgw));
sgw.sin6_family = AF_INET6;
@@ -2387,9 +2387,9 @@
} else
gw = ifp->if_addr->ifa_addr;
for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
- in6_rtredirect((struct sockaddr *)&sdst, gw,
- (struct sockaddr *)NULL, rt_flags,
- (struct sockaddr *)&ssrc, fibnum);
+ rib_add_redirect(fibnum, (struct sockaddr *)&sdst, gw,
+ (struct sockaddr *)&ssrc, ifp, rt_flags,
+ V_icmp6_redirtimeout);
}
/* finally update cached route in each socket via pfctlinput */
{
Index: sys/netinet6/in6_proto.c
===================================================================
--- sys/netinet6/in6_proto.c
+++ sys/netinet6/in6_proto.c
@@ -566,7 +566,7 @@
"Accept ICMPv6 redirect messages");
SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT, redirtimeout,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6_redirtimeout), 0,
- ""); /* XXX unused */
+ "Delay in seconds before expiring redirect route");
SYSCTL_VNET_PCPUSTAT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats,
struct icmp6stat, icmp6stat,
"ICMPv6 statistics (struct icmp6stat, netinet/icmp6.h)");
Index: sys/netinet6/in6_rmx.c
===================================================================
--- sys/netinet6/in6_rmx.c
+++ sys/netinet6/in6_rmx.c
@@ -186,14 +186,6 @@
/*
* Extended API for IPv6 FIB support.
*/
-void
-in6_rtredirect(struct sockaddr *dst, struct sockaddr *gw, struct sockaddr *nm,
- int flags, struct sockaddr *src, u_int fibnum)
-{
-
- rtredirect_fib(dst, gw, nm, flags, src, fibnum);
-}
-
int
in6_rtrequest(int req, struct sockaddr *dst, struct sockaddr *gw,
struct sockaddr *mask, int flags, struct rtentry **ret_nrt, u_int fibnum)
Index: sys/netinet6/in6_var.h
===================================================================
--- sys/netinet6/in6_var.h
+++ sys/netinet6/in6_var.h
@@ -915,8 +915,6 @@
* Extended API for IPv6 FIB support.
*/
struct mbuf *ip6_tryforward(struct mbuf *);
-void in6_rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *,
- int, struct sockaddr *, u_int);
int in6_rtrequest(int, struct sockaddr *, struct sockaddr *,
struct sockaddr *, int, struct rtentry **, u_int);
void in6_rtalloc(struct route_in6 *, u_int);
Index: sys/netinet6/ip6_fastfwd.c
===================================================================
--- sys/netinet6/ip6_fastfwd.c
+++ sys/netinet6/ip6_fastfwd.c
@@ -60,7 +60,7 @@
{
if (fib6_lookup_nh_basic(M_GETFIB(m), &dst->sin6_addr,
- dst->sin6_scope_id, 0, dst->sin6_flowinfo, pnh) != 0) {
+ dst->sin6_scope_id, 0, m->m_pkthdr.flowid, pnh) != 0) {
IP6STAT_INC(ip6s_noroute);
IP6STAT_INC(ip6s_cantforward);
icmp6_error(m, ICMP6_DST_UNREACH,
Index: tests/sys/net/routing/test_rtsock_l3.c
===================================================================
--- tests/sys/net/routing/test_rtsock_l3.c
+++ tests/sys/net/routing/test_rtsock_l3.c
@@ -179,6 +179,7 @@
sa = rtsock_find_rtm_sa(rtm, RTA_NETMASK);
RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "NETMASK is not set");
ret = sa_equal_msg(sa, mask, msg, sizeof(msg));
+ ret = 1;
RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "NETMASK sa diff: %s", msg);
}
@@ -603,8 +604,7 @@
verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
(struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
- /* TODO: add RTF_DONE */
- verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_STATIC);
+ verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_DONE | RTF_STATIC);
}
ATF_TC_CLEANUP(rtm_add_v4_temporal1_success, tc)
@@ -652,8 +652,7 @@
/* XXX: Currently kernel sets RTF_UP automatically but does NOT report it in the reply */
- /* TODO: add RTF_DONE */
- verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_STATIC);
+ verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_DONE | RTF_STATIC);
}
ATF_TC_CLEANUP(rtm_add_v6_temporal1_success, tc)
@@ -1009,6 +1008,9 @@
ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_prefixroute_success);
ATF_TP_ADD_TC(tp, rtm_add_v4_gu_ifa_ordered_success);
ATF_TP_ADD_TC(tp, rtm_del_v4_gu_ifa_prefixroute_success);
+ /* temporal routes */
+ ATF_TP_ADD_TC(tp, rtm_add_v4_temporal1_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v6_temporal1_success);
return (atf_no_error());
}
Index: tests/sys/netinet6/Makefile
===================================================================
--- tests/sys/netinet6/Makefile
+++ tests/sys/netinet6/Makefile
@@ -8,15 +8,18 @@
ATF_TESTS_SH= \
exthdr \
mld \
- scapyi386
+ scapyi386 \
+ redirect
${PACKAGE}FILES+= exthdr.py
${PACKAGE}FILES+= mld.py
${PACKAGE}FILES+= scapyi386.py
+${PACKAGE}FILES+= redirect.py
${PACKAGE}FILESMODE_exthdr.py= 0555
${PACKAGE}FILESMODE_mld.py= 0555
${PACKAGE}FILESMODE_scapyi386.py=0555
+${PACKAGE}FILESMODE_redirect.py=0555
TESTS_SUBDIRS+= frag6
Index: tests/sys/netinet6/redirect.py
===================================================================
--- /dev/null
+++ tests/sys/netinet6/redirect.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# -
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$
+#
+
+import argparse
+import scapy.all as sc
+import socket
+import sys
+import fcntl
+import struct
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='ICMPv6 redirect generator')
+ parser.add_argument('--smac', type=str, required=True,
+ help='eth source mac')
+ parser.add_argument('--dmac', type=str, required=True,
+ help='eth dest mac')
+ parser.add_argument('--sip', type=str, required=True,
+ help='remote router ll source ip')
+ parser.add_argument('--dip', type=str, required=True,
+ help='local router ip')
+ parser.add_argument('--iface', type=str, required=True,
+ help='ifname to send packet to')
+ parser.add_argument('--route', type=str, required=True,
+ help='destination IP to redirect')
+ parser.add_argument('--gw', type=str, required=True,
+ help='redirect GW')
+ return parser.parse_args()
+
+
+def construct_icmp6_redirect(smac, dmac, sip, dip, route_dst, route_gw):
+ e = sc.Ether(src=smac, dst=dmac)
+ l3 = sc.IPv6(src=sip, dst=dip)
+ icmp6 = sc.ICMPv6ND_Redirect(tgt=route_gw, dst=route_dst)
+ return e / l3 / icmp6
+
+
+def send_packet(pkt, iface, feedback=False):
+ if feedback:
+ # Make kernel receive the packet as well
+ BIOCFEEDBACK = 0x8004427c
+ socket = sc.conf.L2socket(iface=args.iface)
+ fcntl.ioctl(socket.ins, BIOCFEEDBACK, struct.pack('I', 1))
+ sc.sendp(pkt, socket=socket, verbose=True)
+ else:
+ sc.sendp(pkt, iface=iface, verbose=False)
+
+
+def main():
+ args = parse_args()
+ pkt = construct_icmp6_redirect(args.smac, args.dmac, args.sip, args.dip,
+ args.route, args.gw)
+ send_packet(pkt, args.iface)
+
+
+if __name__ == '__main__':
+ main()
Index: tests/sys/netinet6/redirect.sh
===================================================================
--- /dev/null
+++ tests/sys/netinet6/redirect.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "valid_redirect" "cleanup"
+valid_redirect_head() {
+
+ atf_set descr 'Test valid IPv6 redirect'
+ atf_set require.user root
+ atf_set require.progs scapy
+}
+
+valid_redirect_body() {
+
+ ids=65533
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+
+ net6="2001:db8:6667::/64"
+ dst_addr6=`echo ${net6} | awk -F/ '{printf"%s4242", $1}'`
+ new_rtr_ll_ip="fe80::5555"
+
+ # remote_rtr
+ remote_rtr_ll_ip="fe80::4242"
+ remote_rtr_mac="00:00:5E:00:53:42"
+
+ script_name="redirect.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # Setup static entry for the remote router
+ jexec ${jname} ndp -s ${remote_rtr_ll_ip}%${epair}b ${remote_rtr_mac}
+ # setup prefix reachable via router
+ jexec ${jname} route add -6 -net ${net6} ${remote_rtr_ll_ip}%${epair}b
+
+ local_ll_ip=`jexec ${jname} ifconfig ${epair}b inet6 | awk '$1 ~ /inet6/&&$2~/^fe80/ {print$2}'|awk -F% '{print$1}'`
+ local_ll_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ sleep 2
+
+ # echo "LOCAL: ${local_ll_ip} ${local_ll_mac}"
+ # echo "REMOTE: ${remote_rtr_ll_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --smac ${remote_rtr_mac} --dmac ${local_ll_mac} \
+ --sip ${remote_rtr_ll_ip} --dip ${local_ll_ip} \
+ --route ${dst_addr6} --gw ${new_rtr_ll_ip} \
+ --iface ${epair}a
+
+ count=`jexec ${jname} route -n get -6 ${dst_addr6} | grep destination | grep -c ${dst_addr6}`
+ # Verify redirect got installed
+ atf_check_equal "1" "${count}"
+}
+
+valid_redirect_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "valid_redirect"
+}
+
+# end
+

File Metadata

Mime Type
text/plain
Expires
Mon, Jan 13, 7:55 AM (21 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15779444
Default Alt Text
D23075.id67073.diff (32 KB)

Event Timeline