+ if (ifp) {
+ ifnet_detach_protocol(ifp, PF_NDRV);
+ }
+}
+
+static int
+ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt)
+{
+ struct ndrv_multiaddr* ndrv_multi;
+ int result;
+
+ if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 ||
+ sopt->sopt_level != SOL_NDRVPROTO || sopt->sopt_valsize > SOCK_MAXADDRLEN)
+ return EINVAL;
+ if (np->nd_if == NULL)
+ return ENXIO;
+ if (!(np->nd_dlist_cnt < ndrv_multi_max_count))
+ return EPERM;
+
+ // Allocate storage
+ MALLOC(ndrv_multi, struct ndrv_multiaddr*, sizeof(struct ndrv_multiaddr) -
+ sizeof(struct sockaddr) + sopt->sopt_valsize, M_IFADDR, M_WAITOK);
+ if (ndrv_multi == NULL)
+ return ENOMEM;
+
+ // Copy in the address
+ result = copyin(sopt->sopt_val, &ndrv_multi->addr, sopt->sopt_valsize);
+
+ // Validate the sockaddr
+ if (result == 0 && sopt->sopt_valsize != ndrv_multi->addr.sa_len)
+ result = EINVAL;
+
+ if (result == 0 && ndrv_have_multicast(np, &ndrv_multi->addr))
+ result = EEXIST;
+
+ if (result == 0)
+ {
+ // Try adding the multicast
+ result = ifnet_add_multicast(np->nd_if, &ndrv_multi->addr,
+ &ndrv_multi->ifma);
+ }
+
+ if (result == 0)
+ {
+ // Add to our linked list
+ ndrv_multi->next = np->nd_multiaddrs;
+ np->nd_multiaddrs = ndrv_multi;
+ np->nd_dlist_cnt++;
+ }
+ else
+ {
+ // Free up the memory, something went wrong
+ FREE(ndrv_multi, M_IFADDR);
+ }
+
+ return result;
+}
+
+static int
+ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt)
+{
+ struct sockaddr* multi_addr;
+ struct ndrv_multiaddr* ndrv_entry = NULL;
+ int result;
+
+ if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 ||
+ sopt->sopt_level != SOL_NDRVPROTO)
+ return EINVAL;
+ if (np->nd_if == NULL || np->nd_dlist_cnt == 0)
+ return ENXIO;
+
+ // Allocate storage
+ MALLOC(multi_addr, struct sockaddr*, sopt->sopt_valsize,
+ M_TEMP, M_WAITOK);
+ if (multi_addr == NULL)
+ return ENOMEM;
+
+ // Copy in the address
+ result = copyin(sopt->sopt_val, multi_addr, sopt->sopt_valsize);
+
+ // Validate the sockaddr
+ if (result == 0 && sopt->sopt_valsize != multi_addr->sa_len)
+ result = EINVAL;
+
+ if (result == 0)
+ {
+ /* Find the old entry */
+ ndrv_entry = ndrv_have_multicast(np, multi_addr);
+
+ if (ndrv_entry == NULL)
+ result = ENOENT;
+ }
+
+ if (result == 0)
+ {
+ // Try deleting the multicast
+ result = ifnet_remove_multicast(ndrv_entry->ifma);
+ }
+
+ if (result == 0)
+ {
+ // Remove from our linked list
+ struct ndrv_multiaddr* cur = np->nd_multiaddrs;
+
+ ifmaddr_release(ndrv_entry->ifma);
+
+ if (cur == ndrv_entry)
+ {
+ np->nd_multiaddrs = cur->next;
+ }
+ else
+ {
+ for (cur = cur->next; cur != NULL; cur = cur->next)
+ {
+ if (cur->next == ndrv_entry)
+ {
+ cur->next = cur->next->next;
+ break;
+ }
+ }
+ }
+
+ np->nd_dlist_cnt--;
+
+ // Free the memory
+ FREE(ndrv_entry, M_IFADDR);
+ }
+ FREE(multi_addr, M_TEMP);
+
+ return result;
+}
+
+static struct ndrv_multiaddr*
+ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* inAddr)
+{
+ struct ndrv_multiaddr* cur;
+ for (cur = np->nd_multiaddrs; cur != NULL; cur = cur->next)
+ {
+
+ if ((inAddr->sa_len == cur->addr.sa_len) &&
+ (bcmp(&cur->addr, inAddr, inAddr->sa_len) == 0))
+ {
+ // Found a match
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+ndrv_remove_all_multicast(struct ndrv_cb* np)
+{
+ struct ndrv_multiaddr* cur;
+
+ if (np->nd_if != NULL)
+ {
+ while (np->nd_multiaddrs != NULL)
+ {
+ cur = np->nd_multiaddrs;
+ np->nd_multiaddrs = cur->next;
+
+ ifnet_remove_multicast(cur->ifma);
+ ifmaddr_release(cur->ifma);
+ FREE(cur, M_IFADDR);
+ }