2f3d56b179f62897577d5f129666dd44fedfffb6
[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 }