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