Introduce the routing flag RTF_LOCAL to track local address routes.

Add functions rt_ifa_addlocal() and rt_ifa_remlocal() to add and remove
local routes for the address and announce the new address and route
to the routing socket.

Add in_ifaddlocal() and in_ifremlocal() to use these functions.
Rename in6_if{add,rem}loop() to in6_if{add,rem}local() and use these
functions.

rtinit() no longer announces the address, just the network route for the
address. As such, calls to rt_newaddrmsg() have been removed from
in_addprefix() and in_scrubprefix().

This solves the problem of potentially more than one announcement, or no
announcement at all for the address in certain situations.
This commit is contained in:
roy 2015-02-26 09:54:46 +00:00
parent 9ce3434ff9
commit 42900924fd
7 changed files with 206 additions and 138 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: route.c,v 1.135 2015/02/25 12:45:34 roy Exp $ */
/* $NetBSD: route.c,v 1.136 2015/02/26 09:54:46 roy Exp $ */
/*-
* Copyright (c) 1998, 2008 The NetBSD Foundation, Inc.
@ -90,10 +90,11 @@
* @(#)route.c 8.3 (Berkeley) 1/9/95
*/
#include "opt_inet.h"
#include "opt_route.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.135 2015/02/25 12:45:34 roy Exp $");
__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.136 2015/02/26 09:54:46 roy Exp $");
#include <sys/param.h>
#include <sys/kmem.h>
@ -960,7 +961,7 @@ rtinit(struct ifaddr *ifa, int cmd, int flags)
;
else switch (cmd) {
case RTM_DELETE:
rt_newaddrmsg(cmd, ifa, error, nrt);
rt_newmsg(cmd, nrt);
if (rt->rt_refcnt <= 0) {
rt->rt_refcnt++;
rtfree(rt);
@ -984,7 +985,7 @@ rtinit(struct ifaddr *ifa, int cmd, int flags)
if (cmd == RTM_LLINFO_UPD && ifa->ifa_rtrequest != NULL)
ifa->ifa_rtrequest(RTM_LLINFO_UPD, rt, &info);
rt_newaddrmsg(RTM_CHANGE, ifa, error, nrt);
rt_newmsg(RTM_CHANGE, nrt);
break;
case RTM_ADD:
rt->rt_refcnt--;
@ -1000,12 +1001,132 @@ rtinit(struct ifaddr *ifa, int cmd, int flags)
if (ifa->ifa_rtrequest != NULL)
ifa->ifa_rtrequest(RTM_ADD, rt, &info);
}
rt_newaddrmsg(cmd, ifa, error, nrt);
rt_newmsg(cmd, nrt);
break;
}
return error;
}
static const struct in_addr inmask32 = {.s_addr = INADDR_BROADCAST};
/* Subroutine for rt_ifa_addlocal() and rt_ifa_remlocal() */
static int
rt_ifa_localrequest(int cmd, struct ifaddr *ifa)
{
struct sockaddr *all1_sa;
struct sockaddr_in all1_sin;
#ifdef INET6
struct sockaddr_in6 all1_sin6;
#endif
struct rtentry *nrt = NULL;
int flags, e;
switch(ifa->ifa_addr->sa_family) {
case AF_INET:
sockaddr_in_init(&all1_sin, &inmask32, 0);
all1_sa = (struct sockaddr *)&all1_sin;
break;
#ifdef INET6
case AF_INET6:
sockaddr_in6_init(&all1_sin6, &in6mask128, 0, 0, 0);
all1_sa = (struct sockaddr *)&all1_sin6;
break;
#endif
default:
return 0;
}
flags = RTF_UP | RTF_HOST | RTF_LOCAL;
if (!(ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))
flags |= RTF_LLINFO;
e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr, all1_sa, flags, &nrt);
/* Make sure rt_ifa be equal to IFA, the second argument of the
* function. */
if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa)
rt_replace_ifa(nrt, ifa);
rt_newaddrmsg(cmd, ifa, e, nrt);
if (nrt) {
if (cmd == RTM_DELETE) {
if (nrt->rt_refcnt <= 0) {
/* XXX: we should free the entry ourselves. */
nrt->rt_refcnt++;
rtfree(nrt);
}
} else {
/* the cmd must be RTM_ADD here */
nrt->rt_refcnt--;
}
}
return e;
}
/*
* Create a local route entry for the address.
* Announce the addition of the address and the route to the routing socket.
*/
int
rt_ifa_addlocal(struct ifaddr *ifa)
{
struct rtentry *rt;
int e;
/* If there is no loopback entry, allocate one. */
rt = rtalloc1(ifa->ifa_addr, 0);
if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 ||
(rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
e = rt_ifa_localrequest(RTM_ADD, ifa);
else {
e = 0;
rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL);
}
if (rt != NULL)
rt->rt_refcnt--;
return e;
}
/*
* Remove the local route entry for the address.
* Announce the removal of the address and the route to the routing socket.
*/
int
rt_ifa_remlocal(struct ifaddr *ifa, struct ifaddr *alt_ifa)
{
struct rtentry *rt;
int e = 0;
rt = rtalloc1(ifa->ifa_addr, 0);
/*
* Before deleting, check if a corresponding loopbacked
* host route surely exists. With this check, we can avoid
* deleting an interface direct route whose destination is
* the same as the address being removed. This can happen
* when removing a subnet-router anycast address on an
* interface attached to a shared medium.
*/
if (rt != NULL &&
(rt->rt_flags & RTF_HOST) &&
(rt->rt_ifp->if_flags & IFF_LOOPBACK))
{
/* If we cannot replace the route's ifaddr with the equivalent
* ifaddr of another interface, I believe it is safest to
* delete the route.
*/
if (alt_ifa == NULL)
e = rt_ifa_localrequest(RTM_DELETE, ifa);
else {
rt_replace_ifa(rt, alt_ifa);
rt_newmsg(RTM_CHANGE, rt);
}
} else
rt_newaddrmsg(RTM_DELADDR, ifa, 0, NULL);
if (rt != NULL)
rt->rt_refcnt--;
return e;
}
/*
* Route timer routines. These routes allow functions to be called
* for various routes at any time. This is useful in supporting

View File

@ -1,4 +1,4 @@
/* $NetBSD: route.h,v 1.86 2015/02/25 12:45:34 roy Exp $ */
/* $NetBSD: route.h,v 1.87 2015/02/26 09:54:46 roy Exp $ */
/*
* Copyright (c) 1980, 1986, 1993
@ -155,6 +155,7 @@ struct ortentry {
#define RTF_PROTO1 0x8000 /* protocol specific routing flag */
#define RTF_SRC 0x10000 /* route has fixed source address */
#define RTF_ANNOUNCE 0x20000 /* announce new ARP or NDP entry */
#define RTF_LOCAL 0x40000 /* route represents a local address */
/*
* Routing statistics.
@ -504,6 +505,8 @@ struct rtentry *rt_lookup(rtbl_t *, const struct sockaddr *,
struct rtentry *rt_deladdr(rtbl_t *, const struct sockaddr *,
const struct sockaddr *);
void rtbl_init(void);
int rt_ifa_addlocal(struct ifaddr *);
int rt_ifa_remlocal(struct ifaddr *, struct ifaddr *);
rtbl_t *rt_gettable(sa_family_t);
void rt_assert_inactive(const struct rtentry *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_arp.c,v 1.160 2014/11/13 16:11:18 christos Exp $ */
/* $NetBSD: if_arp.c,v 1.161 2015/02/26 09:54:46 roy Exp $ */
/*-
* Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc.
@ -68,7 +68,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.160 2014/11/13 16:11:18 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.161 2015/02/26 09:54:46 roy Exp $");
#include "opt_ddb.h"
#include "opt_inet.h"
@ -453,7 +453,9 @@ arp_setgate(struct rtentry *rt, struct sockaddr *gate,
if ((rt->rt_flags & RTF_HOST) == 0 && netmask != NULL &&
satocsin(netmask)->sin_addr.s_addr != 0xffffffff)
rt->rt_flags |= RTF_CLONING;
if (rt->rt_flags & RTF_CLONING) {
if (rt->rt_flags & RTF_CLONING ||
((rt->rt_flags & (RTF_LLINFO | RTF_LOCAL)) && !rt->rt_llinfo))
{
union {
struct sockaddr sa;
struct sockaddr_storage ss;
@ -554,7 +556,9 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
break;
case RTM_ADD:
gate = arp_setgate(rt, gate, info->rti_info[RTAX_NETMASK]);
if (rt->rt_flags & RTF_CLONING) {
if (rt->rt_flags & RTF_CLONING ||
((rt->rt_flags & (RTF_LLINFO | RTF_LOCAL)) && !la))
{
/*
* Give this route an expiration time, even though
* it's a "permanent" route, so that routes cloned
@ -592,7 +596,8 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
}
#endif
}
break;
if (rt->rt_flags & RTF_CLONING)
break;
}
/* Announce a new entry if requested. */
if (rt->rt_flags & RTF_ANNOUNCE) {

View File

@ -1,4 +1,4 @@
/* $NetBSD: in.c,v 1.149 2014/12/01 17:07:43 christos Exp $ */
/* $NetBSD: in.c,v 1.150 2015/02/26 09:54:46 roy Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -91,7 +91,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.149 2014/12/01 17:07:43 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.150 2015/02/26 09:54:46 roy Exp $");
#include "opt_inet.h"
#include "opt_inet_conf.h"
@ -602,6 +602,40 @@ in_control(struct socket *so, u_long cmd, void *data, struct ifnet *ifp)
return error;
}
/* Add ownaddr as loopback rtentry. */
static void
in_ifaddlocal(struct ifaddr *ifa)
{
rt_ifa_addlocal(ifa);
}
/* Rempve loopback entry of ownaddr */
static void
in_ifremlocal(struct ifaddr *ifa)
{
struct in_ifaddr *ia, *p;
struct ifaddr *alt_ifa = NULL;
int ia_count = 0;
ia = (struct in_ifaddr *)ifa;
/* Delete the entry if exactly one ifaddr matches the
* address, ifa->ifa_addr. */
TAILQ_FOREACH(p, &in_ifaddrhead, ia_list) {
if (!in_hosteq(p->ia_addr.sin_addr, ia->ia_addr.sin_addr))
continue;
if (p->ia_ifp != ia->ia_ifp)
alt_ifa = &p->ia_ifa;
if (++ia_count > 1 && alt_ifa != NULL)
break;
}
if (ia_count == 0)
return;
rt_ifa_remlocal(ifa, ia_count == 1 ? NULL : alt_ifa);
}
void
in_purgeaddr(struct ifaddr *ifa)
{
@ -609,6 +643,7 @@ in_purgeaddr(struct ifaddr *ifa)
struct in_ifaddr *ia = (void *) ifa;
in_ifscrub(ifp, ia);
in_ifremlocal(ifa);
LIST_REMOVE(ia, ia_hash);
ifa_remove(ifp, &ia->ia_ifa);
TAILQ_REMOVE(&in_ifaddrhead, ia, ia_list);
@ -857,6 +892,9 @@ in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia,
ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
}
/* Add the local route to the address */
in_ifaddlocal(&ia->ia_ifa);
i = ia->ia_addr.sin_addr.s_addr;
if (IN_CLASSA(i))
ia->ia_netmask = IN_CLASSA_NET;
@ -958,13 +996,9 @@ in_addprefix(struct in_ifaddr *target, int flags)
* interface address, we don't need to bother
*
* XXX RADIX_MPATH implications here? -dyoung
*
* But we should still notify userland of the new address
*/
if (ia->ia_flags & IFA_ROUTE) {
rt_newaddrmsg(RTM_NEWADDR, &target->ia_ifa, 0, NULL);
if (ia->ia_flags & IFA_ROUTE)
return 0;
}
}
/*
@ -974,9 +1008,9 @@ in_addprefix(struct in_ifaddr *target, int flags)
if (error == 0)
target->ia_flags |= IFA_ROUTE;
else if (error == EEXIST) {
/*
/*
* the fact the route already exists is not an error.
*/
*/
error = 0;
}
return error;
@ -995,10 +1029,8 @@ in_scrubprefix(struct in_ifaddr *target)
int error;
/* If we don't have IFA_ROUTE we should still inform userland */
if ((target->ia_flags & IFA_ROUTE) == 0) {
rt_newaddrmsg(RTM_DELADDR, &target->ia_ifa, 0, NULL);
if ((target->ia_flags & IFA_ROUTE) == 0)
return 0;
}
if (rtinitflags(target))
prefix = target->ia_dstaddr.sin_addr;

