- case IPV6_MULTICAST_LOOP:
- /*
- * Set the loopback flag for outgoing multicast packets.
- * Must be zero or one.
- */
- if (m == NULL || m->m_len != sizeof(u_int)) {
- error = EINVAL;
- break;
- }
- bcopy(mtod(m, u_int *), &loop, sizeof(loop));
- if (loop > 1) {
- error = EINVAL;
- break;
- }
- im6o->im6o_multicast_loop = loop;
- imo->imo_multicast_loop = loop;
- break;
-
- case IPV6_JOIN_GROUP:
- /*
- * Add a multicast group membership.
- * Group must be a valid IP6 multicast address.
- */
- if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
- error = EINVAL;
- break;
- }
- mreq = mtod(m, struct ipv6_mreq *);
- /*
- * If the interface is specified, validate it.
- */
- if (mreq->ipv6mr_interface < 0
- || if_index < mreq->ipv6mr_interface) {
- error = ENXIO; /* XXX EINVAL? */
- break;
- }
-
- if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
- /*
- * We use the unspecified address to specify to accept
- * all multicast addresses. Only super user is allowed
- * to do this.
- */
- if (suser(kauth_cred_get(), 0))
- {
- error = EACCES;
- break;
- }
- } else if (IN6_IS_ADDR_V4MAPPED(&mreq->ipv6mr_multiaddr)) {
- struct ip_mreq v4req;
-
- v4req.imr_multiaddr.s_addr = mreq->ipv6mr_multiaddr.s6_addr32[3];
- v4req.imr_interface.s_addr = INADDR_ANY;
-
- /* Find an IPv4 address on the specified interface. */
- if (mreq->ipv6mr_interface != 0) {
- struct in_ifaddr *ifa;
-
- ifp = ifindex2ifnet[mreq->ipv6mr_interface];
-
- lck_mtx_lock(rt_mtx);
- TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
- if (ifa->ia_ifp == ifp) {
- v4req.imr_interface = IA_SIN(ifa)->sin_addr;
- break;
- }
- }
- lck_mtx_unlock(rt_mtx);
-
- if (v4req.imr_multiaddr.s_addr == 0) {
- /* Interface has no IPv4 address. */
- error = EINVAL;
- break;
- }
- }
-
- error = ip_addmembership(imo, &v4req);
- break;
- } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
- error = EINVAL;
- break;
- }
- /*
- * If no interface was explicitly specified, choose an
- * appropriate one according to the given multicast address.
- */
- if (mreq->ipv6mr_interface == 0) {
- /*
- * If the multicast address is in node-local scope,
- * the interface should be a loopback interface.
- * Otherwise, look up the routing table for the
- * address, and choose the outgoing interface.
- * XXX: is it a good approach?
- */
- if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) {
- ifp = &loif[0];
- } else {
- ro.ro_rt = NULL;
- dst = (struct sockaddr_in6 *)&ro.ro_dst;
- bzero(dst, sizeof(*dst));
- dst->sin6_len = sizeof(struct sockaddr_in6);
- dst->sin6_family = AF_INET6;
- dst->sin6_addr = mreq->ipv6mr_multiaddr;
- rtalloc((struct route *)&ro);
- if (ro.ro_rt == NULL) {
- error = EADDRNOTAVAIL;
- break;
- }
- ifp = ro.ro_rt->rt_ifp;
- rtfree(ro.ro_rt);
- }
- } else
- ifp = ifindex2ifnet[mreq->ipv6mr_interface];
-
- /*
- * See if we found an interface, and confirm that it
- * supports multicast
- */
- if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
- error = EADDRNOTAVAIL;
- break;
- }
- /*
- * Put interface index into the multicast address,
- * if the address has link-local scope.
- */
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
- mreq->ipv6mr_multiaddr.s6_addr16[1]
- = htons(mreq->ipv6mr_interface);
- }
- /*
- * See if the membership already exists.
- */
- lck_mtx_lock(nd6_mutex);
- for (imm = im6o->im6o_memberships.lh_first;
- imm != NULL; imm = imm->i6mm_chain.le_next)
- if (imm->i6mm_maddr->in6m_ifp == ifp &&
- IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
- &mreq->ipv6mr_multiaddr))
- break;
- if (imm != NULL) {
- error = EADDRINUSE;
- lck_mtx_unlock(nd6_mutex);
- break;
- }
- /*
- * Everything looks good; add a new record to the multicast
- * address list for the given interface.
- */
- imm = _MALLOC(sizeof(*imm), M_IPMADDR, M_WAITOK);
- if (imm == NULL) {
- error = ENOBUFS;
- lck_mtx_unlock(nd6_mutex);
- break;
- }
- if ((imm->i6mm_maddr =
- in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error, 1)) == NULL) {
- FREE(imm, M_IPMADDR);
- lck_mtx_unlock(nd6_mutex);
- break;
- }
- LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
- lck_mtx_unlock(nd6_mutex);
- break;
-
- case IPV6_LEAVE_GROUP:
- /*
- * Drop a multicast group membership.
- * Group must be a valid IP6 multicast address.
- */
- if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
- error = EINVAL;
- break;
- }
- mreq = mtod(m, struct ipv6_mreq *);
- /*
- * If an interface address was specified, get a pointer
- * to its ifnet structure.
- */
- if (mreq->ipv6mr_interface < 0
- || if_index < mreq->ipv6mr_interface) {
- error = ENXIO; /* XXX EINVAL? */
- break;
- }
- ifp = ifindex2ifnet[mreq->ipv6mr_interface];
-
- if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
- if (suser(kauth_cred_get(), 0)) {
- error = EACCES;
- break;
- }
- } else if (IN6_IS_ADDR_V4MAPPED(&mreq->ipv6mr_multiaddr)) {
- struct ip_mreq v4req;
-
- v4req.imr_multiaddr.s_addr = mreq->ipv6mr_multiaddr.s6_addr32[3];
- v4req.imr_interface.s_addr = INADDR_ANY;
-
- if (ifp != NULL) {
- struct in_ifaddr *ifa;
-
- lck_mtx_lock(rt_mtx);
- TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
- if (ifa->ia_ifp == ifp) {
- v4req.imr_interface = IA_SIN(ifa)->sin_addr;
- break;
- }
- }
- lck_mtx_unlock(rt_mtx);
- }
-
- error = ip_dropmembership(imo, &v4req);
- break;
- } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
- error = EINVAL;
- break;
- }
- /*
- * Put interface index into the multicast address,
- * if the address has link-local scope.
- */
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
- mreq->ipv6mr_multiaddr.s6_addr16[1]
- = htons(mreq->ipv6mr_interface);
- }
- /*
- * Find the membership in the membership list.
- */
- lck_mtx_lock(nd6_mutex);
- for (imm = im6o->im6o_memberships.lh_first;
- imm != NULL; imm = imm->i6mm_chain.le_next) {
- if ((ifp == NULL ||
- imm->i6mm_maddr->in6m_ifp == ifp) &&
- IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
- &mreq->ipv6mr_multiaddr))
- break;
- }
- if (imm == NULL) {
- /* Unable to resolve interface */
- error = EADDRNOTAVAIL;
- lck_mtx_unlock(nd6_mutex);
- break;
- }
- /*
- * Give up the multicast address record to which the
- * membership points.
- */
- LIST_REMOVE(imm, i6mm_chain);
- in6_delmulti(imm->i6mm_maddr, 1);
- lck_mtx_unlock(nd6_mutex);
- FREE(imm, M_IPMADDR);
- break;