1 /* main.c - Application main entry point */
2
3 /*
4 * Copyright (c) 2021 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(net_test, CONFIG_NET_IPV4_LOG_LEVEL);
11
12 #include <zephyr/types.h>
13 #include <stdbool.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <zephyr/linker/sections.h>
18
19 #include <zephyr/ztest.h>
20
21 #include <zephyr/net/net_if.h>
22 #include <zephyr/net/net_pkt.h>
23 #include <zephyr/net/net_ip.h>
24 #include <zephyr/net/net_core.h>
25 #include <zephyr/net/ethernet.h>
26 #include <zephyr/net/dummy.h>
27 #include <zephyr/net/net_mgmt.h>
28 #include <zephyr/net/net_event.h>
29 #include <zephyr/net/igmp.h>
30 #include <zephyr/net/socket.h>
31
32 #include <zephyr/random/random.h>
33
34 #include "ipv4.h"
35 #include "igmp.h"
36
37 #define THREAD_SLEEP 50 /* ms */
38
39 #define NET_LOG_ENABLED 1
40 #include "net_private.h"
41
42 #if defined(CONFIG_NET_IPV4_LOG_LEVEL_DBG)
43 #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
44 #else
45 #define DBG(fmt, ...)
46 #endif
47
48 static const unsigned char igmp_v2_query[] = {
49 /* IPv4 header */
50 0x46, 0xc0, 0x00, 0x20, 0x1b, 0x58, 0x00, 0x00, 0x01, 0x02, 0x66, 0x79,
51 0xc0, 0x00, 0x02, 0x45, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00,
52
53 /* IGMP header */
54 0x11, 0xff, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00,
55 };
56
57 static const unsigned char igmp_v3_query[] = {
58 /* IPv4 header */
59 0x46, 0xc0, 0x00, 0x24, 0xac, 0x72, 0x00, 0x00, 0x01, 0x02, 0xd5, 0x5a,
60 0xc0, 0x00, 0x02, 0x45, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00,
61
62 /* IGMP header */
63 0x11, 0x64, 0xec, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x02, 0x7d, 0x00, 0x00,
64 };
65
66 static struct in_addr my_addr = { { { 192, 0, 2, 1 } } };
67 static struct in_addr mcast_addr = { { { 224, 0, 2, 63 } } };
68 static struct in_addr any_addr = INADDR_ANY_INIT;
69
70 static struct net_if *net_iface;
71 static bool is_group_joined;
72 static bool is_group_left;
73 static bool is_join_msg_ok;
74 static bool is_leave_msg_ok;
75 static bool is_query_received;
76 static bool is_report_sent;
77 static bool is_igmpv2_query_sent;
78 static bool is_igmpv3_query_sent;
79 static bool ignore_already;
80 K_SEM_DEFINE(wait_data, 0, UINT_MAX);
81
82 #define WAIT_TIME 500
83 #define WAIT_TIME_LONG MSEC_PER_SEC
84 #define MY_PORT 1969
85 #define PEER_PORT 13856
86
87 struct net_test_igmp {
88 uint8_t mac_addr[sizeof(struct net_eth_addr)];
89 struct net_linkaddr ll_addr;
90 };
91
net_test_dev_init(const struct device * dev)92 int net_test_dev_init(const struct device *dev)
93 {
94 return 0;
95 }
96
net_test_get_mac(const struct device * dev)97 static uint8_t *net_test_get_mac(const struct device *dev)
98 {
99 struct net_test_igmp *context = dev->data;
100
101 if (context->mac_addr[2] == 0x00) {
102 /* 00-00-5E-00-53-xx Documentation RFC 7042 */
103 context->mac_addr[0] = 0x00;
104 context->mac_addr[1] = 0x00;
105 context->mac_addr[2] = 0x5E;
106 context->mac_addr[3] = 0x00;
107 context->mac_addr[4] = 0x53;
108 context->mac_addr[5] = sys_rand8_get();
109 }
110
111 return context->mac_addr;
112 }
113
net_test_iface_init(struct net_if * iface)114 static void net_test_iface_init(struct net_if *iface)
115 {
116 uint8_t *mac = net_test_get_mac(net_if_get_device(iface));
117
118 net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr),
119 NET_LINK_ETHERNET);
120 }
121
122 #if defined(CONFIG_NET_IPV4_IGMPV3)
get_igmp_hdr(struct net_pkt * pkt)123 static struct net_ipv4_igmp_v3_report *get_igmp_hdr(struct net_pkt *pkt)
124 #else
125 static struct net_ipv4_igmp_v2_query *get_igmp_hdr(struct net_pkt *pkt)
126 #endif
127 {
128 net_pkt_cursor_init(pkt);
129
130 net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) +
131 net_pkt_ipv4_opts_len(pkt));
132
133 #if defined(CONFIG_NET_IPV4_IGMPV3)
134 return (struct net_ipv4_igmp_v3_report *)net_pkt_cursor_get_pos(pkt);
135 #else
136 return (struct net_ipv4_igmp_v2_query *)net_pkt_cursor_get_pos(pkt);
137 #endif
138 }
139
140 #if defined(CONFIG_NET_IPV4_IGMPV3)
get_igmp_group_record(struct net_pkt * pkt)141 static struct net_ipv4_igmp_v3_group_record *get_igmp_group_record(struct net_pkt *pkt)
142 {
143 net_pkt_cursor_init(pkt);
144
145 net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + net_pkt_ipv4_opts_len(pkt));
146 net_pkt_skip(pkt, sizeof(struct net_ipv4_igmp_v3_report));
147
148 return (struct net_ipv4_igmp_v3_group_record *)net_pkt_cursor_get_pos(pkt);
149 }
150 #endif
151
tester_send(const struct device * dev,struct net_pkt * pkt)152 static int tester_send(const struct device *dev, struct net_pkt *pkt)
153 {
154 #if defined(CONFIG_NET_IPV4_IGMPV3)
155 struct net_ipv4_igmp_v3_report *igmp_header;
156 struct net_ipv4_igmp_v3_group_record *igmp_group_record;
157 #else
158 struct net_ipv4_igmp_v2_query *igmp_header;
159 #endif
160
161 if (!pkt->buffer) {
162 TC_ERROR("No data to send!\n");
163 return -ENODATA;
164 }
165
166 igmp_header = get_igmp_hdr(pkt);
167
168 if (igmp_header->type == NET_IPV4_IGMP_QUERY) {
169 NET_DBG("Received query....");
170 is_query_received = true;
171 k_sem_give(&wait_data);
172 } else if (igmp_header->type == NET_IPV4_IGMP_REPORT_V2) {
173 NET_DBG("Received v2 report....");
174 zassert_true(!IS_ENABLED(CONFIG_NET_IPV4_IGMPV3) || is_igmpv2_query_sent,
175 "Wrong IGMP report received (IGMPv2)");
176 is_join_msg_ok = true;
177 is_report_sent = true;
178 k_sem_give(&wait_data);
179 } else if (igmp_header->type == NET_IPV4_IGMP_REPORT_V3) {
180 NET_DBG("Received v3 report....");
181 zassert_true(IS_ENABLED(CONFIG_NET_IPV4_IGMPV3),
182 "Wrong IGMP report received (IGMPv3)");
183 zassert_false(is_igmpv2_query_sent, "IGMPv3 response to IGMPv2 request");
184
185 #if defined(CONFIG_NET_IPV4_IGMPV3)
186 zassert_equal(ntohs(igmp_header->groups_len), 1,
187 "Invalid group length of IGMPv3 report (%d)",
188 igmp_header->groups_len);
189
190 igmp_group_record = get_igmp_group_record(pkt);
191 zassert_equal(igmp_group_record->sources_len, 0,
192 "Invalid sources length of IGMPv3 group record");
193
194 if (igmp_group_record->type == IGMPV3_CHANGE_TO_EXCLUDE_MODE) {
195 is_join_msg_ok = true;
196 } else if (igmp_group_record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE) {
197 is_leave_msg_ok = true;
198 }
199 #else
200 is_join_msg_ok = true;
201 #endif
202 is_report_sent = true;
203 k_sem_give(&wait_data);
204 } else if (igmp_header->type == NET_IPV4_IGMP_LEAVE) {
205 NET_DBG("Received leave....");
206 is_leave_msg_ok = true;
207 k_sem_give(&wait_data);
208 }
209
210 return 0;
211 }
212
213 struct net_test_igmp net_test_data;
214
215 static struct dummy_api net_test_if_api = {
216 .iface_api.init = net_test_iface_init,
217 .send = tester_send,
218 };
219
220 #define _ETH_L2_LAYER DUMMY_L2
221 #define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2)
222
223 NET_DEVICE_INIT(net_test_igmp, "net_test_igmp",
224 net_test_dev_init, NULL, &net_test_data, NULL,
225 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
226 &net_test_if_api, _ETH_L2_LAYER, _ETH_L2_CTX_TYPE,
227 127);
228
group_joined(struct net_mgmt_event_callback * cb,uint32_t nm_event,struct net_if * iface)229 static void group_joined(struct net_mgmt_event_callback *cb,
230 uint32_t nm_event, struct net_if *iface)
231 {
232 if (nm_event != NET_EVENT_IPV4_MCAST_JOIN) {
233 /* Spurious callback. */
234 return;
235 }
236
237 is_group_joined = true;
238
239 k_sem_give(&wait_data);
240 }
241
group_left(struct net_mgmt_event_callback * cb,uint32_t nm_event,struct net_if * iface)242 static void group_left(struct net_mgmt_event_callback *cb,
243 uint32_t nm_event, struct net_if *iface)
244 {
245 if (nm_event != NET_EVENT_IPV4_MCAST_LEAVE) {
246 /* Spurious callback. */
247 return;
248 }
249
250 is_group_left = true;
251
252 k_sem_give(&wait_data);
253 }
254
255 static struct mgmt_events {
256 uint32_t event;
257 net_mgmt_event_handler_t handler;
258 struct net_mgmt_event_callback cb;
259 } mgmt_events[] = {
260 { .event = NET_EVENT_IPV4_MCAST_JOIN, .handler = group_joined },
261 { .event = NET_EVENT_IPV4_MCAST_LEAVE, .handler = group_left },
262 { 0 }
263 };
264
setup_mgmt_events(void)265 static void setup_mgmt_events(void)
266 {
267 int i;
268
269 for (i = 0; mgmt_events[i].event; i++) {
270 net_mgmt_init_event_callback(&mgmt_events[i].cb,
271 mgmt_events[i].handler,
272 mgmt_events[i].event);
273
274 net_mgmt_add_event_callback(&mgmt_events[i].cb);
275 }
276 }
277
igmp_setup(void)278 static void *igmp_setup(void)
279 {
280 struct net_if_addr *ifaddr;
281
282 setup_mgmt_events();
283
284 net_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY));
285
286 zassert_not_null(net_iface, "Interface is NULL");
287
288 ifaddr = net_if_ipv4_addr_add(net_iface, &my_addr, NET_ADDR_MANUAL, 0);
289
290 zassert_not_null(ifaddr, "Cannot add IPv4 address");
291
292 return NULL;
293 }
294
igmp_teardown(void * dummy)295 static void igmp_teardown(void *dummy)
296 {
297 ARG_UNUSED(dummy);
298
299 int i;
300
301 for (i = 0; mgmt_events[i].event; i++) {
302 net_mgmt_del_event_callback(&mgmt_events[i].cb);
303 }
304
305 net_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY));
306
307 net_if_ipv4_addr_rm(net_iface, &my_addr);
308 }
309
prepare_igmp_query(struct net_if * iface,bool is_igmpv3)310 static struct net_pkt *prepare_igmp_query(struct net_if *iface, bool is_igmpv3)
311 {
312 struct net_pkt *pkt;
313
314 const unsigned char *igmp_query = is_igmpv3 ? igmp_v3_query : igmp_v2_query;
315 size_t igmp_query_size = is_igmpv3 ? sizeof(igmp_v3_query) : sizeof(igmp_v2_query);
316
317 pkt = net_pkt_alloc_with_buffer(iface, igmp_query_size, AF_INET, IPPROTO_IGMP, K_FOREVER);
318 zassert_not_null(pkt, "Failed to allocate buffer");
319
320 zassert_ok(net_pkt_write(pkt, igmp_query, igmp_query_size));
321
322 net_pkt_set_overwrite(pkt, true);
323 net_pkt_cursor_init(pkt);
324
325 return pkt;
326 }
327
join_group(void)328 static void join_group(void)
329 {
330 int ret;
331
332 ret = net_ipv4_igmp_join(net_iface, &mcast_addr, NULL);
333
334 if (ignore_already) {
335 zassert_true(ret == 0 || ret == -EALREADY,
336 "Cannot join IPv4 multicast group");
337 } else {
338 zassert_ok(ret, "Cannot join IPv4 multicast group");
339 }
340
341 /* Let the network stack to proceed */
342 k_msleep(THREAD_SLEEP);
343 }
344
leave_group(void)345 static void leave_group(void)
346 {
347 int ret;
348
349 ret = net_ipv4_igmp_leave(net_iface, &mcast_addr);
350
351 zassert_ok(ret, "Cannot leave IPv4 multicast group");
352
353 if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) {
354 /* Let the network stack to proceed */
355 k_msleep(THREAD_SLEEP);
356 } else {
357 k_yield();
358 }
359 }
360
catch_join_group(void)361 static void catch_join_group(void)
362 {
363 is_group_joined = false;
364
365 ignore_already = false;
366
367 join_group();
368
369 zassert_ok(k_sem_take(&wait_data, K_MSEC(WAIT_TIME)), "Timeout while waiting join event");
370
371 zassert_true(is_group_joined, "Did not catch join event");
372
373 is_group_joined = false;
374 }
375
catch_leave_group(void)376 static void catch_leave_group(void)
377 {
378 is_group_joined = false;
379
380 leave_group();
381
382 zassert_ok(k_sem_take(&wait_data, K_MSEC(WAIT_TIME)), "Timeout while waiting leave event");
383
384 zassert_true(is_group_left, "Did not catch leave event");
385
386 is_group_left = false;
387 }
388
verify_join_group(void)389 static void verify_join_group(void)
390 {
391 is_join_msg_ok = false;
392
393 ignore_already = false;
394
395 join_group();
396
397 zassert_ok(k_sem_take(&wait_data, K_MSEC(WAIT_TIME)), "Timeout while waiting join event");
398
399 zassert_true(is_join_msg_ok, "Join msg invalid");
400
401 is_join_msg_ok = false;
402 }
403
verify_leave_group(void)404 static void verify_leave_group(void)
405 {
406 is_leave_msg_ok = false;
407
408 leave_group();
409
410 zassert_ok(k_sem_take(&wait_data, K_MSEC(WAIT_TIME)), "Timeout while waiting leave event");
411
412 zassert_true(is_leave_msg_ok, "Leave msg invalid");
413
414 is_leave_msg_ok = false;
415 }
416
ZTEST(net_igmp,test_igmp_catch_join)417 ZTEST(net_igmp, test_igmp_catch_join)
418 {
419 join_group();
420 leave_group();
421 }
422
ZTEST(net_igmp,test_igmp_catch_catch_join)423 ZTEST(net_igmp, test_igmp_catch_catch_join)
424 {
425 catch_join_group();
426 catch_leave_group();
427 }
428
ZTEST(net_igmp,test_igmp_verify_catch_join)429 ZTEST(net_igmp, test_igmp_verify_catch_join)
430 {
431 verify_join_group();
432 verify_leave_group();
433 }
434
socket_group_with_address(struct in_addr * local_addr,bool do_join)435 static void socket_group_with_address(struct in_addr *local_addr, bool do_join)
436 {
437 struct ip_mreqn mreqn = { 0 };
438 int option;
439 int ret, fd;
440
441 if (do_join) {
442 option = IP_ADD_MEMBERSHIP;
443 } else {
444 option = IP_DROP_MEMBERSHIP;
445 }
446
447 fd = zsock_socket(AF_INET, SOCK_DGRAM, 0);
448 zassert_true(fd >= 0, "Cannot get socket (%d)", -errno);
449
450 ret = zsock_setsockopt(fd, IPPROTO_IP, option,
451 NULL, sizeof(mreqn));
452 zassert_equal(ret, -1, "Incorrect return value (%d)", ret);
453 zassert_equal(errno, EINVAL, "Incorrect errno value (%d)", -errno);
454
455 ret = zsock_setsockopt(fd, IPPROTO_IP, option,
456 (void *)&mreqn, 1);
457 zassert_equal(ret, -1, "Incorrect return value (%d)", ret);
458 zassert_equal(errno, EINVAL, "Incorrect errno value (%d)", -errno);
459
460 /* First try with empty mreqn */
461 ret = zsock_setsockopt(fd, IPPROTO_IP, option,
462 (void *)&mreqn, sizeof(mreqn));
463 zassert_equal(ret, -1, "Incorrect return value (%d)", ret);
464 zassert_equal(errno, EINVAL, "Incorrect errno value (%d)", -errno);
465
466 memcpy(&mreqn.imr_address, local_addr, sizeof(mreqn.imr_address));
467 memcpy(&mreqn.imr_multiaddr, &mcast_addr, sizeof(mreqn.imr_multiaddr));
468
469 ret = zsock_setsockopt(fd, IPPROTO_IP, option,
470 (void *)&mreqn, sizeof(mreqn));
471
472 if (do_join) {
473 if (ignore_already) {
474 zassert_true(ret == 0 || ret == -EALREADY,
475 "Cannot join IPv4 multicast group (%d)", -errno);
476 } else {
477 zassert_ok(ret,
478 "Cannot join IPv4 multicast group (%d) "
479 "with local addr %s",
480 -errno, net_sprint_ipv4_addr(local_addr));
481 }
482 } else {
483 zassert_ok(ret, "Cannot leave IPv4 multicast group (%d)", -errno);
484
485 if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) {
486 /* Let the network stack to proceed */
487 k_msleep(THREAD_SLEEP);
488 } else {
489 k_yield();
490 }
491 }
492
493 zsock_close(fd);
494
495 /* Let the network stack to proceed */
496 k_msleep(THREAD_SLEEP);
497 }
498
socket_group_with_index(struct in_addr * local_addr,bool do_join)499 static void socket_group_with_index(struct in_addr *local_addr, bool do_join)
500 {
501 struct ip_mreqn mreqn = { 0 };
502 int option;
503 int ret, fd;
504
505 if (do_join) {
506 option = IP_ADD_MEMBERSHIP;
507 } else {
508 option = IP_DROP_MEMBERSHIP;
509 }
510
511 fd = zsock_socket(AF_INET, SOCK_DGRAM, 0);
512 zassert_true(fd >= 0, "Cannot get socket (%d)", -errno);
513
514 mreqn.imr_ifindex = net_if_ipv4_addr_lookup_by_index(local_addr);
515 memcpy(&mreqn.imr_multiaddr, &mcast_addr, sizeof(mreqn.imr_multiaddr));
516
517 ret = zsock_setsockopt(fd, IPPROTO_IP, option,
518 (void *)&mreqn, sizeof(mreqn));
519
520 if (do_join) {
521 if (ignore_already) {
522 zassert_true(ret == 0 || ret == -EALREADY,
523 "Cannot join IPv4 multicast group (%d)", -errno);
524 } else {
525 zassert_ok(ret, "Cannot join IPv4 multicast group (%d)", -errno);
526 }
527 } else {
528 zassert_ok(ret, "Cannot leave IPv4 multicast group (%d)", -errno);
529
530 if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) {
531 /* Let the network stack to proceed */
532 k_msleep(THREAD_SLEEP);
533 } else {
534 k_yield();
535 }
536 }
537
538 zsock_close(fd);
539
540 /* Let the network stack to proceed */
541 k_msleep(THREAD_SLEEP);
542 }
543
socket_join_group_with_address(struct in_addr * addr)544 static void socket_join_group_with_address(struct in_addr *addr)
545 {
546 socket_group_with_address(addr, true);
547 }
548
socket_leave_group_with_address(struct in_addr * addr)549 static void socket_leave_group_with_address(struct in_addr *addr)
550 {
551 socket_group_with_address(addr, false);
552 }
553
socket_join_group_with_index(struct in_addr * addr)554 static void socket_join_group_with_index(struct in_addr *addr)
555 {
556 socket_group_with_index(addr, true);
557 }
558
socket_leave_group_with_index(struct in_addr * addr)559 static void socket_leave_group_with_index(struct in_addr *addr)
560 {
561 socket_group_with_index(addr, false);
562 }
563
ZTEST_USER(net_igmp,test_socket_catch_join_with_address)564 ZTEST_USER(net_igmp, test_socket_catch_join_with_address)
565 {
566 socket_join_group_with_address(&any_addr);
567 socket_leave_group_with_address(&any_addr);
568 socket_join_group_with_address(&my_addr);
569 socket_leave_group_with_address(&my_addr);
570 }
571
ZTEST_USER(net_igmp,test_socket_catch_join_with_index)572 ZTEST_USER(net_igmp, test_socket_catch_join_with_index)
573 {
574 socket_join_group_with_index(&any_addr);
575 socket_leave_group_with_index(&any_addr);
576 socket_join_group_with_index(&my_addr);
577 socket_leave_group_with_index(&my_addr);
578 }
579
igmp_send_query(bool is_imgpv3)580 static void igmp_send_query(bool is_imgpv3)
581 {
582 struct net_pkt *pkt;
583
584 is_report_sent = false;
585 is_join_msg_ok = false;
586
587 is_igmpv2_query_sent = false;
588 is_igmpv3_query_sent = false;
589
590 /* Joining group first to get reply on query*/
591 join_group();
592
593 is_igmpv2_query_sent = !is_imgpv3;
594 is_igmpv3_query_sent = is_imgpv3;
595
596 pkt = prepare_igmp_query(net_iface, is_imgpv3);
597 zassert_not_null(pkt, "IGMPv2 query packet prep failed");
598
599 zassert_equal(net_ipv4_input(pkt, false), NET_OK, "Failed to send");
600
601 zassert_ok(k_sem_take(&wait_data, K_MSEC(WAIT_TIME)), "Timeout while waiting query event");
602
603 zassert_true(is_report_sent, "Did not catch query event");
604
605 zassert_true(is_join_msg_ok, "Join msg invalid");
606
607 is_igmpv2_query_sent = false;
608 is_igmpv3_query_sent = false;
609
610 leave_group();
611 }
612
ZTEST_USER(net_igmp,test_igmpv3_query)613 ZTEST_USER(net_igmp, test_igmpv3_query)
614 {
615 igmp_send_query(true);
616 }
617
ZTEST_USER(net_igmp,test_igmpv2_query)618 ZTEST_USER(net_igmp, test_igmpv2_query)
619 {
620 igmp_send_query(false);
621 }
622
ZTEST_USER(net_igmp,test_group_rejoin)623 ZTEST_USER(net_igmp, test_group_rejoin)
624 {
625 /* It is enough if this is tested with IGMPv2 only because we do not
626 * really care about specific IGMP version here.
627 */
628 if (IS_ENABLED(CONFIG_NET_IPV4_IGMPV3)) {
629 ztest_test_skip();
630 }
631
632 socket_join_group_with_index(&my_addr);
633
634 is_report_sent = false;
635
636 net_if_carrier_off(net_iface);
637 net_if_carrier_on(net_iface);
638
639 zassert_true(is_report_sent, "Did not catch query event");
640
641 socket_leave_group_with_index(&my_addr);
642 }
643
644 ZTEST_SUITE(net_igmp, NULL, igmp_setup, NULL, NULL, igmp_teardown);
645