summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/in.h9
-rw-r--r--sys/netinet/ip_output.c30
-rw-r--r--sys/netinet/ip_var.h5
3 files changed, 40 insertions, 4 deletions
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 1476bf4..570ed33 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -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.
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 915512e..9cb9871 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -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);
}
}
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 6d0a122..a051825 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -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 {
OpenPOWER on IntegriCloud