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