version 1.159, 2014/10/11 20:53:16 |
version 1.160, 2014/10/12 19:00:21 |
Line 123 static int ip6_pcbopt(int, u_char *, int |
|
Line 123 static int ip6_pcbopt(int, u_char *, int |
|
static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); |
static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); |
static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, kauth_cred_t, |
static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, kauth_cred_t, |
int, int, int); |
int, int, int); |
static int ip6_setmoptions(const struct sockopt *, struct ip6_moptions **); |
static int ip6_setmoptions(const struct sockopt *, struct in6pcb *); |
static int ip6_getmoptions(struct sockopt *, struct ip6_moptions *); |
static int ip6_getmoptions(struct sockopt *, struct in6pcb *); |
static int ip6_copyexthdr(struct mbuf **, void *, int); |
static int ip6_copyexthdr(struct mbuf **, void *, int); |
static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, |
static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, |
struct ip6_frag **); |
struct ip6_frag **); |
|
|
case IPV6_MULTICAST_LOOP: |
case IPV6_MULTICAST_LOOP: |
case IPV6_JOIN_GROUP: |
case IPV6_JOIN_GROUP: |
case IPV6_LEAVE_GROUP: |
case IPV6_LEAVE_GROUP: |
error = ip6_setmoptions(sopt, &in6p->in6p_moptions); |
error = ip6_setmoptions(sopt, in6p); |
break; |
break; |
|
|
case IPV6_PORTRANGE: |
case IPV6_PORTRANGE: |
|
|
case IPV6_MULTICAST_LOOP: |
case IPV6_MULTICAST_LOOP: |
case IPV6_JOIN_GROUP: |
case IPV6_JOIN_GROUP: |
case IPV6_LEAVE_GROUP: |
case IPV6_LEAVE_GROUP: |
error = ip6_getmoptions(sopt, in6p->in6p_moptions); |
error = ip6_getmoptions(sopt, in6p); |
break; |
break; |
|
|
case IPV6_PORTALGO: |
case IPV6_PORTALGO: |
Line 2267 ip6_freepcbopts(struct ip6_pktopts *pkto |
|
Line 2267 ip6_freepcbopts(struct ip6_pktopts *pkto |
|
free(pktopt, M_IP6OPT); |
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(). |
* Set the IP6 multicast options in response to user setsockopt(). |
*/ |
*/ |
static int |
static int |
ip6_setmoptions(const struct sockopt *sopt, struct ip6_moptions **im6op) |
ip6_setmoptions(const struct sockopt *sopt, struct in6pcb *in6p) |
{ |
{ |
int error = 0; |
int error = 0; |
u_int loop, ifindex; |
u_int loop, ifindex; |
struct ipv6_mreq mreq; |
struct ipv6_mreq mreq; |
|
struct in6_addr ia; |
struct ifnet *ifp; |
struct ifnet *ifp; |
struct ip6_moptions *im6o = *im6op; |
struct ip6_moptions *im6o = in6p->in6p_moptions; |
struct route ro; |
|
struct in6_multi_mship *imm; |
struct in6_multi_mship *imm; |
struct lwp *l = curlwp; /* XXX */ |
|
|
|
if (im6o == NULL) { |
if (im6o == NULL) { |
/* |
/* |
Line 2290 ip6_setmoptions(const struct sockopt *so |
|
Line 2360 ip6_setmoptions(const struct sockopt *so |
|
im6o = malloc(sizeof(*im6o), M_IPMOPTS, M_NOWAIT); |
im6o = malloc(sizeof(*im6o), M_IPMOPTS, M_NOWAIT); |
if (im6o == NULL) |
if (im6o == NULL) |
return (ENOBUFS); |
return (ENOBUFS); |
|
in6p->in6p_moptions = im6o; |
*im6op = im6o; |
|
im6o->im6o_multicast_ifp = NULL; |
im6o->im6o_multicast_ifp = NULL; |
im6o->im6o_multicast_hlim = ip6_defmcasthlim; |
im6o->im6o_multicast_hlim = ip6_defmcasthlim; |
im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; |
im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; |
Line 2362 ip6_setmoptions(const struct sockopt *so |
|
Line 2431 ip6_setmoptions(const struct sockopt *so |
|
* Add a multicast group membership. |
* Add a multicast group membership. |
* Group must be a valid IP6 multicast address. |
* Group must be a valid IP6 multicast address. |
*/ |
*/ |
error = sockopt_get(sopt, &mreq, sizeof(mreq)); |
if ((error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia)))) |
if (error != 0) |
return error; |
break; |
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&mreq.ipv6mr_multiaddr)) { |
if (IN6_IS_ADDR_V4MAPPED(&ia)) { |
/* |
error = ip_setmoptions(&in6p->in6p_v4moptions, sopt); |
* 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; |
|
break; |
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 |
* See if we found an interface, and confirm that it |
* supports multicast |
* supports multicast |
Line 2425 ip6_setmoptions(const struct sockopt *so |
|
Line 2447 ip6_setmoptions(const struct sockopt *so |
|
break; |
break; |
} |
} |
|
|
if (in6_setscope(&mreq.ipv6mr_multiaddr, ifp, NULL)) { |
if (in6_setscope(&ia, ifp, NULL)) { |
error = EADDRNOTAVAIL; /* XXX: should not happen */ |
error = EADDRNOTAVAIL; /* XXX: should not happen */ |
break; |
break; |
} |
} |
Line 2437 ip6_setmoptions(const struct sockopt *so |
|
Line 2459 ip6_setmoptions(const struct sockopt *so |
|
imm != NULL; imm = imm->i6mm_chain.le_next) |
imm != NULL; imm = imm->i6mm_chain.le_next) |
if (imm->i6mm_maddr->in6m_ifp == ifp && |
if (imm->i6mm_maddr->in6m_ifp == ifp && |
IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, |
IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, |
&mreq.ipv6mr_multiaddr)) |
&ia)) |
break; |
break; |
if (imm != NULL) { |
if (imm != NULL) { |
error = EADDRINUSE; |
error = EADDRINUSE; |
Line 2447 ip6_setmoptions(const struct sockopt *so |
|
Line 2469 ip6_setmoptions(const struct sockopt *so |
|
* Everything looks good; add a new record to the multicast |
* Everything looks good; add a new record to the multicast |
* address list for the given interface. |
* 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) |
if (imm == NULL) |
break; |
break; |
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); |
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); |
Line 2462 ip6_setmoptions(const struct sockopt *so |
|
Line 2484 ip6_setmoptions(const struct sockopt *so |
|
if (error != 0) |
if (error != 0) |
break; |
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 |
* If an interface address was specified, get a pointer |
* to its ifnet structure. |
* to its ifnet structure. |
Line 2547 ip6_setmoptions(const struct sockopt *so |
|
Line 2573 ip6_setmoptions(const struct sockopt *so |
|
im6o->im6o_multicast_hlim == ip6_defmcasthlim && |
im6o->im6o_multicast_hlim == ip6_defmcasthlim && |
im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && |
im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && |
im6o->im6o_memberships.lh_first == NULL) { |
im6o->im6o_memberships.lh_first == NULL) { |
free(*im6op, M_IPMOPTS); |
free(in6p->in6p_moptions, M_IPMOPTS); |
*im6op = NULL; |
in6p->in6p_moptions = NULL; |
} |
} |
|
|
return (error); |
return (error); |
Line 2558 ip6_setmoptions(const struct sockopt *so |
|
Line 2584 ip6_setmoptions(const struct sockopt *so |
|
* Return the IP6 multicast options in response to user getsockopt(). |
* Return the IP6 multicast options in response to user getsockopt(). |
*/ |
*/ |
static int |
static int |
ip6_getmoptions(struct sockopt *sopt, struct ip6_moptions *im6o) |
ip6_getmoptions(struct sockopt *sopt, struct in6pcb *in6p) |
{ |
{ |
u_int optval; |
u_int optval; |
int error; |
int error; |
|
struct ip6_moptions *im6o = in6p->in6p_moptions; |
|
|
switch (sopt->sopt_name) { |
switch (sopt->sopt_name) { |
case IPV6_MULTICAST_IF: |
case IPV6_MULTICAST_IF: |