7e388bd7bbe67beb24ee460100fea1237cfc50c7
[rahunas] / src / rh-config.c
1 /**
2  * RahuNAS configuration
3  * Author: Suriya Soutmun <darksolar@gmail.com>
4  * Date:   2008-11-26
5  */
6 #include <ctype.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <syslog.h>
10 #include <dirent.h>
11 #include <errno.h>
12
13 #include "rahunasd.h"
14 #include "rh-config.h"
15
16 GList *interfaces_list = NULL;
17 static unsigned long ifb_reserved = 0;
18
19 enum lcfg_status rahunas_visitor(const char *key, void *data, size_t size, 
20                                  void *user_data) {
21   char *value = strndup(data, size);
22   union rahunas_config *config = (union rahunas_config *)user_data;
23   char *clone_key = NULL;
24   char *sep = NULL; 
25   char *main_key = NULL;
26   char *sub_key = NULL;
27   enum config_type cfg_type;
28   int  valid = 1;
29
30   if(config == NULL)
31     return lcfg_status_error;
32
33   if(value == NULL)
34     return lcfg_status_error;
35
36   clone_key = strdup(key);
37   if (clone_key == NULL)
38     return lcfg_status_error;
39
40   sep = strstr(clone_key, ".");
41   main_key = clone_key;
42   sub_key = sep + 1;
43   *sep = '\0';
44
45   if (strncmp(main_key, "main", 4) == 0) {
46     cfg_type = MAIN;
47   } else if (strncmp(main_key, "service_class", strlen ("service_class")) == 0) {
48     cfg_type = SERVICECLASS;
49   } else {
50     cfg_type = VSERVER;
51     if (config->rh_vserver.vserver_name == NULL)
52       config->rh_vserver.vserver_name = strdup(main_key);
53   }
54
55   switch (cfg_type) {
56     case MAIN:
57       if (strncmp(sub_key, "conf_dir", 8) == 0) {
58         if (config->rh_main.conf_dir != NULL)
59           free(config->rh_main.conf_dir);
60         config->rh_main.conf_dir = strdup(value);
61       } else if (strncmp(sub_key, "serviceclass_conf_dir", 21) == 0) {
62         if (config->rh_main.serviceclass_conf_dir != NULL)
63           free(config->rh_main.serviceclass_conf_dir);
64         config->rh_main.serviceclass_conf_dir = strdup(value);
65       } else if (strncmp(sub_key, "log_file", 8) == 0) {
66         if (config->rh_main.log_file != NULL)
67           free(config->rh_main.log_file);
68         config->rh_main.log_file = strdup(value); 
69       } else if (strncmp(sub_key, "dhcp", 4) == 0) {
70         if (config->rh_main.dhcp != NULL)
71           free(config->rh_main.dhcp);
72         config->rh_main.dhcp = strdup(value);
73       } else if (strncmp(sub_key, "serviceclass_attribute", strlen ("serviceclass_attribute")) == 0) {
74         // Do nothing, no needs to collect this value
75       } else if (strncmp(sub_key, "serviceclass", 12) == 0) {
76         config->rh_main.serviceclass = strncmp(value, "yes", 3) == 0 ? 1 : 0;
77       } else if (strncmp(sub_key, "bandwidth_shape", 15) == 0) {
78         config->rh_main.bandwidth_shape = strncmp(value, "yes", 3) == 0 ? 1 : 0;
79       } else if (strncmp(sub_key, "bittorrent_download_max", 23) == 0) {
80         config->rh_main.bittorrent_download_max = atoi(value); 
81       } else if (strncmp(sub_key, "bittorrent_upload_max", 21) == 0) {
82         config->rh_main.bittorrent_upload_max = atoi(value); 
83       } else if (strncmp(sub_key, "polling_interval", 16) == 0) {
84         config->rh_main.polling_interval = atoi(value);
85       }      
86       break;
87
88     case SERVICECLASS:
89       if (strncmp (sub_key, "serviceclass_id",
90             strlen("serviceclass_id")) == 0) {
91         config->rh_serviceclass.serviceclass_id = atoi (value);
92       } else if (strncmp (sub_key, "serviceclass_name", strlen("serviceclass_name")) == 0) {
93         if (config->rh_serviceclass.serviceclass_name != NULL)
94           free(config->rh_serviceclass.serviceclass_name);
95         config->rh_serviceclass.serviceclass_name = strdup(value);
96       } else if (strncmp (sub_key, "description", strlen("description")) == 0) {
97         if (config->rh_serviceclass.description != NULL)
98           free(config->rh_serviceclass.description);
99         config->rh_serviceclass.description = strdup(value);
100       } else if (strncmp (sub_key, "network", strlen("network")) == 0) {
101         if ((sep = strchr (value, '/')) != NULL) {
102           if (config->rh_serviceclass.network != NULL)
103             free(config->rh_serviceclass.network);
104
105           config->rh_serviceclass.network = strdup(value);
106
107           sep++;
108           config->rh_serviceclass.network_size = atoi (sep);
109
110           if (config->rh_serviceclass.network_size != 0) {
111             config->rh_serviceclass.network_size =
112               (1 << (32 - config->rh_serviceclass.network_size)) - 2;
113           }
114
115           sep = strsep (&value, "/");
116           if (sep != NULL) {
117             if (inet_aton (sep, &config->rh_serviceclass.start_addr) == 0)
118               {
119                 valid = 0;
120               }
121             else
122               {
123                 DP ("service_class: %s - start ip = %s, size: %d",
124                     config->rh_serviceclass.serviceclass_name,
125                     inet_ntoa (config->rh_serviceclass.start_addr),
126                     config->rh_serviceclass.network_size);
127               }
128           } else {
129             config->rh_serviceclass.network_size = 0;
130             valid = 0;
131           }
132         } else {
133           valid = 0;
134         }
135
136         if (!valid) {
137           if (config->rh_serviceclass.serviceclass_name != NULL) {
138             syslog(LOG_ERR, "\"%s\" service_class config config error: "
139                             "invalid network %s",
140                             config->rh_serviceclass.serviceclass_name, value);
141           } else {
142             syslog(LOG_ERR, "unknown service_class config error: "
143                             "invalid network %s", value);
144           }
145         }
146       } else if (strncmp (sub_key, "fake_arpd_iface",
147                  strlen("fake_arpd_iface")) == 0) {
148         if (config->rh_serviceclass.fake_arpd_iface != NULL)
149           free(config->rh_serviceclass.fake_arpd_iface);
150         config->rh_serviceclass.fake_arpd_iface = strdup(value);
151       } else if (strncmp (sub_key, "fake_arpd", strlen("fake_arpd")) == 0) {
152         if (config->rh_serviceclass.fake_arpd != NULL)
153           free(config->rh_serviceclass.fake_arpd);
154         config->rh_serviceclass.fake_arpd = strdup(value);
155       }
156       break;
157
158     case VSERVER:
159       if (strncmp(sub_key, "vserver_id", 10) == 0) {
160         config->rh_vserver.vserver_id = atoi(value);
161       } else if (strncmp(sub_key, "dev_external", 12) == 0) {
162         if (config->rh_vserver.dev_external != NULL)
163           free(config->rh_vserver.dev_external);
164         config->rh_vserver.dev_external = strdup(value);
165       } else if (strncmp(sub_key, "dev_internal", 12) == 0) {
166         if (config->rh_vserver.dev_internal != NULL)
167           free(config->rh_vserver.dev_internal);
168         config->rh_vserver.dev_internal = strdup(value);
169       } else if (strncmp(sub_key, "vlan", 4) == 0) {
170         if (config->rh_vserver.vlan != NULL)
171           free(config->rh_vserver.vlan);
172         config->rh_vserver.vlan = strdup(value);
173       } else if (strncmp(sub_key, "vlan_raw_dev_external", 21) == 0) {
174         if (config->rh_vserver.vlan_raw_dev_external != NULL)
175           free(config->rh_vserver.vlan_raw_dev_external);
176         config->rh_vserver.vlan_raw_dev_external = strdup(value);
177        } else if (strncmp(sub_key, "vlan_raw_dev_internal", 21) == 0) {
178         if (config->rh_vserver.vlan_raw_dev_internal != NULL)
179           free(config->rh_vserver.vlan_raw_dev_internal);
180         config->rh_vserver.vlan_raw_dev_internal = strdup(value);
181       } else if (strncmp(sub_key, "bridge", 6) == 0) {
182         if (config->rh_vserver.bridge != NULL)
183           free(config->rh_vserver.bridge);
184         config->rh_vserver.bridge = strdup(value);
185       } else if (strncmp(sub_key, "masquerade", 10) == 0) {
186         if (config->rh_vserver.masquerade != NULL)
187           free(config->rh_vserver.masquerade);
188         config->rh_vserver.masquerade = strdup(value);
189       } else if (strncmp(sub_key, "ignore_mac", 10) == 0) {
190         if (config->rh_vserver.ignore_mac != NULL)
191           free(config->rh_vserver.ignore_mac);
192         config->rh_vserver.ignore_mac = strdup(value);
193       } else if (strncmp(sub_key, "vserver_ip", 10) == 0) {
194         if (config->rh_vserver.vserver_ip != NULL)
195           free(config->rh_vserver.vserver_ip);
196         config->rh_vserver.vserver_ip = strdup(value); 
197       } else if (strncmp(sub_key, "vserver_fqdn", 12) == 0) {
198         if (config->rh_vserver.vserver_fqdn != NULL)
199           free(config->rh_vserver.vserver_fqdn);
200         config->rh_vserver.vserver_fqdn = strdup(value);
201       } else if (strncmp(sub_key, "vserver_ports_allow", 19) == 0) {
202         if (config->rh_vserver.vserver_ports_allow != NULL)
203           free(config->rh_vserver.vserver_ports_allow);
204         config->rh_vserver.vserver_ports_allow = strdup(value);
205       } else if (strncmp(sub_key, "vserver_ports_intercept", 23) == 0) {
206         if (config->rh_vserver.vserver_ports_intercept != NULL)
207           free(config->rh_vserver.vserver_ports_intercept);
208         config->rh_vserver.vserver_ports_intercept = strdup(value);
209       } else if (strncmp(sub_key, "clients", 7) == 0) {
210         if (config->rh_vserver.clients != NULL)
211           free(config->rh_vserver.clients);
212         config->rh_vserver.clients = strdup(value);
213       } else if (strncmp(sub_key, "excluded", 8) == 0) {
214         if (config->rh_vserver.excluded != NULL)
215           free(config->rh_vserver.excluded);
216         config->rh_vserver.excluded = strdup(value);
217       } else if (strncmp(sub_key, "idle_timeout", 12) == 0) {
218         config->rh_vserver.idle_timeout = atoi(value);
219       } else if (strncmp(sub_key, "dns", 3) == 0) {
220         if (config->rh_vserver.dns != NULL)
221           free(config->rh_vserver.dns);
222         config->rh_vserver.dns = strdup(value);
223       } else if (strncmp(sub_key, "ssh", 3) == 0) {
224         if (config->rh_vserver.ssh != NULL)
225           free(config->rh_vserver.ssh);
226         config->rh_vserver.ssh = strdup(value);
227       } else if (strncmp(sub_key, "proxy", 5) == 0) {
228         if (strncmp(sub_key, "proxy_host", 10) == 0) {
229           if (config->rh_vserver.proxy_host != NULL)
230             free(config->rh_vserver.proxy_host);
231           config->rh_vserver.proxy_host = strdup(value);
232         } else if (strncmp(sub_key, "proxy_port", 10) == 0) {
233           if (config->rh_vserver.proxy_port != NULL)
234             free(config->rh_vserver.proxy_port);
235           config->rh_vserver.proxy_port = strdup(value);
236         } else {
237           if (config->rh_vserver.proxy != NULL)
238             free(config->rh_vserver.proxy);
239           config->rh_vserver.proxy = strdup(value);
240         }
241       } else if (strncmp(sub_key, "bittorrent", 10) == 0) {
242         if (strncmp(sub_key, "bittorrent_allow", 16) == 0) {
243           if (config->rh_vserver.bittorrent_allow != NULL)
244             free(config->rh_vserver.bittorrent_allow);
245           config->rh_vserver.bittorrent_allow = strdup(value);
246         } else {
247           if (config->rh_vserver.bittorrent != NULL)
248             free(config->rh_vserver.bittorrent);
249           config->rh_vserver.bittorrent = strdup(value);
250         }
251       } else if (strncmp(sub_key, "radius_host", 11) == 0) {
252         if (config->rh_vserver.radius_host != NULL)
253           free(config->rh_vserver.radius_host);
254         config->rh_vserver.radius_host = strdup(value);
255       } else if (strncmp(sub_key, "radius_secret", 13) == 0) {
256         if (config->rh_vserver.radius_secret != NULL)
257           free(config->rh_vserver.radius_secret);
258         config->rh_vserver.radius_secret = strdup(value);
259       } else if (strncmp(sub_key, "radius_encrypt", 14) == 0) {
260         if (config->rh_vserver.radius_encrypt != NULL)
261           free(config->rh_vserver.radius_encrypt);
262         config->rh_vserver.radius_encrypt = strdup(value);
263       } else if (strncmp(sub_key, "radius_auth_port", 16) == 0) {
264         if (config->rh_vserver.radius_auth_port != NULL)
265           free(config->rh_vserver.radius_auth_port);
266         config->rh_vserver.radius_auth_port = strdup(value);
267       } else if (strncmp(sub_key, "radius_account_port", 19) == 0) {
268         if (config->rh_vserver.radius_account_port != NULL)
269           free(config->rh_vserver.radius_account_port);
270         config->rh_vserver.radius_account_port = strdup(value);
271       } else if (strncmp(sub_key, "nas_identifier", 14) == 0) {
272         if (config->rh_vserver.nas_identifier != NULL)
273           free(config->rh_vserver.nas_identifier);
274         config->rh_vserver.nas_identifier = strdup(value);
275       } else if (strncmp(sub_key, "nas_port", 8) == 0) {
276         if (config->rh_vserver.nas_port != NULL)
277           free(config->rh_vserver.nas_port);
278         config->rh_vserver.nas_port = strdup(value);
279       } else if (strncmp(sub_key, "nas_login_title", 15) == 0) {
280         if (config->rh_vserver.nas_login_title != NULL)
281           free(config->rh_vserver.nas_login_title);
282         config->rh_vserver.nas_login_title = strdup(value);
283       } else if (strncmp(sub_key, "nas_default_redirect", 20) == 0) {
284         if (config->rh_vserver.nas_default_redirect != NULL)
285           free(config->rh_vserver.nas_default_redirect);
286         config->rh_vserver.nas_default_redirect = strdup(value);
287       } else if (strncmp(sub_key, "nas_default_language", 20) == 0) {
288         if (config->rh_vserver.nas_default_language != NULL)
289           free(config->rh_vserver.nas_default_language);
290         config->rh_vserver.nas_default_language = strdup(value);
291       } else if (strncmp(sub_key, "nas_weblogin_template", 21) == 0) {
292         if (config->rh_vserver.nas_weblogin_template != NULL)
293           free(config->rh_vserver.nas_weblogin_template);
294         config->rh_vserver.nas_weblogin_template = strdup(value);
295       }
296       break;
297   }
298
299   
300   rh_free(&clone_key);
301
302   return lcfg_status_ok;
303 }
304
305 int get_config(const char *cfg_file, union rahunas_config *config) {
306   lcfg_visitor_function visitor_func = rahunas_visitor;
307   struct lcfg *c = lcfg_new(cfg_file);
308   
309   syslog(LOG_INFO, "Parsing config file: %s", cfg_file);
310
311   if (lcfg_parse(c) != lcfg_status_ok) {
312     syslog(LOG_ERR, "config error: %s", lcfg_error_get(c));
313     lcfg_delete(c);
314     return -1;
315   }
316
317   syslog(LOG_INFO, "Processing config file: %s", cfg_file);
318   if (lcfg_accept(c, visitor_func, config) != lcfg_status_ok) {
319     syslog(LOG_ERR, "config error: %s", lcfg_error_get(c));
320     lcfg_delete(c);
321     return -1;
322   }
323
324   lcfg_delete(c);
325
326   return 0;
327 }
328
329
330 int get_value(const char *cfg_file, const char *key, void **data, size_t *len)
331 {
332   lcfg_visitor_function visitor_func = rahunas_visitor;
333   struct lcfg *c = lcfg_new(cfg_file);
334   
335   if (lcfg_parse(c) != lcfg_status_ok) {
336     syslog(LOG_ERR, "config error: %s", lcfg_error_get(c));
337     lcfg_delete(c);
338     return -1;
339   }
340
341   if (lcfg_value_get(c, key, data, len) != lcfg_status_ok) {
342     lcfg_delete(c);
343     return -1;
344   } 
345
346   lcfg_delete(c);
347
348   return 0;
349 }
350
351 int get_vservers_config(const char *conf_dir, struct main_server *server)
352 {
353   DIR *dp;
354   struct dirent *dirp;
355   void *data = NULL;
356   size_t len;
357   char conf_file[200];
358
359   if ((dp = opendir(conf_dir)) == NULL)
360     return errno;
361   
362   while ((dirp = readdir(dp)) != NULL) {
363     if (strstr(dirp->d_name, ".conf") == NULL)
364       continue;
365
366     memset(conf_file, 0, sizeof(conf_file));
367
368     strncat(conf_file, conf_dir, sizeof(conf_file));
369     strncat(conf_file, "/", 1);
370     strncat(conf_file, dirp->d_name, sizeof(conf_file));
371
372     syslog(LOG_INFO, "Loading config file: %s", conf_file);
373     
374     register_vserver(server, conf_file);
375   }
376   
377   closedir(dp);
378   return 0;
379 }
380
381 int get_serviceclass_config(const char *conf_dir, struct main_server *server)
382 {
383   DIR *dp;
384   struct dirent *dirp;
385   void *data = NULL;
386   size_t len;
387   char conf_file[200];
388
389   if ((dp = opendir(conf_dir)) == NULL)
390     return errno;
391
392   while ((dirp = readdir(dp)) != NULL) {
393     if (strstr(dirp->d_name, ".conf") == NULL)
394       continue;
395
396     memset(conf_file, 0, sizeof(conf_file));
397
398     strncat(conf_file, conf_dir, sizeof(conf_file));
399     strncat(conf_file, "/", 1);
400     strncat(conf_file, dirp->d_name, sizeof(conf_file));
401
402     syslog(LOG_INFO, "Loading service class config file: %s", conf_file);
403
404     register_serviceclass(server, conf_file);
405   }
406
407   closedir(dp);
408   return 0;
409 }
410
411 int cleanup_vserver_config(struct rahunas_vserver_config *config)
412 {
413   rh_free(&(config->vserver_name));  
414   rh_free(&(config->dev_external));
415   rh_free(&(config->dev_internal));
416   rh_free(&(config->vlan));
417   rh_free(&(config->vlan_raw_dev_external));
418   rh_free(&(config->vlan_raw_dev_internal));
419   rh_free(&(config->bridge));
420   rh_free(&(config->masquerade));
421   rh_free(&(config->ignore_mac));
422   rh_free(&(config->vserver_ip));
423   rh_free(&(config->vserver_fqdn));
424   rh_free(&(config->vserver_ports_allow));
425   rh_free(&(config->vserver_ports_intercept));
426   rh_free(&(config->clients));
427   rh_free(&(config->excluded));
428   rh_free(&(config->dns));
429   rh_free(&(config->ssh));
430   rh_free(&(config->proxy));
431   rh_free(&(config->proxy_host));
432   rh_free(&(config->proxy_port));
433   rh_free(&(config->bittorrent));
434   rh_free(&(config->bittorrent_allow));
435   rh_free(&(config->radius_host));
436   rh_free(&(config->radius_secret));
437   rh_free(&(config->radius_encrypt));
438   rh_free(&(config->radius_auth_port));
439   rh_free(&(config->radius_account_port));
440   rh_free(&(config->nas_identifier));
441   rh_free(&(config->nas_port));
442   rh_free(&(config->nas_login_title));
443   rh_free(&(config->nas_default_redirect));
444   rh_free(&(config->nas_default_language));
445   rh_free(&(config->nas_weblogin_template));
446
447   return 0;
448 }
449
450 int cleanup_serviceclass_config(struct rahunas_serviceclass_config *config)
451 {
452   rh_free(&(config->serviceclass_name));
453   rh_free(&(config->description));
454   rh_free(&(config->network));
455   rh_free(&(config->fake_arpd));
456   rh_free(&(config->fake_arpd_iface));
457
458   return 0;
459 }
460
461 int cleanup_mainserver_config(struct rahunas_main_config *config)
462 {
463   rh_free(&(config->conf_dir));  
464   rh_free(&(config->serviceclass_conf_dir));
465   rh_free(&(config->log_file));
466   rh_free(&(config->dhcp));
467
468   return 0;
469 }
470
471
472 GList *append_interface (GList *inf, 
473                          const char *inf_name)
474 {
475   GList *runner = NULL;
476   struct interfaces *iface = NULL;
477   struct interfaces *item  = NULL;
478   int    ifb_ifno;
479
480   if (!inf_name)
481     return inf;
482
483   item = (struct interfaces *) malloc (sizeof (struct interfaces));
484   if (!item)
485     return inf;
486
487   runner = g_list_first (inf);
488   while (runner != NULL)
489     {
490       iface = (struct interfaces *)runner->data;
491       if (iface->dev_internal &&
492           strncmp(iface->dev_internal, inf_name, strlen(inf_name)) == 0)
493         {
494           // Already in the list
495           (iface->hit)++;
496           free(item); 
497           return inf;
498         }
499       runner = g_list_next (runner);
500     }
501
502 done:
503   ifb_ifno = ifb_interface_reserve ();
504   if (ifb_ifno < 0)
505     {
506       free (item);
507       return inf;
508     }
509
510   strncpy(item->dev_internal, inf_name, sizeof (item->dev_internal));
511   snprintf(item->dev_ifb, sizeof (item->dev_ifb), "ifb%d", ifb_ifno);
512   item->init = 0;
513   item->hit  = 1;
514   DP ("Interface append: %s, %s", item->dev_internal, item->dev_ifb);
515   
516   return g_list_append (inf, item);
517 }
518
519 GList *remove_interface (GList *inf,
520                          const char *inf_name)
521 {
522   GList *runner = NULL;
523   struct interfaces *iface = NULL;
524   int    ifb_ifno;
525
526   if (!inf_name || !inf)
527     return inf;
528
529   runner = g_list_first (inf);
530   while (runner != NULL)
531     {
532       iface = (struct interfaces *)runner->data;
533       if (iface->dev_internal &&
534           strncmp (iface->dev_internal, inf_name, strlen (inf_name)) == 0)
535         {
536           iface->hit--;
537           if (iface->hit <=0 )
538             {
539               sscanf (iface->dev_ifb, "ifb%d", &ifb_ifno);
540               ifb_interface_release (ifb_ifno);
541               if (iface)
542                 free (iface);
543
544               return g_list_delete_link (inf, runner);
545             }
546         }
547
548       runner = g_list_next (runner);
549     }
550
551   return inf;
552 }
553
554
555 struct interfaces *get_interface (GList *inf,
556                                   const char *inf_name)
557 {
558   GList *runner = NULL;
559   struct interfaces *iface = NULL;
560   
561   if (!inf_name || !inf)
562     return NULL;
563
564   runner = g_list_first (inf);
565
566   while (runner != NULL)
567     {
568       iface = (struct interfaces *) runner->data;
569       if (strncmp (iface->dev_internal, inf_name, strlen (inf_name)) == 0)
570         return iface;
571
572       runner = g_list_next (runner);
573     } 
574
575   return NULL;
576 }
577
578 int ifb_interface_reserve (void)
579 {
580   int i;
581   unsigned long mask = 1;
582
583   for (i=0; i < MAX_IFB_IFACE; i++)
584     {
585       mask = 1 << i;
586       if (!(ifb_reserved & mask))
587         {
588           ifb_reserved |= mask;
589           return i;
590         } 
591     }
592
593   return -1;
594 }
595
596 void ifb_interface_release (int ifno)
597 {
598   unsigned long mask = 1;
599
600   mask <<= ifno;
601   mask = ~mask;
602   ifb_reserved &= mask;
603 }