Split rtinit() into multiple functions.

rtinit[1]() is a function used to add or remove interface address prefix routes,
  similar to ifa_maintain_loopback_route().
It was intended to be family-agnostic. There is a problem with this approach
 in reality.

1) IPv6 code does not use it for the ifa routes. There is a separate layer,
  nd6_prelist_(), providing interface for maintaining interface routes. Its part,
  responsible for the actual route table interaction, mimics rtenty() code.

2) rtinit tries to combine multiple actions in the same function: constructing
  proper route attributes and handling iterations over multiple fibs, for the
  non-zero net.add_addr_allfibs use case. It notably increases the code complexity.

3) dstaddr handling. flags parameter re-uses RTF_ flags. As there is no special flag
 for p2p connections, host routes and p2p routes are handled in the same way.
 Additionally, mapping IFA flags to RTF flags makes the interface pretty messy.
 It make rtinit() to clash with ifa_mainain_loopback_route() for IPV4 interface
 aliases.

4) rtinit() is the last customer passing non-masked prefixes to rib_action(),
 complicating rib_action() implementation.

5) rtinit() coupled ifa announce/withdrawal notifications, producing "false positive"
 ifa messages in certain corner cases.

To address all these points, the following has been done:

* rtinit() has been split into multiple functions:
- Route attribute construction were moved to the per-address-family functions,
 dealing with (2), (3) and (4).
- funnction providing net.add_addr_allfibs handling and route rtsock notificaions
 is the new routing table inteface.
- rtsock ifa notificaion has been moved out as well. resulting set of funcion are only
 responsible for the actual route notifications.

Side effects:
* /32 alias does not result in interface routes (/32 route and "host" route)
* RTF_PINNED is now set for IPv6 prefixes corresponding to the interface addresses

Differential revision:	https://reviews.freebsd.org/D28186
This commit is contained in:
Alexander V. Chernikov 2021-01-09 00:19:25 +00:00
parent bce5d6aa24
commit 81728a538d
11 changed files with 295 additions and 301 deletions

View File

@ -47,6 +47,7 @@
#include <net/route.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <net/slcompress.h>
@ -4881,9 +4882,12 @@ sppp_set_ip_addr(struct sppp *sp, u_long src)
if (ifa != NULL) {
int error;
int fibnum = ifp->if_fib;
rt_addrmsg(RTM_DELETE, ifa, fibnum);
/* delete old route */
error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST);
ia = ifatoia(ifa);
error = in_handle_ifaddr_route(RTM_DELETE, ia);
if (debug && error) {
log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n",
SPP_ARGS(ifp), error);
@ -4891,14 +4895,14 @@ sppp_set_ip_addr(struct sppp *sp, u_long src)
/* set new address */
si->sin_addr.s_addr = htonl(src);
ia = ifatoia(ifa);
IN_IFADDR_WLOCK();
LIST_REMOVE(ia, ia_hash);
LIST_INSERT_HEAD(INADDR_HASH(si->sin_addr.s_addr), ia, ia_hash);
IN_IFADDR_WUNLOCK();
rt_addrmsg(RTM_ADD, ifa, fibnum);
/* add new route */
error = rtinit(ifa, (int)RTM_ADD, RTF_HOST);
error = in_handle_ifaddr_route(RTM_ADD, ia);
if (debug && error) {
log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d",
SPP_ARGS(ifp), error);

View File

