Fix a long-standing limitation in IPv4 multicast group membership.
By making the imo_membership array a dynamically allocated vector, this minimizes disruption to existing IPv4 multicast code. This change breaks the ABI for the kernel module ip_mroute.ko, and may cause a small amount of churn for folks working on the IGMPv3 merge. Previously, sockets were subject to a compile-time limitation on the number of IPv4 group memberships, which was hard-coded to 20. The imo_membership relationship, however, is 1:1 with regards to a tuple of multicast group address and interface address. Users who ran routing protocols such as OSPF ran into this limitation on machines with a large system interface tree.
This commit is contained in:
parent
28c1069b5c
commit
18895e4f42
@ -424,7 +424,14 @@ __END_DECLS
|
||||
*/
|
||||
#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
|
||||
#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
|
||||
#define IP_MAX_MEMBERSHIPS 20 /* per socket */
|
||||
|
||||
/*
|
||||
* The imo_membership vector for each socket is now dynamically allocated at
|
||||
* run-time, bounded by USHRT_MAX, and is reallocated when needed, sized
|
||||
* according to a power-of-two increment.
|
||||
*/
|
||||
#define IP_MIN_MEMBERSHIPS 31
|
||||
#define IP_MAX_MEMBERSHIPS 4095
|
||||
|
||||
/*
|
||||
* Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
|
||||
|
@ -1138,6 +1138,7 @@ static struct ip_moptions *
|
||||
ip_findmoptions(struct inpcb *inp)
|
||||
{
|
||||
struct ip_moptions *imo;
|
||||
struct in_multi **immp;
|
||||
|
||||
INP_LOCK(inp);
|
||||
if (inp->inp_moptions != NULL)
|
||||
@ -1146,6 +1147,8 @@ ip_findmoptions(struct inpcb *inp)
|
||||
INP_UNLOCK(inp);
|
||||
|
||||
imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK);
|
||||
immp = (struct in_multi **)malloc((sizeof(*immp) * IP_MIN_MEMBERSHIPS),
|
||||
M_IPMOPTS, M_WAITOK);
|
||||
|
||||
imo->imo_multicast_ifp = NULL;
|
||||
imo->imo_multicast_addr.s_addr = INADDR_ANY;
|
||||
@ -1153,9 +1156,12 @@ ip_findmoptions(struct inpcb *inp)
|
||||
imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
|
||||
imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
|
||||
imo->imo_num_memberships = 0;
|
||||
imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
|
||||
imo->imo_membership = immp;
|
||||
|
||||
INP_LOCK(inp);
|
||||
if (inp->inp_moptions != NULL) {
|
||||
free(immp, M_IPMOPTS);
|
||||
free(imo, M_IPMOPTS);
|
||||
return (inp->inp_moptions);
|
||||
}
|
||||
@ -1360,11 +1366,32 @@ ip_setmoptions(struct inpcb *inp, struct sockopt *sopt)
|
||||
splx(s);
|
||||
break;
|
||||
}
|
||||
if (i == IP_MAX_MEMBERSHIPS) {
|
||||
if (imo->imo_num_memberships == imo->imo_max_memberships) {
|
||||
struct in_multi **nmships, **omships;
|
||||
size_t newmax;
|
||||
/*
|
||||
* Resize the vector to next power-of-two minus 1. If the
|
||||
* size would exceed the maximum then we know we've really
|
||||
* run out of entries. Otherwise, we realloc() the vector
|
||||
* with the INP lock held to avoid introducing a race.
|
||||
*/
|
||||
nmships = NULL;
|
||||
omships = imo->imo_membership;
|
||||
newmax = ((imo->imo_max_memberships + 1) * 2) - 1;
|
||||
if (newmax <= IP_MAX_MEMBERSHIPS) {
|
||||
nmships = (struct in_multi **)realloc(omships,
|
||||
sizeof(*nmships) * newmax, M_IPMOPTS, M_NOWAIT);
|
||||
if (nmships != NULL) {
|
||||
imo->imo_membership = nmships;
|
||||
imo->imo_max_memberships = newmax;
|
||||
}
|
||||
}
|
||||
if (nmships == NULL) {
|
||||
INP_UNLOCK(inp);
|
||||
error = ETOOMANYREFS;
|
||||
splx(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Everything looks good; add a new record to the multicast
|
||||
@ -1538,6 +1565,7 @@ ip_freemoptions(imo)
|
||||
if (imo != NULL) {
|
||||
for (i = 0; i < imo->imo_num_memberships; ++i)
|
||||
in_delmulti(imo->imo_membership[i]);
|
||||
free(imo->imo_membership, M_IPMOPTS);
|
||||
free(imo, M_IPMOPTS);
|
||||
}
|
||||
}
|
||||
|
@ -85,11 +85,12 @@ struct ipoption {
|
||||
struct ip_moptions {
|
||||
struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */
|
||||
struct in_addr imo_multicast_addr; /* ifindex/addr on MULTICAST_IF */
|
||||
u_long imo_multicast_vif; /* vif num outgoing multicasts */
|
||||
u_char imo_multicast_ttl; /* TTL for outgoing multicasts */
|
||||
u_char imo_multicast_loop; /* 1 => hear sends if a member */
|
||||
u_short imo_num_memberships; /* no. memberships this socket */
|
||||
struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS];
|
||||
u_long imo_multicast_vif; /* vif num outgoing multicasts */
|
||||
u_short imo_max_memberships; /* max memberships this socket */
|
||||
struct in_multi **imo_membership; /* group memberships */
|
||||
};
|
||||
|
||||
struct ipstat {
|
||||
|
Loading…
Reference in New Issue
Block a user