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*   config.c - Contains functions to load and parse config
35*              file, and functions to configure the daemon.
36*/
37
38#include "defs.h"
39
40// Structure to keep configuration for VIFs...
41struct vifconfig {
42    char*               name;
43    short               state;
44    int                 ratelimit;
45    int                 threshold;
46
47    // Keep allowed nets for VIF.
48    struct SubnetList*  allowednets;
49
50    // Next config in list...
51    struct vifconfig*   next;
52};
53
54// Structure to keep vif configuration
55struct vifconfig*   vifconf;
56
57// Keeps common settings...
58#ifdef USE_ATH_HEADER
59struct Config commonConfig;
60#else
61static struct Config commonConfig;
62#endif
63
64// Prototypes...
65struct vifconfig *parsePhyintToken();
66struct SubnetList *parseSubnetAddress(char *addrstr);
67
68
69/**
70*   Initializes common config..
71*/
72void initCommonConfig() {
73#ifdef USE_ATH_HEADER
74    commonConfig.mode = 1;
75#endif
76    commonConfig.robustnessValue = DEFAULT_ROBUSTNESS;
77    commonConfig.queryInterval = INTERVAL_QUERY;
78    commonConfig.queryResponseInterval = INTERVAL_QUERY_RESPONSE;
79
80    // The defaults are calculated from other settings.
81    commonConfig.startupQueryInterval = INTERVAL_QUERY;//(unsigned int)(INTERVAL_QUERY / 4);
82    commonConfig.startupQueryCount = DEFAULT_ROBUSTNESS;
83
84    // Default values for leave intervals...
85    commonConfig.lastMemberQueryInterval = INTERVAL_QUERY_RESPONSE;
86    commonConfig.lastMemberQueryCount    = DEFAULT_ROBUSTNESS;
87
88    // If 1, a leave message is sent upstream on leave messages from downstream.
89    commonConfig.fastUpstreamLeave = 0;
90
91}
92
93/**
94*   Returns a pointer to the common config...
95*/
96struct Config *getCommonConfig() {
97    return &commonConfig;
98}
99
100/**
101*   Loads the configuration from file, and stores the config in
102*   respective holders...
103*/
104int loadConfig(char *configFile) {
105    struct vifconfig  *tmpPtr;
106    struct vifconfig  **currPtr = &vifconf;
107    char *token;
108
109    // Initialize common config
110    initCommonConfig();
111
112    // Test config file reader...
113    if(!openConfigFile(configFile)) {
114        igmp_syslog(LOG_ERR, 0, "Unable to open configfile from %s", configFile);
115    }
116
117    // Get first token...
118    token = nextConfigToken();
119    if(token == NULL) {
120        igmp_syslog(LOG_ERR, 0, "Config file was empty.");
121    }
122
123    // Loop until all configuration is read.
124    while ( token != NULL ) {
125        // Check token...
126        if(strcmp("phyint", token)==0) {
127            // Got a phyint token... Call phyint parser
128            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: Got a phyint token.");
129            tmpPtr = parsePhyintToken();
130            if(tmpPtr == NULL) {
131                // Unparsable token... Exit...
132                closeConfigFile();
133                igmp_syslog(LOG_WARNING, 0, "Unknown token '%s' in configfile", token);
134                return 0;
135            } else {
136
137                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "IF name : %s", tmpPtr->name);
138                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Next ptr : %x", tmpPtr->next);
139                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Ratelimit : %d", tmpPtr->ratelimit);
140                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Threshold : %d", tmpPtr->threshold);
141                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "State : %d", tmpPtr->state);
142                IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Allowednet ptr : %x", tmpPtr->allowednets);
143
144                // Insert config, and move temppointer to next location...
145                *currPtr = tmpPtr;
146                currPtr = &tmpPtr->next;
147            }
148        }
149        else if(strcmp("quickleave", token)==0) {
150            // Got a quickleave token....
151            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: Quick leave mode enabled.");
152            commonConfig.fastUpstreamLeave = 1;
153
154            // Read next token...
155            token = nextConfigToken();
156            continue;
157#ifdef USE_ATH_HEADER
158        }else if(strcmp("mode", token)==0) {
159            // Got a mode token...
160	    token = nextConfigToken();
161            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: set mode to '%s'.", token);
162            commonConfig.mode = atoi( token );
163
164            // Read next token...
165            token = nextConfigToken();
166            continue;
167#endif
168        } else {
169            // Unparsable token... Exit...
170            closeConfigFile();
171            igmp_syslog(LOG_WARNING, 0, "Unknown token '%s' in configfile", token);
172            return 0;
173        }
174        // Get token that was not recognized by phyint parser.
175        token = getCurrentConfigToken();
176    }
177
178    // Close the configfile...
179    closeConfigFile();
180
181    return 1;
182}
183
184/**
185*   Appends extra VIF configuration from config file.
186*/
187void configureVifs() {
188    unsigned Ix;
189    struct IfDesc *Dp;
190    struct vifconfig *confPtr;
191
192    // If no config is availible, just return...
193    if(vifconf == NULL) {
194        return;
195    }
196
197    // Loop through all VIFs...
198    for ( Ix = 0; Dp = getIfByIx( Ix ); Ix++ ) {
199        if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) {
200
201            // Now try to find a matching config...
202            for( confPtr = vifconf; confPtr; confPtr = confPtr->next) {
203
204                // I the VIF names match...
205                if(strcmp(Dp->Name, confPtr->name)==0) {
206                    struct SubnetList *vifLast;
207
208                    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Found config for %s", Dp->Name);
209
210
211                    // Set the VIF state
212                    Dp->state = confPtr->state;
213
214                    Dp->threshold = confPtr->threshold;
215                    Dp->ratelimit = confPtr->ratelimit;
216
217                    // Go to last allowed net on VIF...
218                    for(vifLast = Dp->allowednets; vifLast->next; vifLast = vifLast->next);
219
220                    // Insert the configured nets...
221                    vifLast->next = confPtr->allowednets;
222
223                    break;
224                }
225            }
226        }
227    }
228}
229
230
231/**
232*   Internal function to parse phyint config
233*/
234struct vifconfig *parsePhyintToken() {
235    struct vifconfig  *tmpPtr;
236    struct SubnetList **anetPtr;
237    char *token;
238    short parseError = 0;
239
240    // First token should be the interface name....
241    token = nextConfigToken();
242
243    // Sanitycheck the name...
244    if(token == NULL) return NULL;
245    if(strlen(token) >= sizeof( ((struct ifreq *)NULL)->ifr_name) ) return NULL;
246    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Config for interface %s.", token);
247
248    // Allocate memory for configuration...
249    tmpPtr = (struct vifconfig*)malloc(sizeof(struct vifconfig));
250    if(tmpPtr == NULL) {
251        igmp_syslog(LOG_ERR, 0, "Out of memory.");
252    }
253
254    // Set default values...
255    tmpPtr->next = NULL;    // Important to avoid seg fault...
256    tmpPtr->ratelimit = 0;
257    tmpPtr->threshold = 1;
258    tmpPtr->state = IF_STATE_DOWNSTREAM;
259    tmpPtr->allowednets = NULL;
260
261    // Make a copy of the token to store the IF name
262    tmpPtr->name = (char *)malloc( sizeof(char) * strlen(token) );
263    if(tmpPtr->name == NULL) {
264        igmp_syslog(LOG_ERR, 0, "Out of memory.");
265    }
266    strcpy(tmpPtr->name, token);
267
268    // Set the altnet pointer to the allowednets pointer.
269    anetPtr = &tmpPtr->allowednets;
270
271    // Parse the rest of the config..
272    token = nextConfigToken();
273    while(token != NULL) {
274        if(strcmp("altnet", token)==0) {
275            // Altnet...
276            //struct in_addr  networkAddr; /* moved this avoid warning */
277
278            token = nextConfigToken();
279            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Got altnet token %s.",token);
280
281            *anetPtr = parseSubnetAddress(token);
282            if(*anetPtr == NULL) {
283                parseError = 1;
284                igmp_syslog(LOG_WARNING, 0, "Unable to parse subnet address.");
285                break;
286            } else {
287                anetPtr = &(*anetPtr)->next;
288            }
289        }
290        else if(strcmp("upstream", token)==0) {
291            // Upstream
292            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Got upstream token.");
293            tmpPtr->state = IF_STATE_UPSTREAM;
294        }
295        else if(strcmp("downstream", token)==0) {
296            // Downstream
297            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Got downstream token.");
298            tmpPtr->state = IF_STATE_DOWNSTREAM;
299        }
300        else if(strcmp("disabled", token)==0) {
301            // Disabled
302            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Got disabled token.");
303            tmpPtr->state = IF_STATE_DISABLED;
304        }
305        else if(strcmp("ratelimit", token)==0) {
306            // Ratelimit
307            token = nextConfigToken();
308            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Got ratelimit token '%s'.", token);
309            tmpPtr->ratelimit = atoi( token );
310            if(tmpPtr->ratelimit < 0) {
311                igmp_syslog(LOG_WARNING, 0, "Ratelimit must be 0 or more.");
312                parseError = 1;
313                break;
314            }
315        }
316        else if(strcmp("threshold", token)==0) {
317            // Threshold
318            token = nextConfigToken();
319            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Got threshold token '%s'.", token);
320            tmpPtr->threshold = atoi( token );
321            if(tmpPtr->threshold <= 0 || tmpPtr->threshold > 255) {
322                igmp_syslog(LOG_WARNING, 0, "Threshold must be between 1 and 255.");
323                parseError = 1;
324                break;
325            }
326        }
327        else {
328            // Unknown token. Break...
329            break;
330        }
331        token = nextConfigToken();
332    }
333
334    // Clean up after a parseerror...
335    if(parseError) {
336        free(tmpPtr);
337        tmpPtr = NULL;
338    }
339
340    return tmpPtr;
341}
342
343/**
344*   Parses a subnet address string on the format
345*   a.b.c.d/n into a SubnetList entry.
346*/
347struct SubnetList *parseSubnetAddress(char *addrstr) {
348    struct SubnetList *tmpSubnet;
349    char        *tmpStr;
350    uint32      addr = 0x00000000;
351    uint32      mask = 0xFFFFFFFF;
352
353    // First get the network part of the address...
354    tmpStr = strtok(addrstr, "/");
355    addr = inet_addr(tmpStr);
356
357    tmpStr = strtok(NULL, "/");
358    if(tmpStr != NULL) {
359        int bitcnt = atoi(tmpStr);
360        if(bitcnt <= 0 || bitcnt > 32) {
361            igmp_syslog(LOG_WARNING, 0, "The bits part of the address is invalid : %d.",tmpStr);
362            return NULL;
363        }
364
365        mask <<= (32 - bitcnt);
366    }
367
368    if(addr == -1 || addr == 0) {
369        igmp_syslog(LOG_WARNING, 0, "Unable to parse address token '%s'.", addrstr);
370        return NULL;
371    }
372
373    tmpSubnet = (struct SubnetList*) malloc(sizeof(struct SubnetList));
374    tmpSubnet->subnet_addr = addr;
375    tmpSubnet->subnet_mask = ntohl(mask);
376    tmpSubnet->next = NULL;
377
378    IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "Config: IF: Altnet: Parsed altnet to %s.",
379                 inetFmts(tmpSubnet->subnet_addr, tmpSubnet->subnet_mask,s1));
380
381    return tmpSubnet;
382}
383
384