1#include "defs.h"
2
3struct source_set ZEROSET;
4
5struct source_set_op
6{
7	void (*op)(struct source_set *, struct source_set *, struct source_set *, int);
8	int inverse;
9	int mode_change;
10};
11
12static struct source_set_op host_op[st_max][ev_max] =
13{
14	/* none operation (initial)*/
15	{{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
16	/* IS_IN operation*/
17	{{NULL, 0, 0}, {set_assign, 0, 0}, {set_assign, 0, 1}, {set_assign, 0, 0}, {set_assign, 0, 1}, {set_add, 0, 0}, {set_subtract, 0, 0}},
18	/* IS_EX operation*/
19	{{NULL, 0, 0}, {set_assign, 0, 1}, {set_assign, 0, 0}, {set_assign, 0, 1}, {set_assign, 0, 0}, {set_subtract, 0, 0}, {set_add, 0, 0}}
20};
21
22static struct source_set_op proxy_op[st_max][ev_max] =
23{
24	/* none operation (initial)*/
25	{{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}},
26	/* IS_IN operation*/
27	{{NULL, 0, 0}, {set_add, 0, 0}, {set_subtract, 1, 1}, {set_add, 0, 0}, {set_subtract, 1, 1}, {NULL, 0, 0}, {NULL, 0, 0}},
28	/* IS_EX operation*/
29	{{NULL, 0, 0}, {set_subtract, 0, 0}, {set_intersection, 0, 0}, {set_subtract, 0, 0}, {set_intersection, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}}
30};
31
32/*
33 * add_ebt_rule : add a ebtables' rule
34 * port : insert to which chain
35 * group : wanted multicast group address
36 * newmode : include or exclude mode
37 * newset : new source list set
38*/
39static inline void
40add_ebt_rule(uint32 port, uint32 group, uint32 newmode, struct source_set *newset)
41{
42	char line[256];
43	int i, len;
44	int num = (newset->num == PORT_INIT_STATE)?0:newset->num;
45	if (newset)
46	{
47		len = sprintf(line, "ebtables -I OUTPUT -o ethwan -p ipv4 --ip-dst %u.%u.%u.%u ",
48			NIPQUAD(group));
49		if (num)
50			len += sprintf(line+len, "%s --ip-msip ", (newmode == st_is_in)?"":"!");
51		for (i = 0; i < num; i++)
52			len += sprintf(line+len, "%u.%u.%u.%u%s",
53			NIPQUAD(newset->list[i]), (i == (num-1))?"":",");
54		sprintf(line+len, " -j ACCEPT");
55		DPRINTF("add_ebt_rule :: cmd [%s]\n", line);
56		system(line);
57	}
58}
59
60/*
61 * del_ebt_rule : del a ebtables' rule
62 * port : insert to which chain
63 * group : wanted multicast group address
64 * oldmode : include or exclude mode
65 * oldset : old source list set
66*/
67static inline void
68del_ebt_rule(uint32 port, uint32 group, uint32 oldmode, struct source_set *oldset)
69{
70	char line[256];
71	int i, len;
72	int num = (oldset->num == PORT_INIT_STATE)?0:oldset->num;
73	if (oldset)
74	{
75		len = sprintf(line, "ebtables -D OUTPUT -o ethwan -p ipv4 --ip-dst %u.%u.%u.%u ",
76			NIPQUAD(group), (oldmode == st_is_in)?"":"!");
77		if (num)
78			len += sprintf(line+len, "%s --ip-msip ", (oldmode == st_is_in)?"":"!");
79		for (i = 0; i < num; i++)
80			len += sprintf(line+len, "%u.%u.%u.%u%s",
81			NIPQUAD(oldset->list[i]), (i != (num-1))?",":"");
82		sprintf(line+len, " -j ACCEPT");
83		DPRINTF("del_ebt_rule :: cmd [%s]\n", line);
84		system(line);
85	}
86}
87
88/*
89 * update_ebt_rules : update a ebtables' rule
90 * port : insert to which chain
91 * group : wanted multicast group address
92 * oldmode : include or exclude mode
93 * oldset : old source list set
94 * newmode : include or exclude mode
95 * newset : new source list set
96 * note : if our update procedure causes the media stream discontinuously, we may swap the update commands
97*/
98static inline void
99update_ebt_rules(uint32 port, uint32 group, uint32 oldmode, struct source_set *oldset, uint32 newmode, struct source_set *newset)
100{
101	del_ebt_rule(port, group, oldmode, oldset);
102
103	add_ebt_rule(port, group, newmode, newset);
104}
105
106static inline void
107set_mcast_set(struct Listener *listener, uint32 group)
108{
109	char line[256], i, len;
110	len = sprintf(line, "echo \"%u.%u.%u.%u %u.%u.%u.%u %d %d ",
111		NIPQUAD(listener->srcAddr), NIPQUAD(group), (listener->mode == st_is_in)?1:0, listener->source.num);
112	for (i = 0; i < listener->source.num; i++)
113		len += sprintf(line+len, "%u.%u.%u.%u ", NIPQUAD(listener->source.list[i]));
114	sprintf(line+len, "\" > /proc/mcast_set");
115	system(line);
116}
117
118/*
119 * vaild_grec : to valid the group record
120 * if the group record is valid/invalid, to return true/false.
121 * if the multicast address field is not a multicast address ==> invalid
122 * if the number of source is greater than 8 ==> invalid (for Netgear Spec.)
123 * if the sources have more than one zero address or sources have multicast address ==> invalid
124*/
125static inline int
126vaild_grec(struct igmpv3_grec *grec)
127{
128	int i, flag =0;
129
130	if (!MULTICAST(grec->grec_mca) ||
131		(grec->grec_nsrcs > 8)||
132		(grec->grec_type > IGMPV3_BLOCK_OLD_SOURCE))
133		return 0;
134	for (i = 0; i < grec->grec_nsrcs; i++)
135	{
136		if (!grec->grec_src[i])
137			flag++;
138		if (MULTICAST(grec->grec_src[i]))
139			flag += 2;
140		if (flag > 1)
141			return 0;
142	}
143	return 1;
144}
145
146/*
147 * copy_source_list : copy group record to the temporary source set buffer
148 * set : the source set storage buffer
149 * grec : the group record
150*/
151static inline void
152copy_source_list(struct source_set *set, struct igmpv3_grec *grec)
153{
154	int i;
155	memset(set, 0, sizeof(struct source_set));
156	set->num = grec->grec_nsrcs;
157	for (i = 0; i < set->num; i++)
158		set->list[i] = grec->grec_src[i];
159}
160
161/*
162 * update_proxy_source_info : update the source set of the router
163 * note : port 0 means wireless port,
164 *          port1 ~ portN mean ethernet ports
165 * to collect all ports source set information and to calculate the latest source set
166 * if source set is changed, to use set_source_list to update the source set.
167*/
168static inline void
169update_proxy_source_info(struct RouteTable *croute)
170{
171	int i;
172	struct source_set_op *proxy;
173	struct source_set tmp, tmp1;
174	uint32 mode = croute->port_mode[0];
175
176	memcpy(&tmp, &croute->port_source[0], sizeof(struct source_set));
177	memset(&tmp1, 0, sizeof(struct source_set));
178	for (i = 1; i <= PORT_MAX_NUM; i++)
179	{
180		proxy = &proxy_op[mode][croute->port_mode[i]];
181		proxy->op(&tmp, &croute->port_source[i], &tmp1, proxy->inverse);
182		if (proxy->mode_change)
183			mode = (mode== st_is_in)?st_is_ex:st_is_in;
184		memcpy(&tmp, &tmp1, sizeof(struct source_set));
185	}
186
187	if (mode != croute->mode ||
188		set_comp(&croute->source, &tmp))
189	{
190		croute->mode = mode;
191		memcpy(&croute->source, &tmp, sizeof(struct source_set));
192		set_source_list(croute);
193	}
194
195}
196
197/*
198 * update_port_source_info : update the source set of the specific port
199 * croute : multicast routing table info
200 * port : come from which port
201 * flag : call update_proxy_source_info or not
202 * note : if the source set is changed to the include mode with an empty set, to delete the ebtables' rule
203*/
204static inline void
205update_port_source_info(struct RouteTable *croute, uint32 port, uint32 flag)
206{
207	struct source_set tmp[3];
208	uint32 i = 0, curr_state = st_is_in, next_state;
209	struct Listener *listener = croute->listeners, *next;
210	struct source_set_op *proxy;
211
212	memset(&tmp[0], 0, sizeof(struct source_set));
213	tmp[0].num = PORT_INIT_STATE;
214	if (listener)
215	{
216		while (listener)
217		{
218			next = listener->nextlistener;
219			if (listener->port == port)
220			{
221				if (tmp[0].num == PORT_INIT_STATE)
222				{
223					curr_state = listener->mode;
224					memcpy(&tmp[0], &listener->source, sizeof(struct source_set));
225				}
226				else
227				{
228					memcpy(&tmp[1], &listener->source, sizeof(struct source_set));
229					next_state = listener->mode;
230					proxy = &proxy_op[curr_state][next_state];
231					proxy->op(&tmp[0], &tmp[1], &tmp[2], proxy->inverse);
232					memcpy(&tmp[0], &tmp[2], sizeof(struct source_set));
233					if (proxy->mode_change)
234						curr_state = (curr_state == st_is_in)?st_is_ex:st_is_in;
235				}
236			}
237			listener = next;
238		}
239	}
240
241	if (croute->port_mode[port] != curr_state
242		||set_comp(&croute->port_source[port], &tmp[0]))
243	{
244		if (tmp[0].num == PORT_INIT_STATE)
245		{
246			del_ebt_rule(port, croute->group, croute->port_mode[port], &croute->port_source[port]);
247			memcpy(&croute->port_source[port], tmp, sizeof(struct source_set));
248			croute->port_mode[port] = st_is_in;
249		}
250		else
251		{
252			update_ebt_rules(port, croute->group, croute->port_mode[port], &croute->port_source[port], curr_state, &tmp[0]);
253			memcpy(&croute->port_source[port], &tmp[0], sizeof(struct source_set));
254			croute->port_mode[port] = curr_state;
255		}
256		if (flag)
257			update_proxy_source_info(croute);
258	}
259}
260
261/*
262 * update_all_ports : update all ports information
263 * croute : multicast routing table info
264 * If the clients join(leave) or the port status is changed,
265 * we will call update_all_ports.
266 * And to avoid the update_proxy_source_info is called frequently
267 * so we will call update_proxy_source_info after all ports are updated.
268*/
269void
270update_all_ports(struct RouteTable *croute)
271{
272	int i;
273	if (mc_ctrl_packet(ntohl(croute->group)))
274		return;
275
276	for (i = 0; i <= PORT_MAX_NUM; i++)
277		update_port_source_info(croute, i, (i == PORT_MAX_NUM)?1:0);
278}
279
280/*
281 * add_listener : get a new client
282 * port : come from which port
283 * croute : multicast routing table info
284 * src : the ip address of the new client
285 * tmp1 : the new source set
286 * mode : include or exclude mode
287*/
288static inline void
289add_listener(uint32 port, struct RouteTable *croute, uint32 src, struct source_set *tmp1, int mode)
290{
291	struct Listener *listener = NULL;
292	struct source_set_op *proxy;
293	struct source_set tmp2;
294
295	listener = (struct Listener *)insertListener(port, croute, src);
296	if (!listener)
297		return;
298
299	listener->mode = mode;
300	listener->port = port;
301	listener->version = IGMP_V3_MEMBERSHIP_REPORT;
302	memcpy(&listener->source, tmp1, sizeof(struct source_set));
303
304	update_port_source_info(croute, port, 1);
305	if (!port)
306	{
307		join_group(src, croute->group);
308		set_mcast_set(listener, croute->group);
309	}
310}
311
312/*
313 * create_croute : create a new multicast routing table entry
314 * group : multicast group address
315 * src : the ip address of the new client
316 * port : come from which port
317 * grec_type : the type of group record
318 * source : the new source set
319 * note : In order to pass the cdrouter test case (case 335 ~ case 343).
320 *           If we get any source address, to call add_source_list first before update the source list.
321*/
322static inline void
323create_croute(uint32 group, uint32 src, uint32 port, uint8 grec_type, struct source_set *source)
324{
325	struct RouteTable *croute = NULL;
326	struct Listener *listener = NULL;
327	uint32 mode;
328	acceptGroupReport(port, src, group, IGMP_V3_MEMBERSHIP_REPORT);
329	croute = (struct RouteTable *)findRoute(group);
330
331	if (!croute)
332		return;
333	memcpy(&croute->source, source, sizeof(struct source_set));
334	mode = (grec_type == IGMPV3_MODE_IS_INCLUDE ||
335		grec_type == IGMPV3_CHANGE_TO_INCLUDE||
336		grec_type == IGMPV3_ALLOW_NEW_SOURCE)?st_is_in:st_is_ex;
337	listener = (struct Listener *)findListener(croute, src);
338	if (listener)
339	{
340		memcpy(&listener->source, source, sizeof(struct source_set));
341		listener->mode = mode;
342		listener->port = port;
343		listener->version = IGMP_V3_MEMBERSHIP_REPORT;
344		if (listener->source.num)
345			add_source_list(croute->group, listener->source.list[0]);
346	}
347	update_port_source_info(croute, port, 1);
348	if (!port)
349		set_mcast_set(listener, croute->group);
350}
351
352/*
353 * processV3report : IGMPV3 main function
354 * buf : igmp packet
355 * src : the ip address of the new client
356 * port : come from which port
357*/
358void
359processV3report(char *buf, uint32 src, uint32 port)
360{
361	struct igmpv3_report *igmpv3 = (struct igmpv3_report *)buf;
362	uint16 i, num = igmpv3->ngrec;
363	struct igmpv3_grec *grec = NULL;
364	uint32 group, mode, port_change = 0;
365	struct RouteTable *croute = NULL;
366	struct source_set tmp1, tmp2;
367	struct Listener *listener = NULL;
368	struct source_set_op *host;
369	struct IfDesc  *sourceVif;
370	char *pdata = (char *)igmpv3->grec;
371
372	for (i = 0;(i < num) && pdata; i++, pdata += grec->grec_nsrcs*4 + 8)
373	{
374		grec = (struct igmpv3_grec *)pdata;
375		// check group record is valid or not
376		if (!vaild_grec(grec))
377			return;
378
379		group = grec->grec_mca;
380		if (mc_ctrl_packet(ntohl(group)))
381			continue;
382		croute = (struct RouteTable *)findRoute(group);
383		copy_source_list(&tmp1, grec);
384		if (!croute)
385		{
386			// if the multicast group entry is not existed and group record type is not block old sources,
387			// to add a new multicast group entry
388			if (grec->grec_type < IGMPV3_BLOCK_OLD_SOURCE)
389				create_croute(group, src, port, grec->grec_type, &tmp1);
390		}
391		else
392		{
393			// it should not be happened
394			// to make sure all router mode will be include, exclude or not set.
395			if (croute->mode > 2)
396				croute->mode = 0;
397
398			listener = (struct Listener *)findListener(croute, src);
399			// it should not be happened
400			if (croute->mode == st_none)
401			{
402				switch (grec->grec_type)
403				{
404					case ev_is_in:
405					case ev_to_in:
406					case ev_allow:
407						// skipped leave message
408						if (!memcmp(&tmp1, &ZEROSET, sizeof(struct source_set)))
409							break;
410					case ev_is_ex:
411					case ev_to_ex:
412						create_croute(group, src, port, grec->grec_type, &tmp1);
413					case ev_block:
414					default:
415						break;
416				}
417				continue;
418			}
419			sourceVif = getIfByAddress( src );
420			if(!sourceVif)
421				return;
422			// to update kernel multicast route table
423			insertRoute(port, group, sourceVif->index, src);
424			if (listener)
425			{
426				// to check port is changed or not
427				if (listener->port != port)
428					port_change = 1;
429				host = &host_op[listener->mode][grec->grec_type];
430				host->op(&listener->source, &tmp1, &tmp2, host->inverse);
431				switch (FSM(listener->mode, grec->grec_type))
432				{
433					case FSM(st_is_in, ev_is_in):
434					case FSM(st_is_in, ev_to_in):
435					case FSM(st_is_in, ev_allow):
436						if (port_change || memcmp(&listener->source, &tmp2, sizeof(struct source_set)))
437						{
438							// if the source set is become include mode with an empty set,
439							// to call acceptLeaveMessage
440							if (tmp2.num)
441							{
442								memcpy(&listener->source, &tmp2, sizeof(struct source_set));
443								listener->port = port;
444								if (port_change)
445									update_all_ports(croute);
446								else
447									update_port_source_info(croute, port, 1);
448								if (!port)
449									set_mcast_set(listener, group);
450							}
451							else
452								acceptLeaveMessage(src, group);
453						}
454						break;
455					case FSM(st_is_in, ev_is_ex):
456					case FSM(st_is_in, ev_to_ex):
457						listener->mode = st_is_ex;
458						memcpy(&listener->source, &tmp2, sizeof(struct source_set));
459						listener->port = port;
460						if (port_change)
461							update_all_ports(croute);
462						else
463							update_port_source_info(croute, port, 1);
464						if (!port)
465							set_mcast_set(listener, group);
466						break;
467					case FSM(st_is_in, ev_block):
468						if (tmp2.num)
469						{
470							if (port_change || memcmp(&listener->source, &tmp2, sizeof(struct source_set)))
471							{
472								memcpy(&listener->source, &tmp2, sizeof(struct source_set));
473								listener->port = port;
474								if (port_change)
475									update_all_ports(croute);
476								else
477									update_port_source_info(croute, port, 1);
478								if (!port)
479									set_mcast_set(listener, group);
480							}
481						}
482						else
483							acceptLeaveMessage(src, group);
484						break;
485					case FSM(st_is_ex, ev_is_in):
486					case FSM(st_is_ex, ev_to_in):
487						if (tmp2.num)
488						{
489							listener->port = port;
490							listener->mode = st_is_in;
491							if (memcmp(&listener->source, &tmp2, sizeof(struct source_set)))
492								memcpy(&listener->source, &tmp2, sizeof(struct source_set));
493							if (port_change)
494								update_all_ports(croute);
495							else
496								update_port_source_info(croute, port, 1);
497							if (!port)
498								set_mcast_set(listener, group);
499						}
500						else
501							acceptLeaveMessage(src, group);
502						break;
503					case FSM(st_is_ex, ev_is_ex):
504					case FSM(st_is_ex, ev_to_ex):
505					case FSM(st_is_ex, ev_allow):
506					case FSM(st_is_ex, ev_block):
507						if (port_change || memcmp(&listener->source, &tmp2, sizeof(struct source_set)))
508						{
509							listener->port = port;
510							memcpy(&listener->source, &tmp2, sizeof(struct source_set));
511							if (port_change)
512								update_all_ports(croute);
513							else
514								update_port_source_info(croute, port, 1);
515							if (!port)
516								set_mcast_set(listener, group);
517						}
518						break;
519					default:
520						DPRINTF("%s :: unknown state [%d] or unknown event [%d]", __FUNCTION__, croute->mode, grec->grec_type);
521						break;
522				}
523			}
524			else
525			{
526				switch (grec->grec_type)
527				{
528					case ev_is_in:
529					case ev_to_in:
530					case ev_allow:
531						mode = st_is_in;
532						break;
533					case ev_is_ex:
534					case ev_to_ex:
535						mode = st_is_ex;
536						break;
537					// if an unknown client send a block type message,
538					// to skip it
539					case ev_block:
540					default:
541						continue;
542				}
543				add_listener(port, croute, src, &tmp1, mode);
544			}
545		}
546	}
547}
548
549
550