1/*********************************************************************
2   PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
3   See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
4
5   RFC 1112, 2236, 3376, 3569, 3678, 4607
6
7   Authors: Kristof Roelants (IGMPv3), Simon Maes, Brecht Van Cauwenberghe
8 *********************************************************************/
9
10#include "pico_stack.h"
11#include "pico_ipv4.h"
12#include "pico_igmp.h"
13#include "pico_config.h"
14#include "pico_eth.h"
15#include "pico_addressing.h"
16#include "pico_frame.h"
17#include "pico_tree.h"
18#include "pico_device.h"
19#include "pico_socket.h"
20#include "pico_mcast.h"
21
22#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST)
23
24#ifdef DEBUG_IGMP
25    #define igmp_dbg dbg
26#else
27    #define igmp_dbg(...) do {} while(0)
28#endif
29
30/* membership states */
31#define IGMP_STATE_NON_MEMBER             (0x0)
32#define IGMP_STATE_DELAYING_MEMBER        (0x1)
33#define IGMP_STATE_IDLE_MEMBER            (0x2)
34
35/* events */
36#define IGMP_EVENT_DELETE_GROUP           (0x0)
37#define IGMP_EVENT_CREATE_GROUP           (0x1)
38#define IGMP_EVENT_UPDATE_GROUP           (0x2)
39#define IGMP_EVENT_QUERY_RECV             (0x3)
40#define IGMP_EVENT_REPORT_RECV            (0x4)
41#define IGMP_EVENT_TIMER_EXPIRED          (0x5)
42
43/* message types */
44#define IGMP_TYPE_MEM_QUERY               (0x11)
45#define IGMP_TYPE_MEM_REPORT_V1           (0x12)
46#define IGMP_TYPE_MEM_REPORT_V2           (0x16)
47#define IGMP_TYPE_LEAVE_GROUP             (0x17)
48#define IGMP_TYPE_MEM_REPORT_V3           (0x22)
49
50/* group record types */
51#define IGMP_MODE_IS_INCLUDE              (1)
52#define IGMP_MODE_IS_EXCLUDE              (2)
53#define IGMP_CHANGE_TO_INCLUDE_MODE       (3)
54#define IGMP_CHANGE_TO_EXCLUDE_MODE       (4)
55#define IGMP_ALLOW_NEW_SOURCES            (5)
56#define IGMP_BLOCK_OLD_SOURCES            (6)
57
58/* host flag */
59#define IGMP_HOST_LAST                    (0x1)
60#define IGMP_HOST_NOT_LAST                (0x0)
61
62/* list of timers, counters and their default values */
63#define IGMP_ROBUSTNESS                   (2u)
64#define IGMP_QUERY_INTERVAL               (125) /* secs */
65#define IGMP_QUERY_RESPONSE_INTERVAL      (10u) /* secs */
66#define IGMP_STARTUP_QUERY_INTERVAL       (IGMPV3_QUERY_INTERVAL / 4)
67#define IGMP_STARTUP_QUERY_COUNT          (IGMPV3_ROBUSTNESS)
68#define IGMP_LAST_MEMBER_QUERY_INTERVAL   (1) /* secs */
69#define IGMP_LAST_MEMBER_QUERY_COUNT      (IGMPV3_ROBUSTNESS)
70#define IGMP_UNSOLICITED_REPORT_INTERVAL  (1) /* secs */
71#define IGMP_DEFAULT_MAX_RESPONSE_TIME    (100)
72
73/* custom timers types */
74#define IGMP_TIMER_GROUP_REPORT           (1)
75#define IGMP_TIMER_V1_QUERIER             (2)
76#define IGMP_TIMER_V2_QUERIER             (3)
77
78/* IGMP groups */
79#define IGMP_ALL_HOST_GROUP               long_be(0xE0000001) /* 224.0.0.1 */
80#define IGMP_ALL_ROUTER_GROUP             long_be(0xE0000002) /* 224.0.0.2 */
81#define IGMPV3_ALL_ROUTER_GROUP           long_be(0xE0000016) /* 224.0.0.22 */
82
83/* misc */
84#define IGMP_TIMER_STOPPED                (1)
85#define IP_OPTION_ROUTER_ALERT_LEN        (4u)
86#define IGMP_MAX_GROUPS                   (32) /* max 255 */
87
88PACKED_STRUCT_DEF igmp_message {
89    uint8_t type;
90    uint8_t max_resp_time;
91    uint16_t crc;
92    uint32_t mcast_group;
93};
94
95PACKED_STRUCT_DEF igmpv3_query {
96    uint8_t type;
97    uint8_t max_resp_time;
98    uint16_t crc;
99    uint32_t mcast_group;
100    uint8_t rsq;
101    uint8_t qqic;
102    uint16_t sources;
103};
104
105PACKED_STRUCT_DEF igmpv3_group_record {
106    uint8_t type;
107    uint8_t aux;
108    uint16_t sources;
109    uint32_t mcast_group;
110};
111
112PACKED_STRUCT_DEF igmpv3_report {
113    uint8_t type;
114    uint8_t res0;
115    uint16_t crc;
116    uint16_t res1;
117    uint16_t groups;
118};
119
120struct igmp_timer {
121    uint8_t type;
122    uint8_t stopped;
123    pico_time start;
124    pico_time delay;
125    struct pico_ip4 mcast_link;
126    struct pico_ip4 mcast_group;
127    struct pico_frame *f;
128    void (*callback)(struct igmp_timer *t);
129};
130
131/* queues */
132static struct pico_queue igmp_in = {
133    0
134};
135static struct pico_queue igmp_out = {
136    0
137};
138
139/* finite state machine caller */
140static int pico_igmp_process_event(struct mcast_parameters *p);
141
142/* state callback prototype */
143typedef int (*callback)(struct mcast_parameters *);
144
145static inline int igmpt_type_compare(struct igmp_timer *a,  struct igmp_timer *b)
146{
147    if (a->type < b->type)
148        return -1;
149
150    if (a->type > b->type)
151        return 1;
152
153    return 0;
154}
155
156
157static inline int igmpt_group_compare(struct igmp_timer *a,  struct igmp_timer *b)
158{
159    return pico_ipv4_compare(&a->mcast_group, &b->mcast_group);
160}
161
162static inline int igmpt_link_compare(struct igmp_timer *a,  struct igmp_timer *b)
163{
164    return pico_ipv4_compare(&a->mcast_link, &b->mcast_link);
165}
166
167/* redblack trees */
168static int igmp_timer_cmp(void *ka, void *kb)
169{
170    struct igmp_timer *a = ka, *b = kb;
171    int cmp = igmpt_type_compare(a, b);
172    if (cmp)
173        return cmp;
174
175    cmp = igmpt_group_compare(a, b);
176    if (cmp)
177        return cmp;
178
179    return igmpt_link_compare(a, b);
180
181}
182static PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
183
184static inline int igmpparm_group_compare(struct mcast_parameters *a,  struct mcast_parameters *b)
185{
186    return pico_ipv4_compare(&a->mcast_group.ip4, &b->mcast_group.ip4);
187}
188
189static inline int igmpparm_link_compare(struct mcast_parameters *a,  struct mcast_parameters *b)
190{
191    return pico_ipv4_compare(&a->mcast_link.ip4, &b->mcast_link.ip4);
192}
193
194static int igmp_parameters_cmp(void *ka, void *kb)
195{
196    struct mcast_parameters *a = ka, *b = kb;
197    int cmp = igmpparm_group_compare(a, b);
198    if (cmp)
199        return cmp;
200
201    return igmpparm_link_compare(a, b);
202}
203static PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp);
204
205static int igmp_sources_cmp(void *ka, void *kb)
206{
207    struct pico_ip4 *a = ka, *b = kb;
208    return pico_ipv4_compare(a, b);
209}
210static PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
211static PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
212
213static struct mcast_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
214{
215    struct mcast_parameters test = {
216        0
217    };
218    if (!mcast_link || !mcast_group)
219        return NULL;
220
221    test.mcast_link.ip4 = *mcast_link;
222    test.mcast_group.ip4 = *mcast_group;
223    return pico_tree_findKey(&IGMPParameters, &test);
224}
225
226static int pico_igmp_delete_parameter(struct mcast_parameters *p)
227{
228    if (pico_tree_delete(&IGMPParameters, p))
229        PICO_FREE(p);
230    else
231        return -1;
232
233    return 0;
234}
235
236static void pico_igmp_timer_expired(pico_time now, void *arg)
237{
238    struct igmp_timer *t = NULL, *timer = NULL, test = {
239        0
240    };
241
242    IGNORE_PARAMETER(now);
243    t = (struct igmp_timer *)arg;
244    test.type = t->type;
245    test.mcast_link = t->mcast_link;
246    test.mcast_group = t->mcast_group;
247    igmp_dbg("IGMP: timer expired for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay);
248    timer = pico_tree_findKey(&IGMPTimers, &test);
249    if (!timer) {
250        return;
251    }
252
253    if (timer->stopped == IGMP_TIMER_STOPPED) {
254        pico_tree_delete(&IGMPTimers, timer);
255        PICO_FREE(t);
256        return;
257    }
258
259    if (timer->start + timer->delay < PICO_TIME_MS()) {
260        pico_tree_delete(&IGMPTimers, timer);
261        if (timer->callback)
262            timer->callback(timer);
263
264        PICO_FREE(timer);
265    } else {
266        igmp_dbg("IGMP: restart timer for %08X, delay %lu, new delay %lu\n", t->mcast_group.addr, t->delay,  (timer->start + timer->delay) - PICO_TIME_MS());
267        if (!pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer)) {
268            igmp_dbg("IGMP: Failed to start expiration timer\n");
269            pico_tree_delete(&IGMPTimers, timer);
270            PICO_FREE(timer);
271        }
272    }
273
274    return;
275}
276
277static int pico_igmp_timer_reset(struct igmp_timer *t)
278{
279    struct igmp_timer *timer = NULL, test = {
280        0
281    };
282
283    igmp_dbg("IGMP: reset timer for %08X, delay %lu\n", t->mcast_group.addr, t->delay);
284    test.type = t->type;
285    test.mcast_link = t->mcast_link;
286    test.mcast_group = t->mcast_group;
287    timer = pico_tree_findKey(&IGMPTimers, &test);
288    if (!timer)
289        return -1;
290
291    *timer = *t;
292    timer->start = PICO_TIME_MS();
293    return 0;
294}
295
296static int pico_igmp_timer_start(struct igmp_timer *t)
297{
298    struct igmp_timer *timer = NULL, test = {
299        0
300    };
301
302    igmp_dbg("IGMP: start timer for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay);
303    test.type = t->type;
304    test.mcast_link = t->mcast_link;
305    test.mcast_group = t->mcast_group;
306    timer = pico_tree_findKey(&IGMPTimers, &test);
307    if (timer)
308        return pico_igmp_timer_reset(t);
309
310    timer = PICO_ZALLOC(sizeof(struct igmp_timer));
311    if (!timer) {
312        pico_err = PICO_ERR_ENOMEM;
313        return -1;
314    }
315
316    *timer = *t;
317    timer->start = PICO_TIME_MS();
318
319    if (pico_tree_insert(&IGMPTimers, timer)) {
320        igmp_dbg("IGMP: Failed to insert timer in tree\n");
321        PICO_FREE(timer);
322		return -1;
323	}
324
325    if (!pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer)) {
326        igmp_dbg("IGMP: Failed to start expiration timer\n");
327        pico_tree_delete(&IGMPTimers, timer);
328        PICO_FREE(timer);
329        return -1;
330    }
331    return 0;
332}
333
334static int pico_igmp_timer_stop(struct igmp_timer *t)
335{
336    struct igmp_timer *timer = NULL, test = {
337        0
338    };
339
340    test.type = t->type;
341    test.mcast_link = t->mcast_link;
342    test.mcast_group = t->mcast_group;
343    timer = pico_tree_findKey(&IGMPTimers, &test);
344    if (!timer)
345        return -1;
346
347    igmp_dbg("IGMP: stop timer for %08X, delay %lu\n", timer->mcast_group.addr, timer->delay);
348    timer->stopped = IGMP_TIMER_STOPPED;
349    return 0;
350}
351
352static int pico_igmp_timer_is_running(struct igmp_timer *t)
353{
354    struct igmp_timer *timer = NULL, test = {
355        0
356    };
357
358    test.type = t->type;
359    test.mcast_link = t->mcast_link;
360    test.mcast_group = t->mcast_group;
361    timer = pico_tree_findKey(&IGMPTimers, &test);
362    if (timer)
363        return 1;
364
365    return 0;
366}
367
368static struct igmp_timer *pico_igmp_find_timer(uint8_t type, struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
369{
370    struct igmp_timer test = {
371        0
372    };
373
374    test.type = type;
375    test.mcast_link = *mcast_link;
376    test.mcast_group = *mcast_group;
377    return pico_tree_findKey(&IGMPTimers, &test);
378}
379
380static void pico_igmp_report_expired(struct igmp_timer *t)
381{
382    struct mcast_parameters *p = NULL;
383
384    p = pico_igmp_find_parameter(&t->mcast_link, &t->mcast_group);
385    if (!p)
386        return;
387
388    p->event = IGMP_EVENT_TIMER_EXPIRED;
389    pico_igmp_process_event(p);
390}
391
392static void pico_igmp_v2querier_expired(struct igmp_timer *t)
393{
394    struct pico_ipv4_link *link = NULL;
395    struct pico_tree_node *index = NULL, *_tmp = NULL;
396
397    link = pico_ipv4_link_by_dev(t->f->dev);
398    if (!link)
399        return;
400
401    /* When changing compatibility mode, cancel all pending response
402     * and retransmission timers.
403     */
404    pico_tree_foreach_safe(index, &IGMPTimers, _tmp)
405    {
406        ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
407        pico_tree_delete(&IGMPTimers, index->keyValue);
408    }
409    igmp_dbg("IGMP: switch to compatibility mode IGMPv3\n");
410    link->mcast_compatibility = PICO_IGMPV3;
411    return;
412}
413
414static int pico_igmp_is_checksum_valid(struct pico_frame *f)
415{
416    struct pico_ipv4_hdr *hdr = NULL;
417    uint8_t ihl = 24, datalen = 0;
418
419    hdr = (struct pico_ipv4_hdr *)f->net_hdr;
420    ihl = (uint8_t)((hdr->vhl & 0x0F) * 4); /* IHL is in 32bit words */
421    datalen = (uint8_t)(short_be(hdr->len) - ihl);
422
423    if (short_be(pico_checksum(f->transport_hdr, datalen)) == 0)
424        return 1;
425
426    igmp_dbg("IGMP: invalid checksum\n");
427    return 0;
428}
429
430/* RFC 3376 $7.1 */
431static int pico_igmp_compatibility_mode(struct pico_frame *f)
432{
433    struct pico_ipv4_hdr *hdr = NULL;
434    struct pico_ipv4_link *link = NULL;
435    struct pico_tree_node *index = NULL, *_tmp = NULL;
436    struct igmp_timer t = {
437        0
438    };
439    uint8_t ihl = 24, datalen = 0;
440    struct igmp_message *message = NULL;
441    struct mcast_parameters *p = NULL;
442    struct pico_ip4 mcast_group = {
443        0
444    };
445
446    link = pico_ipv4_link_by_dev(f->dev);
447    if (!link)
448        return -1;
449
450    hdr = (struct pico_ipv4_hdr *) f->net_hdr;
451    ihl = (uint8_t)((hdr->vhl & 0x0F) * 4); /* IHL is in 32bit words */
452    datalen = (uint8_t)(short_be(hdr->len) - ihl);
453    igmp_dbg("IGMP: IHL = %u, LEN = %u, OCTETS = %u\n", ihl, short_be(hdr->len), datalen);
454
455    if (datalen >= 12) {
456        /* IGMPv3 query */
457        t.type = IGMP_TIMER_V2_QUERIER;
458        if (pico_igmp_timer_is_running(&t)) { /* IGMPv2 querier present timer still running */
459            igmp_dbg("Timer is already running\n");
460            return -1;
461        } else {
462            link->mcast_compatibility = PICO_IGMPV3;
463            igmp_dbg("IGMP Compatibility: v3\n");
464            return 0;
465        }
466    } else if (datalen == 8) {
467        struct igmp_message *query = (struct igmp_message *)f->transport_hdr;
468        /* Check if max_resp_time is set RFC 3376 $7.1 */
469        if (query->max_resp_time != 0) {
470            /* IGMPv2 query */
471            /* When changing compatibility mode, cancel all pending response
472             * and retransmission timers.
473             */
474            pico_tree_foreach_safe(index, &IGMPTimers, _tmp)
475            {
476                ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
477                pico_tree_delete(&IGMPTimers, index->keyValue);
478            }
479            igmp_dbg("IGMP: switch to compatibility mode IGMPv2\n");
480            link->mcast_compatibility = PICO_IGMPV2;
481            /* Reset the event and state to prevent deadlock */
482            message = (struct igmp_message *)f->transport_hdr;
483            mcast_group.addr = message->mcast_group;
484            p = pico_igmp_find_parameter(&link->address, &mcast_group);
485            if(p) {
486                p->state = IGMP_STATE_NON_MEMBER;
487                p->event = IGMP_EVENT_CREATE_GROUP;
488            }
489
490            t.type = IGMP_TIMER_V2_QUERIER;
491            t.delay = ((IGMP_ROBUSTNESS * link->mcast_last_query_interval) + IGMP_QUERY_RESPONSE_INTERVAL) * 1000;
492            t.f = f;
493            t.callback = pico_igmp_v2querier_expired;
494            /* only one of this type of timer may exist! */
495            if (pico_igmp_timer_start(&t) < 0)
496                return -1;
497        } else {
498            /* IGMPv1 query, not supported */
499            return -1;
500        }
501    } else {
502        /* invalid query, silently ignored */
503        return -1;
504    }
505
506    return 0;
507}
508
509static struct mcast_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
510{
511    struct igmp_message *message = NULL;
512    struct mcast_parameters *p = NULL;
513    struct pico_ipv4_link *link = NULL;
514    struct pico_ip4 mcast_group = {
515        0
516    };
517
518    link = pico_ipv4_link_by_dev(f->dev);
519    if (!link)
520        return NULL;
521
522    /* IGMPv2 and IGMPv3 have a similar structure for the first 8 bytes */
523    message = (struct igmp_message *)f->transport_hdr;
524    mcast_group.addr = message->mcast_group;
525    p = pico_igmp_find_parameter(&link->address, &mcast_group);
526    if (!p && mcast_group.addr == 0) { /* general query */
527        p = PICO_ZALLOC(sizeof(struct mcast_parameters));
528        if (!p)
529            return NULL;
530
531        p->state = IGMP_STATE_NON_MEMBER;
532        p->mcast_link.ip4 = link->address;
533        p->mcast_group.ip4 = mcast_group;
534        if (pico_tree_insert(&IGMPParameters, p)) {
535            igmp_dbg("IGMP: Failed to insert parameters in tree\n");
536            PICO_FREE(p);
537    		return NULL;
538    	}
539    } else if (!p) {
540        return NULL;
541    }
542
543    switch (message->type) {
544    case IGMP_TYPE_MEM_QUERY:
545        p->event = IGMP_EVENT_QUERY_RECV;
546        break;
547    case IGMP_TYPE_MEM_REPORT_V1:
548        p->event = IGMP_EVENT_REPORT_RECV;
549        break;
550    case IGMP_TYPE_MEM_REPORT_V2:
551        p->event = IGMP_EVENT_REPORT_RECV;
552        break;
553    case IGMP_TYPE_MEM_REPORT_V3:
554        p->event = IGMP_EVENT_REPORT_RECV;
555        break;
556    default:
557        return NULL;
558    }
559    p->max_resp_time = message->max_resp_time; /* if IGMPv3 report this will be 0 (res0 field) */
560    p->f = f;
561
562    return p;
563}
564
565static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
566{
567    struct mcast_parameters *p = NULL;
568    IGNORE_PARAMETER(self);
569
570    if (!pico_igmp_is_checksum_valid(f))
571        goto out;
572
573    p = pico_igmp_analyse_packet(f);
574    if (!p)
575        goto out;
576
577    if (pico_igmp_compatibility_mode(f) < 0)
578        goto out;
579
580    return pico_igmp_process_event(p);
581
582out:
583    pico_frame_discard(f);
584    return 0;
585}
586
587static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f)
588{
589    /* packets are directly transferred to the IP layer by calling pico_ipv4_frame_push */
590    IGNORE_PARAMETER(self);
591    IGNORE_PARAMETER(f);
592    return 0;
593}
594
595/* Interface: protocol definition */
596struct pico_protocol pico_proto_igmp = {
597    .name = "igmp",
598    .proto_number = PICO_PROTO_IGMP,
599    .layer = PICO_LAYER_TRANSPORT,
600    .process_in = pico_igmp_process_in,
601    .process_out = pico_igmp_process_out,
602    .q_in = &igmp_in,
603    .q_out = &igmp_out,
604};
605
606int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *_MCASTFilter, uint8_t state)
607{
608    struct mcast_parameters *p = NULL;
609
610    if (!mcast_link || !mcast_group) {
611        pico_err = PICO_ERR_EINVAL;
612        return -1;
613    }
614
615    if (mcast_group->addr == IGMP_ALL_HOST_GROUP)
616        return 0;
617
618    p = pico_igmp_find_parameter(mcast_link, mcast_group);
619    if (!p && state == PICO_IGMP_STATE_CREATE) {
620        p = PICO_ZALLOC(sizeof(struct mcast_parameters));
621        if (!p) {
622            pico_err = PICO_ERR_ENOMEM;
623            return -1;
624        }
625
626        p->state = IGMP_STATE_NON_MEMBER;
627        p->mcast_link.ip4 = *mcast_link;
628        p->mcast_group.ip4 = *mcast_group;
629        if (pico_tree_insert(&IGMPParameters, p)) {
630            igmp_dbg("IGMP: Failed to insert parameters in tree\n");
631            PICO_FREE(p);
632			return -1;
633		}
634
635    } else if (!p) {
636        pico_err = PICO_ERR_EINVAL;
637        return -1;
638    }
639
640    switch (state) {
641    case PICO_IGMP_STATE_CREATE:
642        p->event = IGMP_EVENT_CREATE_GROUP;
643        break;
644
645    case PICO_IGMP_STATE_UPDATE:
646        p->event = IGMP_EVENT_UPDATE_GROUP;
647        break;
648
649    case PICO_IGMP_STATE_DELETE:
650        p->event = IGMP_EVENT_DELETE_GROUP;
651        break;
652
653    default:
654        return -1;
655    }
656    p->filter_mode = filter_mode;
657    p->MCASTFilter = _MCASTFilter;
658
659    return pico_igmp_process_event(p);
660}
661
662static int pico_igmp_send_report(struct mcast_parameters *p, struct pico_frame *f)
663{
664    struct pico_ip4 dst = {
665        0
666    };
667    struct pico_ip4 mcast_group = {
668        0
669    };
670    struct pico_ipv4_link *link = NULL;
671
672    link = pico_ipv4_link_get((struct pico_ip4*)&p->mcast_link);
673    if (!link)
674        return -1;
675
676    mcast_group = p->mcast_group.ip4;
677    switch (link->mcast_compatibility) {
678    case PICO_IGMPV2:
679        if (p->event == IGMP_EVENT_DELETE_GROUP)
680            dst.addr = IGMP_ALL_ROUTER_GROUP;
681        else
682            dst.addr = mcast_group.addr;
683
684        break;
685
686    case PICO_IGMPV3:
687        dst.addr = IGMPV3_ALL_ROUTER_GROUP;
688        break;
689
690    default:
691        pico_err = PICO_ERR_EPROTONOSUPPORT;
692        return -1;
693    }
694
695    igmp_dbg("IGMP: send membership report on group %08X to %08X\n", mcast_group.addr, dst.addr);
696    pico_ipv4_frame_push(f, &dst, PICO_PROTO_IGMP);
697    return 0;
698}
699static int8_t pico_igmpv3_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
700{
701    struct pico_mcast_group *g = NULL, test = {
702        0
703    };
704    struct pico_tree *IGMPFilter = NULL;
705    struct pico_ipv4_link *link = (struct pico_ipv4_link*) filter->link;
706    filter->p = (struct mcast_parameters *)p;
707    filter->allow = &IGMPAllow;
708    filter->block = &IGMPBlock;
709    filter->filter = IGMPFilter;
710    filter->sources = 0;
711    filter->proto = PICO_IGMPV3;
712    test.mcast_addr = p->mcast_group;
713    g = pico_tree_findKey(link->MCASTGroups, &test);
714    if (!g) {
715        pico_err = PICO_ERR_EINVAL;
716        return -1;
717    }
718
719    filter->g = (struct pico_mcast_group *)g;
720    return pico_mcast_generate_filter(filter, p);
721}
722static int8_t pico_igmpv3_generate_report(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
723{
724    struct igmpv3_report *report = NULL;
725    struct igmpv3_group_record *record = NULL;
726    struct pico_tree_node *index = NULL;
727    struct pico_device *dev = NULL;
728    uint16_t len = 0;
729    uint16_t i = 0;
730    len = (uint16_t)(sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (filter->sources * sizeof(struct pico_ip4)));
731    dev = pico_ipv4_link_find((struct pico_ip4 *)&p->mcast_link);
732    p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(IP_OPTION_ROUTER_ALERT_LEN + len));
733    p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
734    p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
735    p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
736    /* p->f->len is correctly set by alloc */
737
738    report = (struct igmpv3_report *)p->f->transport_hdr;
739    report->type = IGMP_TYPE_MEM_REPORT_V3;
740    report->res0 = 0;
741    report->crc = 0;
742    report->res1 = 0;
743    report->groups = short_be(1);
744
745    record = (struct igmpv3_group_record *)(((uint8_t *)report) + sizeof(struct igmpv3_report));
746    record->type = filter->record_type;
747    record->aux = 0;
748    record->sources = short_be(filter->sources);
749    record->mcast_group = p->mcast_group.ip4.addr;
750    if (filter->filter && !pico_tree_empty(filter->filter)) {
751        uint32_t *source_addr = (uint32_t *)((uint8_t *)record + sizeof(struct igmpv3_group_record));
752        i = 0;
753        pico_tree_foreach(index, filter->filter)
754        {
755            source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr;
756            i++;
757        }
758    }
759
760    if(i != filter->sources) {
761        return -1;
762    }
763
764    report->crc = short_be(pico_checksum(report, len));
765    return 0;
766}
767static int8_t pico_igmpv2_generate_report(struct mcast_parameters *p)
768{
769    struct igmp_message *report = NULL;
770    uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2;
771    struct pico_device *dev = NULL;
772    if (p->event == IGMP_EVENT_DELETE_GROUP)
773        report_type = IGMP_TYPE_LEAVE_GROUP;
774
775    dev = pico_ipv4_link_find((struct pico_ip4 *)&p->mcast_link);
776    p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message));
777    p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
778    p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
779    p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
780    /* p->f->len is correctly set by alloc */
781
782    report = (struct igmp_message *)p->f->transport_hdr;
783    report->type = report_type;
784    report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME;
785    report->mcast_group = p->mcast_group.ip4.addr;
786
787    report->crc = 0;
788    report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message)));
789    return 0;
790}
791static int8_t pico_igmp_generate_report(struct mcast_parameters *p)
792{
793    struct mcast_filter_parameters filter;
794    int8_t result;
795
796    filter.link = (union pico_link *)pico_ipv4_link_get((struct pico_ip4 *) &p->mcast_link);
797    if (!filter.link) {
798        pico_err = PICO_ERR_EINVAL;
799        return -1;
800    }
801
802    switch (filter.link->ipv4.mcast_compatibility) {
803    case PICO_IGMPV1:
804        pico_err = PICO_ERR_EPROTONOSUPPORT;
805        return -1;
806
807    case PICO_IGMPV2:
808    {
809        return pico_igmpv2_generate_report(p);
810    }
811    case PICO_IGMPV3:
812    {
813        result = pico_igmpv3_generate_filter(&filter, p);
814        if(result < 0)
815            return -1;
816
817        if(result != MCAST_NO_REPORT)
818            return pico_igmpv3_generate_report(&filter, p);
819    }
820    break;
821    default:
822        pico_err = PICO_ERR_EINVAL;
823        return -1;
824    }
825
826    return 0;
827}
828
829/* stop timer, send leave if flag set */
830static int stslifs(struct mcast_parameters *p)
831{
832    struct igmp_timer t = {
833        0
834    };
835
836    igmp_dbg("IGMP: event = leave group | action = stop timer, send leave if flag set\n");
837
838    t.type = IGMP_TIMER_GROUP_REPORT;
839    t.mcast_link = p->mcast_link.ip4;
840    t.mcast_group = p->mcast_group.ip4;
841    if (pico_igmp_timer_stop(&t) < 0)
842        return -1;
843
844    if(pico_igmp_generate_report(p) < 0)
845        return -1;
846    /* always send leave, even if not last host */
847    if (pico_igmp_send_report(p, p->f) < 0)
848        return -1;
849
850    pico_igmp_delete_parameter(p);
851    igmp_dbg("IGMP: new state = non-member\n");
852    return 0;
853}
854
855/* send report, set flag, start timer */
856static int srsfst(struct mcast_parameters *p)
857{
858    struct igmp_timer t = {
859        0
860    };
861    struct pico_frame *copy_frame = NULL;
862
863    igmp_dbg("IGMP: event = join group | action = send report, set flag, start timer\n");
864
865    p->last_host = IGMP_HOST_LAST;
866
867    if (pico_igmp_generate_report(p) < 0)
868        return -1;
869
870    if (!p->f)
871        return 0;
872
873    copy_frame = pico_frame_copy(p->f);
874    if (!copy_frame) {
875        pico_err = PICO_ERR_ENOMEM;
876        return -1;
877    }
878
879    if (pico_igmp_send_report(p, copy_frame) < 0)
880        return -1;
881
882    t.type = IGMP_TIMER_GROUP_REPORT;
883    t.mcast_link = p->mcast_link.ip4;
884    t.mcast_group = p->mcast_group.ip4;
885    t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
886    t.f = p->f;
887    t.callback = pico_igmp_report_expired;
888    if (pico_igmp_timer_start(&t) < 0)
889        return -1;
890
891    p->state = IGMP_STATE_DELAYING_MEMBER;
892    igmp_dbg("IGMP: new state = delaying member\n");
893    return 0;
894}
895
896/* merge report, send report, reset timer (IGMPv3 only) */
897static int mrsrrt(struct mcast_parameters *p)
898{
899    struct igmp_timer *t = NULL;
900    struct pico_frame *copy_frame = NULL;
901    struct pico_ipv4_link *link = NULL;
902
903    igmp_dbg("IGMP: event = update group | action = merge report, send report, reset timer (IGMPv3 only)\n");
904
905    link = pico_ipv4_link_get((struct pico_ip4 *)&p->mcast_link);
906    if (!link)
907        return -1;
908
909    if (link->mcast_compatibility != PICO_IGMPV3) {
910        igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
911        return -1;
912    }
913
914    /* XXX: merge with pending report rfc 3376 $5.1 */
915
916    copy_frame = pico_frame_copy(p->f);
917    if (!copy_frame)
918        return -1;
919
920    if (pico_igmp_send_report(p, copy_frame) < 0)
921        return -1;
922
923    t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link.ip4, &p->mcast_group.ip4);
924    if (!t)
925        return -1;
926
927    t->delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
928    pico_igmp_timer_reset(t);
929
930    p->state = IGMP_STATE_DELAYING_MEMBER;
931    igmp_dbg("IGMP: new state = delaying member\n");
932    return 0;
933}
934
935/* send report, start timer (IGMPv3 only) */
936static int srst(struct mcast_parameters *p)
937{
938    struct igmp_timer t = {
939        0
940    };
941    struct pico_frame *copy_frame = NULL;
942    struct pico_ipv4_link *link = NULL;
943
944    igmp_dbg("IGMP: event = update group | action = send report, start timer (IGMPv3 only)\n");
945
946    link = pico_ipv4_link_get(&p->mcast_link.ip4);
947    if (!link)
948        return -1;
949
950    if (link->mcast_compatibility != PICO_IGMPV3) {
951        igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
952        return -1;
953    }
954
955    if (pico_igmp_generate_report(p) < 0)
956        return -1;
957
958    if (!p->f)
959        return 0;
960
961    copy_frame = pico_frame_copy(p->f);
962    if (!copy_frame)
963        return -1;
964
965    if (pico_igmp_send_report(p, copy_frame) < 0)
966        return -1;
967
968    t.type = IGMP_TIMER_GROUP_REPORT;
969    t.mcast_link = p->mcast_link.ip4;
970    t.mcast_group = p->mcast_group.ip4;
971    t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
972    t.f = p->f;
973    t.callback = pico_igmp_report_expired;
974    if (pico_igmp_timer_start(&t) < 0)
975        return -1;
976
977    p->state = IGMP_STATE_DELAYING_MEMBER;
978    igmp_dbg("IGMP: new state = delaying member\n");
979    return 0;
980}
981
982/* send leave if flag set */
983static int slifs(struct mcast_parameters *p)
984{
985    igmp_dbg("IGMP: event = leave group | action = send leave if flag set\n");
986
987    /* always send leave, even if not last host */
988    if(pico_igmp_generate_report(p) < 0)
989        return -1;
990    if (pico_igmp_send_report(p, p->f) < 0)
991        return -1;
992
993    pico_igmp_delete_parameter(p);
994    igmp_dbg("IGMP: new state = non-member\n");
995    return 0;
996}
997
998/* start timer */
999static int st(struct mcast_parameters *p)
1000{
1001    struct igmp_timer t = {
1002        0
1003    };
1004
1005    igmp_dbg("IGMP: event = query received | action = start timer\n");
1006
1007    if (pico_igmp_generate_report(p) < 0) {
1008        igmp_dbg("Failed to generate report\n");
1009        return -1;
1010    }
1011
1012    if (!p->f) {
1013        igmp_dbg("No pending frame\n");
1014        return -1;
1015    }
1016
1017    t.type = IGMP_TIMER_GROUP_REPORT;
1018    t.mcast_link = p->mcast_link.ip4;
1019    t.mcast_group = p->mcast_group.ip4;
1020    t.delay = (pico_rand() % ((1u + p->max_resp_time) * 100u));
1021    t.f = p->f;
1022    t.callback = pico_igmp_report_expired;
1023    if (pico_igmp_timer_start(&t) < 0)
1024        return -1;
1025
1026    p->state = IGMP_STATE_DELAYING_MEMBER;
1027    igmp_dbg("IGMP: new state = delaying member\n");
1028    return 0;
1029}
1030
1031/* stop timer, clear flag */
1032static int stcl(struct mcast_parameters *p)
1033{
1034    struct igmp_timer t = {
1035        0
1036    };
1037
1038    igmp_dbg("IGMP: event = report received | action = stop timer, clear flag\n");
1039
1040    t.type = IGMP_TIMER_GROUP_REPORT;
1041    t.mcast_link = p->mcast_link.ip4;
1042    t.mcast_group = p->mcast_group.ip4;
1043    if (pico_igmp_timer_stop(&t) < 0)
1044        return -1;
1045
1046    p->last_host = IGMP_HOST_NOT_LAST;
1047    p->state = IGMP_STATE_IDLE_MEMBER;
1048    igmp_dbg("IGMP: new state = idle member\n");
1049    return 0;
1050}
1051
1052/* send report, set flag */
1053static int srsf(struct mcast_parameters *p)
1054{
1055    igmp_dbg("IGMP: event = timer expired | action = send report, set flag\n");
1056
1057    if (pico_igmp_send_report(p, p->f) < 0)
1058        return -1;
1059
1060    p->state = IGMP_STATE_IDLE_MEMBER;
1061    igmp_dbg("IGMP: new state = idle member\n");
1062    return 0;
1063}
1064
1065/* reset timer if max response time < current timer */
1066static int rtimrtct(struct mcast_parameters *p)
1067{
1068    struct igmp_timer *t = NULL;
1069    uint32_t time_to_run = 0;
1070
1071    igmp_dbg("IGMP: event = query received | action = reset timer if max response time < current timer\n");
1072
1073    t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link.ip4, &p->mcast_group.ip4);
1074    if (!t)
1075        return -1;
1076
1077    time_to_run = (uint32_t)(t->start + t->delay - PICO_TIME_MS());
1078    if ((p->max_resp_time * 100u) < time_to_run) { /* max_resp_time in units of 1/10 seconds */
1079        t->delay = pico_rand() % ((1u + p->max_resp_time) * 100u);
1080        pico_igmp_timer_reset(t);
1081    }
1082
1083    p->state = IGMP_STATE_DELAYING_MEMBER;
1084    igmp_dbg("IGMP: new state = delaying member\n");
1085    return 0;
1086}
1087
1088static int discard(struct mcast_parameters *p)
1089{
1090    igmp_dbg("IGMP: ignore and discard frame\n");
1091    pico_frame_discard(p->f);
1092    return 0;
1093}
1094
1095/* finite state machine table */
1096static const callback host_membership_diagram_table[3][6] =
1097{ /* event                    |Delete Group  |Create Group |Update Group |Query Received  |Report Received  |Timer Expired */
1098/* state Non-Member      */
1099    { discard,       srsfst,       srsfst,       discard,         discard,          discard },
1100/* state Delaying Member */ { stslifs,       mrsrrt,       mrsrrt,       rtimrtct,        stcl,             srsf    },
1101/* state Idle Member     */ { slifs,         srst,         srst,         st,              discard,          discard }
1102};
1103
1104static int pico_igmp_process_event(struct mcast_parameters *p)
1105{
1106    struct pico_tree_node *index = NULL;
1107    struct mcast_parameters *_p = NULL;
1108
1109    igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.ip4.addr);
1110    if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.ip4.addr == 0) { /* general query */
1111        pico_tree_foreach(index, &IGMPParameters) {
1112            _p = index->keyValue;
1113            _p->max_resp_time = p->max_resp_time;
1114            _p->event = IGMP_EVENT_QUERY_RECV;
1115            igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.ip4.addr, _p->state);
1116            host_membership_diagram_table[_p->state][_p->event](_p);
1117        }
1118    } else {
1119        igmp_dbg("IGMP: state = %u (0: non-member - 1: delaying member - 2: idle member)\n", p->state);
1120        host_membership_diagram_table[p->state][p->event](p);
1121    }
1122
1123    return 0;
1124}
1125
1126#else
1127static struct pico_queue igmp_in = {
1128    0
1129};
1130static struct pico_queue igmp_out = {
1131    0
1132};
1133
1134static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
1135{
1136    IGNORE_PARAMETER(self);
1137    IGNORE_PARAMETER(f);
1138    pico_err = PICO_ERR_EPROTONOSUPPORT;
1139    return -1;
1140}
1141
1142static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f)
1143{
1144    IGNORE_PARAMETER(self);
1145    IGNORE_PARAMETER(f);
1146    return -1;
1147}
1148
1149/* Interface: protocol definition */
1150struct pico_protocol pico_proto_igmp = {
1151    .name = "igmp",
1152    .proto_number = PICO_PROTO_IGMP,
1153    .layer = PICO_LAYER_TRANSPORT,
1154    .process_in = pico_igmp_process_in,
1155    .process_out = pico_igmp_process_out,
1156    .q_in = &igmp_in,
1157    .q_out = &igmp_out,
1158};
1159
1160int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *_MCASTFilter, uint8_t state)
1161{
1162    IGNORE_PARAMETER(mcast_link);
1163    IGNORE_PARAMETER(mcast_group);
1164    IGNORE_PARAMETER(filter_mode);
1165    IGNORE_PARAMETER(_MCASTFilter);
1166    IGNORE_PARAMETER(state);
1167    pico_err = PICO_ERR_EPROTONOSUPPORT;
1168    return -1;
1169}
1170#endif
1171