@ -767,27 +767,3 @@ rt_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum)
return (rtsock_routemsg_info(cmd, info, fibnum));
}
/*
* This is called to generate messages from the routing socket
* indicating a network interface has had addresses associated with it.
*/
void
rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, struct rtentry *rt, int fibnum)
{
KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE,
("unexpected cmd %u", cmd));
KASSERT((fibnum >= 0 && fibnum < rt_numfibs),
("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs));
if (cmd == RTM_ADD) {
rt_addrmsg(cmd, ifa, fibnum);
if (rt != NULL)
rt_routemsg(cmd, rt, nhop_select(rt->rt_nhop, 0), fibnum);
} else {
if (rt != NULL)
rt_routemsg(cmd, rt, nhop_select(rt->rt_nhop, 0), fibnum);
rt_addrmsg(cmd, ifa, fibnum);
}
}

View File

@ -415,7 +415,6 @@ void rt_ifannouncemsg(struct ifnet *, int);
void rt_ifmsg(struct ifnet *);
void rt_missmsg(int, struct rt_addrinfo *, int, int);
void rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int);
void rt_newaddrmsg_fib(int, struct ifaddr *, struct rtentry *, int);
int rt_addrmsg(int, struct ifaddr *, int);
int rt_routemsg(int, struct rtentry *, struct nhop_object *, int);
int rt_routemsg_info(int, struct rt_addrinfo *, int);
@ -433,10 +432,6 @@ void rt_updatemtu(struct ifnet *);
void rt_flushifroutes_af(struct ifnet *, int);
void rt_flushifroutes(struct ifnet *ifp);
/* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */
/* Thes are used by old code not yet converted to use multiple FIBS */
int rtinit(struct ifaddr *, int, int);
/* XXX MRT NEW VERSIONS THAT USE FIBs
* For now the protocol indepedent versions are the same as the AF_INET ones
* but this will change..

View File

@ -52,6 +52,7 @@ int rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
struct rib_cmd_info *rc);
int rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
struct rib_cmd_info *rc);
int rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info);
typedef void route_notification_t(struct rib_cmd_info *rc, void *);
void rib_decompose_notification(struct rib_cmd_info *rc,

View File

@ -65,176 +65,74 @@ SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET,
&VNET_NAME(rt_add_addr_allfibs), 0, "");
/*
* Set up a routing table entry, normally
* for an interface.
* Executes routing tables change specified by @cmd and @info for the fib
* @fibnum. Generates routing message on success.
* Note: it assumes there is only single route (interface route) for the
* provided prefix.
* Returns 0 on success or errno.
*/
static inline int
rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum)
static int
rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
{
RIB_RLOCK_TRACKER;
struct epoch_tracker et;
struct sockaddr *dst;
struct sockaddr *netmask;
struct rib_cmd_info rc;
struct rt_addrinfo info;
int error = 0;
int startfib, endfib;
struct sockaddr_storage ss;
int didwork = 0;
int a_failure = 0;
struct sockaddr_dl_short sdl;
struct rib_head *rnh;
struct nhop_object *nh;
int error;
if (flags & RTF_HOST) {
dst = ifa->ifa_dstaddr;
netmask = NULL;
error = rib_action(fibnum, cmd, info, &rc);
if (error == 0) {
if (cmd == RTM_ADD)
nh = nhop_select(rc.rc_nh_new, 0);
else
nh = nhop_select(rc.rc_nh_old, 0);
rt_routemsg(cmd, rc.rc_rt, nh, fibnum);
}
return (error);
}
/*
* Adds/deletes interface prefix specified by @info to the routing table.
* If V_rt_add_addr_allfibs is set, iterates over all existing routing
* tables, otherwise uses fib in @fibnum. Generates routing message for
* each table.
* Returns 0 on success or errno.
*/
int
rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
{
int error, last_error = 0;
bool didwork = false;
if (V_rt_add_addr_allfibs == 0) {
error = rib_handle_ifaddr_one(fibnum, cmd, info);
didwork = (error == 0);
} else {
dst = ifa->ifa_addr;
netmask = ifa->ifa_netmask;
for (fibnum = 0; fibnum < V_rt_numfibs; fibnum++) {
error = rib_handle_ifaddr_one(fibnum, cmd, info);
if (error == 0)
didwork = true;
else
last_error = error;
}
if (dst->sa_len == 0)
return(EINVAL);
switch (dst->sa_family) {
case AF_INET6:
case AF_INET:
/* We support multiple FIBs. */
break;
default:
fibnum = RT_DEFAULT_FIB;
break;
}
if (fibnum == RT_ALL_FIBS) {
if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD)
startfib = endfib = ifa->ifa_ifp->if_fib;
else {
startfib = 0;
endfib = rt_numfibs - 1;
}
} else {
KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum"));
startfib = fibnum;
endfib = fibnum;
}
/*
* If it's a delete, check that if it exists,
* it's on the correct interface or we might scrub
* a route to another ifa which would
* be confusing at best and possibly worse.
*/
if (cmd == RTM_DELETE) {
/*
* It's a delete, so it should already exist..
* If it's a net, mask off the host bits
* (Assuming we have a mask)
* XXX this is kinda inet specific..
*/
if (netmask != NULL) {
rt_maskedcopy(dst, (struct sockaddr *)&ss, netmask);
dst = (struct sockaddr *)&ss;
}
}
bzero(&sdl, sizeof(struct sockaddr_dl_short));
sdl.sdl_family = AF_LINK;
sdl.sdl_len = sizeof(struct sockaddr_dl_short);
sdl.sdl_type = ifa->ifa_ifp->if_type;
sdl.sdl_index = ifa->ifa_ifp->if_index;
/*
* Now go through all the requested tables (fibs) and do the
* requested action. Realistically, this will either be fib 0
* for protocols that don't do multiple tables or all the
* tables for those that do.
*/
for ( fibnum = startfib; fibnum <= endfib; fibnum++) {
if (cmd == RTM_DELETE) {
struct radix_node *rn;
/*
* Look up an rtentry that is in the routing tree and
* contains the correct info.
*/
rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
if (rnh == NULL)
/* this table doesn't exist but others might */
continue;
RIB_RLOCK(rnh);
rn = rnh->rnh_lookup(dst, netmask, &rnh->head);
error = (rn == NULL ||
(rn->rn_flags & RNF_ROOT) ||
RNTORT(rn)->rt_nhop->nh_ifa != ifa);
RIB_RUNLOCK(rnh);
if (error) {
/* this is only an error if bad on ALL tables */
continue;
}
}
/*
* Do the actual request
*/
bzero((caddr_t)&info, sizeof(info));
info.rti_ifa = ifa;
info.rti_flags = flags |
(ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED;
info.rti_info[RTAX_DST] = dst;
info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl;
info.rti_info[RTAX_NETMASK] = netmask;
NET_EPOCH_ENTER(et);
error = rib_action(fibnum, cmd, &info, &rc);
if (error == 0 && rc.rc_rt != NULL) {
/*
* notify any listening routing agents of the change
*/
/* TODO: interface routes/aliases */
rt_newaddrmsg_fib(cmd, ifa, rc.rc_rt, fibnum);
didwork = 1;
}
NET_EPOCH_EXIT(et);
if (error)
a_failure = error;
}
if (cmd == RTM_DELETE) {
if (didwork) {
error = 0;
} else {
/* we only give an error if it wasn't in any table */
error = ((flags & RTF_HOST) ?
error = ((info->rti_flags & RTF_HOST) ?
EHOSTUNREACH : ENETUNREACH);
}
} else {
if (a_failure) {
if (last_error != 0) {
/* return an error if any of them failed */
error = a_failure;
error = last_error;
}
}
return (error);
}
/*
* Set up a routing table entry, normally
* for an interface.
*/
int
rtinit(struct ifaddr *ifa, int cmd, int flags)
{
struct sockaddr *dst;
int fib = RT_DEFAULT_FIB;
if (flags & RTF_HOST) {
dst = ifa->ifa_dstaddr;
} else {
dst = ifa->ifa_addr;
}
switch (dst->sa_family) {
case AF_INET6:
case AF_INET:
/* We do support multiple FIBs. */
fib = RT_ALL_FIBS;
break;
}
return (rtinit1(ifa, cmd, flags, fib));
}
static int
ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa,
struct sockaddr *ia)

View File

@ -58,6 +58,8 @@ __FBSDID("$FreeBSD$");
#include <net/if_llatbl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/route/route_ctl.h>
#include <net/vnet.h>
#include <netinet/if_ether.h>
@ -709,6 +711,125 @@ in_gifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
return (0);
}
static int
in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *arg)
{
if (nh->nh_ifa == (struct ifaddr *)arg)
return (1);
return (0);
}
static int
in_handle_prefix_route(uint32_t fibnum, int cmd,
struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa)
{
NET_EPOCH_ASSERT();
/* Prepare gateway */
struct sockaddr_dl_short sdl = {
.sdl_family = AF_LINK,
.sdl_len = sizeof(struct sockaddr_dl_short),
.sdl_type = ifa->ifa_ifp->if_type,
.sdl_index = ifa->ifa_ifp->if_index,
};
struct rt_addrinfo info = {
.rti_ifa = ifa,
.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
.rti_info = {
[RTAX_DST] = (struct sockaddr *)dst,
[RTAX_NETMASK] = (struct sockaddr *)netmask,
[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
},
/* Ensure we delete the prefix IFF prefix ifa matches */
.rti_filter = in_match_ifaddr,
.rti_filterdata = ifa,
};
return (rib_handle_ifaddr_info(fibnum, cmd, &info));
}
/*
* Adds or delete interface route corresponding to @ifa.
* There can be multiple options:
* 1) Adding addr with prefix on non-p2p/non-lo interface.
* Example: 192.0.2.1/24. Action: add route towards
* 192.0.2.0/24 via this interface, using ifa as an address source.
* Note: route to 192.0.2.1 will be installed separately via
* ifa_maintain_loopback_route().
* 2) Adding addr with "host" mask.
* Example: 192.0.2.2/32. In this case no action is performed,
* as the route should be installed by ifa_maintain_loopback_route().
* Returns 0 to indicate success.
* 3) Adding address with or without prefix to p2p interface.
* Example: 10.0.0.1/24->10.0.0.2. In this case, all other addresses
* covered by prefix, does not make sense in the context of p2p link.
* Action: add route towards 10.0.0.2 via this interface, using ifa as an
* address source.
* Similar to (1), route to 10.0.0.1 will be installed by
* ifa_maintain_loopback_route().
* 4) Adding address with or without prefix to loopback interface.
* Example: 192.0.2.1/24. In this case, trafic to non-host addresses cannot
* be forwarded, as it would introduce an infinite cycle.
* Similar to (2), perform no action and return 0. Loopback route
* will be installed by ifa_maintain_loopback_route().
*/
int
in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
{
struct ifaddr *ifa = &ia->ia_ifa;
struct in_addr daddr, maddr;
struct sockaddr_in *pmask;
struct epoch_tracker et;
int error;
/* Case 4: ignore loopbacks */
if (ifa->ifa_ifp->if_flags & IFF_LOOPBACK)
return (0);
if (ifa->ifa_ifp->if_flags & IFF_POINTOPOINT) {
/* Case 3: install route towards dst addr */
daddr = ia->ia_dstaddr.sin_addr;
pmask = NULL;
maddr.s_addr = INADDR_BROADCAST;
} else {
daddr = ia->ia_addr.sin_addr;
pmask = &ia->ia_sockmask;
maddr = pmask->sin_addr;
if (maddr.s_addr == INADDR_BROADCAST) {
/* Case 2: ignore /32 routes */
return (0);
}
}
struct sockaddr_in mask = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
.sin_addr = maddr,
};
if (pmask != NULL)
pmask = &mask;
struct sockaddr_in dst = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
.sin_addr.s_addr = daddr.s_addr & maddr.s_addr,
};
uint32_t fibnum = ifa->ifa_ifp->if_fib;
NET_EPOCH_ENTER(et);
error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa);
NET_EPOCH_EXIT(et);
return (error);
}
#define rtinitflags(x) \
((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \
? RTF_HOST : 0)
@ -785,7 +906,8 @@ in_addprefix(struct in_ifaddr *target, int flags)
/*
* No-one seem to have this prefix route, so we try to insert it.
*/
error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags);
rt_addrmsg(RTM_ADD, &target->ia_ifa, target->ia_ifp->if_fib);
error = in_handle_ifaddr_route(RTM_ADD, target);
if (!error)
target->ia_flags |= IFA_ROUTE;
return (error);
@ -917,8 +1039,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
if ((ia->ia_flags & IFA_ROUTE) == 0) {
ifa_ref(&ia->ia_ifa);
IN_IFADDR_RUNLOCK(&in_ifa_tracker);
error = rtinit(&(target->ia_ifa), (int)RTM_DELETE,
rtinitflags(target));
error = in_handle_ifaddr_route(RTM_DELETE, target);
if (error == 0)
target->ia_flags &= ~IFA_ROUTE;
else
@ -927,8 +1048,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
/* Scrub all entries IFF interface is different */
in_scrubprefixlle(target, target->ia_ifp != ia->ia_ifp,
flags);
error = rtinit(&ia->ia_ifa, (int)RTM_ADD,
rtinitflags(ia) | RTF_UP);
error = in_handle_ifaddr_route(RTM_ADD, ia);
if (error == 0)
ia->ia_flags |= IFA_ROUTE;
else
@ -948,7 +1068,8 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
/*
* As no-one seem to have this prefix, we can remove the route.
*/
error = rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target));
rt_addrmsg(RTM_DELETE, &target->ia_ifa, target->ia_ifp->if_fib);
error = in_handle_ifaddr_route(RTM_DELETE, target);
if (error == 0)
target->ia_flags &= ~IFA_ROUTE;
else

