1/*
2**  igmpproxy - IGMP proxy based multicast router
3**  Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
4**
5**  This program is free software; you can redistribute it and/or modify
6**  it under the terms of the GNU General Public License as published by
7**  the Free Software Foundation; either version 2 of the License, or
8**  (at your option) any later version.
9**
10**  This program is distributed in the hope that it will be useful,
11**  but WITHOUT ANY WARRANTY; without even the implied warranty of
12**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13**  GNU General Public License for more details.
14**
15**  You should have received a copy of the GNU General Public License
16**  along with this program; if not, write to the Free Software
17**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18**
19**----------------------------------------------------------------------------
20**
21**  This software is derived work from the following software. The original
22**  source code has been modified from it's original state by the author
23**  of igmpproxy.
24**
25**  smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
26**  - Licensed under the GNU General Public License, version 2
27**
28**  mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of
29**  Leland Stanford Junior University.
30**  - Original license can be found in the "doc/mrouted-LINCESE" file.
31**
32*/
33/**
34*   request.c
35*
36*   Functions for recieveing and processing IGMP requests.
37*
38*/
39
40#include "defs.h"
41
42#ifdef USE_ATH_HEADER
43static int startupQueryCount = 0;
44#endif
45// Prototypes...
46void sendGroupSpecificMemberQuery(void *argument);
47
48typedef struct {
49    uint32      group;
50    uint32      vifAddr;
51    short       started;
52} GroupVifDesc;
53
54#if (SUPPURT_MCAST_TO_UNICAST)
55inline int
56join_group(unsigned long src, unsigned long group)
57{
58	char line[64];
59	sprintf(line, "echo \"a %d.%d.%d.%d %d.%d.%d.%d\" > /proc/mcast",
60		NIPQUAD(src), NIPQUAD(group));
61//	DPRINTF("%s\n",line);
62	system(line);
63	return 0;
64}
65#endif
66
67
68/**
69*   Handles incoming membership reports, and
70*   appends them to the routing table.
71*/
72#ifdef USE_ATH_HEADER
73void acceptGroupReport(uint32 port, uint32 src, uint32 group, uint8 type) {
74#else
75void acceptGroupReport(uint32 src, uint32 group, uint8 type) {
76#endif
77    struct IfDesc  *sourceVif;
78
79    // Sanitycheck the group adress...
80    if(!IN_MULTICAST( ntohl(group) )) {
81        igmp_syslog(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.",
82            inetFmt(group, s1));
83        return;
84    }
85	// add by Kane, skipped control group
86	// ex. 224.0.0.x or 239.255.255.250
87	if (mc_ctrl_packet(ntohl(group))) {
88//		DPRINTF("%s :: skipped group [%x]\n", __FUNCTION__, group);
89		return ;
90	}
91#ifdef USE_ATH_HEADER
92    /* Bug 755: in other mode, if the bridge ip address and the frame from downsteam
93     * is not in the same network, all the join request will be accepted.
94     * We don't care the source ip address in the same network or not.  */
95// we do not accept the frame from downsteam is not in the same network to move the code segments to the insertRoute function
96/*
97	if(commonConfig.mode&0x01){
98        	IF_DEBUG atlog(LOG_DEBUG, 0,
99                     "Should insert group %s port %d to fdb table.",
100                     inetFmt(group, s1),port);
101        	if (port > 0)
102			insertFdb(group, port);
103	}
104*/
105#endif
106    // Find the interface on which the report was recieved.
107    sourceVif = getIfByAddress( src );
108    if(sourceVif == NULL) {
109        igmp_syslog(LOG_WARNING, 0, "No interfaces found for source %s",
110            inetFmt(src,s1));
111        return;
112    }
113
114    if(sourceVif->InAdr.s_addr == src) {
115        igmp_syslog(LOG_NOTICE, 0, "The IGMP message was from myself. Ignoring.");
116        return;
117    }
118
119    // We have a IF so check that it's an downstream IF.
120    if(sourceVif->state == IF_STATE_DOWNSTREAM) {
121#ifdef USE_ATH_HEADER
122		// The membership report was OK... Insert it into the route table..
123	if(commonConfig.mode&0x02){
124#endif
125        IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d",
126            inetFmt(group,s1), inetFmt(src,s2), sourceVif->index);
127
128#if (SUPPORT_IGMPV3)
129		if ((type == IGMP_V1_MEMBERSHIP_REPORT) || (type == IGMP_V2_MEMBERSHIP_REPORT))
130		{
131    			group_compatibilty_mode = IGMPV1_V2;
132		}
133#endif
134
135        // The membership report was OK... Insert it into the route table..
136#if (IGMP_IMMEDIATE_LEAVE)
137#ifdef USE_ATH_HEADER
138		insertRoute(port, group, sourceVif->index, src);
139#else
140		insertRoute(group, sourceVif->index, src);
141#endif
142#else
143        insertRoute(group, sourceVif->index);
144#endif
145#if (SUPPURT_MCAST_TO_UNICAST)
146		join_group(src, group);
147#endif
148#ifdef USE_ATH_HEADER
149	}
150#endif
151    } else {
152        // Log the state of the interface the report was recieved on.
153        igmp_syslog(LOG_INFO, 0, "Mebership report was recieved on %s. Ignoring.",
154            sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface");
155    }
156
157}
158
159/**
160*   Recieves and handles a group leave message.
161*/
162void acceptLeaveMessage(uint32 src, uint32 group) {
163    struct IfDesc   *sourceVif;
164
165    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Got leave message from %s to %s. Starting last member detection.",
166                 inetFmt(src, s1), inetFmt(group, s2));
167
168    // Sanitycheck the group adress...
169    if(!IN_MULTICAST( ntohl(group) )) {
170        igmp_syslog(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.",
171            inetFmt(group, s1));
172        return;
173    }
174	// add by Kane, skipped control group
175	// ex. 224.0.0.x or 239.255.255.250
176	if (mc_ctrl_packet(ntohl(group))) {
177		return ;
178	}
179    // Find the interface on which the report was recieved.
180    sourceVif = getIfByAddress( src );
181    if(sourceVif == NULL) {
182        igmp_syslog(LOG_WARNING, 0, "No interfaces found for source %s",
183            inetFmt(src,s1));
184        return;
185    }
186
187    // We have a IF so check that it's an downstream IF.
188    if(sourceVif->state == IF_STATE_DOWNSTREAM) {
189
190        GroupVifDesc   *gvDesc;
191        gvDesc = (GroupVifDesc*) malloc(sizeof(GroupVifDesc));
192
193        // Tell the route table that we are checking for remaining members...
194#if (IGMP_IMMEDIATE_LEAVE)
195        setRouteLastMemberMode(group, src);
196#else
197        setRouteLastMemberMode(group);
198#endif
199        // Call the group spesific membership querier...
200        gvDesc->group = group;
201        gvDesc->vifAddr = sourceVif->InAdr.s_addr;
202        gvDesc->started = 0;
203
204// modified by kane, to make igmp snooping in realtek board work smoothly
205#if (!IGMP_IMMEDIATE_LEAVE)
206        sendGroupSpecificMemberQuery(gvDesc);
207#endif
208
209    } else {
210        // just ignore the leave request...
211        IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "The found if for %s was not downstream. Ignoring leave request.");
212    }
213}
214
215/**
216*   Sends a group specific member report query until the
217*   group times out...
218*/
219void sendGroupSpecificMemberQuery(void *argument) {
220    struct  Config  *conf = getCommonConfig();
221
222    // Cast argument to correct type...
223    GroupVifDesc   *gvDesc = (GroupVifDesc*) argument;
224
225    if(gvDesc->started) {
226        // If aging returns false, we don't do any further action...
227        if(!lastMemberGroupAge(gvDesc->group)) {
228            return;
229        }
230    } else {
231        gvDesc->started = 1;
232    }
233
234    // Send a group specific membership query...
235    sendIgmp(gvDesc->vifAddr, gvDesc->group,
236             IGMP_MEMBERSHIP_QUERY,
237             conf->lastMemberQueryInterval * IGMP_TIMER_SCALE,
238             gvDesc->group, 0);
239
240    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d",
241        inetFmt(gvDesc->vifAddr,s1), inetFmt(gvDesc->group,s2),
242        conf->lastMemberQueryInterval);
243
244    // Set timeout for next round...
245    timer_setTimer(conf->lastMemberQueryInterval, sendGroupSpecificMemberQuery, gvDesc);
246
247}
248
249#ifdef USE_ATH_HEADER
250void setStartCount()
251{
252    struct Config *conf = getCommonConfig();
253    startupQueryCount = conf->startupQueryCount;
254}
255#endif
256
257/**
258*   Sends a general membership query on downstream VIFs
259*/
260#ifndef USE_ATH_HEADER
261extern struct RouteTable   *routing_table;
262#endif
263void sendGeneralMembershipQuery() {
264    struct  Config  *conf = getCommonConfig();
265    struct  IfDesc  *Dp;
266    int             Ix;
267
268    // Loop through all downstream vifs...
269    for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
270        if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) {
271            if(Dp->state == IF_STATE_DOWNSTREAM) {
272                // Send the membership query...
273                sendIgmp(Dp->InAdr.s_addr, allhosts_group,
274                         IGMP_MEMBERSHIP_QUERY,
275                         conf->queryResponseInterval * IGMP_TIMER_SCALE, 0, 0);
276
277                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d",
278                    inetFmt(Dp->InAdr.s_addr,s1), inetFmt(allhosts_group,s2),
279                    conf->queryResponseInterval);
280            }
281        }
282    }
283
284#ifndef USE_ATH_HEADER
285    // Install timer for aging active routes.
286    timer_setTimer(conf->queryResponseInterval, ageActiveRoutes, NULL);
287
288    // Install timer for next general query...
289    if(conf->startupQueryCount>0) {
290        // Use quick timer...
291        timer_setTimer(conf->startupQueryInterval, sendGeneralMembershipQuery, NULL);
292        // Decrease startup counter...
293        conf->startupQueryCount--;
294    }
295    else {
296        // Use slow timer...
297        timer_setTimer(conf->queryInterval, sendGeneralMembershipQuery, NULL);
298    }
299#endif
300
301}
302#define LOCAL_CONTROL_START 0xE0000000
303#define LOCAL_CONTROL_END   0XE00000FF
304#define SSDP    0XEFFFFFFA
305int mc_ctrl_packet(uint32 group)
306{
307    if (group >= LOCAL_CONTROL_START && group <= LOCAL_CONTROL_END) {
308        return 1;
309    }
310    else if (group == SSDP) {
311        return 1;
312    }
313    return 0;
314}
315
316#ifdef USE_ATH_HEADER
317/**
318*   Sends a general membership query on downstream VIFs
319*/
320void
321igmpTimerHandle()
322{
323    struct Config *conf = getCommonConfig();
324
325    //if (commonConfig.mode&0x02)
326        sendGeneralMembershipQuery();
327
328    // Install timer for aging active routes.
329    if (startupQueryCount == 0 || startupQueryCount == conf->startupQueryCount) {
330        if (commonConfig.mode&0x02)
331            timer_setTimer(conf->queryResponseInterval, ageActiveRoutes, NULL);
332
333//        if (commonConfig.mode&0x01)
334//            timer_setTimer(conf->queryResponseInterval, ageActiveFdbs, NULL);
335    }
336
337    // Install timer for next general query...
338    if (startupQueryCount > 0) {
339        // Use quick timer...
340        timer_setTimer(conf->startupQueryInterval, igmpTimerHandle,
341                       NULL);
342        // Decrease startup counter...
343        startupQueryCount--;
344    } else {
345        // Use slow timer...
346        timer_setTimer(conf->queryInterval, igmpTimerHandle, NULL);
347    }
348}
349#endif
350
351/* This function is used for BT IGMP to receive IGMP Query */
352void accept_membership_query(uint32 src, uint32 dst, uint32 group, int code)
353{
354	struct  IfDesc  *Dp;
355	int     Ix;
356
357	if(src != htonl(get_if_ip(LAN_IF))) {
358		for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
359			if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) {
360				if(Dp->state == IF_STATE_DOWNSTREAM) {
361					// Send the membership query...
362					sendIgmp(Dp->InAdr.s_addr, dst, IGMP_MEMBERSHIP_QUERY, code, group, 0);
363
364					IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Sent membership query from %s to %s.",
365							inetFmt(Dp->InAdr.s_addr,s1), inetFmt(dst,s2));
366				}
367			}
368		}
369	}
370}
371
372