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:
bms 2006-05-14 14:22:49 +00:00
parent 28c1069b5c
commit 18895e4f42
3 changed files with 40 additions and 4 deletions

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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 {