Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pico_igmp.c Source File

pico_igmp.c

00001 /*********************************************************************
00002    PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
00003    See LICENSE and COPYING for usage.
00004 
00005    RFC 1112, 2236, 3376, 3569, 3678, 4607
00006 
00007    Authors: Kristof Roelants (IGMPv3), Simon Maes, Brecht Van Cauwenberghe
00008  *********************************************************************/
00009 
00010 #include "pico_stack.h"
00011 #include "pico_ipv4.h"
00012 #include "pico_igmp.h"
00013 #include "pico_config.h"
00014 #include "pico_eth.h"
00015 #include "pico_addressing.h"
00016 #include "pico_frame.h"
00017 #include "pico_tree.h"
00018 #include "pico_device.h"
00019 #include "pico_socket.h"
00020 
00021 #if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST) 
00022 
00023 #define igmp_dbg(...) do {} while(0)
00024 /* #define igmp_dbg dbg */
00025 
00026 /* membership states */
00027 #define IGMP_STATE_NON_MEMBER             (0x0)
00028 #define IGMP_STATE_DELAYING_MEMBER        (0x1)
00029 #define IGMP_STATE_IDLE_MEMBER            (0x2)
00030 
00031 /* events */
00032 #define IGMP_EVENT_DELETE_GROUP           (0x0)
00033 #define IGMP_EVENT_CREATE_GROUP           (0x1)
00034 #define IGMP_EVENT_UPDATE_GROUP           (0x2)
00035 #define IGMP_EVENT_QUERY_RECV             (0x3)
00036 #define IGMP_EVENT_REPORT_RECV            (0x4)
00037 #define IGMP_EVENT_TIMER_EXPIRED          (0x5)
00038 
00039 /* message types */
00040 #define IGMP_TYPE_MEM_QUERY               (0x11)
00041 #define IGMP_TYPE_MEM_REPORT_V1           (0x12)
00042 #define IGMP_TYPE_MEM_REPORT_V2           (0x16)
00043 #define IGMP_TYPE_LEAVE_GROUP             (0x17)
00044 #define IGMP_TYPE_MEM_REPORT_V3           (0x22)
00045 
00046 /* group record types */
00047 #define IGMP_MODE_IS_INCLUDE              (1)
00048 #define IGMP_MODE_IS_EXCLUDE              (2)
00049 #define IGMP_CHANGE_TO_INCLUDE_MODE       (3)
00050 #define IGMP_CHANGE_TO_EXCLUDE_MODE       (4)
00051 #define IGMP_ALLOW_NEW_SOURCES            (5)
00052 #define IGMP_BLOCK_OLD_SOURCES            (6)
00053 
00054 /* host flag */
00055 #define IGMP_HOST_LAST                    (0x1)
00056 #define IGMP_HOST_NOT_LAST                (0x0)
00057 
00058 /* list of timers, counters and their default values */
00059 #define IGMP_ROBUSTNESS                   (2u)
00060 #define IGMP_QUERY_INTERVAL               (125) /* secs */
00061 #define IGMP_QUERY_RESPONSE_INTERVAL      (10u) /* secs */
00062 #define IGMP_STARTUP_QUERY_INTERVAL       (IGMPV3_QUERY_INTERVAL / 4)
00063 #define IGMP_STARTUP_QUERY_COUNT          (IGMPV3_ROBUSTNESS)
00064 #define IGMP_LAST_MEMBER_QUERY_INTERVAL   (1) /* secs */
00065 #define IGMP_LAST_MEMBER_QUERY_COUNT      (IGMPV3_ROBUSTNESS)
00066 #define IGMP_UNSOLICITED_REPORT_INTERVAL  (1) /* secs */
00067 #define IGMP_DEFAULT_MAX_RESPONSE_TIME    (100)
00068 
00069 /* custom timers types */
00070 #define IGMP_TIMER_GROUP_REPORT           (1)
00071 #define IGMP_TIMER_V1_QUERIER             (2)
00072 #define IGMP_TIMER_V2_QUERIER             (3)
00073 
00074 /* IGMP groups */
00075 #define IGMP_ALL_HOST_GROUP               long_be(0xE0000001) /* 224.0.0.1 */
00076 #define IGMP_ALL_ROUTER_GROUP             long_be(0xE0000002) /* 224.0.0.2 */
00077 #define IGMPV3_ALL_ROUTER_GROUP           long_be(0xE0000016) /* 224.0.0.22 */
00078 
00079 /* misc */
00080 #define IGMP_TIMER_STOPPED                (1)
00081 #define IP_OPTION_ROUTER_ALERT_LEN        (4u)
00082 #define IGMP_MAX_GROUPS                   (32) /* max 255 */
00083 
00084 PACKED_STRUCT_DEF igmp_message {
00085     uint8_t type;
00086     uint8_t max_resp_time;
00087     uint16_t crc;
00088     uint32_t mcast_group;
00089 };
00090 
00091 PACKED_STRUCT_DEF igmpv3_query {
00092     uint8_t type;
00093     uint8_t max_resp_time;
00094     uint16_t crc;
00095     uint32_t mcast_group;
00096     uint8_t rsq;
00097     uint8_t qqic;
00098     uint16_t sources;
00099 };
00100 
00101 PACKED_STRUCT_DEF igmpv3_group_record {
00102     uint8_t type;
00103     uint8_t aux;
00104     uint16_t sources;
00105     uint32_t mcast_group;
00106 };
00107 
00108 PACKED_STRUCT_DEF igmpv3_report {
00109     uint8_t type;
00110     uint8_t res0;
00111     uint16_t crc;
00112     uint16_t res1;
00113     uint16_t groups;
00114 };
00115 
00116 struct igmp_parameters {
00117     uint8_t event;
00118     uint8_t state;
00119     uint8_t last_host;
00120     uint8_t filter_mode;
00121     uint8_t max_resp_time;
00122     struct pico_ip4 mcast_link;
00123     struct pico_ip4 mcast_group;
00124     struct pico_tree *MCASTFilter;
00125     struct pico_frame *f;
00126 };
00127 
00128 struct igmp_timer {
00129     uint8_t type;
00130     uint8_t stopped;
00131     pico_time start;
00132     pico_time delay;
00133     struct pico_ip4 mcast_link;
00134     struct pico_ip4 mcast_group;
00135     struct pico_frame *f;
00136     void (*callback)(struct igmp_timer *t);
00137 };
00138 
00139 /* queues */
00140 static struct pico_queue igmp_in = {
00141     0
00142 };
00143 static struct pico_queue igmp_out = {
00144     0
00145 };
00146 
00147 /* finite state machine caller */
00148 static int pico_igmp_process_event(struct igmp_parameters *p);
00149 
00150 /* state callback prototype */
00151 typedef int (*callback)(struct igmp_parameters *);
00152 
00153 static inline int igmpt_type_compare(struct igmp_timer *a,  struct igmp_timer *b)
00154 {
00155     if (a->type < b->type)
00156         return -1;
00157 
00158     if (a->type > b->type)
00159         return 1;
00160 
00161     return 0;
00162 }
00163 
00164 
00165 static inline int igmpt_group_compare(struct igmp_timer *a,  struct igmp_timer *b)
00166 {
00167     return pico_ipv4_compare(&a->mcast_group, &b->mcast_group);
00168 }
00169 
00170 static inline int igmpt_link_compare(struct igmp_timer *a,  struct igmp_timer *b)
00171 {
00172     return pico_ipv4_compare(&a->mcast_link, &b->mcast_link);
00173 }
00174 
00175 /* redblack trees */
00176 static int igmp_timer_cmp(void *ka, void *kb)
00177 {
00178     struct igmp_timer *a = ka, *b = kb;
00179     int cmp = igmpt_type_compare(a, b);
00180     if (cmp)
00181         return cmp;
00182 
00183     cmp = igmpt_group_compare(a, b);
00184     if (cmp)
00185         return cmp;
00186 
00187     return igmpt_link_compare(a, b);
00188 
00189 }
00190 PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
00191 
00192 static inline int igmpparm_group_compare(struct igmp_parameters *a,  struct igmp_parameters *b)
00193 {
00194     return pico_ipv4_compare(&a->mcast_group, &b->mcast_group);
00195 }
00196 
00197 static inline int igmpparm_link_compare(struct igmp_parameters *a,  struct igmp_parameters *b)
00198 {
00199     return pico_ipv4_compare(&a->mcast_link, &b->mcast_link);
00200 }
00201 
00202 static int igmp_parameters_cmp(void *ka, void *kb)
00203 {
00204     struct igmp_parameters *a = ka, *b = kb;
00205     int cmp = igmpparm_group_compare(a, b);
00206     if (cmp)
00207         return cmp;
00208 
00209     return igmpparm_link_compare(a, b);
00210 }
00211 PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp);
00212 
00213 static int igmp_sources_cmp(void *ka, void *kb)
00214 {
00215     struct pico_ip4 *a = ka, *b = kb;
00216     return pico_ipv4_compare(a, b);
00217 }
00218 PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
00219 PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
00220 
00221 static struct igmp_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
00222 {
00223     struct igmp_parameters test = {
00224         0
00225     };
00226     if (!mcast_link || !mcast_group)
00227         return NULL;
00228 
00229     test.mcast_link.addr = mcast_link->addr;
00230     test.mcast_group.addr = mcast_group->addr;
00231     return pico_tree_findKey(&IGMPParameters, &test);
00232 }
00233 
00234 static int pico_igmp_delete_parameter(struct igmp_parameters *p)
00235 {
00236     if (pico_tree_delete(&IGMPParameters, p))
00237         PICO_FREE(p);
00238     else
00239         return -1;
00240 
00241     return 0;
00242 }
00243 
00244 static void pico_igmp_timer_expired(pico_time now, void *arg)
00245 {
00246     struct igmp_timer *t = NULL, *timer = NULL, test = {
00247         0
00248     };
00249 
00250     IGNORE_PARAMETER(now);
00251     t = (struct igmp_timer *)arg;
00252     test.type = t->type;
00253     test.mcast_link = t->mcast_link;
00254     test.mcast_group = t->mcast_group;
00255     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);
00256     timer = pico_tree_findKey(&IGMPTimers, &test);
00257     if (!timer) {
00258         return;
00259     }
00260 
00261     if (timer->stopped == IGMP_TIMER_STOPPED) {
00262         PICO_FREE(t);
00263         return;
00264     }
00265 
00266     if (timer->start + timer->delay < PICO_TIME_MS()) {
00267         pico_tree_delete(&IGMPTimers, timer);
00268         if (timer->callback)
00269             timer->callback(timer);
00270 
00271         PICO_FREE(timer);
00272     } else {
00273         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());
00274         pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer);
00275     }
00276 
00277     return;
00278 }
00279 
00280 static int pico_igmp_timer_reset(struct igmp_timer *t)
00281 {
00282     struct igmp_timer *timer = NULL, test = {
00283         0
00284     };
00285 
00286     igmp_dbg("IGMP: reset timer for %08X, delay %lu\n", t->mcast_group.addr, t->delay);
00287     test.type = t->type;
00288     test.mcast_link = t->mcast_link;
00289     test.mcast_group = t->mcast_group;
00290     timer = pico_tree_findKey(&IGMPTimers, &test);
00291     if (!timer)
00292         return -1;
00293 
00294     *timer = *t;
00295     timer->start = PICO_TIME_MS();
00296     return 0;
00297 }
00298 
00299 static int pico_igmp_timer_start(struct igmp_timer *t)
00300 {
00301     struct igmp_timer *timer = NULL, test = {
00302         0
00303     };
00304 
00305     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);
00306     test.type = t->type;
00307     test.mcast_link = t->mcast_link;
00308     test.mcast_group = t->mcast_group;
00309     timer = pico_tree_findKey(&IGMPTimers, &test);
00310     if (timer)
00311         return pico_igmp_timer_reset(t);
00312 
00313     timer = PICO_ZALLOC(sizeof(struct igmp_timer));
00314     if (!timer) {
00315         pico_err = PICO_ERR_ENOMEM;
00316         return -1;
00317     }
00318 
00319     *timer = *t;
00320     timer->start = PICO_TIME_MS();
00321 
00322     pico_tree_insert(&IGMPTimers, timer);
00323     pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer);
00324     return 0;
00325 }
00326 
00327 static int pico_igmp_timer_stop(struct igmp_timer *t)
00328 {
00329     struct igmp_timer *timer = NULL, test = {
00330         0
00331     };
00332 
00333     test.type = t->type;
00334     test.mcast_link = t->mcast_link;
00335     test.mcast_group = t->mcast_group;
00336     timer = pico_tree_findKey(&IGMPTimers, &test);
00337     if (!timer)
00338         return 0;
00339 
00340     igmp_dbg("IGMP: stop timer for %08X, delay %lu\n", timer->mcast_group.addr, timer->delay);
00341     timer->stopped = IGMP_TIMER_STOPPED;
00342     return 0;
00343 }
00344 
00345 static int pico_igmp_timer_is_running(struct igmp_timer *t)
00346 {
00347     struct igmp_timer *timer = NULL, test = {
00348         0
00349     };
00350 
00351     test.type = t->type;
00352     test.mcast_link = t->mcast_link;
00353     test.mcast_group = t->mcast_group;
00354     timer = pico_tree_findKey(&IGMPTimers, &test);
00355     if (timer)
00356         return 1;
00357 
00358     return 0;
00359 }
00360 
00361 static struct igmp_timer *pico_igmp_find_timer(uint8_t type, struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
00362 {
00363     struct igmp_timer test = {
00364         0
00365     };
00366 
00367     test.type = type;
00368     test.mcast_link = *mcast_link;
00369     test.mcast_group = *mcast_group;
00370     return pico_tree_findKey(&IGMPTimers, &test);
00371 }
00372 
00373 static void pico_igmp_report_expired(struct igmp_timer *t)
00374 {
00375     struct igmp_parameters *p = NULL;
00376 
00377     p = pico_igmp_find_parameter(&t->mcast_link, &t->mcast_group);
00378     if (!p)
00379         return;
00380 
00381     p->event = IGMP_EVENT_TIMER_EXPIRED;
00382     pico_igmp_process_event(p);
00383 }
00384 
00385 static void pico_igmp_v2querier_expired(struct igmp_timer *t)
00386 {
00387     struct pico_ipv4_link *link = NULL;
00388     struct pico_tree_node *index = NULL, *_tmp = NULL;
00389 
00390     link = pico_ipv4_link_by_dev(t->f->dev);
00391     if (!link)
00392         return;
00393 
00394     /* When changing compatibility mode, cancel all pending response
00395      * and retransmission timers.
00396      */
00397     pico_tree_foreach_safe(index, &IGMPTimers, _tmp)
00398     {
00399         ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
00400         pico_tree_delete(&IGMPTimers, index->keyValue);
00401     }
00402     igmp_dbg("IGMP: switch to compatibility mode IGMPv3\n");
00403     link->mcast_compatibility = PICO_IGMPV3;
00404     return;
00405 }
00406 
00407 static int pico_igmp_is_checksum_valid(struct pico_frame *f)
00408 {
00409     struct pico_ipv4_hdr *hdr = NULL;
00410     uint8_t ihl = 24, datalen = 0;
00411 
00412     hdr = (struct pico_ipv4_hdr *)f->net_hdr;
00413     ihl = (uint8_t)((hdr->vhl & 0x0F) * 4); /* IHL is in 32bit words */
00414     datalen = (uint8_t)(short_be(hdr->len) - ihl);
00415 
00416     if (short_be(pico_checksum(f->transport_hdr, datalen)) == 0)
00417         return 1;
00418 
00419     igmp_dbg("IGMP: invalid checksum\n");
00420     return 0;
00421 }
00422 
00423 /* RFC 3376 $7.1 */
00424 static int pico_igmp_compatibility_mode(struct pico_frame *f)
00425 {
00426     struct pico_ipv4_hdr *hdr = NULL;
00427     struct pico_ipv4_link *link = NULL;
00428     struct pico_tree_node *index = NULL, *_tmp = NULL;
00429     struct igmp_timer t = {
00430         0
00431     };
00432     uint8_t ihl = 24, datalen = 0;
00433 
00434     link = pico_ipv4_link_by_dev(f->dev);
00435     if (!link)
00436         return -1;
00437 
00438     hdr = (struct pico_ipv4_hdr *) f->net_hdr;
00439     ihl = (uint8_t)((hdr->vhl & 0x0F) * 4); /* IHL is in 32bit words */
00440     datalen = (uint8_t)(short_be(hdr->len) - ihl);
00441     igmp_dbg("IGMP: IHL = %u, LEN = %u, OCTETS = %u\n", ihl, short_be(hdr->len), datalen);
00442 
00443     if (datalen >= 12) {
00444         /* IGMPv3 query */
00445         t.type = IGMP_TIMER_V2_QUERIER;
00446         if (pico_igmp_timer_is_running(&t)) { /* IGMPv2 querier present timer still running */
00447             igmp_dbg("Timer is already running\n");
00448             return -1;
00449         } else {
00450             link->mcast_compatibility = PICO_IGMPV3;
00451             igmp_dbg("IGMP Compatibility: v3\n");
00452             return 0;
00453         }
00454     } else if (datalen == 8) {
00455         struct igmp_message *query = (struct igmp_message *)f->transport_hdr;
00456         if (query->max_resp_time != 0) {
00457             /* IGMPv2 query */
00458             /* When changing compatibility mode, cancel all pending response
00459              * and retransmission timers.
00460              */
00461             pico_tree_foreach_safe(index, &IGMPTimers, _tmp)
00462             {
00463                 ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
00464                 pico_tree_delete(&IGMPTimers, index->keyValue);
00465             }
00466             igmp_dbg("IGMP: switch to compatibility mode IGMPv2\n");
00467             link->mcast_compatibility = PICO_IGMPV2;
00468             t.type = IGMP_TIMER_V2_QUERIER;
00469             t.delay = ((IGMP_ROBUSTNESS * link->mcast_last_query_interval) + IGMP_QUERY_RESPONSE_INTERVAL) * 1000;
00470             t.f = f;
00471             t.callback = pico_igmp_v2querier_expired;
00472             /* only one of this type of timer may exist! */
00473             pico_igmp_timer_start(&t);
00474         } else {
00475             /* IGMPv1 query, not supported */
00476             return -1;
00477         }
00478     } else {
00479         /* invalid query, silently ignored */
00480         return -1;
00481     }
00482 
00483     return 0;
00484 }
00485 
00486 static struct igmp_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
00487 {
00488     struct igmp_message *message = NULL;
00489     struct igmp_parameters *p = NULL;
00490     struct pico_ipv4_link *link = NULL;
00491     struct pico_ip4 mcast_group = {
00492         0
00493     };
00494 
00495     link = pico_ipv4_link_by_dev(f->dev);
00496     if (!link)
00497         return NULL;
00498 
00499     /* IGMPv2 and IGMPv3 have a similar structure for the first 8 bytes */
00500     message = (struct igmp_message *)f->transport_hdr;
00501     mcast_group.addr = message->mcast_group;
00502     p = pico_igmp_find_parameter(&link->address, &mcast_group);
00503     if (!p && mcast_group.addr == 0) { /* general query */
00504         p = PICO_ZALLOC(sizeof(struct igmp_parameters));
00505         if (!p)
00506             return NULL;
00507 
00508         p->state = IGMP_STATE_NON_MEMBER;
00509         p->mcast_link.addr = link->address.addr;
00510         p->mcast_group.addr = mcast_group.addr;
00511         pico_tree_insert(&IGMPParameters, p);
00512     } else if (!p) {
00513         return NULL;
00514     }
00515 
00516     switch (message->type) {
00517     case IGMP_TYPE_MEM_QUERY:
00518         p->event = IGMP_EVENT_QUERY_RECV;
00519         break;
00520     case IGMP_TYPE_MEM_REPORT_V1:
00521         p->event = IGMP_EVENT_REPORT_RECV;
00522         break;
00523     case IGMP_TYPE_MEM_REPORT_V2:
00524         p->event = IGMP_EVENT_REPORT_RECV;
00525         break;
00526     case IGMP_TYPE_MEM_REPORT_V3:
00527         p->event = IGMP_EVENT_REPORT_RECV;
00528         break;
00529     default:
00530         return NULL;
00531     }
00532     p->max_resp_time = message->max_resp_time; /* if IGMPv3 report this will be 0 (res0 field) */
00533     p->f = f;
00534 
00535     return p;
00536 }
00537 
00538 static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
00539 {
00540     struct igmp_parameters *p = NULL;
00541     IGNORE_PARAMETER(self);
00542 
00543     if (!pico_igmp_is_checksum_valid(f))
00544         goto out;
00545 
00546     if (pico_igmp_compatibility_mode(f) < 0)
00547         goto out;
00548 
00549     p = pico_igmp_analyse_packet(f);
00550     if (!p)
00551         goto out;
00552 
00553     return pico_igmp_process_event(p);
00554 
00555 out:
00556     pico_frame_discard(f);
00557     return 0;
00558 }
00559 
00560 static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f)
00561 {
00562     /* packets are directly transferred to the IP layer by calling pico_ipv4_frame_push */
00563     IGNORE_PARAMETER(self);
00564     IGNORE_PARAMETER(f);
00565     return 0;
00566 }
00567 
00568 /* Interface: protocol definition */
00569 struct pico_protocol pico_proto_igmp = {
00570     .name = "igmp",
00571     .proto_number = PICO_PROTO_IGMP,
00572     .layer = PICO_LAYER_TRANSPORT,
00573     .process_in = pico_igmp_process_in,
00574     .process_out = pico_igmp_process_out,
00575     .q_in = &igmp_in,
00576     .q_out = &igmp_out,
00577 };
00578 
00579 int 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)
00580 {
00581     struct igmp_parameters *p = NULL;
00582 
00583     if (mcast_group->addr == IGMP_ALL_HOST_GROUP)
00584         return 0;
00585 
00586     p = pico_igmp_find_parameter(mcast_link, mcast_group);
00587     if (!p && state == PICO_IGMP_STATE_CREATE) {
00588         p = PICO_ZALLOC(sizeof(struct igmp_parameters));
00589         if (!p) {
00590             pico_err = PICO_ERR_ENOMEM;
00591             return -1;
00592         }
00593 
00594         if (!mcast_link || !mcast_group) {
00595             pico_err = PICO_ERR_EINVAL;
00596             return -1;
00597         }
00598 
00599         p->state = IGMP_STATE_NON_MEMBER;
00600         p->mcast_link = *mcast_link;
00601         p->mcast_group = *mcast_group;
00602         pico_tree_insert(&IGMPParameters, p);
00603     } else if (!p) {
00604         pico_err = PICO_ERR_EINVAL;
00605         return -1;
00606     }
00607 
00608     switch (state) {
00609     case PICO_IGMP_STATE_CREATE:
00610         p->event = IGMP_EVENT_CREATE_GROUP;
00611         break;
00612 
00613     case PICO_IGMP_STATE_UPDATE:
00614         p->event = IGMP_EVENT_UPDATE_GROUP;
00615         break;
00616 
00617     case PICO_IGMP_STATE_DELETE:
00618         p->event = IGMP_EVENT_DELETE_GROUP;
00619         break;
00620 
00621     default:
00622         return -1;
00623     }
00624     p->filter_mode = filter_mode;
00625     p->MCASTFilter = _MCASTFilter;
00626 
00627     return pico_igmp_process_event(p);
00628 }
00629 
00630 static int pico_igmp_send_report(struct igmp_parameters *p, struct pico_frame *f)
00631 {
00632     struct pico_ip4 dst = {
00633         0
00634     };
00635     struct pico_ip4 mcast_group = {
00636         0
00637     };
00638     struct pico_ipv4_link *link = NULL;
00639 
00640     link = pico_ipv4_link_get(&p->mcast_link);
00641     if (!link)
00642         return -1;
00643 
00644     mcast_group.addr = p->mcast_group.addr;
00645     switch (link->mcast_compatibility) {
00646     case PICO_IGMPV2:
00647         if (p->event == IGMP_EVENT_DELETE_GROUP)
00648             dst.addr = IGMP_ALL_ROUTER_GROUP;
00649         else
00650             dst.addr = mcast_group.addr;
00651 
00652         break;
00653 
00654     case PICO_IGMPV3:
00655         dst.addr = IGMPV3_ALL_ROUTER_GROUP;
00656         break;
00657 
00658     default:
00659         pico_err = PICO_ERR_EPROTONOSUPPORT;
00660         return -1;
00661     }
00662 
00663     igmp_dbg("IGMP: send membership report on group %08X to %08X\n", mcast_group.addr, dst.addr);
00664     pico_ipv4_frame_push(f, &dst, PICO_PROTO_IGMP);
00665     return 0;
00666 }
00667 
00668 static int8_t pico_igmp_generate_report(struct igmp_parameters *p)
00669 {
00670     struct pico_ipv4_link *link = NULL;
00671     int i = 0;
00672 
00673     link = pico_ipv4_link_get(&p->mcast_link);
00674     if (!link) {
00675         pico_err = PICO_ERR_EINVAL;
00676         return -1;
00677     }
00678 
00679     switch (link->mcast_compatibility) {
00680     case PICO_IGMPV1:
00681         pico_err = PICO_ERR_EPROTONOSUPPORT;
00682         return -1;
00683 
00684     case PICO_IGMPV2:
00685     {
00686         struct igmp_message *report = NULL;
00687         uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2;
00688         if (p->event == IGMP_EVENT_DELETE_GROUP)
00689             report_type = IGMP_TYPE_LEAVE_GROUP;
00690 
00691         p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message));
00692         p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
00693         p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
00694         p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
00695         p->f->dev = pico_ipv4_link_find(&p->mcast_link);
00696         /* p->f->len is correctly set by alloc */
00697 
00698         report = (struct igmp_message *)p->f->transport_hdr;
00699         report->type = report_type;
00700         report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME;
00701         report->mcast_group = p->mcast_group.addr;
00702 
00703         report->crc = 0;
00704         report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message)));
00705         break;
00706     }
00707     case PICO_IGMPV3:
00708     {
00709         struct igmpv3_report *report = NULL;
00710         struct igmpv3_group_record *record = NULL;
00711         struct pico_mcast_group *g = NULL, test = {
00712             0
00713         };
00714         struct pico_tree_node *index = NULL, *_tmp = NULL;
00715         struct pico_tree *IGMPFilter = NULL;
00716         struct pico_ip4 *source = NULL;
00717         uint8_t record_type = 0;
00718         uint8_t sources = 0;
00719         uint16_t len = 0;
00720 
00721         test.mcast_addr = p->mcast_group;
00722         g = pico_tree_findKey(link->MCASTGroups, &test);
00723         if (!g) {
00724             pico_err = PICO_ERR_EINVAL;
00725             return -1;
00726         }
00727 
00728         if (p->event == IGMP_EVENT_DELETE_GROUP) { /* "non-existent" state of filter mode INCLUDE and empty source list */
00729             p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
00730             p->MCASTFilter = NULL;
00731         }
00732 
00733         if (p->event == IGMP_EVENT_QUERY_RECV) {
00734             goto igmp3_report;
00735         }
00736 
00737 
00738         /* cleanup filters */
00739         pico_tree_foreach_safe(index, &IGMPAllow, _tmp)
00740         {
00741             pico_tree_delete(&IGMPAllow, index->keyValue);
00742         }
00743         pico_tree_foreach_safe(index, &IGMPBlock, _tmp)
00744         {
00745             pico_tree_delete(&IGMPBlock, index->keyValue);
00746         }
00747 
00748         switch (g->filter_mode) {
00749 
00750         case PICO_IP_MULTICAST_INCLUDE:
00751             switch (p->filter_mode) {
00752             case PICO_IP_MULTICAST_INCLUDE:
00753                 if (p->event == IGMP_EVENT_DELETE_GROUP) { /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
00754                     /* TO_IN (B) */
00755                     record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
00756                     IGMPFilter = &IGMPAllow;
00757                     if (p->MCASTFilter) {
00758                         pico_tree_foreach(index, p->MCASTFilter) /* B */
00759                         {
00760                             pico_tree_insert(&IGMPAllow, index->keyValue);
00761                             sources++;
00762                         }
00763                     } /* else { IGMPAllow stays empty } */
00764 
00765                     break;
00766                 }
00767 
00768                 /* ALLOW (B-A) */
00769                 /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
00770                 if (p->event == IGMP_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
00771                     record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
00772                 else
00773                     record_type = IGMP_ALLOW_NEW_SOURCES;
00774 
00775                 IGMPFilter = &IGMPAllow;
00776                 pico_tree_foreach(index, p->MCASTFilter) /* B */
00777                 {
00778                     pico_tree_insert(&IGMPAllow, index->keyValue);
00779                     sources++;
00780                 }
00781                 pico_tree_foreach(index, &g->MCASTSources) /* A */
00782                 {
00783                     source = pico_tree_findKey(&IGMPAllow, index->keyValue);
00784                     if (source) {
00785                         pico_tree_delete(&IGMPAllow, source);
00786                         sources--;
00787                     }
00788                 }
00789                 if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
00790                     break;
00791 
00792                 /* BLOCK (A-B) */
00793                 record_type = IGMP_BLOCK_OLD_SOURCES;
00794                 IGMPFilter = &IGMPBlock;
00795                 pico_tree_foreach(index, &g->MCASTSources) /* A */
00796                 {
00797                     pico_tree_insert(&IGMPBlock, index->keyValue);
00798                     sources++;
00799                 }
00800                 pico_tree_foreach(index, p->MCASTFilter) /* B */
00801                 {
00802                     source = pico_tree_findKey(&IGMPBlock, index->keyValue);
00803                     if (source) {
00804                         pico_tree_delete(&IGMPBlock, source);
00805                         sources--;
00806                     }
00807                 }
00808                 if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
00809                     break;
00810 
00811                 /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report (RFC 3376 $5.1) */
00812                 p->f = NULL;
00813                 return 0;
00814 
00815             case PICO_IP_MULTICAST_EXCLUDE:
00816                 /* TO_EX (B) */
00817                 record_type = IGMP_CHANGE_TO_EXCLUDE_MODE;
00818                 IGMPFilter = &IGMPBlock;
00819                 pico_tree_foreach(index, p->MCASTFilter) /* B */
00820                 {
00821                     pico_tree_insert(&IGMPBlock, index->keyValue);
00822                     sources++;
00823                 }
00824                 break;
00825 
00826             default:
00827                 pico_err = PICO_ERR_EINVAL;
00828                 return -1;
00829             }
00830             break;
00831 
00832         case PICO_IP_MULTICAST_EXCLUDE:
00833             switch (p->filter_mode) {
00834             case PICO_IP_MULTICAST_INCLUDE:
00835                 /* TO_IN (B) */
00836                 record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
00837                 IGMPFilter = &IGMPAllow;
00838                 if (p->MCASTFilter) {
00839                     pico_tree_foreach(index, p->MCASTFilter) /* B */
00840                     {
00841                         pico_tree_insert(&IGMPAllow, index->keyValue);
00842                         sources++;
00843                     }
00844                 } /* else { IGMPAllow stays empty } */
00845 
00846                 break;
00847 
00848             case PICO_IP_MULTICAST_EXCLUDE:
00849                 /* BLOCK (B-A) */
00850                 record_type = IGMP_BLOCK_OLD_SOURCES;
00851                 IGMPFilter = &IGMPBlock;
00852                 pico_tree_foreach(index, p->MCASTFilter)
00853                 {
00854                     pico_tree_insert(&IGMPBlock, index->keyValue);
00855                     sources++;
00856                 }
00857                 pico_tree_foreach(index, &g->MCASTSources) /* A */
00858                 {
00859                     source = pico_tree_findKey(&IGMPBlock, index->keyValue); /* B */
00860                     if (source) {
00861                         pico_tree_delete(&IGMPBlock, source);
00862                         sources--;
00863                     }
00864                 }
00865                 if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
00866                     break;
00867 
00868                 /* ALLOW (A-B) */
00869                 record_type = IGMP_ALLOW_NEW_SOURCES;
00870                 IGMPFilter = &IGMPAllow;
00871                 pico_tree_foreach(index, &g->MCASTSources)
00872                 {
00873                     pico_tree_insert(&IGMPAllow, index->keyValue);
00874                     sources++;
00875                 }
00876                 pico_tree_foreach(index, p->MCASTFilter) /* B */
00877                 {
00878                     source = pico_tree_findKey(&IGMPAllow, index->keyValue); /* A */
00879                     if (source) {
00880                         pico_tree_delete(&IGMPAllow, source);
00881                         sources--;
00882                     }
00883                 }
00884                 if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
00885                     break;
00886 
00887                 /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report (RFC 3376 $5.1) */
00888                 p->f = NULL;
00889                 return 0;
00890 
00891             default:
00892                 pico_err = PICO_ERR_EINVAL;
00893                 return -1;
00894             }
00895             break;
00896 
00897         default:
00898             pico_err = PICO_ERR_EINVAL;
00899             return -1;
00900         }
00901 
00902 igmp3_report:
00903         len = (uint16_t)(sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (sources * sizeof(struct pico_ip4)));
00904         p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t)(IP_OPTION_ROUTER_ALERT_LEN + len));
00905         p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
00906         p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
00907         p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN);
00908         p->f->dev = pico_ipv4_link_find(&p->mcast_link);
00909         /* p->f->len is correctly set by alloc */
00910 
00911         report = (struct igmpv3_report *)p->f->transport_hdr;
00912         report->type = IGMP_TYPE_MEM_REPORT_V3;
00913         report->res0 = 0;
00914         report->crc = 0;
00915         report->res1 = 0;
00916         report->groups = short_be(1);
00917 
00918         record = (struct igmpv3_group_record *)(((uint8_t *)report) + sizeof(struct igmpv3_report));
00919         record->type = record_type;
00920         record->aux = 0;
00921         record->sources = short_be(sources);
00922         record->mcast_group = p->mcast_group.addr;
00923         if (IGMPFilter && !pico_tree_empty(IGMPFilter)) {
00924             uint32_t *source_addr = (uint32_t *)((uint8_t *)record + sizeof(struct igmpv3_group_record));
00925             i = 0;
00926             pico_tree_foreach(index, IGMPFilter)
00927             {
00928                 source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr;
00929                 i++;
00930             }
00931         }
00932 
00933         report->crc = short_be(pico_checksum(report, len));
00934         break;
00935     }
00936 
00937     default:
00938         pico_err = PICO_ERR_EINVAL;
00939         return -1;
00940     }
00941     return 0;
00942 }
00943 
00944 /* stop timer, send leave if flag set */
00945 static int stslifs(struct igmp_parameters *p)
00946 {
00947     struct igmp_timer t = {
00948         0
00949     };
00950 
00951     igmp_dbg("IGMP: event = leave group | action = stop timer, send leave if flag set\n");
00952 
00953     t.type = IGMP_TIMER_GROUP_REPORT;
00954     t.mcast_link = p->mcast_link;
00955     t.mcast_group = p->mcast_group;
00956     if (pico_igmp_timer_stop(&t) < 0)
00957         return -1;
00958 
00959     /* always send leave, even if not last host */
00960     if (pico_igmp_send_report(p, p->f) < 0)
00961         return -1;
00962 
00963     pico_igmp_delete_parameter(p);
00964     igmp_dbg("IGMP: new state = non-member\n");
00965     return 0;
00966 }
00967 
00968 /* send report, set flag, start timer */
00969 static int srsfst(struct igmp_parameters *p)
00970 {
00971     struct igmp_timer t = {
00972         0
00973     };
00974     struct pico_frame *copy_frame = NULL;
00975 
00976     igmp_dbg("IGMP: event = join group | action = send report, set flag, start timer\n");
00977 
00978     p->last_host = IGMP_HOST_LAST;
00979 
00980     if (pico_igmp_generate_report(p) < 0)
00981         return -1;
00982 
00983     if (!p->f)
00984         return 0;
00985 
00986     copy_frame = pico_frame_copy(p->f);
00987     if (!copy_frame) {
00988         pico_err = PICO_ERR_ENOMEM;
00989         return -1;
00990     }
00991 
00992     if (pico_igmp_send_report(p, copy_frame) < 0)
00993         return -1;
00994 
00995     t.type = IGMP_TIMER_GROUP_REPORT;
00996     t.mcast_link = p->mcast_link;
00997     t.mcast_group = p->mcast_group;
00998     t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
00999     t.f = p->f;
01000     t.callback = pico_igmp_report_expired;
01001     pico_igmp_timer_start(&t);
01002 
01003     p->state = IGMP_STATE_DELAYING_MEMBER;
01004     igmp_dbg("IGMP: new state = delaying member\n");
01005     return 0;
01006 }
01007 
01008 /* merge report, send report, reset timer (IGMPv3 only) */
01009 static int mrsrrt(struct igmp_parameters *p)
01010 {
01011     struct igmp_timer *t = NULL;
01012     struct pico_frame *copy_frame = NULL;
01013     struct pico_ipv4_link *link = NULL;
01014 
01015     igmp_dbg("IGMP: event = update group | action = merge report, send report, reset timer (IGMPv3 only)\n");
01016 
01017     link = pico_ipv4_link_get(&p->mcast_link);
01018     if (!link)
01019         return -1;
01020 
01021     if (link->mcast_compatibility != PICO_IGMPV3) {
01022         igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
01023         return -1;
01024     }
01025 
01026     /* XXX: merge with pending report rfc 3376 $5.1 */
01027 
01028     copy_frame = pico_frame_copy(p->f);
01029     if (!copy_frame)
01030         return -1;
01031 
01032     if (pico_igmp_send_report(p, copy_frame) < 0)
01033         return -1;
01034 
01035     t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
01036     if (!t)
01037         return -1;
01038 
01039     t->delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
01040     pico_igmp_timer_reset(t);
01041 
01042     p->state = IGMP_STATE_DELAYING_MEMBER;
01043     igmp_dbg("IGMP: new state = delaying member\n");
01044     return 0;
01045 }
01046 
01047 /* send report, start timer (IGMPv3 only) */
01048 static int srst(struct igmp_parameters *p)
01049 {
01050     struct igmp_timer t = {
01051         0
01052     };
01053     struct pico_frame *copy_frame = NULL;
01054     struct pico_ipv4_link *link = NULL;
01055 
01056     igmp_dbg("IGMP: event = update group | action = send report, start timer (IGMPv3 only)\n");
01057 
01058     link = pico_ipv4_link_get(&p->mcast_link);
01059     if (!link)
01060         return -1;
01061 
01062     if (link->mcast_compatibility != PICO_IGMPV3) {
01063         igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
01064         return -1;
01065     }
01066 
01067     if (pico_igmp_generate_report(p) < 0)
01068         return -1;
01069 
01070     if (!p->f)
01071         return 0;
01072 
01073     copy_frame = pico_frame_copy(p->f);
01074     if (!copy_frame)
01075         return -1;
01076 
01077     if (pico_igmp_send_report(p, copy_frame) < 0)
01078         return -1;
01079 
01080     t.type = IGMP_TIMER_GROUP_REPORT;
01081     t.mcast_link = p->mcast_link;
01082     t.mcast_group = p->mcast_group;
01083     t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000));
01084     t.f = p->f;
01085     t.callback = pico_igmp_report_expired;
01086     pico_igmp_timer_start(&t);
01087 
01088     p->state = IGMP_STATE_DELAYING_MEMBER;
01089     igmp_dbg("IGMP: new state = delaying member\n");
01090     return 0;
01091 }
01092 
01093 /* send leave if flag set */
01094 static int slifs(struct igmp_parameters *p)
01095 {
01096     igmp_dbg("IGMP: event = leave group | action = send leave if flag set\n");
01097 
01098     /* always send leave, even if not last host */
01099     if (pico_igmp_send_report(p, p->f) < 0)
01100         return -1;
01101 
01102     pico_igmp_delete_parameter(p);
01103     igmp_dbg("IGMP: new state = non-member\n");
01104     return 0;
01105 }
01106 
01107 /* start timer */
01108 static int st(struct igmp_parameters *p)
01109 {
01110     struct igmp_timer t = {
01111         0
01112     };
01113 
01114     igmp_dbg("IGMP: event = query received | action = start timer\n");
01115 
01116     if (pico_igmp_generate_report(p) < 0) {
01117         igmp_dbg("Failed to generate report\n");
01118         return -1;
01119     }
01120 
01121     if (!p->f) {
01122         igmp_dbg("No pending frame\n");
01123         return -1;
01124     }
01125 
01126     t.type = IGMP_TIMER_GROUP_REPORT;
01127     t.mcast_link = p->mcast_link;
01128     t.mcast_group = p->mcast_group;
01129     t.delay = (pico_rand() % ((1u + p->max_resp_time) * 100u));
01130     t.f = p->f;
01131     t.callback = pico_igmp_report_expired;
01132     pico_igmp_timer_start(&t);
01133 
01134     p->state = IGMP_STATE_DELAYING_MEMBER;
01135     igmp_dbg("IGMP: new state = delaying member\n");
01136     return 0;
01137 }
01138 
01139 /* stop timer, clear flag */
01140 static int stcl(struct igmp_parameters *p)
01141 {
01142     struct igmp_timer t = {
01143         0
01144     };
01145 
01146     igmp_dbg("IGMP: event = report received | action = stop timer, clear flag\n");
01147 
01148     t.type = IGMP_TIMER_GROUP_REPORT;
01149     t.mcast_link = p->mcast_link;
01150     t.mcast_group = p->mcast_group;
01151     if (pico_igmp_timer_stop(&t) < 0)
01152         return -1;
01153 
01154     p->last_host = IGMP_HOST_NOT_LAST;
01155     p->state = IGMP_STATE_IDLE_MEMBER;
01156     igmp_dbg("IGMP: new state = idle member\n");
01157     return 0;
01158 }
01159 
01160 /* send report, set flag */
01161 static int srsf(struct igmp_parameters *p)
01162 {
01163     igmp_dbg("IGMP: event = timer expired | action = send report, set flag\n");
01164 
01165     if (pico_igmp_send_report(p, p->f) < 0)
01166         return -1;
01167 
01168     p->state = IGMP_STATE_IDLE_MEMBER;
01169     igmp_dbg("IGMP: new state = idle member\n");
01170     return 0;
01171 }
01172 
01173 /* reset timer if max response time < current timer */
01174 static int rtimrtct(struct igmp_parameters *p)
01175 {
01176     struct igmp_timer *t = NULL;
01177     uint32_t time_to_run = 0;
01178 
01179     igmp_dbg("IGMP: event = query received | action = reset timer if max response time < current timer\n");
01180 
01181     t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
01182     if (!t)
01183         return -1;
01184 
01185     time_to_run = (uint32_t)(t->start + t->delay - PICO_TIME_MS());
01186     if ((p->max_resp_time * 100u) < time_to_run) { /* max_resp_time in units of 1/10 seconds */
01187         t->delay = pico_rand() % ((1u + p->max_resp_time) * 100u);
01188         pico_igmp_timer_reset(t);
01189     }
01190 
01191     p->state = IGMP_STATE_DELAYING_MEMBER;
01192     igmp_dbg("IGMP: new state = delaying member\n");
01193     return 0;
01194 }
01195 
01196 static int discard(struct igmp_parameters *p)
01197 {
01198     igmp_dbg("IGMP: ignore and discard frame\n");
01199     pico_frame_discard(p->f);
01200     return 0;
01201 }
01202 
01203 /* finite state machine table */
01204 const callback host_membership_diagram_table[3][6] =
01205 { /* event                    |Delete Group  |Create Group |Update Group |Query Received  |Report Received  |Timer Expired */
01206 /* state Non-Member      */
01207     { discard,       srsfst,       srsfst,       discard,         discard,          discard },
01208 /* state Delaying Member */ { stslifs,       mrsrrt,       mrsrrt,       rtimrtct,        stcl,             srsf    },
01209 /* state Idle Member     */ { slifs,         srst,         srst,         st,              discard,          discard }
01210 };
01211 
01212 static int pico_igmp_process_event(struct igmp_parameters *p)
01213 {
01214     struct pico_tree_node *index = NULL;
01215     struct igmp_parameters *_p = NULL;
01216 
01217     igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.addr);
01218     if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.addr == 0) { /* general query */
01219         pico_tree_foreach(index, &IGMPParameters) {
01220             _p = index->keyValue;
01221             _p->max_resp_time = p->max_resp_time;
01222             _p->event = IGMP_EVENT_QUERY_RECV;
01223             igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.addr, _p->state);
01224             host_membership_diagram_table[_p->state][_p->event](_p);
01225         }
01226     } else {
01227         igmp_dbg("IGMP: state = %u (0: non-member - 1: delaying member - 2: idle member)\n", p->state);
01228         host_membership_diagram_table[p->state][p->event](p);
01229     }
01230 
01231     return 0;
01232 }
01233 
01234 #else
01235 static struct pico_queue igmp_in = {
01236     0
01237 };
01238 static struct pico_queue igmp_out = {
01239     0
01240 };
01241 
01242 static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f) {
01243     IGNORE_PARAMETER(self);
01244     IGNORE_PARAMETER(f);
01245     pico_err = PICO_ERR_EPROTONOSUPPORT;
01246     return -1;
01247 }
01248 
01249 static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) {
01250     IGNORE_PARAMETER(self);
01251     IGNORE_PARAMETER(f);
01252     return -1;
01253 }
01254 
01255 /* Interface: protocol definition */
01256 struct pico_protocol pico_proto_igmp = {
01257     .name = "igmp",
01258     .proto_number = PICO_PROTO_IGMP,
01259     .layer = PICO_LAYER_TRANSPORT,
01260     .process_in = pico_igmp_process_in,
01261     .process_out = pico_igmp_process_out,
01262     .q_in = &igmp_in,
01263     .q_out = &igmp_out,
01264 };
01265 
01266 int 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) {
01267     IGNORE_PARAMETER(mcast_link);
01268     IGNORE_PARAMETER(mcast_group);
01269     IGNORE_PARAMETER(filter_mode);
01270     IGNORE_PARAMETER(_MCASTFilter);
01271     IGNORE_PARAMETER(state);
01272     pico_err = PICO_ERR_EPROTONOSUPPORT;
01273     return -1;
01274 }
01275 #endif