1 /** @file
2 * @brief IPv4 IGMP related functions
3 */
4
5 /*
6 * Copyright (c) 2021 Intel Corporation
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_DECLARE(net_ipv4, CONFIG_NET_IPV4_LOG_LEVEL);
13
14 #include <errno.h>
15 #include <zephyr/net/net_core.h>
16 #include <zephyr/net/net_pkt.h>
17 #include <zephyr/net/net_stats.h>
18 #include <zephyr/net/net_context.h>
19 #include <zephyr/net/net_mgmt.h>
20 #include <zephyr/net/igmp.h>
21 #include "net_private.h"
22 #include "connection.h"
23 #include "ipv4.h"
24 #include "net_stats.h"
25 #include "igmp.h"
26
27 /* Timeout for various buffer allocations in this file. */
28 #define PKT_WAIT_TIME K_MSEC(50)
29
30 #define IPV4_OPT_HDR_ROUTER_ALERT_LEN 4
31
32 #define IGMPV2_PAYLOAD_MIN_LEN 8
33 #define IGMPV3_PAYLOAD_MIN_LEN 12
34
35 static const struct in_addr all_systems = { { { 224, 0, 0, 1 } } };
36 #if defined(CONFIG_NET_IPV4_IGMPV3)
37 static const struct in_addr igmp_multicast_addr = { { { 224, 0, 0, 22 } } };
38 #else
39 static const struct in_addr all_routers = { { { 224, 0, 0, 2 } } };
40 #endif
41
42 #define dbg_addr(action, pkt_str, src, dst) \
43 NET_DBG("%s %s from %s to %s", action, pkt_str, \
44 net_sprint_ipv4_addr(src), \
45 net_sprint_ipv4_addr(dst));
46
47 #define dbg_addr_recv(pkt_str, src, dst) \
48 dbg_addr("Received", pkt_str, src, dst)
49
50 enum igmp_version {
51 IGMPV1,
52 IGMPV2,
53 IGMPV3,
54 };
55
igmp_v2_create(struct net_pkt * pkt,const struct in_addr * addr,uint8_t type)56 static int igmp_v2_create(struct net_pkt *pkt, const struct in_addr *addr,
57 uint8_t type)
58 {
59 NET_PKT_DATA_ACCESS_DEFINE(igmp_access,
60 struct net_ipv4_igmp_v2_report);
61 struct net_ipv4_igmp_v2_report *igmp;
62
63 igmp = (struct net_ipv4_igmp_v2_report *)
64 net_pkt_get_data(pkt, &igmp_access);
65 if (!igmp) {
66 return -ENOBUFS;
67 }
68
69 igmp->type = type;
70 igmp->max_rsp = 0U;
71 net_ipaddr_copy(&igmp->address, addr);
72 igmp->chksum = 0;
73
74 if (net_pkt_set_data(pkt, &igmp_access)) {
75 return -ENOBUFS;
76 }
77
78 igmp->chksum = net_calc_chksum_igmp(pkt);
79
80 net_pkt_set_overwrite(pkt, true);
81 net_pkt_cursor_init(pkt);
82
83 net_pkt_skip(pkt, offsetof(struct net_ipv4_igmp_v2_report, chksum));
84 if (net_pkt_write(pkt, &igmp->chksum, sizeof(igmp->chksum))) {
85 return -ENOBUFS;
86 }
87
88 return 0;
89 }
90
91 #if defined(CONFIG_NET_IPV4_IGMPV3)
igmp_v3_create(struct net_pkt * pkt,uint8_t type,struct net_if_mcast_addr mcast[],size_t mcast_len)92 static int igmp_v3_create(struct net_pkt *pkt, uint8_t type, struct net_if_mcast_addr mcast[],
93 size_t mcast_len)
94 {
95 NET_PKT_DATA_ACCESS_DEFINE(igmp_access, struct net_ipv4_igmp_v3_report);
96 NET_PKT_DATA_ACCESS_DEFINE(group_record_access, struct net_ipv4_igmp_v3_group_record);
97 struct net_ipv4_igmp_v3_report *igmp;
98 struct net_ipv4_igmp_v3_group_record *group_record;
99
100 uint16_t group_count = 0;
101
102 igmp = (struct net_ipv4_igmp_v3_report *)net_pkt_get_data(pkt, &igmp_access);
103 if (!igmp) {
104 return -ENOBUFS;
105 }
106
107 for (int i = 0; i < mcast_len; i++) {
108 /* We don't need to send an IGMP membership report to the IGMP
109 * all systems multicast address of 224.0.0.1 so skip over it.
110 * Since the IGMP all systems multicast address is marked as
111 * used and joined during init time, we have to check this
112 * address separately to skip over it.
113 */
114 if (!mcast[i].is_used || !mcast[i].is_joined ||
115 net_ipv4_addr_cmp_raw((uint8_t *)&mcast[i].address.in_addr,
116 (uint8_t *)&all_systems)) {
117 continue;
118 }
119
120 group_count++;
121 }
122
123 igmp->type = type;
124 igmp->reserved_1 = 0U;
125 igmp->reserved_2 = 0U;
126 igmp->groups_len = htons(group_count);
127 /* Setting initial value of chksum to 0 to calculate chksum as described in RFC 3376
128 * ch 4.1.2
129 */
130 igmp->chksum = 0;
131
132 if (net_pkt_set_data(pkt, &igmp_access)) {
133 return -ENOBUFS;
134 }
135
136 for (int i = 0; i < mcast_len; i++) {
137 /* We don't need to send an IGMP membership report to the IGMP
138 * all systems multicast address of 224.0.0.1 so skip over it.
139 * Since the IGMP all systems multicast address is marked as
140 * used and joined during init time, we have to check this
141 * address separately to skip over it.
142 */
143 if (!mcast[i].is_used || !mcast[i].is_joined ||
144 net_ipv4_addr_cmp_raw((uint8_t *)&mcast[i].address.in_addr,
145 (uint8_t *)&all_systems)) {
146 continue;
147 }
148
149 group_record = (struct net_ipv4_igmp_v3_group_record *)net_pkt_get_data(
150 pkt, &group_record_access);
151 if (!group_record) {
152 return -ENOBUFS;
153 }
154
155 group_record->type = mcast[i].record_type;
156 group_record->aux_len = 0U;
157 net_ipaddr_copy(&group_record->address, &mcast[i].address.in_addr);
158 group_record->sources_len = htons(mcast[i].sources_len);
159
160 if (net_pkt_set_data(pkt, &group_record_access)) {
161 return -ENOBUFS;
162 }
163
164 for (int j = 0; j < mcast[i].sources_len; j++) {
165 if (net_pkt_write(pkt, &mcast[i].sources[j].in_addr.s_addr,
166 sizeof(mcast[i].sources[j].in_addr.s_addr))) {
167 return -ENOBUFS;
168 }
169 }
170 }
171
172 igmp->chksum = net_calc_chksum_igmp(pkt);
173
174 net_pkt_set_overwrite(pkt, true);
175 net_pkt_cursor_init(pkt);
176
177 net_pkt_skip(pkt, offsetof(struct net_ipv4_igmp_v3_report, chksum));
178 if (net_pkt_write(pkt, &igmp->chksum, sizeof(igmp->chksum))) {
179 return -ENOBUFS;
180 }
181
182 return 0;
183 }
184 #endif
185
igmp_v2_create_packet(struct net_pkt * pkt,const struct in_addr * dst,const struct in_addr * group,uint8_t type)186 static int igmp_v2_create_packet(struct net_pkt *pkt, const struct in_addr *dst,
187 const struct in_addr *group, uint8_t type)
188 {
189 const uint32_t router_alert = 0x94040000; /* RFC 2213 ch 2.1 */
190 int ret;
191
192 /* TTL set to 1, RFC 3376 ch 2 */
193 net_pkt_set_ipv4_ttl(pkt, 1U);
194
195 ret = net_ipv4_create_full(pkt,
196 net_if_ipv4_select_src_addr(
197 net_pkt_iface(pkt),
198 dst),
199 dst,
200 0U,
201 0U,
202 0U,
203 0U);
204 if (ret) {
205 return -ENOBUFS;
206 }
207
208 /* Add router alert option, RFC 3376 ch 2 */
209 if (net_pkt_write_be32(pkt, router_alert)) {
210 return -ENOBUFS;
211 }
212
213 net_pkt_set_ipv4_opts_len(pkt, IPV4_OPT_HDR_ROUTER_ALERT_LEN);
214
215 return igmp_v2_create(pkt, group, type);
216 }
217
218 #if defined(CONFIG_NET_IPV4_IGMPV3)
igmp_v3_create_packet(struct net_pkt * pkt,const struct in_addr * dst,struct net_if_mcast_addr mcast[],size_t mcast_len,uint8_t type)219 static int igmp_v3_create_packet(struct net_pkt *pkt, const struct in_addr *dst,
220 struct net_if_mcast_addr mcast[], size_t mcast_len, uint8_t type)
221 {
222 const uint32_t router_alert = 0x94040000; /* RFC 2213 ch 2.1 */
223 int ret;
224
225 /* TTL set to 1, RFC 3376 ch 2 */
226 net_pkt_set_ipv4_ttl(pkt, 1U);
227
228 ret = net_ipv4_create_full(pkt, net_if_ipv4_select_src_addr(net_pkt_iface(pkt), dst), dst,
229 0U, 0U, 0U, 0U);
230 if (ret) {
231 return -ENOBUFS;
232 }
233
234 /* Add router alert option, RFC 3376 ch 2 */
235 if (net_pkt_write_be32(pkt, router_alert)) {
236 return -ENOBUFS;
237 }
238
239 net_pkt_set_ipv4_opts_len(pkt, IPV4_OPT_HDR_ROUTER_ALERT_LEN);
240
241 return igmp_v3_create(pkt, type, mcast, mcast_len);
242 }
243 #endif
244
igmp_send(struct net_pkt * pkt)245 static int igmp_send(struct net_pkt *pkt)
246 {
247 int ret;
248
249 net_pkt_cursor_init(pkt);
250 net_ipv4_finalize(pkt, IPPROTO_IGMP);
251
252 ret = net_send_data(pkt);
253 if (ret < 0) {
254 net_stats_update_ipv4_igmp_drop(net_pkt_iface(pkt));
255 return ret;
256 }
257
258 net_stats_update_ipv4_igmp_sent(net_pkt_iface(pkt));
259
260 return 0;
261 }
262
send_igmp_report(struct net_if * iface,struct net_ipv4_igmp_v2_query * igmp_v2_hdr)263 static int send_igmp_report(struct net_if *iface,
264 struct net_ipv4_igmp_v2_query *igmp_v2_hdr)
265 {
266 struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
267 struct net_pkt *pkt = NULL;
268 int i, count = 0;
269 int ret = 0;
270
271 if (!ipv4) {
272 return -ENOENT;
273 }
274
275 for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
276 /* We don't need to send an IGMP membership report to the IGMP
277 * all systems multicast address of 224.0.0.1 so skip over it.
278 * Since the IGMP all systems multicast address is marked as
279 * used and joined during init time, we have to check this
280 * address separately to skip over it.
281 */
282 if (!ipv4->mcast[i].is_used || !ipv4->mcast[i].is_joined ||
283 net_ipv4_addr_cmp_raw((uint8_t *)&ipv4->mcast[i].address.in_addr,
284 (uint8_t *)&all_systems)) {
285 continue;
286 }
287
288 count++;
289 }
290
291 if (count == 0) {
292 return -ESRCH;
293 }
294
295 for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
296 /* We don't need to send an IGMP membership report to the IGMP
297 * all systems multicast address of 224.0.0.1 so skip over it.
298 * Since the IGMP all systems multicast address is marked as
299 * used and joined during init time, we have to check this
300 * address separately to skip over it.
301 */
302 if (!ipv4->mcast[i].is_used || !ipv4->mcast[i].is_joined ||
303 net_ipv4_addr_cmp_raw((uint8_t *)&ipv4->mcast[i].address.in_addr,
304 (uint8_t *)&all_systems)) {
305 continue;
306 }
307
308 pkt = net_pkt_alloc_with_buffer(iface,
309 IPV4_OPT_HDR_ROUTER_ALERT_LEN +
310 sizeof(struct net_ipv4_igmp_v2_report),
311 AF_INET, IPPROTO_IGMP,
312 PKT_WAIT_TIME);
313 if (!pkt) {
314 return -ENOMEM;
315 }
316
317 /* Send the IGMP V2 membership report to the group multicast
318 * address, as per RFC 2236 Section 9.
319 */
320 ret = igmp_v2_create_packet(pkt, &ipv4->mcast[i].address.in_addr,
321 &ipv4->mcast[i].address.in_addr,
322 NET_IPV4_IGMP_REPORT_V2);
323 if (ret < 0) {
324 goto drop;
325 }
326
327 ret = igmp_send(pkt);
328 if (ret < 0) {
329 goto drop;
330 }
331
332 /* So that we do not free the data while it is being sent */
333 pkt = NULL;
334 }
335
336 drop:
337 if (pkt) {
338 net_pkt_unref(pkt);
339 }
340
341 return ret;
342 }
343
344 #if defined(CONFIG_NET_IPV4_IGMPV3)
send_igmp_v3_report(struct net_if * iface,struct net_ipv4_igmp_v3_query * igmp_v3_hdr)345 static int send_igmp_v3_report(struct net_if *iface, struct net_ipv4_igmp_v3_query *igmp_v3_hdr)
346 {
347 struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
348 struct net_pkt *pkt = NULL;
349 int i, group_count = 0, source_count = 0;
350 int ret = 0;
351
352 if (!ipv4) {
353 return -ENOENT;
354 }
355
356 for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
357 /* We don't need to send an IGMP membership report to the IGMP
358 * all systems multicast address of 224.0.0.1 so skip over it.
359 * Since the IGMP all systems multicast address is marked as
360 * used and joined during init time, we have to check this
361 * address separately to skip over it.
362 */
363 if (!ipv4->mcast[i].is_used || !ipv4->mcast[i].is_joined ||
364 net_ipv4_addr_cmp_raw((uint8_t *)&ipv4->mcast[i].address.in_addr,
365 (uint8_t *)&all_systems)) {
366 continue;
367 }
368
369 group_count++;
370 source_count += ipv4->mcast[i].sources_len;
371 }
372
373 if (group_count == 0) {
374 return -ESRCH;
375 }
376
377 pkt = net_pkt_alloc_with_buffer(
378 iface,
379 IPV4_OPT_HDR_ROUTER_ALERT_LEN + sizeof(struct net_ipv4_igmp_v3_report) +
380 sizeof(struct net_ipv4_igmp_v3_group_record) * group_count +
381 sizeof(struct in_addr) * source_count,
382 AF_INET, IPPROTO_IGMP, PKT_WAIT_TIME);
383 if (!pkt) {
384 return -ENOMEM;
385 }
386
387 /* Send the IGMP V3 membership report to the igmp multicast
388 * address, as per RFC 3376 Section 4.2.14.
389 */
390
391 ret = igmp_v3_create_packet(pkt, &igmp_multicast_addr, ipv4->mcast, NET_IF_MAX_IPV4_MADDR,
392 NET_IPV4_IGMP_REPORT_V3);
393 if (ret < 0) {
394 goto drop;
395 }
396
397 ret = igmp_send(pkt);
398 if (ret < 0) {
399 goto drop;
400 }
401
402 /* So that we do not free the data while it is being sent */
403 pkt = NULL;
404
405 drop:
406 if (pkt) {
407 net_pkt_unref(pkt);
408 }
409
410 return ret;
411 }
412 #endif
413
net_ipv4_igmp_input(struct net_pkt * pkt,struct net_ipv4_hdr * ip_hdr)414 enum net_verdict net_ipv4_igmp_input(struct net_pkt *pkt, struct net_ipv4_hdr *ip_hdr)
415 {
416 int ret;
417 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(igmpv2_access, struct net_ipv4_igmp_v2_query);
418
419 struct net_ipv4_igmp_v2_query *igmpv2_hdr;
420 int igmp_buf_len =
421 pkt->buffer->len - (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv4_opts_len(pkt));
422 #if defined(CONFIG_NET_IPV4_IGMPV3)
423 struct net_ipv4_igmp_v3_query *igmpv3_hdr;
424 enum igmp_version version;
425
426 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(igmpv3_access, struct net_ipv4_igmp_v3_query);
427
428 /* Detect IGMP type (RFC 3376 ch 7.1) */
429 if (igmp_buf_len == IGMPV2_PAYLOAD_MIN_LEN) {
430 /* IGMPv1/2 detected */
431 version = IGMPV2;
432 } else if (igmp_buf_len >= IGMPV3_PAYLOAD_MIN_LEN) {
433 /* IGMPv3 detected */
434 version = IGMPV3;
435 } else {
436 #else
437 if (igmp_buf_len < IGMPV2_PAYLOAD_MIN_LEN) {
438 #endif
439 NET_DBG("DROP: unsupported payload length");
440 return NET_DROP;
441 }
442
443 if (!net_ipv4_addr_cmp_raw(ip_hdr->dst, (uint8_t *)&all_systems)) {
444 NET_DBG("DROP: Invalid dst address");
445 return NET_DROP;
446 }
447
448 #if defined(CONFIG_NET_IPV4_IGMPV3)
449 if (version == IGMPV3) {
450 igmpv3_hdr = (struct net_ipv4_igmp_v3_query *)net_pkt_get_data(pkt, &igmpv3_access);
451 if (!igmpv3_hdr) {
452 NET_DBG("DROP: NULL %sv3 header", "IGMP");
453 return NET_DROP;
454 }
455 } else {
456 #endif
457 igmpv2_hdr = (struct net_ipv4_igmp_v2_query *)net_pkt_get_data(pkt, &igmpv2_access);
458 if (!igmpv2_hdr) {
459 NET_DBG("DROP: NULL %s header", "IGMP");
460 return NET_DROP;
461 }
462 #if defined(CONFIG_NET_IPV4_IGMPV3)
463 }
464 #endif
465
466 ret = net_calc_chksum_igmp(pkt);
467 if (ret != 0u) {
468 NET_DBG("DROP: Invalid checksum");
469 goto drop;
470 }
471
472 #if defined(CONFIG_NET_IPV4_IGMPV3)
473 if (version == IGMPV3) {
474 net_pkt_acknowledge_data(pkt, &igmpv3_access);
475 } else {
476 #endif
477 net_pkt_acknowledge_data(pkt, &igmpv2_access);
478 #if defined(CONFIG_NET_IPV4_IGMPV3)
479 }
480 #endif
481
482 dbg_addr_recv("Internet Group Management Protocol", &ip_hdr->src, &ip_hdr->dst);
483
484 net_stats_update_ipv4_igmp_recv(net_pkt_iface(pkt));
485
486 #if defined(CONFIG_NET_IPV4_IGMPV3)
487 if (version == IGMPV3) {
488 (void)send_igmp_v3_report(net_pkt_iface(pkt), igmpv3_hdr);
489 } else {
490 #endif
491 (void)send_igmp_report(net_pkt_iface(pkt), igmpv2_hdr);
492 #if defined(CONFIG_NET_IPV4_IGMPV3)
493 }
494 #endif
495
496 net_pkt_unref(pkt);
497
498 return NET_OK;
499
500 drop:
501 net_stats_update_ipv4_igmp_drop(net_pkt_iface(pkt));
502
503 return NET_DROP;
504 }
505
506 #if !defined(CONFIG_NET_IPV4_IGMPV3)
507 static int igmp_send_generic(struct net_if *iface,
508 const struct in_addr *addr,
509 bool join)
510 {
511 struct net_pkt *pkt;
512 int ret;
513
514 pkt = net_pkt_alloc_with_buffer(iface,
515 IPV4_OPT_HDR_ROUTER_ALERT_LEN +
516 sizeof(struct net_ipv4_igmp_v2_report),
517 AF_INET, IPPROTO_IGMP,
518 PKT_WAIT_TIME);
519 if (!pkt) {
520 return -ENOMEM;
521 }
522
523 /* Send the IGMP V2 membership report to the group multicast
524 * address, as per RFC 2236 Section 9. The leave report
525 * should be sent to the ALL ROUTERS multicast address (224.0.0.2)
526 */
527 ret = igmp_v2_create_packet(pkt,
528 join ? addr : &all_routers, addr,
529 join ? NET_IPV4_IGMP_REPORT_V2 : NET_IPV4_IGMP_LEAVE);
530 if (ret < 0) {
531 goto drop;
532 }
533
534 ret = igmp_send(pkt);
535 if (ret < 0) {
536 goto drop;
537 }
538
539 return 0;
540
541 drop:
542 net_pkt_unref(pkt);
543
544 return ret;
545 }
546 #endif
547
548 #if defined(CONFIG_NET_IPV4_IGMPV3)
549 static int igmpv3_send_generic(struct net_if *iface, struct net_if_mcast_addr *mcast)
550 {
551 struct net_pkt *pkt;
552 int ret;
553
554 pkt = net_pkt_alloc_with_buffer(iface,
555 IPV4_OPT_HDR_ROUTER_ALERT_LEN +
556 sizeof(struct net_ipv4_igmp_v3_report) +
557 sizeof(struct net_ipv4_igmp_v3_group_record) +
558 sizeof(struct in_addr) * mcast->sources_len,
559 AF_INET, IPPROTO_IGMP, PKT_WAIT_TIME);
560 if (!pkt) {
561 return -ENOMEM;
562 }
563
564 ret = igmp_v3_create_packet(pkt, &igmp_multicast_addr, mcast, 1, NET_IPV4_IGMP_REPORT_V3);
565 if (ret < 0) {
566 goto drop;
567 }
568
569 ret = igmp_send(pkt);
570 if (ret < 0) {
571 goto drop;
572 }
573
574 return 0;
575
576 drop:
577 net_pkt_unref(pkt);
578
579 return ret;
580 }
581 #endif
582
583 int net_ipv4_igmp_join(struct net_if *iface, const struct in_addr *addr,
584 const struct igmp_param *param)
585 {
586 struct net_if_mcast_addr *maddr;
587 int ret;
588
589 #if defined(CONFIG_NET_IPV4_IGMPV3)
590 if (param != NULL) {
591 if (param->sources_len > CONFIG_NET_IF_MCAST_IPV4_SOURCE_COUNT) {
592 return -ENOMEM;
593 }
594 }
595 #endif
596
597 maddr = net_if_ipv4_maddr_lookup(addr, &iface);
598 if (maddr && net_if_ipv4_maddr_is_joined(maddr)) {
599 return -EALREADY;
600 }
601
602 if (!maddr) {
603 maddr = net_if_ipv4_maddr_add(iface, addr);
604 if (!maddr) {
605 return -ENOMEM;
606 }
607 }
608
609 #if defined(CONFIG_NET_IPV4_IGMPV3)
610 if (param != NULL) {
611 maddr->record_type = param->include ? IGMPV3_CHANGE_TO_INCLUDE_MODE
612 : IGMPV3_CHANGE_TO_EXCLUDE_MODE;
613 maddr->sources_len = param->sources_len;
614 for (int i = 0; i < param->sources_len; i++) {
615 net_ipaddr_copy(&maddr->sources[i].in_addr.s_addr,
616 ¶m->source_list[i].s_addr);
617 }
618 } else {
619 maddr->record_type = IGMPV3_CHANGE_TO_EXCLUDE_MODE;
620 }
621 #endif
622
623 net_if_ipv4_maddr_join(iface, maddr);
624
625 #if defined(CONFIG_NET_IPV4_IGMPV3)
626 ret = igmpv3_send_generic(iface, maddr);
627 #else
628 ret = igmp_send_generic(iface, addr, true);
629 #endif
630 if (ret < 0) {
631 net_if_ipv4_maddr_leave(iface, maddr);
632 return ret;
633 }
634
635 #if defined(CONFIG_NET_IPV4_IGMPV3)
636 if (param != NULL) {
637 /* Updating the record type for further use after sending the join report */
638 maddr->record_type =
639 param->include ? IGMPV3_MODE_IS_INCLUDE : IGMPV3_MODE_IS_EXCLUDE;
640 }
641 #endif
642
643 net_if_mcast_monitor(iface, &maddr->address, true);
644
645 net_mgmt_event_notify_with_info(NET_EVENT_IPV4_MCAST_JOIN, iface, &maddr->address.in_addr,
646 sizeof(struct in_addr));
647
648 return ret;
649 }
650
651 int net_ipv4_igmp_leave(struct net_if *iface, const struct in_addr *addr)
652 {
653 struct net_if_mcast_addr *maddr;
654 int ret;
655
656 maddr = net_if_ipv4_maddr_lookup(addr, &iface);
657 if (!maddr) {
658 return -ENOENT;
659 }
660
661 #if defined(CONFIG_NET_IPV4_IGMPV3)
662 maddr->record_type = IGMPV3_CHANGE_TO_INCLUDE_MODE;
663 maddr->sources_len = 0;
664
665 ret = igmpv3_send_generic(iface, maddr);
666 #else
667 ret = igmp_send_generic(iface, addr, false);
668 #endif
669 if (ret < 0) {
670 return ret;
671 }
672
673 if (!net_if_ipv4_maddr_rm(iface, addr)) {
674 return -EINVAL;
675 }
676
677 net_if_ipv4_maddr_leave(iface, maddr);
678
679 net_if_mcast_monitor(iface, &maddr->address, false);
680
681 net_mgmt_event_notify_with_info(NET_EVENT_IPV4_MCAST_LEAVE, iface, &maddr->address.in_addr,
682 sizeof(struct in_addr));
683 return ret;
684 }
685
686 void net_ipv4_igmp_init(struct net_if *iface)
687 {
688 struct net_if_mcast_addr *maddr;
689
690 /* Ensure multicast addresses are available */
691 if (CONFIG_NET_IF_MCAST_IPV4_ADDR_COUNT < 1) {
692 return;
693 }
694
695 /* This code adds the IGMP all systems 224.0.0.1 multicast address
696 * to the list of multicast addresses of the given interface.
697 * The address is marked as joined. However, an IGMP membership
698 * report is not generated for this address. Populating this
699 * address in the list of multicast addresses of the interface
700 * and marking it as joined is helpful for multicast hash filter
701 * implementations that need a list of multicast addresses it needs
702 * to add to the multicast hash filter after a multicast address
703 * has been removed from the membership list.
704 */
705 maddr = net_if_ipv4_maddr_lookup(&all_systems, &iface);
706 if (maddr && net_if_ipv4_maddr_is_joined(maddr)) {
707 return;
708 }
709
710 if (!maddr) {
711 maddr = net_if_ipv4_maddr_add(iface, &all_systems);
712 if (!maddr) {
713 return;
714 }
715 }
716
717 net_if_ipv4_maddr_join(iface, maddr);
718
719 net_if_mcast_monitor(iface, &maddr->address, true);
720 }
721