diff options
Diffstat (limited to 'sys/netinet/if_ether.c')
-rw-r--r-- | sys/netinet/if_ether.c | 93 |
1 files changed, 78 insertions, 15 deletions
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 72c2a60..2214542 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -107,6 +107,7 @@ VNET_PCPUSTAT_SYSUNINIT(arpstat); #endif /* VIMAGE */ static VNET_DEFINE(int, arp_maxhold) = 1; +static VNET_DEFINE(int, arp_on_link) = 1; #define V_arpt_keep VNET(arpt_keep) #define V_arpt_down VNET(arpt_down) @@ -114,6 +115,7 @@ static VNET_DEFINE(int, arp_maxhold) = 1; #define V_arp_maxtries VNET(arp_maxtries) #define V_arp_proxyall VNET(arp_proxyall) #define V_arp_maxhold VNET(arp_maxhold) +#define V_arp_on_link VNET(arp_on_link) SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arpt_keep), 0, @@ -136,6 +138,9 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_log_per_second, CTLFLAG_RW, &arp_maxpps, 0, "Maximum number of remotely triggered ARP messages that can be " "logged per second"); +SYSCTL_INT(_net_link_ether_inet, OID_AUTO, arp_on_link, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(arp_on_link), 0, + "Send gratuitous ARP's on interface link up events"); #define ARP_LOG(pri, ...) do { \ if (ppsratecheck(&arp_lastlog, &arp_curpps, arp_maxpps)) \ @@ -156,6 +161,7 @@ static void arp_mark_lle_reachable(struct llentry *la); static void arp_iflladdr(void *arg __unused, struct ifnet *ifp); static eventhandler_tag iflladdr_tag; +static eventhandler_tag ifnet_link_event_tag; static const struct netisr_handler arp_nh = { .nh_name = "arp", @@ -1176,43 +1182,96 @@ arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa) if (ntohl(dst_in->sin_addr.s_addr) == INADDR_ANY) return; - arp_announce_ifaddr(ifp, dst_in->sin_addr, IF_LLADDR(ifp)); + arp_announce_addr(ifp, &dst_in->sin_addr, IF_LLADDR(ifp)); arp_add_ifa_lle(ifp, dst); } -void -arp_announce_ifaddr(struct ifnet *ifp, struct in_addr addr, u_char *enaddr) +void __noinline +arp_announce_addr(struct ifnet *ifp, const struct in_addr *addr, u_char *enaddr) { - if (ntohl(addr.s_addr) != INADDR_ANY) - arprequest(ifp, &addr, &addr, enaddr); + if (ntohl(addr->s_addr) != INADDR_ANY) + arprequest(ifp, addr, addr, enaddr); } /* - * Sends gratuitous ARPs for each ifaddr to notify other - * nodes about the address change. + * Send gratuitous ARPs for all interfaces addresses to notify other nodes of + * changes. + * + * This is a noop if the interface isn't up or has been flagged for no ARP. */ -static __noinline void -arp_handle_ifllchange(struct ifnet *ifp) +void __noinline +arp_announce(struct ifnet *ifp) { + int i, cnt, entries; + u_char *lladdr; struct ifaddr *ifa; + struct in_addr *addr, *head; + + if (!(ifp->if_flags & IFF_UP) || (ifp->if_flags & IFF_NOARP)) + return; + + entries = 8; + cnt = 0; + head = malloc(sizeof(*addr) * entries, M_TEMP, M_NOWAIT); + if (head == NULL) { + log(LOG_INFO, "arp_announce: malloc %d entries failed\n", + entries); + return; + } + /* Take a copy then process to avoid locking issues. */ + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family == AF_INET) - arp_ifinit(ifp, ifa); + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + if (cnt == entries) { + addr = (struct in_addr *)realloc(head, sizeof(*addr) * + (entries + 8), M_TEMP, M_NOWAIT); + if (addr == NULL) { + log(LOG_INFO, "arp_announce: realloc to %d " + "entries failed\n", entries + 8); + /* Process what we have. */ + break; + } + entries += 8; + head = addr; + } + + addr = head + cnt; + bcopy(IFA_IN(ifa), addr, sizeof(*addr)); + cnt++; } + IF_ADDR_RUNLOCK(ifp); + + lladdr = IF_LLADDR(ifp); + for (i = 0; i < cnt; i++) { + arp_announce_addr(ifp, head + i, lladdr); + } + free(head, M_TEMP); +} + +/* + * A handler for interface linkstate change events. + */ +static void +arp_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) +{ + + if (linkstate == LINK_STATE_UP && V_arp_on_link) + arp_announce(ifp); } /* - * A handler for interface link layer address change event. + * A handler for interface link layer address change events. */ static __noinline void arp_iflladdr(void *arg __unused, struct ifnet *ifp) { - if ((ifp->if_flags & IFF_UP) != 0) - arp_handle_ifllchange(ifp); + arp_announce(ifp); } static void @@ -1220,8 +1279,12 @@ arp_init(void) { netisr_register(&arp_nh); - if (IS_DEFAULT_VNET(curvnet)) + + if (IS_DEFAULT_VNET(curvnet)) { iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, arp_iflladdr, NULL, EVENTHANDLER_PRI_ANY); + ifnet_link_event_tag = EVENTHANDLER_REGISTER(ifnet_link_event, + arp_ifnet_link_event, 0, EVENTHANDLER_PRI_ANY); + } } SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); |