Module Name: src Committed By: christos Date: Sun Oct 12 19:00:21 UTC 2014
Modified Files: src/sys/netinet: ip_output.c src/sys/netinet6: ip6_output.c ip6_var.h Log Message: Refactor the multicast membership code so that we can handle v4 mapped addresses using the v6 membership ioctls. To generate a diff of this commit: cvs rdiff -u -r1.231 -r1.232 src/sys/netinet/ip_output.c cvs rdiff -u -r1.159 -r1.160 src/sys/netinet6/ip6_output.c cvs rdiff -u -r1.62 -r1.63 src/sys/netinet6/ip6_var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netinet/ip_output.c diff -u src/sys/netinet/ip_output.c:1.231 src/sys/netinet/ip_output.c:1.232 --- src/sys/netinet/ip_output.c:1.231 Sat Oct 11 17:12:51 2014 +++ src/sys/netinet/ip_output.c Sun Oct 12 15:00:21 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_output.c,v 1.231 2014/10/11 21:12:51 christos Exp $ */ +/* $NetBSD: ip_output.c,v 1.232 2014/10/12 19:00:21 christos Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -91,7 +91,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.231 2014/10/11 21:12:51 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.232 2014/10/12 19:00:21 christos Exp $"); #include "opt_inet.h" #include "opt_ipsec.h" @@ -124,6 +124,10 @@ __KERNEL_RCSID(0, "$NetBSD: ip_output.c, #include <netinet/portalgo.h> #include <netinet/udp.h> +#ifdef INET6 +#include <netinet6/ip6_var.h> +#endif + #ifdef MROUTING #include <netinet/ip_mroute.h> #endif @@ -1359,6 +1363,158 @@ ip_getoptval(const struct sockopt *sopt, return 0; } +static int +ip_get_membership(const struct sockopt *sopt, struct ifnet **ifp, + struct in_addr *ia, bool add) +{ + int error; + struct ip_mreq mreq; + + error = sockopt_get(sopt, &mreq, sizeof(mreq)); + if (error) + return error; + + if (!IN_MULTICAST(mreq.imr_multiaddr.s_addr)) + return EINVAL; + + memcpy(ia, &mreq.imr_multiaddr, sizeof(*ia)); + + if (in_nullhost(mreq.imr_interface)) { + union { + struct sockaddr dst; + struct sockaddr_in dst4; + } u; + struct route ro; + + if (!add) { + *ifp = NULL; + return 0; + } + /* + * If no interface address was provided, use the interface of + * the route to the given multicast address. + */ + struct rtentry *rt; + memset(&ro, 0, sizeof(ro)); + + sockaddr_in_init(&u.dst4, ia, 0); + rtcache_setdst(&ro, &u.dst); + *ifp = (rt = rtcache_init(&ro)) != NULL ? rt->rt_ifp : NULL; + rtcache_free(&ro); + } else { + *ifp = ip_multicast_if(&mreq.imr_interface, NULL); + if (!add && *ifp == NULL) + return EADDRNOTAVAIL; + } + return 0; +} + +/* + * Add a multicast group membership. + * Group must be a valid IP multicast address. + */ +static int +ip_add_membership(struct ip_moptions *imo, const struct sockopt *sopt) +{ + struct ifnet *ifp; + struct in_addr ia; + int i, error; + + if (sopt->sopt_size == sizeof(struct ip_mreq)) + error = ip_get_membership(sopt, &ifp, &ia, true); + else +#ifdef INET6 + error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia)); +#else + return EINVAL; +#endif + + if (error) + return error; + + /* + * See if we found an interface, and confirm that it + * supports multicast. + */ + if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) + return EADDRNOTAVAIL; + + /* + * See if the membership already exists or if all the + * membership slots are full. + */ + for (i = 0; i < imo->imo_num_memberships; ++i) { + if (imo->imo_membership[i]->inm_ifp == ifp && + in_hosteq(imo->imo_membership[i]->inm_addr, ia)) + break; + } + if (i < imo->imo_num_memberships) + return EADDRINUSE; + + if (i == IP_MAX_MEMBERSHIPS) + return ETOOMANYREFS; + + /* + * Everything looks good; add a new record to the multicast + * address list for the given interface. + */ + if ((imo->imo_membership[i] = in_addmulti(&ia, ifp)) == NULL) + return ENOBUFS; + + ++imo->imo_num_memberships; + return 0; +} + +/* + * Drop a multicast group membership. + * Group must be a valid IP multicast address. + */ +static int +ip_drop_membership(struct ip_moptions *imo, const struct sockopt *sopt) +{ + struct in_addr ia; + struct ifnet *ifp; + int i, error; + + if (sopt->sopt_size == sizeof(struct ip_mreq)) + error = ip_get_membership(sopt, &ifp, &ia, false); + else +#ifdef INET6 + error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia)); +#else + return EINVAL; +#endif + + if (error) + return error; + + /* + * Find the membership in the membership array. + */ + for (i = 0; i < imo->imo_num_memberships; ++i) { + if ((ifp == NULL || + imo->imo_membership[i]->inm_ifp == ifp) && + in_hosteq(imo->imo_membership[i]->inm_addr, ia)) + break; + } + if (i == imo->imo_num_memberships) + return EADDRNOTAVAIL; + + /* + * Give up the multicast address record to which the + * membership points. + */ + in_delmulti(imo->imo_membership[i]); + + /* + * Remove the gap in the membership array. + */ + for (++i; i < imo->imo_num_memberships; ++i) + imo->imo_membership[i-1] = imo->imo_membership[i]; + --imo->imo_num_memberships; + return 0; +} + /* * Set the IP multicast options in response to user setsockopt(). */ @@ -1367,9 +1523,8 @@ ip_setmoptions(struct ip_moptions **pimo { struct ip_moptions *imo = *pimo; struct in_addr addr; - struct ip_mreq lmreq, *mreq; struct ifnet *ifp; - int i, ifindex, error = 0; + int ifindex, error = 0; if (!imo) { /* @@ -1438,134 +1593,12 @@ ip_setmoptions(struct ip_moptions **pimo error = ip_getoptval(sopt, &imo->imo_multicast_loop, 1); break; - case IP_ADD_MEMBERSHIP: - /* - * Add a multicast group membership. - * Group must be a valid IP multicast address. - */ - error = sockopt_get(sopt, &lmreq, sizeof(lmreq)); - if (error) - break; - - mreq = &lmreq; - - if (!IN_MULTICAST(mreq->imr_multiaddr.s_addr)) { - error = EINVAL; - break; - } - /* - * If no interface address was provided, use the interface of - * the route to the given multicast address. - */ - if (in_nullhost(mreq->imr_interface)) { - struct rtentry *rt; - union { - struct sockaddr dst; - struct sockaddr_in dst4; - } u; - struct route ro; - - memset(&ro, 0, sizeof(ro)); - - sockaddr_in_init(&u.dst4, &mreq->imr_multiaddr, 0); - rtcache_setdst(&ro, &u.dst); - ifp = (rt = rtcache_init(&ro)) != NULL ? rt->rt_ifp - : NULL; - rtcache_free(&ro); - } else { - ifp = ip_multicast_if(&mreq->imr_interface, NULL); - } - /* - * See if we found an interface, and confirm that it - * supports multicast. - */ - if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { - error = EADDRNOTAVAIL; - break; - } - /* - * See if the membership already exists or if all the - * membership slots are full. - */ - for (i = 0; i < imo->imo_num_memberships; ++i) { - if (imo->imo_membership[i]->inm_ifp == ifp && - in_hosteq(imo->imo_membership[i]->inm_addr, - mreq->imr_multiaddr)) - break; - } - if (i < imo->imo_num_memberships) { - error = EADDRINUSE; - break; - } - if (i == IP_MAX_MEMBERSHIPS) { - error = ETOOMANYREFS; - break; - } - /* - * Everything looks good; add a new record to the multicast - * address list for the given interface. - */ - if ((imo->imo_membership[i] = - in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) { - error = ENOBUFS; - break; - } - ++imo->imo_num_memberships; + case IP_ADD_MEMBERSHIP: /* IPV6_JOIN_GROUP */ + error = ip_add_membership(imo, sopt); break; - case IP_DROP_MEMBERSHIP: - /* - * Drop a multicast group membership. - * Group must be a valid IP multicast address. - */ - error = sockopt_get(sopt, &lmreq, sizeof(lmreq)); - if (error) - break; - - mreq = &lmreq; - - if (!IN_MULTICAST(mreq->imr_multiaddr.s_addr)) { - error = EINVAL; - break; - } - /* - * If an interface address was specified, get a pointer - * to its ifnet structure. - */ - if (in_nullhost(mreq->imr_interface)) - ifp = NULL; - else { - ifp = ip_multicast_if(&mreq->imr_interface, NULL); - if (ifp == NULL) { - error = EADDRNOTAVAIL; - break; - } - } - /* - * Find the membership in the membership array. - */ - for (i = 0; i < imo->imo_num_memberships; ++i) { - if ((ifp == NULL || - imo->imo_membership[i]->inm_ifp == ifp) && - in_hosteq(imo->imo_membership[i]->inm_addr, - mreq->imr_multiaddr)) - break; - } - if (i == imo->imo_num_memberships) { - error = EADDRNOTAVAIL; - break; - } - /* - * Give up the multicast address record to which the - * membership points. - */ - in_delmulti(imo->imo_membership[i]); - /* - * Remove the gap in the membership array. - */ - for (++i; i < imo->imo_num_memberships; ++i) - imo->imo_membership[i-1] = imo->imo_membership[i]; - --imo->imo_num_memberships; + case IP_DROP_MEMBERSHIP: /* IPV6_LEAVE_GROUP */ + error = ip_drop_membership(imo, sopt); break; default: Index: src/sys/netinet6/ip6_output.c diff -u src/sys/netinet6/ip6_output.c:1.159 src/sys/netinet6/ip6_output.c:1.160 --- src/sys/netinet6/ip6_output.c:1.159 Sat Oct 11 16:53:16 2014 +++ src/sys/netinet6/ip6_output.c Sun Oct 12 15:00:21 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: ip6_output.c,v 1.159 2014/10/11 20:53:16 christos Exp $ */ +/* $NetBSD: ip6_output.c,v 1.160 2014/10/12 19:00:21 christos Exp $ */ /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */ /* @@ -62,7 +62,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.159 2014/10/11 20:53:16 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.160 2014/10/12 19:00:21 christos Exp $"); #include "opt_inet.h" #include "opt_inet6.h" @@ -123,8 +123,8 @@ static int ip6_pcbopt(int, u_char *, int static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, kauth_cred_t, int, int, int); -static int ip6_setmoptions(const struct sockopt *, struct ip6_moptions **); -static int ip6_getmoptions(struct sockopt *, struct ip6_moptions *); +static int ip6_setmoptions(const struct sockopt *, struct in6pcb *); +static int ip6_getmoptions(struct sockopt *, struct in6pcb *); static int ip6_copyexthdr(struct mbuf **, void *, int); static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, struct ip6_frag **); @@ -1636,7 +1636,7 @@ else \ case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: - error = ip6_setmoptions(sopt, &in6p->in6p_moptions); + error = ip6_setmoptions(sopt, in6p); break; case IPV6_PORTRANGE: @@ -1860,7 +1860,7 @@ else \ case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: - error = ip6_getmoptions(sopt, in6p->in6p_moptions); + error = ip6_getmoptions(sopt, in6p); break; case IPV6_PORTALGO: @@ -2267,20 +2267,90 @@ ip6_freepcbopts(struct ip6_pktopts *pkto free(pktopt, M_IP6OPT); } +int +ip6_get_membership(const struct sockopt *sopt, struct ifnet **ifp, void *v, + size_t l) +{ + struct ipv6_mreq mreq; + int error; + struct in6_addr *ia = &mreq.ipv6mr_multiaddr; + struct in_addr *ia4 = (void *)&ia->s6_addr32[3]; + error = sockopt_get(sopt, &mreq, sizeof(mreq)); + if (error != 0) + return error; + + if (IN6_IS_ADDR_UNSPECIFIED(ia)) { + /* + * We use the unspecified address to specify to accept + * all multicast addresses. Only super user is allowed + * to do this. + */ + if (kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_IPV6, + KAUTH_REQ_NETWORK_IPV6_JOIN_MULTICAST, NULL, NULL, NULL)) + return EACCES; + } else if (IN6_IS_ADDR_V4MAPPED(ia)) { + // Don't bother if we are not going to use ifp. + if (l == sizeof(*ia)) { + memcpy(v, ia, l); + return 0; + } + } else if (!IN6_IS_ADDR_MULTICAST(ia)) { + return EINVAL; + } + + /* + * If no interface was explicitly specified, choose an + * appropriate one according to the given multicast address. + */ + if (mreq.ipv6mr_interface == 0) { + struct rtentry *rt; + union { + struct sockaddr dst; + struct sockaddr_in dst4; + struct sockaddr_in6 dst6; + } u; + struct route ro; + + /* + * Look up the routing table for the + * address, and choose the outgoing interface. + * XXX: is it a good approach? + */ + memset(&ro, 0, sizeof(ro)); + if (IN6_IS_ADDR_V4MAPPED(ia)) + sockaddr_in_init(&u.dst4, ia4, 0); + else + sockaddr_in6_init(&u.dst6, ia, 0, 0, 0); + rtcache_setdst(&ro, &u.dst); + *ifp = (rt = rtcache_init(&ro)) != NULL ? rt->rt_ifp : NULL; + rtcache_free(&ro); + } else { + /* + * If the interface is specified, validate it. + */ + if ((*ifp = if_byindex(mreq.ipv6mr_interface)) == NULL) + return ENXIO; /* XXX EINVAL? */ + } + if (sizeof(*ia) == l) + memcpy(v, ia, l); + else + memcpy(v, ia4, l); + return 0; +} + /* * Set the IP6 multicast options in response to user setsockopt(). */ static int -ip6_setmoptions(const struct sockopt *sopt, struct ip6_moptions **im6op) +ip6_setmoptions(const struct sockopt *sopt, struct in6pcb *in6p) { int error = 0; u_int loop, ifindex; struct ipv6_mreq mreq; + struct in6_addr ia; struct ifnet *ifp; - struct ip6_moptions *im6o = *im6op; - struct route ro; + struct ip6_moptions *im6o = in6p->in6p_moptions; struct in6_multi_mship *imm; - struct lwp *l = curlwp; /* XXX */ if (im6o == NULL) { /* @@ -2290,8 +2360,7 @@ ip6_setmoptions(const struct sockopt *so im6o = malloc(sizeof(*im6o), M_IPMOPTS, M_NOWAIT); if (im6o == NULL) return (ENOBUFS); - - *im6op = im6o; + in6p->in6p_moptions = im6o; im6o->im6o_multicast_ifp = NULL; im6o->im6o_multicast_hlim = ip6_defmcasthlim; im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; @@ -2362,60 +2431,13 @@ ip6_setmoptions(const struct sockopt *so * Add a multicast group membership. * Group must be a valid IP6 multicast address. */ - error = sockopt_get(sopt, &mreq, sizeof(mreq)); - if (error != 0) - break; + if ((error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia)))) + return error; - 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 (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_IPV6, - KAUTH_REQ_NETWORK_IPV6_JOIN_MULTICAST, NULL, NULL, NULL)) - { - error = EACCES; - break; - } - } else if (!IN6_IS_ADDR_MULTICAST(&mreq.ipv6mr_multiaddr)) { - error = EINVAL; + if (IN6_IS_ADDR_V4MAPPED(&ia)) { + error = ip_setmoptions(&in6p->in6p_v4moptions, sopt); break; } - - /* - * If no interface was explicitly specified, choose an - * appropriate one according to the given multicast address. - */ - if (mreq.ipv6mr_interface == 0) { - struct rtentry *rt; - union { - struct sockaddr dst; - struct sockaddr_in6 dst6; - } u; - - /* - * Look up the routing table for the - * address, and choose the outgoing interface. - * XXX: is it a good approach? - */ - memset(&ro, 0, sizeof(ro)); - sockaddr_in6_init(&u.dst6, &mreq.ipv6mr_multiaddr, 0, - 0, 0); - rtcache_setdst(&ro, &u.dst); - ifp = (rt = rtcache_init(&ro)) != NULL ? rt->rt_ifp - : NULL; - rtcache_free(&ro); - } else { - /* - * If the interface is specified, validate it. - */ - if ((ifp = if_byindex(mreq.ipv6mr_interface)) == NULL) { - error = ENXIO; /* XXX EINVAL? */ - break; - } - } - /* * See if we found an interface, and confirm that it * supports multicast @@ -2425,7 +2447,7 @@ ip6_setmoptions(const struct sockopt *so break; } - if (in6_setscope(&mreq.ipv6mr_multiaddr, ifp, NULL)) { + if (in6_setscope(&ia, ifp, NULL)) { error = EADDRNOTAVAIL; /* XXX: should not happen */ break; } @@ -2437,7 +2459,7 @@ ip6_setmoptions(const struct sockopt *so 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)) + &ia)) break; if (imm != NULL) { error = EADDRINUSE; @@ -2447,7 +2469,7 @@ ip6_setmoptions(const struct sockopt *so * Everything looks good; add a new record to the multicast * address list for the given interface. */ - imm = in6_joingroup(ifp, &mreq.ipv6mr_multiaddr, &error, 0); + imm = in6_joingroup(ifp, &ia, &error, 0); if (imm == NULL) break; LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); @@ -2462,6 +2484,10 @@ ip6_setmoptions(const struct sockopt *so if (error != 0) break; + if (IN6_IS_ADDR_V4MAPPED(&mreq.ipv6mr_multiaddr)) { + error = ip_setmoptions(&in6p->in6p_v4moptions, sopt); + break; + } /* * If an interface address was specified, get a pointer * to its ifnet structure. @@ -2547,8 +2573,8 @@ ip6_setmoptions(const struct sockopt *so im6o->im6o_multicast_hlim == ip6_defmcasthlim && im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && im6o->im6o_memberships.lh_first == NULL) { - free(*im6op, M_IPMOPTS); - *im6op = NULL; + free(in6p->in6p_moptions, M_IPMOPTS); + in6p->in6p_moptions = NULL; } return (error); @@ -2558,10 +2584,11 @@ ip6_setmoptions(const struct sockopt *so * Return the IP6 multicast options in response to user getsockopt(). */ static int -ip6_getmoptions(struct sockopt *sopt, struct ip6_moptions *im6o) +ip6_getmoptions(struct sockopt *sopt, struct in6pcb *in6p) { u_int optval; int error; + struct ip6_moptions *im6o = in6p->in6p_moptions; switch (sopt->sopt_name) { case IPV6_MULTICAST_IF: Index: src/sys/netinet6/ip6_var.h diff -u src/sys/netinet6/ip6_var.h:1.62 src/sys/netinet6/ip6_var.h:1.63 --- src/sys/netinet6/ip6_var.h:1.62 Thu Jun 5 19:48:16 2014 +++ src/sys/netinet6/ip6_var.h Sun Oct 12 15:00:21 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: ip6_var.h,v 1.62 2014/06/05 23:48:16 rmind Exp $ */ +/* $NetBSD: ip6_var.h,v 1.63 2014/10/12 19:00:21 christos Exp $ */ /* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ /* @@ -316,6 +316,7 @@ extern const struct pr_usrreqs rip6_usrr int icmp6_ctloutput(int, struct socket *, struct sockopt *); +struct mbuf; void ip6_init(void); void ip6_input(struct mbuf *); const struct ip6aux *ip6_getdstifaddr(struct mbuf *); @@ -329,7 +330,7 @@ int ip6_lasthdr(struct mbuf *, int, int, struct m_tag *ip6_addaux(struct mbuf *); struct m_tag *ip6_findaux(struct mbuf *); void ip6_delaux(struct mbuf *); - +struct ip6_hdr; int ip6_mforward(struct ip6_hdr *, struct ifnet *, struct mbuf *); int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *); void ip6_savecontrol(struct in6pcb *, struct mbuf **, struct ip6_hdr *, @@ -395,6 +396,8 @@ struct in6_addr *in6_selectsrc(struct s int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_moptions *, struct route *, struct ifnet **, struct rtentry **, int); +int ip6_get_membership(const struct sockopt *, struct ifnet **, void *, + size_t); u_int32_t ip6_randomid(void); u_int32_t ip6_randomflowlabel(void);