1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
/*---------------------------------------------------------------
* Copyright (c) 2023
* Broadcom Corporation
* All Rights Reserved.
*---------------------------------------------------------------
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and
* the following disclaimers.
*
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimers in the documentation and/or other materials
* provided with the distribution.
*
*
* Neither the name of Broadcom Coporation,
* nor the names of its contributors may be used to endorse
* or promote products derived from this Software without
* specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ________________________________________________________________
*
* iperf_multicast_api.c
* pull iperf multicast code for maitainability
*
* by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
*
*
* Joins the multicast group or source and group (SSM S,G)
*
* taken from: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.hale001/ipv6d0141001708.htm
*
* Multicast function IPv4 IPv6 Protocol-independent
* ================== ==== ==== ====================
* Level of specified option on setsockopt()/getsockopt() IPPROTO_IP IPPROTO_IPV6 IPPROTO_IP or IPPROTO_IPV6
* Join a multicast group IP_ADD_MEMBERSHIP IPV6_JOIN_GROUP MCAST_JOIN_GROUP
* Leave a multicast group or leave all sources of that
* multicast group IP_DROP_MEMBERSHIP IPV6_LEAVE_GROUP MCAST_LEAVE_GROUP
* Select outbound interface for sending multicast datagrams IP_MULTICAST_IF IPV6_MULTICAST_IF NA
* Set maximum hop count IP_MULTICAST_TTL IPV6_MULTICAST_HOPS NA
* Enable multicast loopback IP_MULTICAST_LOOP IPV6_MULTICAST_LOOP NA
* Join a source multicast group IP_ADD_SOURCE_MEMBERSHIP NA MCAST_JOIN_SOURCE_GROUP
* Leave a source multicast group IP_DROP_SOURCE_MEMBERSHIP NA MCAST_LEAVE_SOURCE_GROUP
* Block data from a source to a multicast group IP_BLOCK_SOURCE NA MCAST_BLOCK_SOURCE
* Unblock a previously blocked source for a multicast group IP_UNBLOCK_SOURCE NA MCAST_UNBLOCK_SOURCE
*
*
* Reminder: The os will decide which version of IGMP or MLD to use. This may be controlled by system settings, e.g.:
*
* [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep mld | grep force
* net.ipv6.conf.all.force_mld_version = 0
* net.ipv6.conf.default.force_mld_version = 0
* net.ipv6.conf.lo.force_mld_version = 0
* net.ipv6.conf.eth0.force_mld_version = 0
*
* [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep igmp | grep force
* net.ipv4.conf.all.force_igmp_version = 0
* net.ipv4.conf.default.force_igmp_version = 0
* net.ipv4.conf.lo.force_igmp_version = 0
* net.ipv4.conf.eth0.force_igmp_version = 0
*
* ------------------------------------------------------------------- */
#include "headers.h"
#include "Settings.hpp"
#include "iperf_multicast_api.h"
#include "SocketAddr.h"
#include "util.h"
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
static unsigned int mcast_iface (struct thread_Settings *inSettings) {
unsigned int iface=0;
/* Set the interface or any */
if (inSettings->mIfrname) {
#if HAVE_NET_IF_H && !WIN32
iface = if_nametoindex(inSettings->mIfrname);
FAIL_errno(!iface, "mcast if_nametoindex", inSettings);
#else
fprintf(stderr, "multicast bind to device not supported on this platform\n");
#endif
}
return iface;
}
// IP_MULTICAST_ALL is on be default, disable it here.
// If set to 1, the socket will receive messages from all the groups that have been joined
// globally on the whole system. Otherwise, it will deliver messages only from the
// groups that have been explicitly joined (for example via the IP_ADD_MEMBERSHIP option)
// on this particular socket.
#if HAVE_MULTICAST_ALL_DISABLE
static int iperf_multicast_all_disable (struct thread_Settings *inSettings) {
int rc = 0;
#if HAVE_DECL_IP_MULTICAST_ALL
int mc_all = 0;
rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all));
FAIL_errno(rc == SOCKET_ERROR, "ip_multicast_all", inSettings);
#endif
return rc;
}
#endif
// This is the older mulitcast join code. Both SSM and binding the
// an interface requires the newer socket options. Using the older
// code here will maintain compatiblity with previous iperf versions
static int iperf_multicast_join_v4_legacy (struct thread_Settings *inSettings) {
#if HAVE_DECL_IP_ADD_MEMBERSHIP
#if (HAVE_STRUCT_IP_MREQ) || (HAVE_STRUCT_IP_MREQN)
#if HAVE_STRUCT_IP_MREQ
struct ip_mreq mreq;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
size_t len = sizeof(struct ip_mreq);
#elif HAVE_STRUCT_IP_MREQN
// struct ip_mreqn {
// struct in_addr imr_multiaddr; /* IP multicast address of group */
// struct in_addr imr_interface; /* local IP address of interface */
// int imr_ifindex; /* interface index */
// }
struct ip_mreqn mreq;
size_t len = sizeof(struct ip_mreqn);
mreq.imr_address.s_addr = htonl(INADDR_ANY);
mreq.imr_ifindex = mcast_iface(inSettings);
#endif
memcpy(&mreq.imr_multiaddr, SockAddr_get_in_addr(&inSettings->multicast_group), sizeof(mreq.imr_multiaddr));
int rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char*)(&mreq), len);
FAIL_errno(rc == SOCKET_ERROR, "multicast join", inSettings);
#if HAVE_MULTICAST_ALL_DISABLE
iperf_multicast_all_disable(inSettings);
#endif
return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL);
#endif
#endif
return IPERF_MULTICAST_JOIN_UNSUPPORTED;
}
static int iperf_multicast_join_v4_pi (struct thread_Settings *inSettings) {
#if HAVE_DECL_MCAST_JOIN_GROUP
int rc = -1;
struct group_req group_req;
memset(&group_req, 0, sizeof(struct group_req));
memcpy(&group_req.gr_group, (struct sockaddr_in *)(&inSettings->multicast_group), sizeof(struct sockaddr_in));
group_req.gr_interface = mcast_iface(inSettings);
group_req.gr_group.ss_family = AF_INET;
rc = setsockopt(inSettings->mSock, IPPROTO_IP, MCAST_JOIN_GROUP, (const char *)(&group_req),
(socklen_t) sizeof(struct group_source_req));
FAIL_errno(rc == SOCKET_ERROR, "mcast v4 join group pi", inSettings);
return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL);
#else
return IPERF_MULTICAST_JOIN_UNSUPPORTED;
#endif
}
static int iperf_multicast_join_v6 (struct thread_Settings *inSettings) {
#if (HAVE_DECL_IPV6_JOIN_GROUP || HAVE_DECL_IPV6_ADD_MEMBERSHIP)
#if HAVE_STRUCT_IPV6_MREQ
struct ipv6_mreq mreq;
memcpy(&mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr(&inSettings->multicast_group), sizeof(mreq.ipv6mr_multiaddr));
mreq.ipv6mr_interface = mcast_iface(inSettings);
#if HAVE_DECL_IPV6_JOIN_GROUP
int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_JOIN_GROUP, \
(char*)(&mreq), sizeof(mreq));
#else
int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, \
(char*)(&mreq), sizeof(mreq));
#endif
FAIL_errno(rc == SOCKET_ERROR, "multicast v6 join", inSettings);
return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL);
#endif
#endif
return IPERF_MULTICAST_JOIN_UNSUPPORTED;
}
static int iperf_multicast_join_v6_pi (struct thread_Settings *inSettings) {
#if HAVE_DECL_MCAST_JOIN_GROUP
int rc = -1;
struct group_req group_req;
memset(&group_req, 0, sizeof(struct group_req));
memcpy(&group_req.gr_group, (struct sockaddr_in6 *)(&inSettings->multicast_group), sizeof(struct sockaddr_in6));
group_req.gr_interface = mcast_iface(inSettings);
group_req.gr_group.ss_family = AF_INET6;
rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, MCAST_JOIN_GROUP, (const char *)(&group_req),
(socklen_t) sizeof(struct group_source_req));
FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join group", inSettings);
return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL);
#endif
return IPERF_MULTICAST_JOIN_UNSUPPORTED;
}
static int iperf_multicast_ssm_join_v4 (struct thread_Settings *inSettings) {
#if HAVE_SSM_MULTICAST
int rc;
struct sockaddr_in *group;
struct sockaddr_in *source;
// Fill out both structures because we don't which one will succeed
// and both may need to be tried
#if HAVE_STRUCT_IP_MREQ_SOURCE
struct ip_mreq_source imr;
memset (&imr, 0, sizeof (imr));
#endif
#if HAVE_STRUCT_GROUP_SOURCE_REQ
struct group_source_req group_source_req;
memset(&group_source_req, 0, sizeof(struct group_source_req));
group_source_req.gsr_interface = mcast_iface(inSettings);
group=(struct sockaddr_in *)(&group_source_req.gsr_group);
source=(struct sockaddr_in *)(&group_source_req.gsr_source);
#else
struct sockaddr_in imrgroup;
struct sockaddr_in imrsource;
group = &imrgroup;
source = &imrsource;
#endif
source->sin_family = AF_INET;
group->sin_family = AF_INET;
/* Set the group and SSM source*/
memcpy(group, (struct sockaddr_in *)(&inSettings->multicast_group), sizeof(struct sockaddr_in));
memcpy(source, (struct sockaddr_in *)(&inSettings->multicast_group_source), sizeof(struct sockaddr_in));
#if HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
source->sin_len = group->sin_len;
#endif
source->sin_port = 0; /* Ignored */
rc = -1;
#if HAVE_DECL_MCAST_JOIN_SOURCE_GROUP
rc = setsockopt(inSettings->mSock,IPPROTO_IP,MCAST_JOIN_SOURCE_GROUP, (const char *)(&group_source_req), \
sizeof(struct group_source_req));
WARN(rc == SOCKET_ERROR, "mcast v4 join ssm join_src");
#endif
#if (HAVE_DECL_IP_ADD_SOURCE_MEMBERSHIP && HAVE_STRUCT_IP_MREQ_SOURCE)
// Some operating systems will have MCAST_JOIN_SOURCE_GROUP but still fail
// In those cases try the IP_ADD_SOURCE_MEMBERSHIP
if (rc < 0) {
#if HAVE_STRUCT_IP_MREQ_SOURCE_IMR_MULTIADDR_S_ADDR
imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr;
imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr;
#else
// Some Android versions declare mreq_source without an s_addr
imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr.s_addr;
imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr.s_addr;
#endif
rc = setsockopt (inSettings->mSock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*)(&imr), sizeof (imr));
FAIL_errno(rc == SOCKET_ERROR, "mcast v4 join ssm add_src", inSettings);
}
#endif
return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL);
#endif
return IPERF_MULTICAST_JOIN_UNSUPPORTED;
}
static int iperf_multicast_ssm_join_v6 (struct thread_Settings *inSettings) {
#if (HAVE_IPV6_MULTICAST && HAVE_SSM_MULTICAST && HAVE_DECL_MCAST_JOIN_SOURCE_GROUP)
int rc;
// Here it's either an SSM S,G multicast join or a *,G with an interface specifier
// Use the newer socket options when these are specified
struct group_source_req group_source_req;
struct sockaddr_in6 *group;
struct sockaddr_in6 *source;
memset(&group_source_req, 0, sizeof(struct group_source_req));
group_source_req.gsr_interface = mcast_iface(inSettings);
group=(struct sockaddr_in6*)(&group_source_req.gsr_group);
source=(struct sockaddr_in6*)(&group_source_req.gsr_source);
source->sin6_family = AF_INET6;
group->sin6_family = AF_INET6;
/* Set the group and SSM source*/
memcpy(group, (struct sockaddr_in *)(&inSettings->multicast_group), sizeof(struct sockaddr_in6));
memcpy(source, (struct sockaddr_in *)(&inSettings->multicast_group_source), sizeof(struct sockaddr_in6));
group->sin6_port = 0; /* Ignored */
#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
source->sin6_len = group->sin6_len;
#endif
rc = setsockopt(inSettings->mSock,IPPROTO_IPV6,MCAST_JOIN_SOURCE_GROUP, (const char *)(&group_source_req),
sizeof(struct group_source_req));
FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join source group", inSettings);
return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL);
#endif
return IPERF_MULTICAST_JOIN_UNSUPPORTED;
}
enum McastJoinResponse iperf_multicast_join (struct thread_Settings *inSettings) {
int rc = IPERF_MULTICAST_JOIN_FAIL;
if (!isSSMMulticast(inSettings)) {
// *.G join
if (!SockAddr_isIPv6(&inSettings->multicast_group)) {
if (!mcast_iface(inSettings)) {
rc = iperf_multicast_join_v4_legacy(inSettings);
}
if (rc != IPERF_MULTICAST_JOIN_SUCCESS) {
rc = iperf_multicast_join_v4_pi(inSettings);
}
} else {
rc = iperf_multicast_join_v6(inSettings);
if (rc != IPERF_MULTICAST_JOIN_SUCCESS) {
rc = iperf_multicast_join_v6_pi(inSettings);
}
}
} else {
// SSM or S,G join
if (!SockAddr_isIPv6(&inSettings->multicast_group)) {
rc = iperf_multicast_ssm_join_v4(inSettings);
} else {
rc = iperf_multicast_ssm_join_v6(inSettings);
}
}
return rc;
}
static void iperf_multicast_sync_ifrname (struct thread_Settings *inSettings) {
if (inSettings->mIfrname && !inSettings->mIfrnametx) {
int len = strlen(inSettings->mIfrname);
inSettings->mIfrnametx = calloc((len + 1), sizeof(char));
if (inSettings->mIfrnametx) {
strncpy(inSettings->mIfrnametx, inSettings->mIfrname, len+1);
}
}
if (!inSettings->mIfrname && inSettings->mIfrnametx) {
int len = strlen(inSettings->mIfrnametx);
inSettings->mIfrname = calloc((len + 1), sizeof(char));
if (inSettings->mIfrname) {
strncpy(inSettings->mIfrname, inSettings->mIfrnametx, len+1);
}
}
}
bool iperf_multicast_sendif_v4 (struct thread_Settings *inSettings) {
bool result = false;
#if HAVE_DECL_IP_MULTICAST_IF
struct in_addr interface_addr;
memcpy(&interface_addr, SockAddr_get_in_addr(&inSettings->local), sizeof(interface_addr));
int rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_IF, \
(char*)(&interface_addr), sizeof(interface_addr));
if ((rc != SOCKET_ERROR) && SockAddr_Ifrname(inSettings)) {
iperf_multicast_sync_ifrname(inSettings);
}
FAIL_errno(rc == SOCKET_ERROR, "v4 multicast if", inSettings);
result = ((rc == 0) ? true : false);
#endif
return result;
}
bool iperf_multicast_sendif_v6 (struct thread_Settings *inSettings) {
int result = false;
#if HAVE_DECL_IPV6_MULTICAST_IF && HAVE_NET_IF_H && !WIN32
if (inSettings->mIfrnametx) {
unsigned int ifindex = if_nametoindex(inSettings->mIfrnametx);
if (ifindex) {
int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex));
if (rc == 0) {
iperf_multicast_sync_ifrname(inSettings);
result = true;
}
}
}
#endif
return result;
}
|