summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authorsmh <smh@FreeBSD.org>2015-12-15 16:02:11 +0000
committersmh <smh@FreeBSD.org>2015-12-15 16:02:11 +0000
commit864cf1812819836284d12030ce553ee743ca10f0 (patch)
tree681a26be2c813d8809988c0d761c31d1aecdf72e /sys/net
parent2dd258160a988888832a006ec38aa1384ed5ab95 (diff)
downloadFreeBSD-src-864cf1812819836284d12030ce553ee743ca10f0.zip
FreeBSD-src-864cf1812819836284d12030ce553ee743ca10f0.tar.gz
Fix lagg failover due to missing notifications
When using lagg failover mode neither Gratuitous ARP (IPv4) or Unsolicited Neighbour Advertisements (IPv6) are sent to notify other nodes that the address may have moved. This results is slow failover, dropped packets and network outages for the lagg interface when the primary link goes down. We now use the new if_link_state_change_cond with the force param set to allow lagg to force through link state changes and hence fire a ifnet_link_event which are now monitored by rip and nd6. Upon receiving these events each protocol trigger the relevant notifications: * inet4 => Gratuitous ARP * inet6 => Unsolicited Neighbour Announce This also fixes the carp IPv6 NA's that stopped working after r251584 which added the ipv6_route__llma route. The new behavour can be controlled using the sysctls: * net.link.ether.inet.arp_on_link * net.inet6.icmp6.nd6_on_link Also removed unused param from lagg_port_state and added descriptions for the sysctls while here. PR: 156226 MFC after: 1 month Sponsored by: Multiplay Differential Revision: https://reviews.freebsd.org/D4111
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if.c24
-rw-r--r--sys/net/if_lagg.c13
-rw-r--r--sys/net/if_lagg.h2
-rw-r--r--sys/net/if_var.h1
4 files changed, 30 insertions, 10 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index b88c05e..177f356 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -126,7 +126,7 @@ SX_SYSINIT(ifdescr_sx, &ifdescr_sx, "ifnet descr");
void (*bridge_linkstate_p)(struct ifnet *ifp);
void (*ng_ether_link_state_p)(struct ifnet *ifp, int state);
-void (*lagg_linkstate_p)(struct ifnet *ifp, int state);
+void (*lagg_linkstate_p)(struct ifnet *ifp);
/* These are external hooks for CARP. */
void (*carp_linkstate_p)(struct ifnet *ifp);
void (*carp_demote_adj_p)(int, char *);
@@ -1984,6 +1984,8 @@ if_unroute(struct ifnet *ifp, int flag, int fam)
if (ifp->if_carp)
(*carp_linkstate_p)(ifp);
+ if (ifp->if_lagg)
+ (*lagg_linkstate_p)(ifp);
rt_ifmsg(ifp);
}
@@ -2005,6 +2007,8 @@ if_route(struct ifnet *ifp, int flag, int fam)
pfctlinput(PRC_IFUP, ifa->ifa_addr);
if (ifp->if_carp)
(*carp_linkstate_p)(ifp);
+ if (ifp->if_lagg)
+ (*lagg_linkstate_p)(ifp);
rt_ifmsg(ifp);
#ifdef INET6
in6_if_up(ifp);
@@ -2019,17 +2023,27 @@ int (*vlan_tag_p)(struct ifnet *, uint16_t *);
int (*vlan_setcookie_p)(struct ifnet *, void *);
void *(*vlan_cookie_p)(struct ifnet *);
+void
+if_link_state_change(struct ifnet *ifp, int link_state)
+{
+
+ return if_link_state_change_cond(ifp, link_state, 0);
+}
+
/*
* Handle a change in the interface link state. To avoid LORs
* between driver lock and upper layer locks, as well as possible
* recursions, we post event to taskqueue, and all job
* is done in static do_link_state_change().
+ *
+ * If the current link state matches link_state and force isn't
+ * specified no action is taken.
*/
void
-if_link_state_change(struct ifnet *ifp, int link_state)
+if_link_state_change_cond(struct ifnet *ifp, int link_state, int force)
{
- /* Return if state hasn't changed. */
- if (ifp->if_link_state == link_state)
+
+ if (ifp->if_link_state == link_state && !force)
return;
ifp->if_link_state = link_state;
@@ -2057,7 +2071,7 @@ do_link_state_change(void *arg, int pending)
if (ifp->if_bridge)
(*bridge_linkstate_p)(ifp);
if (ifp->if_lagg)
- (*lagg_linkstate_p)(ifp, link_state);
+ (*lagg_linkstate_p)(ifp);
if (IS_DEFAULT_VNET(curvnet))
devctl_notify("IFNET", ifp->if_xname,
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 730a044..6ca3f22 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -106,7 +106,7 @@ static int lagg_port_create(struct lagg_softc *, struct ifnet *);
static int lagg_port_destroy(struct lagg_port *, int);
static struct mbuf *lagg_input(struct ifnet *, struct mbuf *);
static void lagg_linkstate(struct lagg_softc *);
-static void lagg_port_state(struct ifnet *, int);
+static void lagg_port_state(struct ifnet *);
static int lagg_port_ioctl(struct ifnet *, u_long, caddr_t);
static int lagg_port_output(struct ifnet *, struct mbuf *,
const struct sockaddr *, struct route *);
@@ -1774,7 +1774,12 @@ lagg_linkstate(struct lagg_softc *sc)
break;
}
}
- if_link_state_change(sc->sc_ifp, new_link);
+
+ /*
+ * Force state change to ensure ifnet_link_event is generated allowing
+ * protocols to notify other nodes of potential address move.
+ */
+ if_link_state_change_cond(sc->sc_ifp, new_link, 1);
/* Update if_baudrate to reflect the max possible speed */
switch (sc->sc_proto) {
@@ -1797,7 +1802,7 @@ lagg_linkstate(struct lagg_softc *sc)
}
static void
-lagg_port_state(struct ifnet *ifp, int state)
+lagg_port_state(struct ifnet *ifp)
{
struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg;
struct lagg_softc *sc = NULL;
@@ -1813,7 +1818,7 @@ lagg_port_state(struct ifnet *ifp, int state)
LAGG_WUNLOCK(sc);
}
-struct lagg_port *
+static struct lagg_port *
lagg_link_active(struct lagg_softc *sc, struct lagg_port *lp)
{
struct lagg_port *lp_next, *rval = NULL;
diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h
index 195ac3a..ea59762 100644
--- a/sys/net/if_lagg.h
+++ b/sys/net/if_lagg.h
@@ -281,7 +281,7 @@ struct lagg_port {
#define LAGG_UNLOCK_ASSERT(_sc) rm_assert(&(_sc)->sc_mtx, RA_UNLOCKED)
extern struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *);
-extern void (*lagg_linkstate_p)(struct ifnet *, int );
+extern void (*lagg_linkstate_p)(struct ifnet *);
int lagg_enqueue(struct ifnet *, struct mbuf *);
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 5911cec..2cbd76c 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -500,6 +500,7 @@ struct ifmultiaddr *
void if_free(struct ifnet *);
void if_initname(struct ifnet *, const char *, int);
void if_link_state_change(struct ifnet *, int);
+void if_link_state_change_cond(struct ifnet *, int, int);
int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3);
void if_ref(struct ifnet *);
void if_rele(struct ifnet *);
OpenPOWER on IntegriCloud