Further refactor IPv4 interface route creation.

* Fix bug with /32 aliases introduced in 81728a538d.
* Explicitly document business logic for IPv4 ifa routes.
* Remove remnants of rtinit()
* Deduplicate ifa->route prefix code by moving it into ia_getrtprefix()
* Deduplicate conditional check for ifa_maintain_loopback_route()  by
 moving into ia_need_loopback_route()
* Remove now-unused flags argument from in_addprefix().

Reviewed by:		donner
PR:			252883
Differential Revision:	https://reviews.freebsd.org/D28246
This commit is contained in:
Alexander V. Chernikov 2021-01-19 23:50:34 +00:00
parent bbff3a72b2
commit 130aebbab0
3 changed files with 130 additions and 113 deletions

View File

@ -79,6 +79,8 @@ static int in_gifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *);
static void in_socktrim(struct sockaddr_in *);
static void in_purgemaddrs(struct ifnet *);
static bool ia_need_loopback_route(const struct in_ifaddr *);
VNET_DEFINE_STATIC(int, nosameprefix);
#define V_nosameprefix VNET(nosameprefix)
SYSCTL_INT(_net_inet_ip, OID_AUTO, no_same_prefix, CTLFLAG_VNET | CTLFLAG_RW,
@ -445,10 +447,6 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
if (ifp->if_flags & IFF_POINTOPOINT)
ia->ia_dstaddr = *dstaddr;
/* XXXGL: rtinit() needs this strange assignment. */
if (ifp->if_flags & IFF_LOOPBACK)
ia->ia_dstaddr = ia->ia_addr;
if (vhid != 0) {
error = (*carp_attach_p)(&ia->ia_ifa, vhid);
if (error)
@ -481,12 +479,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
* Add route for the network.
*/
if (vhid == 0) {
int flags = RTF_UP;
if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
flags |= RTF_HOST;
error = in_addprefix(ia, flags);
error = in_addprefix(ia);
if (error)
goto fail1;
}
@ -494,10 +487,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
/*
* Add a loopback route to self.
*/
if (vhid == 0 && (ifp->if_flags & IFF_LOOPBACK) == 0 &&
ia->ia_addr.sin_addr.s_addr != INADDR_ANY &&
!((ifp->if_flags & IFF_POINTOPOINT) &&
ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)) {
if (vhid == 0 && ia_need_loopback_route(ia)) {
struct in_ifaddr *eia;
eia = in_localip_more(ia);
@ -723,7 +713,8 @@ in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *ar
static int
in_handle_prefix_route(uint32_t fibnum, int cmd,
struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa)
struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa,
struct ifnet *ifp)
{
NET_EPOCH_ASSERT();
@ -738,6 +729,7 @@ in_handle_prefix_route(uint32_t fibnum, int cmd,
struct rt_addrinfo info = {
.rti_ifa = ifa,
.rti_ifp = ifp,
.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
.rti_info = {
[RTAX_DST] = (struct sockaddr *)dst,
@ -753,29 +745,105 @@ in_handle_prefix_route(uint32_t fibnum, int cmd,
}
/*
* Adds or delete interface route corresponding to @ifa.
* Routing table interaction with interface addresses.
*
* In general, two types of routes needs to be installed:
* a) "interface" or "prefix" route, telling user that the addresses
* behind the ifa prefix are reached directly.
* b) "loopback" route installed for the ifa address, telling user that
* the address belongs to local system.
*
* Handling for (a) and (b) differs in multi-fib aspects, hence they
* are implemented in different functions below.
*
* The cases above may intersect - /32 interface aliases results in
* the same prefix produced by (a) and (b). This blurs the definition
* of the "loopback" route and complicate interactions. The interaction
* table is defined below. The case numbers are used in the multiple
* functions below to refer to the particular test case.
*
* 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.
* 1) Adding address with prefix on non-p2p/non-loopback interface.
* Example: 192.0.2.1/24. Action:
* * add "prefix" route towards 192.0.2.0/24 via @ia interface,
* using @ia as an address source.
* * add "loopback" route towards 192.0.2.1 via V_loif, saving
* @ia ifp in the gateway and using @ia as an address source.
*
* 2) Adding address with /32 mask to non-p2p/non-loopback interface.
* Example: 192.0.2.2/32. Action:
* * add "prefix" host route via V_loif, using @ia as an address source.
*
* 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().
* Example: 10.0.0.1/24->10.0.0.2. Action:
* * add "prefix" host route towards 10.0.0.2 via this interface, using @ia
* as an address source. Note: no sense in installing full /24 as the interface
* is point-to-point.
* * add "loopback" route towards 10.0.9.1 via V_loif, saving
* @ia ifp in the gateway and using @ia as an address source.
*
* 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().
* Example: 192.0.2.1/24. Action:
* * add "prefix" host route via @ia interface, using @ia as an address source.
* Note: Skip installing /24 prefix as it would introduce TTL loop
* for the traffic destined to these addresses.
*/
/*
* Checks if @ia needs to install loopback route to @ia address via
* ifa_maintain_loopback_route().
*
* Return true on success.
*/
static bool
ia_need_loopback_route(const struct in_ifaddr *ia)
{
struct ifnet *ifp = ia->ia_ifp;
/* Case 4: Skip loopback interfaces */
if ((ifp->if_flags & IFF_LOOPBACK) ||
(ia->ia_addr.sin_addr.s_addr == INADDR_ANY))
return (false);
/* Clash avoidance: Skip p2p interfaces with both addresses are equal */
if ((ifp->if_flags & IFF_POINTOPOINT) &&
ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)
return (false);
/* Case 2: skip /32 prefixes */
if (!(ifp->if_flags & IFF_POINTOPOINT) &&
(ia->ia_sockmask.sin_addr.s_addr == INADDR_BROADCAST))
return (false);
return (true);
}
/*
* Calculate "prefix" route corresponding to @ia.
*/
static void
ia_getrtprefix(const struct in_ifaddr *ia, struct in_addr *prefix, struct in_addr *mask)
{
if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) {
/* Case 3: return host route for dstaddr */
*prefix = ia->ia_dstaddr.sin_addr;
mask->s_addr = INADDR_BROADCAST;
} else if (ia->ia_ifp->if_flags & IFF_LOOPBACK) {
/* Case 4: return host route for ifaddr */
*prefix = ia->ia_addr.sin_addr;
mask->s_addr = INADDR_BROADCAST;
} else {
/* Cases 1,2: return actual ia prefix */
*prefix = ia->ia_addr.sin_addr;
*mask = ia->ia_sockmask.sin_addr;
prefix->s_addr &= mask->s_addr;
}
}
/*
* Adds or delete interface "prefix" route corresponding to @ifa.
* Returns 0 on success or errno.
*/
int
in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
@ -786,25 +854,7 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
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);
}
}
ia_getrtprefix(ia, &daddr, &maddr);
struct sockaddr_in mask = {
.sin_family = AF_INET,
@ -812,8 +862,7 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
.sin_addr = maddr,
};
if (pmask != NULL)
pmask = &mask;
pmask = (maddr.s_addr != INADDR_BROADCAST) ? &mask : NULL;
struct sockaddr_in dst = {
.sin_family = AF_INET,
@ -821,56 +870,44 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
.sin_addr.s_addr = daddr.s_addr & maddr.s_addr,
};
struct ifnet *ifp = ia->ia_ifp;
if ((maddr.s_addr == INADDR_BROADCAST) &&
(!(ia->ia_ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)))) {
/* Case 2: host route on broadcast interface */
ifp = V_loif;
}
uint32_t fibnum = ifa->ifa_ifp->if_fib;
NET_EPOCH_ENTER(et);
error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa);
error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa, ifp);
NET_EPOCH_EXIT(et);
return (error);
}
#define rtinitflags(x) \
((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \
? RTF_HOST : 0)
/*
* Check if we have a route for the given prefix already.
*/
static bool
in_hasrtprefix(struct in_ifaddr *target, int flags)
in_hasrtprefix(struct in_ifaddr *target)
{
struct rm_priotracker in_ifa_tracker;
struct in_ifaddr *ia;
struct in_addr prefix, mask, p, m;
bool result = false;
if ((flags & RTF_HOST) != 0) {
prefix = target->ia_dstaddr.sin_addr;
mask.s_addr = 0;
} else {
prefix = target->ia_addr.sin_addr;
mask = target->ia_sockmask.sin_addr;
prefix.s_addr &= mask.s_addr;
}
ia_getrtprefix(target, &prefix, &mask);
IN_IFADDR_RLOCK(&in_ifa_tracker);
/* Look for an existing address with the same prefix, mask, and fib */
CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
if (rtinitflags(ia)) {
p = ia->ia_dstaddr.sin_addr;
ia_getrtprefix(ia, &p, &m);
if (prefix.s_addr != p.s_addr)
continue;
} else {
p = ia->ia_addr.sin_addr;
m = ia->ia_sockmask.sin_addr;
p.s_addr &= m.s_addr;
if (prefix.s_addr != p.s_addr ||
mask.s_addr != m.s_addr)
continue;
if (prefix.s_addr != p.s_addr ||
mask.s_addr != m.s_addr)
continue;
}
if (target->ia_ifp->if_fib != ia->ia_ifp->if_fib)
continue;
@ -889,11 +926,11 @@ in_hasrtprefix(struct in_ifaddr *target, int flags)
}
int
in_addprefix(struct in_ifaddr *target, int flags)
in_addprefix(struct in_ifaddr *target)
{
int error;
if (in_hasrtprefix(target, flags)) {
if (in_hasrtprefix(target)) {
if (V_nosameprefix)
return (EEXIST);
else {
@ -967,9 +1004,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
/*
* Remove the loopback route to the interface address.
*/
if ((target->ia_addr.sin_addr.s_addr != INADDR_ANY) &&
!(target->ia_ifp->if_flags & IFF_LOOPBACK) &&
(flags & LLE_STATIC)) {
if (ia_need_loopback_route(target) && (flags & LLE_STATIC)) {
struct in_ifaddr *eia;
/*
@ -989,14 +1024,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
}
}
if (rtinitflags(target)) {
prefix = target->ia_dstaddr.sin_addr;
mask.s_addr = 0;
} else {
prefix = target->ia_addr.sin_addr;
mask = target->ia_sockmask.sin_addr;
prefix.s_addr &= mask.s_addr;
}
ia_getrtprefix(target, &prefix, &mask);
if ((target->ia_flags & IFA_ROUTE) == 0) {
rt_addrmsg(RTM_DELETE, &target->ia_ifa, target->ia_ifp->if_fib);
@ -1013,20 +1041,11 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
IN_IFADDR_RLOCK(&in_ifa_tracker);
CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
if (rtinitflags(ia)) {
p = ia->ia_dstaddr.sin_addr;
ia_getrtprefix(ia, &p, &m);
if (prefix.s_addr != p.s_addr)
continue;
} else {
p = ia->ia_addr.sin_addr;
m = ia->ia_sockmask.sin_addr;
p.s_addr &= m.s_addr;
if (prefix.s_addr != p.s_addr ||
mask.s_addr != m.s_addr)
continue;
}
if (prefix.s_addr != p.s_addr ||
mask.s_addr != m.s_addr)
continue;
if ((ia->ia_ifp->if_flags & IFF_UP) == 0)
continue;
@ -1077,8 +1096,6 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags)
return (error);
}
#undef rtinitflags
void
in_ifscrub_all(void)
{

View File

@ -461,7 +461,7 @@ int in_leavegroup_locked(struct in_multi *,
/*const*/ struct in_mfilter *);
int in_control(struct socket *, u_long, caddr_t, struct ifnet *,
struct thread *);
int in_addprefix(struct in_ifaddr *, int);
int in_addprefix(struct in_ifaddr *);
int in_scrubprefix(struct in_ifaddr *, u_int);
void in_ifscrub_all(void);
int in_handle_ifaddr_route(int, struct in_ifaddr *);

View File

@ -1065,7 +1065,7 @@ carp_ifa_addroute(struct ifaddr *ifa)
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
in_addprefix(ifatoia(ifa), RTF_UP);
in_addprefix(ifatoia(ifa));
ifa_add_loopback_route(ifa,
(struct sockaddr *)&ifatoia(ifa)->ia_addr);
break;