diff options
author | bms <bms@FreeBSD.org> | 2007-03-20 00:36:10 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2007-03-20 00:36:10 +0000 |
commit | 4ffc00490175f1ea8b4a87149bed2b0076df6f3b (patch) | |
tree | a99ae311b25195ab5b7322b739f496dec7a7cd69 /sys/netgraph/ng_ether.c | |
parent | a8db64a5b356ec426744ef16079c671b79e6369b (diff) | |
download | FreeBSD-src-4ffc00490175f1ea8b4a87149bed2b0076df6f3b.zip FreeBSD-src-4ffc00490175f1ea8b4a87149bed2b0076df6f3b.tar.gz |
Implement reference counting for ifmultiaddr, in_multi, and in6_multi
structures. Detect when ifnet instances are detached from the network
stack and perform appropriate cleanup to prevent memory leaks.
This has been implemented in such a way as to be backwards ABI compatible.
Kernel consumers are changed to use if_delmulti_ifma(); in_delmulti()
is unable to detect interface removal by design, as it performs searches
on structures which are removed with the interface.
With this architectural change, the panics FreeBSD users have experienced
with carp and pfsync should be resolved.
Obtained from: p4 branch bms_netdev
Reviewed by: andre
Sponsored by: Garance A Drosehn
Idea from: NetBSD
MFC after: 1 month
Diffstat (limited to 'sys/netgraph/ng_ether.c')
-rw-r--r-- | sys/netgraph/ng_ether.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/sys/netgraph/ng_ether.c b/sys/netgraph/ng_ether.c index 9e3c5c3..8c13c8f 100644 --- a/sys/netgraph/ng_ether.c +++ b/sys/netgraph/ng_ether.c @@ -501,7 +501,7 @@ ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) case NGM_ETHER_ADD_MULTI: { struct sockaddr_dl sa_dl; - struct ifmultiaddr *ifm; + struct ifmultiaddr *ifma; if (msg->header.arglen != ETHER_ADDR_LEN) { error = EINVAL; @@ -513,8 +513,23 @@ ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) sa_dl.sdl_alen = ETHER_ADDR_LEN; bcopy((void *)msg->data, LLADDR(&sa_dl), ETHER_ADDR_LEN); - error = if_addmulti(priv->ifp, - (struct sockaddr *)&sa_dl, &ifm); + /* + * Netgraph is only permitted to join groups once + * via the if_addmulti() KPI, because it cannot hold + * struct ifmultiaddr * between calls. It may also + * lose a race while we check if the membership + * already exists. + */ + IF_ADDR_LOCK(priv->ifp); + ifma = if_findmulti(priv->ifp, + (struct sockaddr *)&sa_dl); + IF_ADDR_UNLOCK(priv->ifp); + if (ifma != NULL) { + error = EADDRINUSE; + } else { + error = if_addmulti(priv->ifp, + (struct sockaddr *)&sa_dl, &ifma); + } break; } case NGM_ETHER_DEL_MULTI: |