Support struct ip_mreqn as argument for IP_ADD_MEMBERSHIP. Legacy support
for struct ip_mreq remains in place. The struct ip_mreqn is Linux extension to classic BSD multicast API. It has extra field allowing to specify the interface index explicitly. In Linux it used as argument for IP_MULTICAST_IF and IP_ADD_MEMBERSHIP. FreeBSD kernel also declares this structure and supports it as argument to IP_MULTICAST_IF since r170613. So, we have structure declared but not fully supported, this confused third party application configure scripts. Code handling IP_ADD_MEMBERSHIP was mixed together with code for IP_ADD_SOURCE_MEMBERSHIP. Bringing legacy and new structure support into the mess would made the "argument switcharoo" intolerable, so code was separated into its own switch case clause. MFC after: 3 months Differential Revision: https://reviews.freebsd.org/D19276
This commit is contained in:
parent
e806165bee
commit
0dfc145abe
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=344481
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)ip.4 8.2 (Berkeley) 11/30/93
|
.\" @(#)ip.4 8.2 (Berkeley) 11/30/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd August 19, 2018
|
.Dd February 22, 2019
|
||||||
.Dt IP 4
|
.Dt IP 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -571,32 +571,55 @@ To join a multicast group, use the
|
|||||||
.Dv IP_ADD_MEMBERSHIP
|
.Dv IP_ADD_MEMBERSHIP
|
||||||
option:
|
option:
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
struct ip_mreq mreq;
|
struct ip_mreqn mreqn;
|
||||||
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn));
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
where
|
where
|
||||||
.Fa mreq
|
.Fa mreqn
|
||||||
is the following structure:
|
is the following structure:
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
struct ip_mreq {
|
struct ip_mreqn {
|
||||||
struct in_addr imr_multiaddr; /* IP multicast address of group */
|
struct in_addr imr_multiaddr; /* IP multicast address of group */
|
||||||
struct in_addr imr_interface; /* local IP address of interface */
|
struct in_addr imr_interface; /* local IP address of interface */
|
||||||
|
int imr_ifindex; /* interface index */
|
||||||
}
|
}
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
.Va imr_interface
|
.Va imr_ifindex
|
||||||
should be set to the
|
should be set to the index of a particular multicast-capable interface if
|
||||||
.Tn IP
|
|
||||||
address of a particular multicast-capable interface if
|
|
||||||
the host is multihomed.
|
the host is multihomed.
|
||||||
It may be set to
|
If
|
||||||
.Dv INADDR_ANY
|
.Va imr_ifindex
|
||||||
to choose the default interface, although this is not recommended;
|
is non-zero, value of
|
||||||
this is considered to be the first interface corresponding
|
.Va imr_interface
|
||||||
to the default route.
|
is ignored.
|
||||||
Otherwise, the first multicast-capable interface
|
Otherwise, if
|
||||||
configured in the system will be used.
|
.Va imr_ifindex
|
||||||
|
is 0, kernel will use IP address from
|
||||||
|
.Va imr_interface
|
||||||
|
to lookup the interface.
|
||||||
|
Value of
|
||||||
|
.Va imr_interface
|
||||||
|
may be set to
|
||||||
|
.Va INADDR_ANY
|
||||||
|
to choose the default interface, although this is not recommended; this is
|
||||||
|
considered to be the first interface corresponding to the default route.
|
||||||
|
Otherwise, the first multicast-capable interface configured in the system
|
||||||
|
will be used.
|
||||||
|
.Pp
|
||||||
|
Legacy
|
||||||
|
.Vt "struct ip_mreq" ,
|
||||||
|
that lacks
|
||||||
|
.Va imr_ifindex
|
||||||
|
field is also supported by
|
||||||
|
.Dv IP_ADD_MEMBERSHIP
|
||||||
|
setsockopt.
|
||||||
|
In this case kernel would behave as if
|
||||||
|
.Va imr_ifindex
|
||||||
|
was set to zero:
|
||||||
|
.Va imr_interface
|
||||||
|
will be used to lookup interface.
|
||||||
.Pp
|
.Pp
|
||||||
Prior to
|
Prior to
|
||||||
.Fx 7.0 ,
|
.Fx 7.0 ,
|
||||||
|
@ -2049,41 +2049,50 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
|
|||||||
ssa->ss.ss_family = AF_UNSPEC;
|
ssa->ss.ss_family = AF_UNSPEC;
|
||||||
|
|
||||||
switch (sopt->sopt_name) {
|
switch (sopt->sopt_name) {
|
||||||
case IP_ADD_MEMBERSHIP:
|
case IP_ADD_MEMBERSHIP: {
|
||||||
case IP_ADD_SOURCE_MEMBERSHIP: {
|
struct ip_mreqn mreqn;
|
||||||
struct ip_mreq_source mreqs;
|
|
||||||
|
|
||||||
if (sopt->sopt_name == IP_ADD_MEMBERSHIP) {
|
if (sopt->sopt_valsize == sizeof(struct ip_mreqn))
|
||||||
error = sooptcopyin(sopt, &mreqs,
|
error = sooptcopyin(sopt, &mreqn,
|
||||||
sizeof(struct ip_mreq),
|
sizeof(struct ip_mreqn), sizeof(struct ip_mreqn));
|
||||||
sizeof(struct ip_mreq));
|
else
|
||||||
/*
|
error = sooptcopyin(sopt, &mreqn,
|
||||||
* Do argument switcharoo from ip_mreq into
|
sizeof(struct ip_mreq), sizeof(struct ip_mreq));
|
||||||
* ip_mreq_source to avoid using two instances.
|
|
||||||
*/
|
|
||||||
mreqs.imr_interface = mreqs.imr_sourceaddr;
|
|
||||||
mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
|
|
||||||
} else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
|
|
||||||
error = sooptcopyin(sopt, &mreqs,
|
|
||||||
sizeof(struct ip_mreq_source),
|
|
||||||
sizeof(struct ip_mreq_source));
|
|
||||||
}
|
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
gsa->sin.sin_family = AF_INET;
|
gsa->sin.sin_family = AF_INET;
|
||||||
gsa->sin.sin_len = sizeof(struct sockaddr_in);
|
gsa->sin.sin_len = sizeof(struct sockaddr_in);
|
||||||
gsa->sin.sin_addr = mreqs.imr_multiaddr;
|
gsa->sin.sin_addr = mreqn.imr_multiaddr;
|
||||||
|
|
||||||
if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
|
|
||||||
ssa->sin.sin_family = AF_INET;
|
|
||||||
ssa->sin.sin_len = sizeof(struct sockaddr_in);
|
|
||||||
ssa->sin.sin_addr = mreqs.imr_sourceaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
|
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
|
if (sopt->sopt_valsize == sizeof(struct ip_mreqn) &&
|
||||||
|
mreqn.imr_ifindex != 0)
|
||||||
|
ifp = ifnet_byindex(mreqn.imr_ifindex);
|
||||||
|
else
|
||||||
|
ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
|
||||||
|
mreqn.imr_address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IP_ADD_SOURCE_MEMBERSHIP: {
|
||||||
|
struct ip_mreq_source mreqs;
|
||||||
|
|
||||||
|
error = sooptcopyin(sopt, &mreqs, sizeof(struct ip_mreq_source),
|
||||||
|
sizeof(struct ip_mreq_source));
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
gsa->sin.sin_family = ssa->sin.sin_family = AF_INET;
|
||||||
|
gsa->sin.sin_len = ssa->sin.sin_len =
|
||||||
|
sizeof(struct sockaddr_in);
|
||||||
|
|
||||||
|
gsa->sin.sin_addr = mreqs.imr_multiaddr;
|
||||||
|
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
ssa->sin.sin_addr = mreqs.imr_sourceaddr;
|
||||||
|
|
||||||
ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
|
ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
|
||||||
mreqs.imr_interface);
|
mreqs.imr_interface);
|
||||||
CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
|
CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
|
||||||
|
Loading…
Reference in New Issue
Block a user