View File

@ -1,4 +1,4 @@
/* $NetBSD: ip_carp.c,v 1.59 2014/07/31 02:37:25 ozaki-r Exp $ */
/* $NetBSD: ip_carp.c,v 1.60 2015/02/26 09:54:46 roy Exp $ */
/* $OpenBSD: ip_carp.c,v 1.113 2005/11/04 08:11:54 mcbride Exp $ */
/*
@ -31,7 +31,7 @@
#include "opt_mbuftrace.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ip_carp.c,v 1.59 2014/07/31 02:37:25 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: ip_carp.c,v 1.60 2015/02/26 09:54:46 roy Exp $");
/*
* TODO:
@ -446,9 +446,9 @@ carp_setroute(struct carp_softc *sc, int cmd)
#ifdef INET6
case AF_INET6:
if (cmd == RTM_ADD)
in6_ifaddloop(ifa);
in6_ifaddlocal(ifa);
else
in6_ifremloop(ifa);
in6_ifremlocal(ifa);
break;
#endif /* INET6 */
default:

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6.c,v 1.183 2015/02/25 00:26:58 roy Exp $ */
/* $NetBSD: in6.c,v 1.184 2015/02/26 09:54:46 roy Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.183 2015/02/25 00:26:58 roy Exp $");
__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.184 2015/02/26 09:54:46 roy Exp $");
#include "opt_inet.h"
#include "opt_compat_netbsd.h"
@ -144,70 +144,6 @@ static int in6_ifinit(struct ifnet *, struct in6_ifaddr *,
const struct sockaddr_in6 *, int);
static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
/*
* Subroutine for in6_ifaddloop() and in6_ifremloop().
* This routine does actual work.
*/
static void
in6_ifloop_request(int cmd, struct ifaddr *ifa)
{
struct sockaddr_in6 all1_sa;
struct rtentry *nrt = NULL;
int e;
sockaddr_in6_init(&all1_sa, &in6mask128, 0, 0, 0);
/*
* We specify the address itself as the gateway, and set the
* RTF_LLINFO flag, so that the corresponding host route would have
* the flag, and thus applications that assume traditional behavior
* would be happy. Note that we assume the caller of the function
* (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest,
* which changes the outgoing interface to the loopback interface.
*/
e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr,
(struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt);
if (e != 0) {
log(LOG_ERR, "in6_ifloop_request: "
"%s operation failed for %s (errno=%d)\n",
cmd == RTM_ADD ? "ADD" : "DELETE",
ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr),
e);
}
/*
* Make sure rt_ifa be equal to IFA, the second argument of the
* function.
* We need this because when we refer to rt_ifa->ia6_flags in
* ip6_input, we assume that the rt_ifa points to the address instead
* of the loopback address.
*/
if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa)
rt_replace_ifa(nrt, ifa);
/*
* Report the addition/removal of the address to the routing socket
* unless the address is marked tentative, where it will be reported
* once DAD completes.
* XXX: since we called rtinit for a p2p interface with a destination,
* we end up reporting twice in such a case. Should we rather
* omit the second report?
*/
if (nrt) {
rt_newaddrmsg(cmd, ifa, e, nrt);
if (cmd == RTM_DELETE) {
if (nrt->rt_refcnt <= 0) {
/* XXX: we should free the entry ourselves. */
nrt->rt_refcnt++;
rtfree(nrt);
}
} else {
/* the cmd must be RTM_ADD here */
nrt->rt_refcnt--;
}
}
}
/*
* Add ownaddr as loopback rtentry. We previously add the route only if
* necessary (ex. on a p2p link). However, since we now manage addresses
@ -216,28 +152,21 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa)
* any more.
*/
void
in6_ifaddloop(struct ifaddr *ifa)
in6_ifaddlocal(struct ifaddr *ifa)
{
struct rtentry *rt;
/* If there is no loopback entry, allocate one. */
rt = rtalloc1(ifa->ifa_addr, 0);
if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 ||
(rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
in6_ifloop_request(RTM_ADD, ifa);
if (rt != NULL)
rt->rt_refcnt--;
rt_ifa_addlocal(ifa);
}
/*
* Remove loopback rtentry of ownaddr generated by in6_ifaddloop(),
* Remove loopback rtentry of ownaddr generated by in6_ifaddlocal(),
* if it exists.
*/
void
in6_ifremloop(struct ifaddr *ifa)
in6_ifremlocal(struct ifaddr *ifa)
{
struct in6_ifaddr *alt_ia = NULL, *ia;
struct rtentry *rt;
struct in6_ifaddr *ia;
struct ifaddr *alt_ifa = NULL;
int ia_count = 0;
/*
@ -272,38 +201,15 @@ in6_ifremloop(struct ifaddr *ifa)
if (!IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr))
continue;
if (ia->ia_ifp != ifa->ifa_ifp)
alt_ia = ia;
if (++ia_count > 1 && alt_ia != NULL)
alt_ifa = &ia->ia_ifa;
if (++ia_count > 1 && alt_ifa != NULL)
break;
}
if (ia_count == 0)
return;
if ((rt = rtalloc1(ifa->ifa_addr, 0)) == NULL)
return;
rt->rt_refcnt--;
/*
* Before deleting, check if a corresponding loopbacked
* host route surely exists. With this check, we can avoid
* deleting an interface direct route whose destination is
* the same as the address being removed. This can happen
* when removing a subnet-router anycast address on an
* interface attached to a shared medium.
*/
if ((rt->rt_flags & RTF_HOST) == 0 ||
(rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
return;
/* If we cannot replace the route's ifaddr with the equivalent
* ifaddr of another interface, I believe it is safest to
* delete the route.
*/
if (ia_count == 1 || alt_ia == NULL)
in6_ifloop_request(RTM_DELETE, ifa);
else
rt_replace_ifa(rt, &alt_ia->ia_ifa);
rt_ifa_remlocal(ifa, ia_count == 1 ? NULL : alt_ifa);
}
int
@ -1383,7 +1289,7 @@ in6_purgeaddr(struct ifaddr *ifa)
}
/* Remove ownaddr's loopback rtentry, if it exists. */
in6_ifremloop(&(ia->ia_ifa));
in6_ifremlocal(&(ia->ia_ifa));
/*
* leave from multicast groups we have joined for the interface
@ -1781,8 +1687,9 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
/* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */
if (newhost) {
/* set the rtrequest function to create llinfo */
ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
in6_ifaddloop(&ia->ia_ifa);
if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0)
ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
in6_ifaddlocal(&ia->ia_ifa);
} else {
/* Inform the routing socket of new flags/timings */
rt_newaddrmsg(RTM_NEWADDR, &ia->ia_ifa, 0, NULL);

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6_var.h,v 1.71 2014/09/05 06:06:31 matt Exp $ */
/* $NetBSD: in6_var.h,v 1.72 2015/02/26 09:54:46 roy Exp $ */
/* $KAME: in6_var.h,v 1.81 2002/06/08 11:16:51 itojun Exp $ */
/*
@ -693,8 +693,8 @@ int in6_if2idlen (struct ifnet *);
void *in6_domifattach(struct ifnet *);
void in6_domifdetach(struct ifnet *, void *);
void in6_restoremkludge(struct in6_ifaddr *, struct ifnet *);
void in6_ifremloop(struct ifaddr *);
void in6_ifaddloop(struct ifaddr *);
void in6_ifremlocal(struct ifaddr *);
void in6_ifaddlocal(struct ifaddr *);
void in6_createmkludge(struct ifnet *);
void in6_purgemkludge(struct ifnet *);
struct in6_ifaddr *in6ifa_ifpforlinklocal(const struct ifnet *, int);