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