Fix compiler warnings
[rahunas] / src / rh-ipset.c
1 /**
2  * RahuNAS ipset implementation
3  * Author: Neutron Soutmun <neo.neutron@gmail.com>
4  * Date:   2008-08-20
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <syslog.h>
13 #include <ipset/ip_set_rahunas_ipiphash.h>
14 #include "rh-ipset.h"
15 #include "rh-utils.h"
16
17 ip_set_id_t max_sets;
18 static int protocol_version = 0;
19
20 #define DONT_ALIGN    (protocol_version == IP_SET_PROTOCOL_UNALIGNED)
21 #define ALIGNED(len)  IPSET_VALIGN(len, DONT_ALIGN)
22
23 int kernel_getsocket(void)
24 {
25   int sockfd = -1;
26   int tries = GETSOCK_TRIES;
27
28   while (tries > 0) {
29     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
30     if (sockfd < 0) {
31       syslog(LOG_ERR, "Failed kernel_getsocket() errno=%d, Retry....", errno);
32       sleep(2);
33     } else {
34       return sockfd;
35     }
36     tries--;
37   }
38   
39   exit(EXIT_FAILURE);
40 }
41
42 int wrapped_getsockopt(void *data, socklen_t *size)
43 {
44   int res;
45   int sockfd = kernel_getsocket();
46
47   if (sockfd < 0)
48     return -1;
49
50   /* Send! */
51   res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
52   if (res != 0)
53     DP("res=%d errno=%d", res, errno);
54
55   shutdown(sockfd, SHUT_RDWR);
56   close(sockfd);
57
58   return res;
59 }
60
61 int wrapped_setsockopt(void *data, socklen_t size)
62 {
63   int res;
64   int sockfd = kernel_getsocket();
65
66   if (sockfd < 0)
67     return -1;
68
69   /* Send! */
70   res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
71   if (res != 0)
72     DP("res=%d errno=%d", res, errno);
73
74   shutdown(sockfd, SHUT_RDWR);
75   close(sockfd);
76
77   return res; 
78 }
79
80 void kernel_getfrom(void *data, socklen_t * size)
81 {
82   int res = wrapped_getsockopt(data, size);
83
84   if (res != 0)
85     DP("res=%d errno=%d", res, errno);
86 }
87
88 int kernel_sendto_handleerrno(unsigned int op, void *data, socklen_t size)
89 {
90   int res = wrapped_setsockopt(data, size);
91
92   if (res !=0) {
93     if (errno == EEXIST)
94       return -1;
95     else
96       DP("res=%d errno=%d", res, errno);
97   }
98
99   return 0;
100 }
101
102 void kernel_sendto(void *data, size_t size)
103 {
104   int res = wrapped_setsockopt(data, size);
105
106   if (res != 0)
107     DP("res=%d errno=%d", res, errno);
108 }
109
110 int kernel_getfrom_handleerrno(void *data, socklen_t * size)
111 {
112   int res = wrapped_getsockopt(data, size);
113
114   if (res != 0) {
115     if (errno == EAGAIN)
116       return -1;
117     else
118       DP("res=%d errno=%d", res, errno);
119   }
120
121   return 0;
122 }
123
124 static void check_protocolversion(void)
125 {
126   struct ip_set_req_version req_version;
127   socklen_t size = sizeof(struct ip_set_req_version);
128   int res;
129
130   if (protocol_version)
131     return;
132
133   req_version.op = IP_SET_OP_VERSION;
134   res = wrapped_getsockopt(&req_version, &size);
135
136   if (res != 0)
137     logmsg(RH_LOG_ERROR, "Couldn't verify kernel module version!");
138
139   if (!(req_version.version == IP_SET_PROTOCOL_VERSION
140         || req_version.version == IP_SET_PROTOCOL_UNALIGNED)) {
141     logmsg(RH_LOG_ERROR, "Kernel ip_set module is of protocol version %u."
142          "I'm of protocol version %u.\n"
143          "Please upgrade your kernel and/or ipset(8) utillity.",
144          req_version.version, IP_SET_PROTOCOL_VERSION);
145   }
146
147   protocol_version = req_version.version;
148   DP ("Protocol Version %u", protocol_version);
149 }
150
151 struct set *set_adt_get(const char *name)
152 {
153   struct ip_set_req_adt_get req_adt_get;
154   struct set *set;
155   socklen_t size;
156
157   DP("%s", name);
158
159   check_protocolversion ();
160
161   req_adt_get.op = IP_SET_OP_ADT_GET;
162   req_adt_get.version = protocol_version;
163   strncpy(req_adt_get.set.name, name, IP_SET_MAXNAMELEN);
164   size = sizeof(struct ip_set_req_adt_get);
165
166   kernel_getfrom((void *) &req_adt_get, &size);
167
168   set = rh_malloc(sizeof(struct set));
169   strcpy(set->name, name);
170   set->index = req_adt_get.set.index;
171
172   return set;
173 }
174
175
176 void parse_ip(const char *str, ip_set_ip_t *ip)
177 {
178   struct in_addr addr;
179
180   DP("%s", str);
181
182   if (inet_aton(str, &addr) != 0) {
183     *ip = ntohl(addr.s_addr);
184     return;
185   }
186 }
187
188 void parse_mac(const char *mac, unsigned char *ethernet)
189 {
190   unsigned int i = 0;
191   if (!mac)
192     return;
193
194   if (strlen(mac) != ETH_ALEN * 3 - 1)
195     return;
196
197   DP("%s", mac);
198
199   for (i = 0; i < ETH_ALEN; i++) {
200     long number;
201     char *end;
202     
203     number = strtol(mac + i * 3, &end, 16);
204     
205     if (end == mac + i * 3 + 2 && number >= 0 && number <=255)
206       ethernet[i] = number;
207     else
208       return;
209   }
210   return;
211 }
212
213 char *ip_tostring(ip_set_ip_t ip)
214 {
215   struct in_addr addr;
216   addr.s_addr = htonl(ip);
217
218   return inet_ntoa(addr);
219 }
220
221 char *mac_tostring(unsigned char macaddress[ETH_ALEN])
222 {
223   static char mac_string[18] = "";
224  
225   snprintf(mac_string, sizeof (mac_string), "%02X:%02X:%02X:%02X:%02X:%02X", 
226           macaddress[0], macaddress[1], macaddress[2],
227           macaddress[3], macaddress[4], macaddress[5]);
228   return mac_string; 
229 }
230
231 int set_adtip(struct set *rahunas_set, const char *adtip, const char *adtmac, 
232               unsigned int op)
233 {
234   ip_set_ip_t ip;
235   unsigned char mac[ETH_ALEN] = {0,0,0,0,0,0};
236   parse_ip(adtip, &ip);  
237   parse_mac(adtmac, mac);
238
239   return set_adtip_nb(rahunas_set, &ip, mac, op);
240 }
241
242 int set_adtip_nb(struct set *rahunas_set, ip_set_ip_t *adtip, 
243                  unsigned char adtmac[ETH_ALEN], unsigned int op)
244 {
245   struct ip_set_req_adt *req_adt = NULL;
246   struct ip_set_req_rahunas req;
247
248   size_t size;
249   void *data;
250   int res = 0;
251
252   check_protocolversion ();
253
254   if (rahunas_set == NULL)
255     return -1;
256
257   size = ALIGNED(sizeof(struct ip_set_req_adt)) + sizeof(struct ip_set_req_rahunas);
258   data = rh_malloc(size);
259
260   memcpy(&req.ip, adtip, sizeof(ip_set_ip_t));
261   memcpy(&req.ethernet, adtmac, ETH_ALEN);
262
263   req_adt = (struct ip_set_req_adt *) data;
264   req_adt->op = op;
265   req_adt->index = rahunas_set->index;
266   memcpy(data + ALIGNED(sizeof(struct ip_set_req_adt)), &req,
267            sizeof(struct ip_set_req_rahunas));
268
269   if (kernel_sendto_handleerrno(op, data, size) == -1)
270     switch (op) {
271     case IP_SET_OP_ADD_IP:
272       DP("%s:%s is already in set", ip_tostring(adtip), mac_tostring(adtmac));
273       res = RH_IS_IN_SET;
274       break;
275     case IP_SET_OP_DEL_IP:
276       DP("%s:%s is not in set", ip_tostring(adtip), mac_tostring(adtmac));
277       res = RH_IS_NOT_IN_SET; 
278       break;
279     case IP_SET_OP_TEST_IP:
280       DP("%s:%s is in set", ip_tostring(adtip), mac_tostring(adtmac));
281       res = RH_IS_IN_SET;
282       break;
283     default:
284       break;
285     }
286   else
287     switch (op) {
288     case IP_SET_OP_TEST_IP:
289       DP("%s:%s is not in set", ip_tostring(adtip), mac_tostring(adtmac));
290       res = RH_IS_NOT_IN_SET;
291       break;
292     default:
293       break;   
294     }
295
296   rh_free(&data);
297
298   return res;
299 }
300
301 int set_ipiphash_adtip(struct set *rahunas_set, const char *ip,
302                        const char *ip1, unsigned int op)
303 {
304   ip_set_ip_t _ip;
305   ip_set_ip_t _ip1;
306   parse_ip(ip, &_ip);
307   parse_ip(ip1, &_ip1);
308
309   return set_ipiphash_adtip_nb(rahunas_set, &_ip, &_ip1, op);
310 }
311
312 int set_ipiphash_adtip_nb(struct set *rahunas_set, ip_set_ip_t *ip,
313                           ip_set_ip_t *ip1, unsigned int op)
314 {
315   struct ip_set_req_adt *req_adt = NULL;
316   struct ip_set_req_rahunas_ipiphash req;
317
318   size_t size;
319   void *data;
320   int res = 0;
321
322   check_protocolversion ();
323
324   if (rahunas_set == NULL)
325     return -1;
326
327   size = ALIGNED(sizeof(struct ip_set_req_adt)) + sizeof(struct ip_set_req_rahunas_ipiphash);
328   data = rh_malloc(size);
329
330   memcpy(&req.ip, ip, sizeof(ip_set_ip_t));
331   memcpy(&req.ip1, ip1, sizeof(ip_set_ip_t));
332
333   req_adt = (struct ip_set_req_adt *) data;
334   req_adt->op = op;
335   req_adt->index = rahunas_set->index;
336   memcpy(data + ALIGNED(sizeof(struct ip_set_req_adt)), &req,
337            sizeof(struct ip_set_req_rahunas_ipiphash));
338
339   if (kernel_sendto_handleerrno(op, data, size) == -1)
340     switch (op) {
341     case IP_SET_OP_ADD_IP:
342       DP("%s:%s is already in set", ip_tostring(ip), ip_tostring(ip1));
343       res = RH_IS_IN_SET;
344       break;
345     case IP_SET_OP_DEL_IP:
346       DP("%s:%s is not in set", ip_tostring(ip), ip_tostring(ip1));
347       res = RH_IS_NOT_IN_SET;
348       break;
349     case IP_SET_OP_TEST_IP:
350       DP("%s:%s is in set", ip_tostring(ip), ip_tostring(ip1));
351       res = RH_IS_IN_SET;
352       break;
353     default:
354       break;
355     }
356   else
357     switch (op) {
358     case IP_SET_OP_TEST_IP:
359       DP("%s:%s is not in set", ip_tostring(ip), ip_tostring(ip1));
360       res = RH_IS_NOT_IN_SET;
361       break;
362     default:
363       break;
364     }
365
366   rh_free(&data);
367
368   return res;
369 }
370
371 void set_flush(const char *name)
372 {
373   struct ip_set_req_std req;
374
375   check_protocolversion ();
376
377   req.op = IP_SET_OP_FLUSH;
378   req.version = protocol_version;
379   strncpy(req.name, name, IP_SET_MAXNAMELEN);
380
381   kernel_sendto(&req, sizeof(struct ip_set_req_std));
382 }
383
384 size_t load_set_list(RHVServer *vs, const char name[IP_SET_MAXNAMELEN],
385           ip_set_id_t *idx,
386           unsigned int op, unsigned int cmd)
387 {
388   void *data = NULL;
389   struct ip_set_req_max_sets req_max_sets;
390   struct ip_set_name_list *name_list;
391   struct set *set;
392   ip_set_id_t i;
393   socklen_t size, req_size;
394   int repeated = 0, res = 0;
395
396   check_protocolversion ();
397
398   DP("%s %s", cmd == CMD_MAX_SETS ? "MAX_SETS"
399         : cmd == CMD_LIST_SIZE ? "LIST_SIZE"
400         : "SAVE_SIZE",
401         name);
402   
403 tryagain:
404
405   /* Get max_sets */
406   req_max_sets.op = IP_SET_OP_MAX_SETS;
407   req_max_sets.version = protocol_version;
408   strncpy(req_max_sets.set.name, name, IP_SET_MAXNAMELEN);
409   size = sizeof(req_max_sets);
410   kernel_getfrom(&req_max_sets, &size);
411
412   DP("got MAX_SETS: sets %d, max_sets %d",
413      req_max_sets.sets, req_max_sets.max_sets);
414
415   *idx = req_max_sets.set.index;
416   max_sets = req_max_sets.max_sets;
417
418   if (req_max_sets.sets == 0) {
419     /* No sets in kernel */
420     return 0;
421   }
422
423   /* Get setnames */
424   size = req_size = ALIGNED(sizeof(struct ip_set_req_setnames))
425         + req_max_sets.sets * ALIGNED(sizeof(struct ip_set_name_list));
426   data = rh_malloc(size);
427   ((struct ip_set_req_setnames *) data)->op = op;
428   ((struct ip_set_req_setnames *) data)->index = *idx;
429
430   res = kernel_getfrom_handleerrno(data, &size);
431
432   if (res != 0 || size != req_size) {
433     rh_free(&data);
434     if (repeated++ < LIST_TRIES)
435       goto tryagain;
436
437     DP("Tried to get sets from kernel %d times"
438          " and failed. Please try again when the load on"
439          " the sets has gone down.", LIST_TRIES);
440     return -1;
441   }
442
443   /* Size to get set members, bindings */
444   size = ((struct ip_set_req_setnames *)data)->size;
445
446   rh_free(&data);
447   
448   return size;
449 }
450
451 int get_header_from_set (RHVServer *vs)
452 {
453   struct ip_set_req_rahunas_create *header = NULL;
454   void *data = NULL;
455   ip_set_id_t idx;
456   socklen_t size, req_size;
457   size_t offset;
458   int res = 0;
459   in_addr_t first_ip;
460   in_addr_t last_ip;
461
462   if (vs == NULL)
463     return (-1);
464
465   check_protocolversion ();
466
467   size = req_size = load_set_list(vs, vs->vserver_config->vserver_name, &idx, 
468                                   IP_SET_OP_LIST_SIZE, CMD_LIST); 
469
470   DP("Get Set Size: %d", size);
471   
472   if (size) {
473     data = rh_malloc(size);
474     if (data == NULL)
475       return (-1);
476
477     ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST;
478     ((struct ip_set_req_list *) data)->index = idx;
479     res = kernel_getfrom_handleerrno(data, &size);
480     DP("get_lists getsockopt() res=%d errno=%d", res, errno);
481
482     if (res != 0 || size != req_size) {
483       rh_free(&data);
484       return -EAGAIN;
485     }
486     size = 0;
487   }
488
489   offset = ALIGNED(sizeof(struct ip_set_list));
490   header = (struct ip_set_req_rahunas_create *) (data + offset);
491
492   first_ip = htonl(header->from); 
493   last_ip = htonl(header->to); 
494   
495   memcpy(&(vs->v_map->first_ip), &first_ip, sizeof(in_addr_t));
496   memcpy(&(vs->v_map->last_ip), &last_ip, sizeof(in_addr_t));
497   vs->v_map->size = ntohl(last_ip) - ntohl(first_ip) + 1;
498
499   logmsg(RH_LOG_NORMAL, "[%s] First IP: %s", 
500          vs->vserver_config->vserver_name,
501          ip_tostring(ntohl(vs->v_map->first_ip)));
502   logmsg(RH_LOG_NORMAL, "[%s] Last  IP: %s", 
503          vs->vserver_config->vserver_name,
504          ip_tostring(ntohl(vs->v_map->last_ip)));
505   logmsg(RH_LOG_NORMAL, "[%s] Set Size: %lu", 
506          vs->vserver_config->vserver_name,
507          vs->v_map->size);
508
509   rh_free(&data);
510   return res;
511 }
512
513 int walk_through_set (int (*callback)(void *), RHVServer *vs)
514 {
515   struct processing_set process;
516   void *data = NULL;
517   ip_set_id_t idx;
518   socklen_t size, req_size;
519   int res = 0;
520
521   check_protocolversion ();
522
523   pthread_mutex_lock (&RHMtxLock);
524
525   size = req_size = load_set_list(vs, vs->vserver_config->vserver_name, &idx, 
526                                   IP_SET_OP_LIST_SIZE, CMD_LIST); 
527
528   DP("Get Set Size: %d", size);
529   
530   if (size) {
531     data = rh_malloc(size);
532     ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST;
533     ((struct ip_set_req_list *) data)->index = idx;
534     res = kernel_getfrom_handleerrno(data, &size);
535     DP("get_lists getsockopt() res=%d errno=%d", res, errno);
536
537     if (res != 0 || size != req_size) {
538       rh_free(&data);
539       pthread_mutex_unlock (&RHMtxLock);
540       return -EAGAIN;
541     }
542     size = 0;
543   }
544   
545   if (data != NULL) {
546     process.vs = vs;
547     process.list = data;
548
549     pthread_mutex_unlock (&RHMtxLock);
550     (*callback)(&process);
551     rh_free (&data);
552     return 0;
553   }
554
555   pthread_mutex_unlock (&RHMtxLock);
556   return 0;
557 }
558
559 struct ip_set_req_rahunas *get_data_from_set (void *data, unsigned int id,
560                                               const struct rahunas_map *map)
561 {
562   struct ip_set_list *setlist = NULL;
563   size_t header_offset = 0;
564   size_t offset = 0;
565   struct ip_set_req_rahunas *d = NULL;
566   ip_set_ip_t ip;
567
568   if (data == NULL || map == NULL)
569     return NULL;
570
571   ip = ntohl (map->first_ip) + id;
572
573   setlist = (struct ip_set_list *) (data);
574
575   header_offset = ALIGNED(sizeof(struct ip_set_list)) + setlist->header_size;
576
577   DP ("members_size %u", setlist->members_size);
578
579   while (offset < setlist->members_size) {
580     d = data + header_offset + offset;
581     DP ("req_ip: %u, seek_ip: %u", ip, d->ip);
582
583     if (ip == d->ip)
584       return d;
585
586     offset += ALIGNED (sizeof (struct ip_set_req_rahunas));
587   }
588
589   return NULL;
590 }