summaryrefslogtreecommitdiffstats
path: root/sys/netinet/if_ether.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/if_ether.c')
-rw-r--r--sys/netinet/if_ether.c93
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);
OpenPOWER on IntegriCloud