View File

@ -464,6 +464,7 @@ int in_control(struct socket *, u_long, caddr_t, struct ifnet *,
int in_addprefix(struct in_ifaddr *, int);
int in_scrubprefix(struct in_ifaddr *, u_int);
void in_ifscrub_all(void);
int in_handle_ifaddr_route(int, struct in_ifaddr *);
void ip_input(struct mbuf *);
void ip_direct_input(struct mbuf *);
void in_ifadown(struct ifaddr *ifa, int);

View File

@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/route/route_ctl.h>
#include <net/vnet.h>
#include <netinet/in.h>
@ -864,7 +865,8 @@ rip_ctlinput(int cmd, struct sockaddr *sa, void *vip)
err = ifa_del_loopback_route((struct ifaddr *)ia, sa);
err = rtinit(&ia->ia_ifa, RTM_ADD, flags);
rt_addrmsg(RTM_ADD, &ia->ia_ifa, ia->ia_ifp->if_fib);
err = in_handle_ifaddr_route(RTM_ADD, ia);
if (err == 0)
ia->ia_flags |= IFA_ROUTE;

View File

@ -91,6 +91,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/route/route_ctl.h>
#include <net/route/nhop.h>
#include <net/if_dl.h>
#include <net/vnet.h>
@ -1272,6 +1273,48 @@ in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
return (error);
}
/*
* Adds or deletes interface route for p2p ifa.
* Returns 0 on success or errno.
*/
static int
in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia)
{
struct epoch_tracker et;
struct ifaddr *ifa = &ia->ia_ifa;
int error;
/* Prepare gateway */
struct sockaddr_dl_short sdl = {
.sdl_family = AF_LINK,
.sdl_len = sizeof(struct sockaddr_dl_short),
.sdl_type = ifa->ifa_ifp->if_type,
.sdl_index = ifa->ifa_ifp->if_index,
};
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_len = sizeof(struct sockaddr_in6),
.sin6_addr = ia->ia_dstaddr.sin6_addr,
};
struct rt_addrinfo info = {
.rti_ifa = ifa,
.rti_flags = RTF_PINNED | RTF_HOST,
.rti_info = {
[RTAX_DST] = (struct sockaddr *)&dst,
[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
},
};
/* Don't set additional per-gw filters on removal */
NET_EPOCH_ENTER(et);
error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info);
NET_EPOCH_EXIT(et);
return (error);
}
void
in6_purgeaddr(struct ifaddr *ifa)
{
@ -1305,10 +1348,12 @@ in6_purgeaddr(struct ifaddr *ifa)
in6_leavegroup(imm->i6mm_maddr, NULL);
free(imm, M_IP6MADDR);
}
/* Check if we need to remove p2p route */
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
if (ia->ia_dstaddr.sin6_family != AF_INET6)
plen = 0;
if ((ia->ia_flags & IFA_ROUTE) && plen == 128) {
error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags |
(ia->ia_dstaddr.sin6_family == AF_INET6 ? RTF_HOST : 0));
error = in6_handle_dstaddr_rtrequest(RTM_DELETE, ia);
if (error != 0)
log(LOG_INFO, "%s: err=%d, destination address delete "
"failed\n", __func__, error);
@ -1416,7 +1461,7 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia,
if (pdst->sin6_family == AF_INET6 &&
!IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) {
if ((ia->ia_flags & IFA_ROUTE) != 0 &&
(rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) {
(in6_handle_dstaddr_rtrequest(RTM_DELETE, ia) != 0)) {
nd6log((LOG_ERR, "in6_update_ifa_internal: failed to "
"remove a route to the old destination: %s\n",
ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
@ -1436,13 +1481,12 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia,
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 &&
ia->ia_dstaddr.sin6_family == AF_INET6) {
int rtflags = RTF_UP | RTF_HOST;
/*
* Handle the case for ::1 .
*/
if (ifp->if_flags & IFF_LOOPBACK)
ia->ia_flags |= IFA_RTSELF;
error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags);
error = in6_handle_dstaddr_rtrequest(RTM_ADD, ia);
if (error)
goto done;
ia->ia_flags |= IFA_ROUTE;

View File

@ -2020,73 +2020,61 @@ pfxlist_onlink_check(void)
ND6_ONLINK_UNLOCK();
}
/*
* Add or remove interface route specified by @dst, @netmask and @ifp.
* ifa can be NULL.
* Returns 0 on success
*/
static int
nd6_prefix_rtrequest(uint32_t fibnum, int cmd, struct sockaddr_in6 *dst,
struct sockaddr_in6 *netmask, struct ifnet *ifp, struct ifaddr *ifa)
{
struct epoch_tracker et;
int error;
/* Prepare gateway */
struct sockaddr_dl_short sdl = {
.sdl_family = AF_LINK,
.sdl_len = sizeof(struct sockaddr_dl_short),
.sdl_type = ifp->if_type,
.sdl_index = ifp->if_index,
};
struct rt_addrinfo info = {
.rti_ifa = ifa,
.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
.rti_info = {
[RTAX_DST] = (struct sockaddr *)dst,
[RTAX_NETMASK] = (struct sockaddr *)netmask,
[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
},
};
/* Don't set additional per-gw filters on removal */
NET_EPOCH_ENTER(et);
error = rib_handle_ifaddr_info(fibnum, cmd, &info);
NET_EPOCH_EXIT(et);
return (error);
}
static int
nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
{
struct sockaddr_dl_short sdl;
struct sockaddr_in6 mask6;
u_long rtflags;
int error, a_failure, fibnum, maxfib;
int error;
bzero(&mask6, sizeof(mask6));
mask6.sin6_len = sizeof(mask6);
mask6.sin6_addr = pr->ndpr_mask;
rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP;
bzero(&sdl, sizeof(struct sockaddr_dl_short));
sdl.sdl_len = sizeof(struct sockaddr_dl_short);
sdl.sdl_family = AF_LINK;
sdl.sdl_type = ifa->ifa_ifp->if_type;
sdl.sdl_index = ifa->ifa_ifp->if_index;
if(V_rt_add_addr_allfibs) {
fibnum = 0;
maxfib = rt_numfibs;
} else {
fibnum = ifa->ifa_ifp->if_fib;
maxfib = fibnum + 1;
}
a_failure = 0;
for (; fibnum < maxfib; fibnum++) {
struct rt_addrinfo info;
struct rib_cmd_info rc;
bzero((caddr_t)&info, sizeof(info));
info.rti_flags = rtflags;
info.rti_info[RTAX_DST] = (struct sockaddr *)&pr->ndpr_prefix;
info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl;
info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask6;
NET_EPOCH_ASSERT();
error = rib_action(fibnum, RTM_ADD, &info, &rc);
if (error != 0) {
char ip6buf[INET6_ADDRSTRLEN];
char ip6bufg[INET6_ADDRSTRLEN];
char ip6bufm[INET6_ADDRSTRLEN];
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
nd6log((LOG_ERR, "%s: failed to add "
"route for a prefix (%s/%d) on %s, gw=%s, mask=%s, "
"flags=%lx errno = %d\n", __func__,
ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
pr->ndpr_plen, if_name(pr->ndpr_ifp),
ip6_sprintf(ip6bufg, &sin6->sin6_addr),
ip6_sprintf(ip6bufm, &mask6.sin6_addr),
rtflags, error));
/* Save last error to return, see rtinit(). */
a_failure = error;
continue;
}
struct sockaddr_in6 mask6 = {
.sin6_family = AF_INET6,
.sin6_len = sizeof(struct sockaddr_in6),
.sin6_addr = pr->ndpr_mask,
};
struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL;
error = nd6_prefix_rtrequest(pr->ndpr_ifp->if_fib, RTM_ADD,
&pr->ndpr_prefix, pmask6, pr->ndpr_ifp, ifa);
if (error == 0)
pr->ndpr_stateflags |= NDPRF_ONLINK;
struct nhop_object *nh = nhop_select(rc.rc_nh_new, 0);
rt_routemsg(RTM_ADD, rc.rc_rt, nh, fibnum);
}
/* Return the last error we got. */
return (a_failure);
return (error);
}
static int
@ -2178,11 +2166,10 @@ nd6_prefix_offlink(struct nd_prefix *pr)
int error = 0;
struct ifnet *ifp = pr->ndpr_ifp;
struct nd_prefix *opr;
struct sockaddr_in6 sa6, mask6;
struct sockaddr_in6 sa6;
char ip6buf[INET6_ADDRSTRLEN];
uint64_t genid;
int fibnum, maxfib, a_failure;
struct epoch_tracker et;
int a_failure;
ND6_ONLINK_LOCK_ASSERT();
ND6_UNLOCK_ASSERT();
@ -2190,50 +2177,16 @@ nd6_prefix_offlink(struct nd_prefix *pr)
if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0)
return (EEXIST);
bzero(&sa6, sizeof(sa6));
sa6.sin6_family = AF_INET6;
sa6.sin6_len = sizeof(sa6);
bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
sizeof(struct in6_addr));
bzero(&mask6, sizeof(mask6));
mask6.sin6_family = AF_INET6;
mask6.sin6_len = sizeof(sa6);
bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
struct sockaddr_in6 mask6 = {
.sin6_family = AF_INET6,
.sin6_len = sizeof(struct sockaddr_in6),
.sin6_addr = pr->ndpr_mask,
};
struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL;
if (V_rt_add_addr_allfibs) {
fibnum = 0;
maxfib = rt_numfibs;
} else {
fibnum = ifp->if_fib;
maxfib = fibnum + 1;
}
error = nd6_prefix_rtrequest(ifp->if_fib, RTM_DELETE,
&pr->ndpr_prefix, pmask6, ifp, NULL);
a_failure = 0;
NET_EPOCH_ENTER(et);
for (; fibnum < maxfib; fibnum++) {
struct rt_addrinfo info;
struct rib_cmd_info rc;
bzero((caddr_t)&info, sizeof(info));
info.rti_flags = RTF_GATEWAY;
info.rti_info[RTAX_DST] = (struct sockaddr *)&sa6;
info.rti_info[RTAX_GATEWAY] = NULL;
info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask6;
NET_EPOCH_ASSERT();
error = rib_action(fibnum, RTM_DELETE, &info, &rc);
if (error != 0) {
/* Save last error to return, see rtinit(). */
a_failure = error;
continue;
}
/* report route deletion to the routing socket. */
struct nhop_object *nh = nhop_select(rc.rc_nh_old, 0);
rt_routemsg(RTM_DELETE, rc.rc_rt, nh, fibnum);
}
NET_EPOCH_EXIT(et);
error = a_failure;
a_failure = 1;
if (error == 0) {
pr->ndpr_stateflags &= ~NDPRF_ONLINK;
@ -2283,7 +2236,7 @@ nd6_prefix_offlink(struct nd_prefix *pr)
/* XXX: can we still set the NDPRF_ONLINK flag? */
nd6log((LOG_ERR,
"%s: failed to delete route: %s/%d on %s (errno=%d)\n",
__func__, ip6_sprintf(ip6buf, &sa6.sin6_addr),
__func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
pr->ndpr_plen, if_name(ifp), error));
}

View File

@ -1127,8 +1127,7 @@ ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc)
/* gateway should be link sdl with ifindex of an address interface */
verify_link_gateway(rtm, c->ifindex);
/* TODO: PINNED? */
int expected_rt_flags = RTF_UP | RTF_DONE;
int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED;
verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
}
@ -1257,7 +1256,7 @@ ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc)
/* gateway should be link sdl with ifindex of an address interface */
verify_link_gateway(rtm, c->ifindex);
int expected_rt_flags = RTF_DONE;
int expected_rt_flags = RTF_DONE | RTF_PINNED;
verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
}