From 42900924fde883fb66607932136fb23ea3d1b6db Mon Sep 17 00:00:00 2001 From: roy Date: Thu, 26 Feb 2015 09:54:46 +0000 Subject: [PATCH] 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. --- sys/net/route.c | 131 +++++++++++++++++++++++++++++++++++++++-- sys/net/route.h | 5 +- sys/netinet/if_arp.c | 15 +++-- sys/netinet/in.c | 56 ++++++++++++++---- sys/netinet/ip_carp.c | 8 +-- sys/netinet6/in6.c | 123 +++++--------------------------------- sys/netinet6/in6_var.h | 6 +- 7 files changed, 206 insertions(+), 138 deletions(-) diff --git a/sys/net/route.c b/sys/net/route.c index b5def64c9ce8..90ccbb144e3e 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -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 -__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 #include @@ -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 diff --git a/sys/net/route.h b/sys/net/route.h index e866f3c32622..18f8a20bb030 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -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 *); diff --git a/sys/netinet/if_arp.c b/sys/netinet/if_arp.c index ffeccdc3388a..c16ba7f3c521 100644 --- a/sys/netinet/if_arp.c +++ b/sys/netinet/if_arp.c @@ -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 -__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) { diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 32b27121e2a4..a7b7deabac18 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -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 -__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; diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 9446d3dc2a8d..03010bb1dd89 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -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 -__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: diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 757c2c07e136..124a6e458c98 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -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 -__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); diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index a1bbe8057b46..bb8be2cacfd7 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -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);