From 1435dcd94f4defeed211acb37a28f4a255f41616 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" Date: Sat, 17 Mar 2018 17:05:48 +0000 Subject: [PATCH] Fix outgoing TCP/UDP packet drop on arp/ndp entry expiration. Current arp/nd code relies on the feedback from the datapath indicating that the entry is still used. This mechanism is incorporated into the arpresolve()/nd6_resolve() routines. After the inpcb route cache introduction, the packet path for the locally-originated packets changed, passing cached lle pointer to the ether_output() directly. This resulted in the arp/ndp entry expire each time exactly after the configured max_age interval. During the small window between the ARP/NDP request and reply from the router, most of the packets got lost. Fix this behaviour by plugging datapath notification code to the packet path used by route cache. Unify the notification code by using single inlined function with the per-AF callbacks. Reported by: sthaug at nethelp.no Reviewed by: ae MFC after: 2 weeks --- sys/net/if_ethersubr.c | 8 +++++++- sys/net/if_llatbl.h | 15 +++++++++++++++ sys/netinet/if_ether.c | 16 ++++------------ sys/netinet/in.c | 14 ++++++++++++++ sys/netinet6/in6.c | 20 ++++++++++++++++++++ 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 8f280055da8..c8cd6d3411a 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -311,7 +311,13 @@ ether_output(struct ifnet *ifp, struct mbuf *m, if (lle == NULL) { /* if we lookup, keep cache */ addref = 1; - } + } else + /* + * Notify LLE code that + * the entry was used + * by datapath. + */ + llentry_mark_used(lle); } if (lle != NULL) { phdr = lle->r_linkdata; diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h index fcf93883154..9cec4ac75e5 100644 --- a/sys/net/if_llatbl.h +++ b/sys/net/if_llatbl.h @@ -149,6 +149,7 @@ typedef void (llt_fill_sa_entry_t)(const struct llentry *, struct sockaddr *); typedef void (llt_free_tbl_t)(struct lltable *); typedef void (llt_link_entry_t)(struct lltable *, struct llentry *); typedef void (llt_unlink_entry_t)(struct llentry *); +typedef void (llt_mark_used_t)(struct llentry *); typedef int (llt_foreach_cb_t)(struct lltable *, struct llentry *, void *); typedef int (llt_foreach_entry_t)(struct lltable *, llt_foreach_cb_t *, void *); @@ -173,6 +174,7 @@ struct lltable { llt_unlink_entry_t *llt_unlink_entry; llt_fill_sa_entry_t *llt_fill_sa_entry; llt_free_tbl_t *llt_free_tbl; + llt_mark_used_t *llt_mark_used; }; MALLOC_DECLARE(M_LLTABLE); @@ -247,6 +249,19 @@ lla_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) return (llt->llt_lookup(llt, flags, l3addr)); } +/* + * Notify the LLE code that the entry was used by datapath. + */ +static __inline void +llentry_mark_used(struct llentry *lle) +{ + + if (lle->r_skip_req == 0) + return; + if ((lle->r_flags & RLLE_VALID) != 0) + lle->lle_tbl->llt_mark_used(lle); +} + int lla_rt_output(struct rt_msghdr *, struct rt_addrinfo *); #include diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 800b51e599e..8b072503914 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -504,12 +504,8 @@ arpresolve_full(struct ifnet *ifp, int is_gw, int flags, struct mbuf *m, } bcopy(lladdr, desten, ll_len); - /* Check if we have feedback request from arptimer() */ - if (la->r_skip_req != 0) { - LLE_REQ_LOCK(la); - la->r_skip_req = 0; /* Notify that entry was used */ - LLE_REQ_UNLOCK(la); - } + /* Notify LLE code that the entry was used by datapath */ + llentry_mark_used(la); if (pflags != NULL) *pflags = la->la_flags & (LLE_VALID|LLE_IFADDR); if (plle) { @@ -640,12 +636,8 @@ arpresolve(struct ifnet *ifp, int is_gw, struct mbuf *m, bcopy(la->r_linkdata, desten, la->r_hdrlen); if (pflags != NULL) *pflags = LLE_VALID | (la->r_flags & RLLE_IFADDR); - /* Check if we have feedback request from arptimer() */ - if (la->r_skip_req != 0) { - LLE_REQ_LOCK(la); - la->r_skip_req = 0; /* Notify that entry was used */ - LLE_REQ_UNLOCK(la); - } + /* Notify the LLE handling code that the entry was used. */ + llentry_mark_used(la); if (plle) { LLE_ADDREF(la); *plle = la; diff --git a/sys/netinet/in.c b/sys/netinet/in.c index f4f87491182..6ecef6c0a44 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -1067,6 +1067,19 @@ in_lltable_destroy_lle_unlocked(struct llentry *lle) free(lle, M_LLTABLE); } +/* + * Called by the datapath to indicate that + * the entry was used. + */ +static void +in_lltable_mark_used(struct llentry *lle) +{ + + LLE_REQ_LOCK(lle); + lle->r_skip_req = 0; + LLE_REQ_UNLOCK(lle); +} + /* * Called by LLE_FREE_LOCKED when number of references * drops to zero. @@ -1470,6 +1483,7 @@ in_lltattach(struct ifnet *ifp) llt->llt_fill_sa_entry = in_lltable_fill_sa_entry; llt->llt_free_entry = in_lltable_free_entry; llt->llt_match_prefix = in_lltable_match_prefix; + llt->llt_mark_used = in_lltable_mark_used; lltable_link(llt); return (llt); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index fa8f77f6729..8a26e736d62 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -2148,6 +2148,25 @@ in6_lltable_rtcheck(struct ifnet *ifp, return 0; } +/* + * Called by the datapath to indicate that the entry was used. + */ +static void +in6_lltable_mark_used(struct llentry *lle) +{ + + LLE_REQ_LOCK(lle); + lle->r_skip_req = 0; + + /* + * Set the hit time so the callback function + * can determine the remaining time before + * transiting to the DELAY state. + */ + lle->lle_hittime = time_uptime; + LLE_REQ_UNLOCK(lle); +} + static inline uint32_t in6_lltable_hash_dst(const struct in6_addr *dst, uint32_t hsize) { @@ -2380,6 +2399,7 @@ in6_lltattach(struct ifnet *ifp) llt->llt_fill_sa_entry = in6_lltable_fill_sa_entry; llt->llt_free_entry = in6_lltable_free_entry; llt->llt_match_prefix = in6_lltable_match_prefix; + llt->llt_mark_used = in6_lltable_mark_used; lltable_link(llt); return (llt);