1/*********************************************************************
2   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
3   See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
4
5   This module handles the equalities between the IGMP and the MLD protocol
6   Authors: Roel Postelmans
7 *********************************************************************/
8
9#include "pico_stack.h"
10#include "pico_ipv6.h"
11#include "pico_mld.h"
12#include "pico_config.h"
13#include "pico_eth.h"
14#include "pico_addressing.h"
15#include "pico_frame.h"
16#include "pico_tree.h"
17#include "pico_device.h"
18#include "pico_socket.h"
19#include "pico_icmp6.h"
20#include "pico_dns_client.h"
21#include "pico_mld.h"
22#include "pico_igmp.h"
23#include "pico_constants.h"
24#include "pico_mcast.h"
25
26#if (((defined(PICO_SUPPORT_MLD) && defined(PICO_SUPPORT_IPV6)) || defined(PICO_SUPPORT_IGMP)) && defined(PICO_SUPPORT_MCAST))
27
28#ifdef DEBUG_MCAST
29#define multicast_dbg dbg
30#else
31#define multicast_dbg(...) do {} while(0)
32#endif
33
34#define MCAST_EVENT_DELETE_GROUP           (0x0)
35#define MCAST_EVENT_CREATE_GROUP           (0x1)
36#define MCAST_EVENT_UPDATE_GROUP           (0x2)
37#define MCAST_EVENT_QUERY_RECV             (0x3)
38#define MCAST_EVENT_REPORT_RECV            (0x4)
39#define MCAST_EVENT_TIMER_EXPIRED          (0x5)
40
41#define MCAST_MODE_IS_INCLUDE              (1)
42#define MCAST_MODE_IS_EXCLUDE              (2)
43#define MCAST_CHANGE_TO_INCLUDE_MODE       (3)
44#define MCAST_CHANGE_TO_EXCLUDE_MODE       (4)
45
46#define MCAST_MODE_IS_INCLUDE              (1)
47#define MCAST_MODE_IS_EXCLUDE              (2)
48#define MCAST_CHANGE_TO_INCLUDE_MODE       (3)
49#define MCAST_CHANGE_TO_EXCLUDE_MODE       (4)
50#define MCAST_ALLOW_NEW_SOURCES            (5)
51#define MCAST_BLOCK_OLD_SOURCES            (6)
52
53typedef int (*mcast_callback)(struct mcast_filter_parameters *);
54
55static void pico_mcast_src_filtering_cleanup(struct mcast_filter_parameters*mcast )
56{
57    struct pico_tree_node *index = NULL, *_tmp = NULL;
58    /* cleanup filters */
59    pico_tree_foreach_safe(index, mcast->allow, _tmp)
60    {
61        pico_tree_delete(mcast->allow, index->keyValue);
62    }
63    pico_tree_foreach_safe(index, mcast->block, _tmp)
64    {
65        pico_tree_delete(mcast->block, index->keyValue);
66    }
67}
68static int pico_mcast_src_filtering_inc_inc(struct mcast_filter_parameters*mcast )
69{
70    struct pico_tree_node *index = NULL;
71    union pico_address *source;
72    /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
73    if (mcast->p->event == MCAST_EVENT_DELETE_GROUP) {
74        /* TO_IN (B) */
75        mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
76        mcast->filter = mcast->allow;
77        if (mcast->p->MCASTFilter) {
78            pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
79            {
80                if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
81               	    multicast_dbg("MCAST: Failed to insert entry in tree\n");
82                    return -1;
83                }
84                mcast->sources++;
85            }
86        } /* else { allow stays empty } */
87
88        return 0;
89    }
90
91    /* ALLOW (B-A) */
92    /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
93    if (mcast->p->event == MCAST_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
94        mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
95    else
96        mcast->record_type = MCAST_ALLOW_NEW_SOURCES;
97
98    mcast->filter = mcast->allow;
99    pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
100    {
101        if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
102            multicast_dbg("MCAST: Failed to insert entry in tree\n");
103            return -1;
104		}
105        mcast->sources++;
106    }
107    pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
108    {
109        source = pico_tree_findKey(mcast->allow, index->keyValue);
110        if (source) {
111            pico_tree_delete(mcast->allow, source);
112            mcast->sources--;
113        }
114    }
115    if (!pico_tree_empty(mcast->allow)) /* record type is ALLOW */
116        return 0;
117
118    /* BLOCK (A-B) */
119    mcast->record_type = MCAST_BLOCK_OLD_SOURCES;
120    mcast->filter = mcast->block;
121    pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
122    {
123        if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
124            multicast_dbg("MCAST: Failed to insert entry in tree\n");
125            return -1;
126		}
127        mcast->sources++;
128    }
129    pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
130    {
131        source = pico_tree_findKey(mcast->block, index->keyValue);
132        if (source) {
133            pico_tree_delete(mcast->block, source);
134            mcast->sources--;
135        }
136    }
137    if (!pico_tree_empty(mcast->block)) /* record type is BLOCK */
138        return 0;
139
140    /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report  */
141    (mcast->p)->f = NULL;
142    return MCAST_NO_REPORT;
143}
144
145static int pico_mcast_src_filtering_inc_excl(struct mcast_filter_parameters*mcast )
146{
147    struct pico_tree_node *index = NULL;
148    mcast->record_type = MCAST_CHANGE_TO_EXCLUDE_MODE;
149    mcast->filter = mcast->block;
150    pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
151    {
152        if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
153            multicast_dbg("MCAST: Failed to insert entry in tree\n");
154            return -1;
155		}
156        mcast->sources++;
157    }
158    return 0;
159}
160static int pico_mcast_src_filtering_excl_inc(struct mcast_filter_parameters*mcast )
161{
162    struct pico_tree_node *index = NULL;
163    mcast->record_type = MCAST_CHANGE_TO_INCLUDE_MODE;
164    mcast->filter = mcast->allow;
165    if (mcast->p->MCASTFilter) {
166        pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
167        {
168            if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
169                multicast_dbg("MCAST: Failed to insert entry in tree\n");
170                return -1;
171			}
172            mcast->sources++;
173        }
174    } /* else { allow stays empty } */
175
176    return 0;
177}
178static int pico_mcast_src_filtering_excl_excl(struct mcast_filter_parameters*mcast )
179{
180    struct pico_tree_node *index = NULL;
181    struct pico_ip6 *source = NULL;
182    mcast->record_type = MCAST_BLOCK_OLD_SOURCES;
183    mcast->filter = mcast->block;
184    pico_tree_foreach(index, mcast->p->MCASTFilter)
185    {
186        if (pico_tree_insert(mcast->block, index->keyValue) == &LEAF) {
187            multicast_dbg("MCAST: Failed to insert entry in tree\n");
188            return -1;
189		}
190
191        mcast->sources++;
192    }
193    pico_tree_foreach(index, &mcast->g->MCASTSources) /* A */
194    {
195        source = pico_tree_findKey(mcast->block, index->keyValue); /* B */
196        if (source) {
197            pico_tree_delete(mcast->block, source);
198            mcast->sources--;
199        }
200    }
201    if (!pico_tree_empty(mcast->block)) /* record type is BLOCK */
202        return 0;
203
204    /* ALLOW (A-B) */
205    mcast->record_type = MCAST_ALLOW_NEW_SOURCES;
206    mcast->filter = mcast->allow;
207    pico_tree_foreach(index, &mcast->g->MCASTSources)
208    {
209        if (pico_tree_insert(mcast->allow, index->keyValue) == &LEAF) {
210            multicast_dbg("MCAST: Failed to insert entry in tree\n");
211            return -1;
212		}
213        mcast->sources++;
214    }
215    pico_tree_foreach(index, mcast->p->MCASTFilter) /* B */
216    {
217        source = pico_tree_findKey(mcast->allow, index->keyValue); /* A */
218        if (source) {
219            pico_tree_delete(mcast->allow, source);
220            mcast->sources--;
221        }
222    }
223    if (!pico_tree_empty(mcast->allow)) /* record type is ALLOW */
224        return 0;
225
226    /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report  */
227    mcast->p->f = NULL;
228    return MCAST_NO_REPORT;
229}
230static const mcast_callback mcast_filter_state[2][2] =
231{
232  { pico_mcast_src_filtering_excl_excl, pico_mcast_src_filtering_excl_inc},
233  { pico_mcast_src_filtering_inc_excl,  pico_mcast_src_filtering_inc_inc }
234};
235int8_t pico_mcast_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p)
236{
237    int ret = -1;
238    /* "non-existent" state of filter mode INCLUDE and empty source list */
239    if (p->event == MCAST_EVENT_DELETE_GROUP) {
240        p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
241        p->MCASTFilter = NULL;
242    }
243
244    if (p->event == MCAST_EVENT_QUERY_RECV)
245        return 0;
246
247    pico_mcast_src_filtering_cleanup(filter);
248
249    if(filter->g->filter_mode <= PICO_IP_MULTICAST_INCLUDE )
250    {
251        if(p->filter_mode <= PICO_IP_MULTICAST_INCLUDE)
252        {
253            ret = mcast_filter_state[filter->g->filter_mode][p->filter_mode](filter);
254        }
255    }
256
257    return (int8_t) ret;
258}
259#endif
260