Use :retab to conver tab to 2 spaces
[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 "rh-ipset.h"
13 #include "rh-utils.h"
14
15 int kernel_getsocket(void)
16 {
17   int sockfd = -1;
18   int tries = GETSOCK_TRIES;
19
20   while (tries > 0) {
21     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
22     if (sockfd < 0) {
23       syslog(LOG_ERR, "Failed kernel_getsocket() errno=%d, Retry....", errno);
24       sleep(2);
25     } else {
26       return sockfd;
27     }
28     tries--;
29   }
30   
31   exit(EXIT_FAILURE);
32 }
33
34 int wrapped_getsockopt(void *data, socklen_t *size)
35 {
36   int res;
37   int sockfd = kernel_getsocket();
38
39   if (sockfd < 0)
40     return -1;
41
42   /* Send! */
43   res = getsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
44   if (res != 0)
45     DP("res=%d errno=%d", res, errno);
46
47   shutdown(sockfd, SHUT_RDWR);
48   close(sockfd);
49
50   return res;
51 }
52
53 int wrapped_setsockopt(void *data, socklen_t size)
54 {
55   int res;
56   int sockfd = kernel_getsocket();
57
58   if (sockfd < 0)
59     return -1;
60
61   /* Send! */
62   res = setsockopt(sockfd, SOL_IP, SO_IP_SET, data, size);
63   if (res != 0)
64     DP("res=%d errno=%d", res, errno);
65
66   shutdown(sockfd, SHUT_RDWR);
67   close(sockfd);
68
69   return res; 
70 }
71
72 void kernel_getfrom(void *data, socklen_t * size)
73 {
74   int res = wrapped_getsockopt(data, size);
75
76   if (res != 0)
77     DP("res=%d errno=%d", res, errno);
78 }
79
80 int kernel_sendto_handleerrno(unsigned op, void *data, socklen_t size)
81 {
82   int res = wrapped_setsockopt(data, size);
83
84   if (res !=0) {
85     DP("res=%d errno=%d", res, errno);
86     return -1;
87   }
88
89   return 0;
90 }
91
92 void kernel_sendto(void *data, size_t size)
93 {
94   int res = wrapped_setsockopt(data, size);
95
96   if (res != 0)
97     DP("res=%d errno=%d", res, errno);
98 }
99
100 int kernel_getfrom_handleerrno(void *data, size_t * size)
101 {
102   int res = wrapped_getsockopt(data, size);
103
104   if (res != 0) {
105     DP("res=%d errno=%d", res, errno);
106     return -1;
107   }
108
109   return 0;
110 }
111
112 struct set *set_adt_get(const char *name)
113 {
114   struct ip_set_req_adt_get req_adt_get;
115   struct set *set;
116   socklen_t size;
117
118   DP("%s", name);
119
120   req_adt_get.op = IP_SET_OP_ADT_GET;
121   req_adt_get.version = IP_SET_PROTOCOL_VERSION;
122   strcpy(req_adt_get.set.name, name);
123   size = sizeof(struct ip_set_req_adt_get);
124
125   kernel_getfrom((void *) &req_adt_get, &size);
126
127   set = rh_malloc(sizeof(struct set));
128   strcpy(set->name, name);
129   set->index = req_adt_get.set.index;
130
131   return set;
132 }
133
134
135 void parse_ip(const char *str, ip_set_ip_t *ip)
136 {
137   struct in_addr addr;
138
139   DP("%s", str);
140
141   if (inet_aton(str, &addr) != 0) {
142     *ip = ntohl(addr.s_addr);
143     return;
144   }
145 }
146
147 void parse_mac(const char *mac, unsigned char *ethernet)
148 {
149   unsigned int i = 0;
150   if (strlen(mac) != ETH_ALEN * 3 - 1)
151     return;
152
153   DP("%s", mac);
154
155   for (i = 0; i < ETH_ALEN; i++) {
156     long number;
157     char *end;
158     
159     number = strtol(mac + i * 3, &end, 16);
160     
161     if (end == mac + i * 3 + 2 && number >= 0 && number <=255)
162       ethernet[i] = number;
163     else
164       return;
165   }
166   return;
167 }
168
169 char *ip_tostring(ip_set_ip_t ip)
170 {
171   struct in_addr addr;
172   addr.s_addr = htonl(ip);
173
174   return inet_ntoa(addr);
175 }
176
177 char *mac_tostring(unsigned char macaddress[ETH_ALEN])
178 {
179   static char mac_string[18] = "";
180  
181   sprintf(mac_string, "%02X:%02X:%02X:%02X:%02X:%02X", 
182           macaddress[0], macaddress[1], macaddress[2],
183           macaddress[3], macaddress[4], macaddress[5]);
184   return mac_string; 
185 }
186
187 int set_adtip(struct set *rahunas_set, const char *adtip, const char *adtmac, 
188               unsigned op)
189 {
190   ip_set_ip_t ip;
191   unsigned char mac[ETH_ALEN] = {0,0,0,0,0,0};
192   parse_ip(adtip, &ip);  
193   parse_mac(adtmac, &mac);
194
195   return set_adtip_nb(rahunas_set, &ip, mac, op);
196 }
197
198 int set_adtip_nb(struct set *rahunas_set, ip_set_ip_t *adtip, 
199                      unsigned char adtmac[ETH_ALEN], unsigned op)
200 {
201   struct ip_set_req_adt *req_adt = NULL;
202   struct ip_set_req_rahunas req;
203
204   size_t size;
205   void *data;
206   int res = 0;
207
208   if (rahunas_set == NULL)
209     return -1;
210
211   size = sizeof(struct ip_set_req_adt) + sizeof(struct ip_set_req_rahunas);
212   data = rh_malloc(size);
213
214   memcpy(&req.ip, adtip, sizeof(ip_set_ip_t));
215   memcpy(&req.ethernet, adtmac, ETH_ALEN);
216
217   req_adt = (struct ip_set_req_adt *) data;
218   req_adt->op = op;
219   req_adt->index = rahunas_set->index;
220   memcpy(data + sizeof(struct ip_set_req_adt), &req, 
221            sizeof(struct ip_set_req_rahunas));
222
223   if (kernel_sendto_handleerrno(op, data, size) == -1)
224     switch (op) {
225     case IP_SET_OP_ADD_IP:
226       DP("%s:%s is already in set", ip_tostring(adtip), mac_tostring(adtmac));
227       res = RH_IS_IN_SET;
228       break;
229     case IP_SET_OP_DEL_IP:
230       DP("%s:%s is not in set", ip_tostring(adtip), mac_tostring(adtmac));
231       res = RH_IS_NOT_IN_SET; 
232       break;
233     case IP_SET_OP_TEST_IP:
234       DP("%s:%s is in set", ip_tostring(adtip), mac_tostring(adtmac));
235       res = RH_IS_IN_SET;
236       break;
237     default:
238       break;
239     }
240   else
241     switch (op) {
242     case IP_SET_OP_TEST_IP:
243       DP("%s:%s is not in set", ip_tostring(adtip), mac_tostring(adtmac));
244       res = RH_IS_NOT_IN_SET;
245       break;
246     default:
247       break;   
248     }
249
250   rh_free(&data);
251
252   return res;
253 }
254
255 void set_flush(const char *name)
256 {
257   struct ip_set_req_std req;
258
259   req.op = IP_SET_OP_FLUSH;
260   req.version = IP_SET_PROTOCOL_VERSION;
261   strcpy(req.name, name);
262
263   kernel_sendto(&req, sizeof(struct ip_set_req_std));
264 }
265
266 size_t load_set_list(const char name[IP_SET_MAXNAMELEN],
267           ip_set_id_t *idx,
268           unsigned op, unsigned cmd)
269 {
270   void *data = NULL;
271   struct ip_set_req_max_sets req_max_sets;
272   struct ip_set_name_list *name_list;
273   struct set *set;
274   ip_set_id_t i;
275   socklen_t size, req_size;
276   int repeated = 0, res = 0;
277
278   DP("%s %s", cmd == CMD_MAX_SETS ? "MAX_SETS"
279         : cmd == CMD_LIST_SIZE ? "LIST_SIZE"
280         : "SAVE_SIZE",
281         name);
282   
283 tryagain:
284   if (set_list != NULL) {
285     for (i = 0; i < max_sets; i++)
286       if (set_list[i] != NULL) {
287         free(set_list[i]);
288         set_list[i] = NULL;
289       }
290     free(set_list);
291     set_list = NULL;
292   }
293   /* Get max_sets */
294   req_max_sets.op = IP_SET_OP_MAX_SETS;
295   req_max_sets.version = IP_SET_PROTOCOL_VERSION;
296   strcpy(req_max_sets.set.name, name);
297   size = sizeof(req_max_sets);
298   kernel_getfrom(&req_max_sets, &size);
299
300   DP("got MAX_SETS: sets %d, max_sets %d",
301      req_max_sets.sets, req_max_sets.max_sets);
302
303   max_sets = req_max_sets.max_sets;
304   set_list = rh_malloc(max_sets * sizeof(struct set *));
305   memset(set_list, 0, max_sets * sizeof(struct set *));
306   *idx = req_max_sets.set.index;
307
308   if (req_max_sets.sets == 0)
309     /* No sets in kernel */
310     return 0;
311
312   /* Get setnames */
313   size = req_size = sizeof(struct ip_set_req_setnames) 
314         + req_max_sets.sets * sizeof(struct ip_set_name_list);
315   data = rh_malloc(size);
316   ((struct ip_set_req_setnames *) data)->op = op;
317   ((struct ip_set_req_setnames *) data)->index = *idx;
318
319   res = kernel_getfrom_handleerrno(data, &size);
320
321   if (res != 0 || size != req_size) {
322     rh_free(&data);
323     if (repeated++ < LIST_TRIES)
324       goto tryagain;
325
326     DP("Tried to get sets from kernel %d times"
327          " and failed. Please try again when the load on"
328          " the sets has gone down.", LIST_TRIES);
329     return -1;
330   }
331     
332   /* Load in setnames */
333   size = sizeof(struct ip_set_req_setnames);      
334   while (size + sizeof(struct ip_set_name_list) <= req_size) {
335     name_list = (struct ip_set_name_list *)
336       (data + size);
337     set = rh_malloc(sizeof(struct set));
338     strcpy(set->name, name_list->name);
339     set->index = name_list->index;
340     set->id = name_list->id;
341     set_list[name_list->index] = set;
342     size += sizeof(struct ip_set_name_list);
343   }
344   /* Size to get set members, bindings */
345   size = ((struct ip_set_req_setnames *)data)->size;
346   rh_free(&data);
347   
348   return size;
349 }
350
351 int get_header_from_set (struct rahunas_map *map)
352 {
353   struct ip_set_req_rahunas_create *header = NULL;
354   void *data = NULL;
355   ip_set_id_t idx;
356   socklen_t size, req_size;
357   size_t offset;
358   int res = 0;
359   in_addr_t first_ip;
360   in_addr_t last_ip;
361
362   size = req_size = load_set_list(rh_config.set_name, &idx, 
363                                   IP_SET_OP_LIST_SIZE, CMD_LIST); 
364
365   DP("Get Set Size: %d", size);
366   
367   if (size) {
368     data = rh_malloc(size);
369     ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST;
370     ((struct ip_set_req_list *) data)->index = idx;
371     res = kernel_getfrom_handleerrno(data, &size);
372     DP("get_lists getsockopt() res=%d errno=%d", res, errno);
373
374     if (res != 0 || size != req_size) {
375       rh_free(&data);
376       return -EAGAIN;
377     }
378     size = 0;
379   }
380
381   offset = sizeof(struct ip_set_list);
382   header = (struct ip_set_req_rahunas_create *) (data + offset);
383
384   first_ip = htonl(header->from); 
385   last_ip = htonl(header->to); 
386   
387   memcpy(&map->first_ip, &first_ip, sizeof(in_addr_t));
388   memcpy(&map->last_ip, &last_ip, sizeof(in_addr_t));
389   map->size = ntohl(map->last_ip) - ntohl(map->first_ip) + 1;
390
391   logmsg(RH_LOG_NORMAL, "First IP: %s", ip_tostring(ntohl(map->first_ip)));
392   logmsg(RH_LOG_NORMAL, "Last  IP: %s", ip_tostring(ntohl(map->last_ip)));
393   logmsg(RH_LOG_NORMAL, "Set Size: %lu", map->size);
394
395   rh_free(&data);
396   return res;
397 }
398
399 int walk_through_set (int (*callback)(void *))
400 {
401   void *data = NULL;
402   ip_set_id_t idx;
403   socklen_t size, req_size;
404   int res = 0;
405
406   size = req_size = load_set_list(rh_config.set_name, &idx, 
407                                   IP_SET_OP_LIST_SIZE, CMD_LIST); 
408
409   DP("Get Set Size: %d", size);
410   
411   if (size) {
412     data = rh_malloc(size);
413     ((struct ip_set_req_list *) data)->op = IP_SET_OP_LIST;
414     ((struct ip_set_req_list *) data)->index = idx;
415     res = kernel_getfrom_handleerrno(data, &size);
416     DP("get_lists getsockopt() res=%d errno=%d", res, errno);
417
418     if (res != 0 || size != req_size) {
419       rh_free(&data);
420       return -EAGAIN;
421     }
422     size = 0;
423   }
424   
425   if (data != NULL)
426     (*callback)(data);
427
428   rh_free(&data);
429   return res;
430 }