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