summaryrefslogtreecommitdiffstats
path: root/sys/netinet/in.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/in.c')
-rw-r--r--sys/netinet/in.c110
1 files changed, 74 insertions, 36 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index bf1ebae..7c890b1 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/vinet.h>
+#include <netinet/igmp_var.h>
static int in_mask2len(struct in_addr *);
static void in_len2mask(struct in_addr *, int);
@@ -215,12 +216,14 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
struct in_addr allhosts_addr;
struct in_addr dst;
struct in_ifaddr *oia;
+ struct in_ifinfo *ii;
struct in_aliasreq *ifra = (struct in_aliasreq *)data;
struct sockaddr_in oldaddr;
int error, hostIsNew, iaIsNew, maskIsNew, s;
int iaIsFirst;
ia = NULL;
+ ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
iaIsFirst = 0;
iaIsNew = 0;
allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
@@ -425,8 +428,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
if (error != 0 && iaIsNew)
break;
if (error == 0) {
- if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0)
- in_addmulti(&allhosts_addr, ifp);
+ if (iaIsFirst &&
+ (ifp->if_flags & IFF_MULTICAST) != 0) {
+ error = in_joingroup(ifp, &allhosts_addr,
+ NULL, &ii->ii_allhosts);
+ }
EVENTHANDLER_INVOKE(ifaddr_event, ifp);
}
return (0);
@@ -472,8 +478,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
(ifra->ifra_broadaddr.sin_family == AF_INET))
ia->ia_broadaddr = ifra->ifra_broadaddr;
if (error == 0) {
- if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0)
- in_addmulti(&allhosts_addr, ifp);
+ if (iaIsFirst &&
+ (ifp->if_flags & IFF_MULTICAST) != 0) {
+ error = in_joingroup(ifp, &allhosts_addr,
+ NULL, &ii->ii_allhosts);
+ }
EVENTHANDLER_INVOKE(ifaddr_event, ifp);
}
return (error);
@@ -515,18 +524,18 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
/*
* If this is the last IPv4 address configured on this
* interface, leave the all-hosts group.
- * XXX: This is quite ugly because of locking and structure.
+ * No state-change report need be transmitted.
*/
oia = NULL;
IFP_TO_IA(ifp, oia);
if (oia == NULL) {
- struct in_multi *inm;
-
IFF_LOCKGIANT(ifp);
IN_MULTI_LOCK();
- IN_LOOKUP_MULTI(allhosts_addr, ifp, inm);
- if (inm != NULL)
- in_delmulti_locked(inm);
+ if (ii->ii_allhosts) {
+ (void)in_leavegroup_locked(ii->ii_allhosts,
+ NULL);
+ ii->ii_allhosts = NULL;
+ }
IN_MULTI_UNLOCK();
IFF_UNLOCKGIANT(ifp);
}
@@ -993,37 +1002,56 @@ in_broadcast(struct in_addr in, struct ifnet *ifp)
}
/*
+ * On interface removal, clean up IPv4 data structures hung off of the ifnet.
+ */
+void
+in_ifdetach(struct ifnet *ifp)
+{
+ INIT_VNET_INET(ifp->if_vnet);
+
+ in_pcbpurgeif0(&V_ripcbinfo, ifp);
+ in_pcbpurgeif0(&V_udbinfo, ifp);
+ in_purgemaddrs(ifp);
+}
+
+/*
* Delete all IPv4 multicast address records, and associated link-layer
* multicast address records, associated with ifp.
+ * XXX It looks like domifdetach runs AFTER the link layer cleanup.
*/
static void
in_purgemaddrs(struct ifnet *ifp)
{
INIT_VNET_INET(ifp->if_vnet);
- struct in_multi *inm;
- struct in_multi *oinm;
+ LIST_HEAD(,in_multi) purgeinms;
+ struct in_multi *inm, *tinm;
+ struct ifmultiaddr *ifma;
- IFF_LOCKGIANT(ifp);
+ LIST_INIT(&purgeinms);
IN_MULTI_LOCK();
- LIST_FOREACH_SAFE(inm, &V_in_multihead, inm_link, oinm) {
- if (inm->inm_ifp == ifp)
- in_delmulti_locked(inm);
+
+ /*
+ * Extract list of in_multi associated with the detaching ifp
+ * which the PF_INET layer is about to release.
+ * We need to do this as IF_ADDR_LOCK() may be re-acquired
+ * by code further down.
+ */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_INET)
+ continue;
+ inm = (struct in_multi *)ifma->ifma_protospec;
+ LIST_INSERT_HEAD(&purgeinms, inm, inm_link);
}
- IN_MULTI_UNLOCK();
- IFF_UNLOCKGIANT(ifp);
-}
+ IF_ADDR_UNLOCK(ifp);
-/*
- * On interface removal, clean up IPv4 data structures hung off of the ifnet.
- */
-void
-in_ifdetach(struct ifnet *ifp)
-{
- INIT_VNET_INET(ifp->if_vnet);
+ LIST_FOREACH_SAFE(inm, &purgeinms, inm_link, tinm) {
+ inm_release_locked(inm);
+ LIST_REMOVE(inm, inm_link);
+ }
+ igmp_ifdetach(ifp);
- in_pcbpurgeif0(&V_ripcbinfo, ifp);
- in_pcbpurgeif0(&V_udbinfo, ifp);
- in_purgemaddrs(ifp);
+ IN_MULTI_UNLOCK();
}
#include <sys/syslog.h>
@@ -1250,9 +1278,13 @@ in_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
void *
in_domifattach(struct ifnet *ifp)
-{
- struct lltable *llt = lltable_init(ifp, AF_INET);
-
+{
+ struct in_ifinfo *ii;
+ struct lltable *llt;
+
+ ii = malloc(sizeof(struct in_ifinfo), M_IFADDR, M_WAITOK|M_ZERO);
+
+ llt = lltable_init(ifp, AF_INET);
if (llt != NULL) {
llt->llt_new = in_lltable_new;
llt->llt_free = in_lltable_free;
@@ -1260,13 +1292,19 @@ in_domifattach(struct ifnet *ifp)
llt->llt_lookup = in_lltable_lookup;
llt->llt_dump = in_lltable_dump;
}
- return (llt);
+ ii->ii_llt = llt;
+
+ ii->ii_igmp = igmp_domifattach(ifp);
+
+ return ii;
}
void
-in_domifdetach(struct ifnet *ifp __unused, void *aux)
+in_domifdetach(struct ifnet *ifp, void *aux)
{
- struct lltable *llt = (struct lltable *)aux;
+ struct in_ifinfo *ii = (struct in_ifinfo *)aux;
- lltable_free(llt);
+ igmp_domifdetach(ifp);
+ lltable_free(ii->ii_llt);
+ free(ii, M_IFADDR);
}
OpenPOWER on IntegriCloud