mirror of
https://github.com/opnsense/src.git
synced 2026-02-19 02:30:08 -05:00
Fix lladdr change propagation for on vlans on top of it.
Fix lladdr update when setting mac address manually. Fix lladdr_event for slave ports addition. MFC after: 4 weeks Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D4004
This commit is contained in:
parent
1db7984861
commit
bb3d23fd35
3 changed files with 89 additions and 52 deletions
|
|
@ -3314,8 +3314,10 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching)
|
|||
*
|
||||
* At this time we only support certain types of interfaces,
|
||||
* and we don't allow the length of the address to change.
|
||||
*
|
||||
* Set noinline to be dtrace-friendly
|
||||
*/
|
||||
int
|
||||
__noinline int
|
||||
if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len)
|
||||
{
|
||||
struct sockaddr_dl *sdl;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ static const char laggname[] = "lagg";
|
|||
|
||||
static void lagg_lladdr(struct lagg_softc *, uint8_t *);
|
||||
static void lagg_capabilities(struct lagg_softc *);
|
||||
static void lagg_port_lladdr(struct lagg_port *, uint8_t *);
|
||||
static void lagg_port_lladdr(struct lagg_port *, uint8_t *, lagg_llqtype);
|
||||
static void lagg_port_setlladdr(void *, int);
|
||||
static int lagg_port_create(struct lagg_softc *, struct ifnet *);
|
||||
static int lagg_port_destroy(struct lagg_port *, int);
|
||||
|
|
@ -543,6 +543,7 @@ lagg_clone_destroy(struct ifnet *ifp)
|
|||
lagg_port_destroy(lp, 1);
|
||||
/* Unhook the aggregation protocol */
|
||||
lagg_proto_detach(sc);
|
||||
LAGG_UNLOCK_ASSERT(sc);
|
||||
|
||||
ifmedia_removeall(&sc->sc_media);
|
||||
ether_ifdetach(ifp);
|
||||
|
|
@ -557,7 +558,12 @@ lagg_clone_destroy(struct ifnet *ifp)
|
|||
free(sc, M_DEVBUF);
|
||||
}
|
||||
|
||||
static void
|
||||
/*
|
||||
* Set link-layer address on the lagg interface itself.
|
||||
*
|
||||
* Set noinline to be dtrace-friendly
|
||||
*/
|
||||
static __noinline void
|
||||
lagg_lladdr(struct lagg_softc *sc, uint8_t *lladdr)
|
||||
{
|
||||
struct ifnet *ifp = sc->sc_ifp;
|
||||
|
|
@ -577,11 +583,16 @@ lagg_lladdr(struct lagg_softc *sc, uint8_t *lladdr)
|
|||
bcopy(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN);
|
||||
lagg_proto_lladdr(sc);
|
||||
|
||||
/*
|
||||
* Send notification request for lagg interface
|
||||
* itself. Note that new lladdr is already set.
|
||||
*/
|
||||
bzero(&lp, sizeof(lp));
|
||||
lp.lp_ifp = sc->sc_ifp;
|
||||
lp.lp_softc = sc;
|
||||
|
||||
lagg_port_lladdr(&lp, lladdr);
|
||||
/* Do not request lladdr change */
|
||||
lagg_port_lladdr(&lp, lladdr, LAGG_LLQTYPE_VIRT);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -622,51 +633,58 @@ lagg_capabilities(struct lagg_softc *sc)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr)
|
||||
/*
|
||||
* Enqueue interface lladdr notification.
|
||||
* If request is already queued, it is updated.
|
||||
* If setting lladdr is also desired, @do_change has to be set to 1.
|
||||
*
|
||||
* Set noinline to be dtrace-friendly
|
||||
*/
|
||||
static __noinline void
|
||||
lagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr, lagg_llqtype llq_type)
|
||||
{
|
||||
struct lagg_softc *sc = lp->lp_softc;
|
||||
struct ifnet *ifp = lp->lp_ifp;
|
||||
struct lagg_llq *llq;
|
||||
int pending = 0;
|
||||
int primary;
|
||||
|
||||
LAGG_WLOCK_ASSERT(sc);
|
||||
|
||||
primary = (sc->sc_primary->lp_ifp == ifp) ? 1 : 0;
|
||||
if (primary == 0 && (lp->lp_detaching ||
|
||||
memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0))
|
||||
/*
|
||||
* Do not enqueue requests where lladdr is the same for
|
||||
* "physical" interfaces (e.g. ports in lagg)
|
||||
*/
|
||||
if (llq_type == LAGG_LLQTYPE_PHYS &&
|
||||
memcmp(IF_LLADDR(ifp), lladdr, ETHER_ADDR_LEN) == 0)
|
||||
return;
|
||||
|
||||
/* Check to make sure its not already queued to be changed */
|
||||
SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) {
|
||||
if (llq->llq_ifp == ifp && llq->llq_primary == primary) {
|
||||
pending = 1;
|
||||
break;
|
||||
if (llq->llq_ifp == ifp) {
|
||||
/* Update lladdr, it may have changed */
|
||||
bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT);
|
||||
if (llq == NULL) /* XXX what to do */
|
||||
return;
|
||||
}
|
||||
llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (llq == NULL) /* XXX what to do */
|
||||
return;
|
||||
|
||||
/* Update the lladdr even if pending, it may have changed */
|
||||
llq->llq_ifp = ifp;
|
||||
llq->llq_primary = primary;
|
||||
llq->llq_type = llq_type;
|
||||
bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
|
||||
|
||||
if (!pending)
|
||||
SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
|
||||
/* XXX: We should insert to tail */
|
||||
SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
|
||||
|
||||
taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the interface MAC address from a taskqueue to avoid a LOR.
|
||||
*
|
||||
* Set noinline to be dtrace-friendly
|
||||
*/
|
||||
static void
|
||||
static __noinline void
|
||||
lagg_port_setlladdr(void *arg, int pending)
|
||||
{
|
||||
struct lagg_softc *sc = (struct lagg_softc *)arg;
|
||||
|
|
@ -688,17 +706,20 @@ lagg_port_setlladdr(void *arg, int pending)
|
|||
ifp = llq->llq_ifp;
|
||||
|
||||
CURVNET_SET(ifp->if_vnet);
|
||||
if (llq->llq_primary == 0) {
|
||||
/*
|
||||
* Set the link layer address on the laggport interface.
|
||||
* if_setlladdr() triggers gratuitous ARPs for INET.
|
||||
*/
|
||||
error = 0;
|
||||
|
||||
/*
|
||||
* Set the link layer address on the laggport interface.
|
||||
* Note that if_setlladdr() or iflladdr_event handler
|
||||
* may result in arp transmission / lltable updates.
|
||||
*/
|
||||
if (llq->llq_type == LAGG_LLQTYPE_PHYS)
|
||||
error = if_setlladdr(ifp, llq->llq_lladdr,
|
||||
ETHER_ADDR_LEN);
|
||||
if (error)
|
||||
printf("%s: setlladdr failed on %s\n", __func__,
|
||||
ifp->if_xname);
|
||||
} else
|
||||
if (error)
|
||||
printf("%s: setlladdr failed on %s\n", __func__,
|
||||
ifp->if_xname);
|
||||
else
|
||||
EVENTHANDLER_INVOKE(iflladdr_event, ifp);
|
||||
CURVNET_RESTORE();
|
||||
head = SLIST_NEXT(llq, llq_entries);
|
||||
|
|
@ -730,7 +751,7 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp)
|
|||
}
|
||||
|
||||
/* XXX Disallow non-ethernet interfaces (this should be any of 802) */
|
||||
if (ifp->if_type != IFT_ETHER)
|
||||
if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN)
|
||||
return (EPROTONOSUPPORT);
|
||||
|
||||
/* Allow the first Ethernet member to define the MTU */
|
||||
|
|
@ -784,10 +805,15 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp)
|
|||
|
||||
if (SLIST_EMPTY(&sc->sc_ports)) {
|
||||
sc->sc_primary = lp;
|
||||
/* First port in lagg. Update/notify lagg lladdress */
|
||||
lagg_lladdr(sc, IF_LLADDR(ifp));
|
||||
} else {
|
||||
/* Update link layer address for this port */
|
||||
lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp));
|
||||
|
||||
/*
|
||||
* Update link layer address for this port and
|
||||
* send notifications to other subsystems.
|
||||
*/
|
||||
lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp), LAGG_LLQTYPE_PHYS);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -873,7 +899,7 @@ lagg_port_destroy(struct lagg_port *lp, int rundelport)
|
|||
if (!lp->lp_detaching) {
|
||||
lagg_ether_cmdmulti(lp, 0);
|
||||
lagg_setflags(lp, 0);
|
||||
lagg_port_lladdr(lp, lp->lp_lladdr);
|
||||
lagg_port_lladdr(lp, lp->lp_lladdr, LAGG_LLQTYPE_PHYS);
|
||||
}
|
||||
|
||||
/* Restore interface */
|
||||
|
|
@ -905,19 +931,16 @@ lagg_port_destroy(struct lagg_port *lp, int rundelport)
|
|||
}
|
||||
lagg_lladdr(sc, lladdr);
|
||||
|
||||
/* Mark lp0 as new primary */
|
||||
sc->sc_primary = lp0;
|
||||
|
||||
/*
|
||||
* Update link layer address for each port. No port is
|
||||
* marked as primary at this moment.
|
||||
* Enqueue lladdr update/notification for each port
|
||||
* (new primary needs update as well, to switch from
|
||||
* old lladdr to its 'real' one).
|
||||
*/
|
||||
SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries)
|
||||
lagg_port_lladdr(lp_ptr, lladdr);
|
||||
/*
|
||||
* Mark lp0 as the new primary. This invokes an
|
||||
* iflladdr_event.
|
||||
*/
|
||||
sc->sc_primary = lp0;
|
||||
if (lp0 != NULL)
|
||||
lagg_port_lladdr(lp0, lladdr);
|
||||
lagg_port_lladdr(lp_ptr, lladdr, LAGG_LLQTYPE_PHYS);
|
||||
}
|
||||
|
||||
/* Remove any pending lladdr changes from the queue */
|
||||
|
|
@ -1149,8 +1172,8 @@ static void
|
|||
lagg_init(void *xsc)
|
||||
{
|
||||
struct lagg_softc *sc = (struct lagg_softc *)xsc;
|
||||
struct lagg_port *lp;
|
||||
struct ifnet *ifp = sc->sc_ifp;
|
||||
struct lagg_port *lp;
|
||||
|
||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
||||
return;
|
||||
|
|
@ -1158,9 +1181,14 @@ lagg_init(void *xsc)
|
|||
LAGG_WLOCK(sc);
|
||||
|
||||
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
||||
/* Update the port lladdrs */
|
||||
|
||||
/*
|
||||
* Update the port lladdrs if needed.
|
||||
* This might be if_setlladdr() notification
|
||||
* that lladdr has been changed.
|
||||
*/
|
||||
SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
|
||||
lagg_port_lladdr(lp, IF_LLADDR(ifp));
|
||||
lagg_port_lladdr(lp, IF_LLADDR(ifp), LAGG_LLQTYPE_PHYS);
|
||||
|
||||
lagg_proto_init(sc);
|
||||
|
||||
|
|
@ -1244,6 +1272,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
|||
|
||||
LAGG_WLOCK(sc);
|
||||
lagg_proto_detach(sc);
|
||||
LAGG_UNLOCK_ASSERT(sc);
|
||||
lagg_proto_attach(sc, ra->ra_proto);
|
||||
break;
|
||||
case SIOCGLAGGOPTS:
|
||||
|
|
|
|||
|
|
@ -201,11 +201,16 @@ struct lagg_mc {
|
|||
SLIST_ENTRY(lagg_mc) mc_entries;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LAGG_LLQTYPE_PHYS = 0, /* Task related to physical (underlying) port */
|
||||
LAGG_LLQTYPE_VIRT, /* Task related to lagg interface itself */
|
||||
} lagg_llqtype;
|
||||
|
||||
/* List of interfaces to have the MAC address modified */
|
||||
struct lagg_llq {
|
||||
struct ifnet *llq_ifp;
|
||||
uint8_t llq_lladdr[ETHER_ADDR_LEN];
|
||||
uint8_t llq_primary;
|
||||
lagg_llqtype llq_type;
|
||||
SLIST_ENTRY(lagg_llq) llq_entries;
|
||||
};
|
||||
|
||||
|
|
@ -273,6 +278,7 @@ struct lagg_port {
|
|||
#define LAGG_WUNLOCK(_sc) rm_wunlock(&(_sc)->sc_mtx)
|
||||
#define LAGG_RLOCK_ASSERT(_sc) rm_assert(&(_sc)->sc_mtx, RA_RLOCKED)
|
||||
#define LAGG_WLOCK_ASSERT(_sc) rm_assert(&(_sc)->sc_mtx, RA_WLOCKED)
|
||||
#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 );
|
||||
|
|
|
|||
Loading…
Reference in a new issue