Page MenuHomeFreeBSD

D23075.id66564.diff
No OneTemporary

D23075.id66564.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, int 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,8 +497,8 @@
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 rtredirect_fib(struct sockaddr *, struct sockaddr *,
+ int, struct sockaddr *, int, 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);
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 */
@@ -585,131 +590,125 @@
}
-/*
- * 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.
- */
-void
-rtredirect_fib(struct sockaddr *dst,
- struct sockaddr *gateway,
- struct sockaddr *netmask,
- int flags,
- struct sockaddr *src,
- u_int fibnum)
+static int
+verify_redirect_gateway(struct sockaddr *src, struct sockaddr *dst,
+ struct sockaddr *gateway, int flags, u_int fibnum)
{
struct rtentry *rt;
- int error = 0;
- struct rt_addrinfo info;
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;
- }
/* 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 ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL)
+ return (ENETUNREACH);
+
+ /* TODO: fib-aware */
+ if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway))
+ return (EHOSTUNREACH);
+
+ 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 (rt != NULL) {
if (!sa_equal(src, rt->rt_gateway)) {
- error = EINVAL;
- goto done;
+ RTFREE_LOCKED(rt);
+ return (EINVAL);
}
if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
- error = EINVAL;
- goto done;
+ RTFREE_LOCKED(rt);
+ return (EINVAL);
}
}
- if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
- error = EHOSTUNREACH;
- goto done;
+
+ /* If host route already exists, ignore redirect. */
+ if (rt != NULL && (rt->rt_flags & RTF_HOST)) {
+ RTFREE_LOCKED(rt);
+ return (EEXIST);
}
- /*
- * 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)
+ /* If the prefix is directly reachable, ignore redirect */
+ if (rt != NULL && !(rt->rt_flags & RTF_GATEWAY)) {
RTFREE_LOCKED(rt);
- out:
- if (error)
+ return (EEXIST);
+ }
+
+ RTFREE_LOCKED(rt);
+ return (0);
+}
+
+
+/*
+ * 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.
+ */
+int
+rtredirect_fib(struct sockaddr *dst, struct sockaddr *gateway,
+ int flags, struct sockaddr *src, int expire_sec, u_int fibnum)
+{
+ struct rtentry *rt;
+ int error = 0;
+ struct rt_addrinfo info;
+ struct rt_metrics rti_rmx;
+ struct ifaddr *ifa;
+
+ NET_EPOCH_ASSERT();
+
+ if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL)
+ return (error);
+
+ error = verify_redirect_gateway(src, dst, gateway, flags, fibnum);
+ if (error != 0)
+ return (error);
+
+ /* verify the gateway is directly reachable */
+ if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL)
+ return (ENETUNREACH);
+ ifa_ref(ifa);
+
+ flags |= RTF_DYNAMIC;
+
+ bzero(&info, sizeof(info));
+ info.rti_info[RTAX_DST] = dst;
+ info.rti_info[RTAX_GATEWAY] = gateway;
+ info.rti_ifa = ifa;
+ info.rti_flags = flags;
+
+ /* Setup route metrics to define expire time */
+ bzero(&rti_rmx, sizeof(rti_rmx));
+ /* Define expire time as absolute */
+ rti_rmx.rmx_expire = expire_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) {
RTSTAT_INC(rts_badredirect);
- bzero((caddr_t)&info, sizeof(info));
+ return (error);
+ }
+
+ RT_LOCK(rt);
+ flags = rt->rt_flags;
+ RTFREE_LOCKED(rt);
+
+ RTSTAT_INC(rts_dynamic);
+
+ 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;
rt_missmsg_fib(RTM_REDIRECT, &info, flags, error, fibnum);
+
+ return (0);
}
/*
@@ -1059,6 +1058,56 @@
}
/*
+ * Iterates over rtable specified by @fibnum and @af and deletes elements
+ * marked by @filter_f.
+ * @fibnum: rtable id
+ * @family: AF_ address family
+ * @filter_f: lambda function returning non-zero value for items to delete
+ * @arg: data to pass to the @filter_f
+ * @report: true if rtsock notification is needed.
+ */
+void
+rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, int report)
+{
+ struct rib_head *rnh;
+ struct rt_delinfo di;
+ struct rtentry *rt;
+
+ 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.
* Deletes each element for which @filter_f function returned
* non-zero value.
@@ -1068,16 +1117,9 @@
void
rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg)
{
- struct rib_head *rnh;
- struct rt_delinfo di;
- struct rtentry *rt;
uint32_t fibnum;
int i, start, end;
- bzero(&di, sizeof(di));
- di.info.rti_filter = filter_f;
- di.info.rti_filterdata = arg;
-
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
/* Do we want some specific family? */
if (af != AF_UNSPEC) {
@@ -1089,32 +1131,10 @@
}
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 +1719,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,156 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This file contains code responsible for expiring temporal routes
+ * (typically, redirect-originated) from the route tables.
+ */
+
+#include <sys/cdefs.h>
+#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 <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 = (time_t *)arg;
+
+ if (rt->rt_expire == 0)
+ return (0);
+
+ if (rt->rt_expire <= time_uptime) {
+ printf("expiring %p, ts %lu\n", rt, rt->rt_expire);
+ return (1);
+ }
+
+ /*
+ * 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 = (struct rib_head *)arg;
+ time_t next_expire = 0;
+ int ticks;
+
+ 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) {
+ ticks = (next_expire - time_uptime) * hz;
+ if (ticks < 0)
+ ticks = 0;
+ callout_reset(&rnh->expire_callout, ticks, expire_callout, rnh);
+ 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 ticks;
+
+ 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.
+ */
+ ticks = (rt->rt_expire - time_uptime) * hz;
+ if (ticks < 0)
+ ticks = 0;
+ callout_reset(&rnh->expire_callout, ticks, expire_callout, rnh);
+
+ 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,
@@ -690,10 +696,10 @@
#endif
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
for ( fibnum = 0; fibnum < rt_numfibs; fibnum++) {
- in_rtredirect((struct sockaddr *)&icmpsrc,
+ rtredirect_fib((struct sockaddr *)&icmpsrc,
(struct sockaddr *)&icmpdst,
- (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
- (struct sockaddr *)&icmpgw, fibnum);
+ RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&icmpgw,
+ V_redirtimeout, fibnum);
}
pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc);
break;
Index: sys/netinet6/icmp6.c
===================================================================
--- sys/netinet6/icmp6.c
+++ sys/netinet6/icmp6.c
@@ -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);
+ rtredirect_fib((struct sockaddr *)&sdst, gw,
+ rt_flags, (struct sockaddr *)&ssrc,
+ V_icmp6_redirtimeout, fibnum);
}
/* 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
@@ -917,8 +917,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: 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,77 @@
+#!/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, help='eth source mac')
+ parser.add_argument('--dmac', type=str, help='eth dest mac')
+ parser.add_argument('--sip', type=str, help='remote router ll source ip')
+ parser.add_argument('--dip', type=str, help='local router ip')
+ parser.add_argument('--iface', type=str, help='ifname to send packet to')
+ parser.add_argument('--route', type=str, help='destination IP to redirect')
+ parser.add_argument('--gw', type=str, 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
Tue, May 19, 5:05 AM (16 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33293202
Default Alt Text
D23075.id66564.diff (27 KB)

Event Timeline