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*   mroute-api.c
35*
36*   This module contains the interface routines to the Linux mrouted API
37*/
38
39
40#define USE_LINUX_IN_H
41#include "defs.h"
42#ifdef USE_ATH_HEADER
43#include <unistd.h>
44#endif
45
46
47// MAX_MC_VIFS from mclab.h must have same value as MAXVIFS from mroute.h
48#if MAX_MC_VIFS != MAXVIFS
49# error "constants don't match, correct mclab.h"
50#endif
51
52// need an IGMP socket as interface for the mrouted API
53// - receives the IGMP messages
54int         MRouterFD;        /* socket for all network I/O  */
55char        *recv_buf;           /* input packet buffer         */
56char        *send_buf;           /* output packet buffer        */
57int	    recv_len;
58
59
60// my internal virtual interfaces descriptor vector
61static struct VifDesc {
62    struct IfDesc *IfDp;
63} VifDescVc[ MAXVIFS ];
64
65
66
67/*
68** Initialises the mrouted API and locks it by this exclusively.
69**
70** returns: - 0 if the functions succeeds
71**          - the errno value for non-fatal failure condition
72*/
73int enableMRouter()
74{
75    int Va = 1;
76
77    if ( (MRouterFD  = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0 )
78        igmp_syslog( LOG_ERR, errno, "IGMP socket open" );
79
80    if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_INIT,
81                     (void *)&Va, sizeof( Va ) ) )
82        return errno;
83
84    return 0;
85}
86
87/*
88** Diables the mrouted API and relases by this the lock.
89**
90*/
91void disableMRouter()
92{
93    if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_DONE, NULL, 0 )
94         || close( MRouterFD )
95       ) {
96        MRouterFD = 0;
97        igmp_syslog( LOG_ERR, errno, "MRT_DONE/close" );
98    }
99
100    MRouterFD = 0;
101}
102
103/*
104** Adds the interface '*IfDp' as virtual interface to the mrouted API
105**
106*/
107void addVIF( struct IfDesc *IfDp )
108{
109    struct vifctl VifCtl;
110    struct VifDesc *VifDp;
111
112    /* search free VifDesc
113     */
114    for ( VifDp = VifDescVc; VifDp < VCEP( VifDescVc ); VifDp++ ) {
115        if ( ! VifDp->IfDp )
116            break;
117    }
118
119    /* no more space
120     */
121    if ( VifDp >= VCEP( VifDescVc ) )
122        igmp_syslog( LOG_ERR, ENOMEM, "addVIF, out of VIF space" );
123
124    VifDp->IfDp = IfDp;
125
126    VifCtl.vifc_vifi  = VifDp - VifDescVc;
127    VifCtl.vifc_flags = 0;        /* no tunnel, no source routing, register ? */
128    VifCtl.vifc_threshold  = VifDp->IfDp->threshold;    // Packet TTL must be at least 1 to pass them
129    VifCtl.vifc_rate_limit = VifDp->IfDp->ratelimit;    // Ratelimit
130
131    VifCtl.vifc_lcl_addr.s_addr = VifDp->IfDp->InAdr.s_addr;
132    VifCtl.vifc_rmt_addr.s_addr = INADDR_ANY;
133
134    // Set the index...
135    VifDp->IfDp->index = VifCtl.vifc_vifi;
136
137    igmp_syslog( LOG_NOTICE, 0, "adding VIF, Ix %d Fl 0x%x IP 0x%08x %s, Threshold: %d, Ratelimit: %d",
138         VifCtl.vifc_vifi, VifCtl.vifc_flags,  VifCtl.vifc_lcl_addr.s_addr, VifDp->IfDp->Name,
139         VifCtl.vifc_threshold, VifCtl.vifc_rate_limit);
140
141    IF_DEBUG {
142        struct SubnetList *currSubnet;
143        for(currSubnet = IfDp->allowednets; currSubnet; currSubnet = currSubnet->next) {
144            igmp_syslog(LOG_DEBUG, 0, "        Network for [%s] : %s",
145                IfDp->Name,
146                inetFmts(currSubnet->subnet_addr, currSubnet->subnet_mask, s1));
147        }
148    }
149
150    if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_ADD_VIF,
151                     (char *)&VifCtl, sizeof( VifCtl ) ) )
152        igmp_syslog( LOG_ERR, errno, "MRT_ADD_VIF" );
153
154}
155
156/*
157** Adds the multicast routed '*Dp' to the kernel routes
158**
159** returns: - 0 if the function succeeds
160**          - the errno value for non-fatal failure condition
161*/
162int addMRoute( struct MRouteDesc *Dp )
163{
164    struct mfcctl CtlReq;
165
166    CtlReq.mfcc_origin    = Dp->OriginAdr;
167    CtlReq.mfcc_mcastgrp  = Dp->McAdr;
168    CtlReq.mfcc_parent    = Dp->InVif;
169
170    /* copy the TTL vector
171     */
172    if (    sizeof( CtlReq.mfcc_ttls ) != sizeof( Dp->TtlVc )
173            || VCMC( CtlReq.mfcc_ttls ) != VCMC( Dp->TtlVc )
174       )
175        igmp_syslog( LOG_ERR, 0, "data types doesn't match in " __FILE__ ", source adaption needed !" );
176
177    memcpy( CtlReq.mfcc_ttls, Dp->TtlVc, sizeof( CtlReq.mfcc_ttls ) );
178
179    {
180        char FmtBuO[ 32 ], FmtBuM[ 32 ];
181
182        igmp_syslog( LOG_NOTICE, 0, "Adding MFC: %s -> %s, InpVIf: %d",
183             fmtInAdr( FmtBuO, CtlReq.mfcc_origin ),
184             fmtInAdr( FmtBuM, CtlReq.mfcc_mcastgrp ),
185             CtlReq.mfcc_parent == ALL_VIFS ? -1 : CtlReq.mfcc_parent
186           );
187    }
188
189    if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_ADD_MFC,
190                     (void *)&CtlReq, sizeof( CtlReq ) ) )
191        igmp_syslog( LOG_WARNING, errno, "MRT_ADD_MFC" );
192
193        return 0;
194}
195
196/*
197** Removes the multicast routed '*Dp' from the kernel routes
198**
199** returns: - 0 if the function succeeds
200**          - the errno value for non-fatal failure condition
201*/
202int delMRoute( struct MRouteDesc *Dp )
203{
204    struct mfcctl CtlReq;
205
206    CtlReq.mfcc_origin    = Dp->OriginAdr;
207    CtlReq.mfcc_mcastgrp  = Dp->McAdr;
208    CtlReq.mfcc_parent    = Dp->InVif;
209
210    /* clear the TTL vector
211     */
212    memset( CtlReq.mfcc_ttls, 0, sizeof( CtlReq.mfcc_ttls ) );
213
214    {
215        char FmtBuO[ 32 ], FmtBuM[ 32 ];
216
217        igmp_syslog( LOG_NOTICE, 0, "Removing MFC: %s -> %s, InpVIf: %d",
218             fmtInAdr( FmtBuO, CtlReq.mfcc_origin ),
219             fmtInAdr( FmtBuM, CtlReq.mfcc_mcastgrp ),
220             CtlReq.mfcc_parent == ALL_VIFS ? -1 : CtlReq.mfcc_parent
221           );
222    }
223
224    if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_DEL_MFC,
225                     (void *)&CtlReq, sizeof( CtlReq ) ) )
226        igmp_syslog( LOG_WARNING, errno, "MRT_DEL_MFC" );
227    return 0;
228}
229
230/*
231** Returns for the virtual interface index for '*IfDp'
232**
233** returns: - the vitrual interface index if the interface is registered
234**          - -1 if no virtual interface exists for the interface
235**
236*/
237int getVifIx( struct IfDesc *IfDp )
238{
239    struct VifDesc *Dp;
240
241    for ( Dp = VifDescVc; Dp < VCEP( VifDescVc ); Dp++ )
242        if ( Dp->IfDp == IfDp )
243            return Dp - VifDescVc;
244
245    return -1;
246}
247
248#if (DNI_MULTI_LAN_SUPPORT)
249int add_source_list(uint32 mgroup, uint32 src_ip)
250{
251	struct ip_data
252	{
253		unsigned short family;
254		unsigned long ip;
255	} group, src;
256	union
257	{
258		struct group_req gr;
259		struct group_source_req gsr;
260	} opt;
261	int ret = 0, optlen;
262
263	memset(&opt, 0, sizeof(opt));
264	group.family = src.family = AF_INET;
265	group.ip = mgroup;
266	src.ip = src_ip;
267	memcpy(&opt.gsr.gsr_source, &src, 8);
268	memcpy(&opt.gsr.gsr_group, &group, 8);
269	optlen = sizeof(opt.gsr);
270	if ((ret =setsockopt(getMcGroupSock(), IPPROTO_IP, MCAST_JOIN_SOURCE_GROUP, (void *)&opt, optlen)) < 0)
271	{
272		DPRINTF("%s :: setsockopt : MCAST_JOIN_SOURCE_GROUP error [%d]\n", __FUNCTION__, ret);
273		ret = 1;
274	}
275	return ret;
276}
277
278int set_source_list(struct RouteTable *croute)
279{
280	struct ip_msfilter *imsfp = NULL;
281	int i, ret = 0, num = croute->source.num;
282	struct IfDesc *vif;
283
284	imsfp = (struct ip_msfilter *)malloc(IP_MSFILTER_SIZE(num));
285	if (!imsfp)
286	{
287		DPRINTF("%s :: out of memory...\n", __FUNCTION__);
288		return 1;
289	}
290	memset (imsfp, 0, IP_MSFILTER_SIZE(num));
291	imsfp->imsf_fmode = (croute->mode == st_is_in)?1:0;
292	imsfp->imsf_numsrc = num;
293	imsfp->imsf_multiaddr = croute->group;
294	for (i = 0; i < num; i++)
295		imsfp->imsf_slist[i] = croute->source.list[i];
296
297	vif = getIfByIx( upStreamVif );
298	imsfp->imsf_interface = htonl(vif->InAdr.s_addr);
299	i = IP_MSFILTER_SIZE(num);
300	if ((ret = setsockopt(getMcGroupSock(), IPPROTO_IP, IP_MSFILTER, imsfp,i)) < 0 )
301	{
302		DPRINTF("%s :: setsockopt : IP_MSFILTER error [%d]\n", __FUNCTION__, ret);
303		ret = 1;
304	}
305	free(imsfp);
306	return ret;
307}
308#endif
309
310