• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firewall3/ipsets.c

  1 /*
  2  * firewall3 - 3rd OpenWrt UCI firewall implementation
  3  *
  4  *   Copyright (C) 2013 Jo-Philipp Wich <jo@mein.io>
  5  *
  6  * Permission to use, copy, modify, and/or distribute this software for any
  7  * purpose with or without fee is hereby granted, provided that the above
  8  * copyright notice and this permission notice appear in all copies.
  9  *
 10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 17  */
 18 
 19 #include <ctype.h>
 20 
 21 #include "ipsets.h"
 22 
 23 
 24 const struct fw3_option fw3_ipset_opts[] = {
 25         FW3_OPT("enabled",       bool,           ipset,     enabled),
 26         FW3_OPT("reload_set",    bool,           ipset,     reload_set),
 27         FW3_OPT("counters",      bool,           ipset,     counters),
 28         FW3_OPT("comment",       bool,           ipset,     comment),
 29 
 30         FW3_OPT("name",          string,         ipset,     name),
 31         FW3_OPT("family",        family,         ipset,     family),
 32 
 33         FW3_OPT("storage",       ipset_method,   ipset,     method),
 34         FW3_LIST("match",        ipset_datatype, ipset,     datatypes),
 35 
 36         FW3_OPT("iprange",       address,        ipset,     iprange),
 37         FW3_OPT("portrange",     port,           ipset,     portrange),
 38 
 39         FW3_OPT("netmask",       int,            ipset,     netmask),
 40         FW3_OPT("maxelem",       int,            ipset,     maxelem),
 41         FW3_OPT("hashsize",      int,            ipset,     hashsize),
 42         FW3_OPT("timeout",       int,            ipset,     timeout),
 43 
 44         FW3_OPT("external",      string,         ipset,     external),
 45 
 46         FW3_LIST("entry",        setentry,       ipset,     entries),
 47         FW3_OPT("loadfile",      string,         ipset,     loadfile),
 48 
 49         { }
 50 };
 51 
 52 #define T(m, t1, t2, t3, r, o) \
 53         { FW3_IPSET_METHOD_##m, \
 54           FW3_IPSET_TYPE_##t1 | (FW3_IPSET_TYPE_##t2 << 8) | (FW3_IPSET_TYPE_##t3 << 16), \
 55           r, o }
 56 
 57 enum ipset_optflag {
 58         OPT_IPRANGE   = (1 << 0),
 59         OPT_PORTRANGE = (1 << 1),
 60         OPT_NETMASK   = (1 << 2),
 61         OPT_HASHSIZE  = (1 << 3),
 62         OPT_MAXELEM   = (1 << 4),
 63         OPT_FAMILY    = (1 << 5),
 64 };
 65 
 66 struct ipset_type {
 67         enum fw3_ipset_method method;
 68         uint32_t types;
 69         uint8_t required;
 70         uint8_t optional;
 71 };
 72 
 73 static struct ipset_type ipset_types[] = {
 74         T(BITMAP, IP,   UNSPEC, UNSPEC, OPT_IPRANGE, OPT_NETMASK),
 75         T(BITMAP, IP,   MAC,    UNSPEC, OPT_IPRANGE, 0),
 76         T(BITMAP, PORT, UNSPEC, UNSPEC, OPT_PORTRANGE, 0),
 77 
 78         T(HASH,   IP,   UNSPEC, UNSPEC, 0,
 79           OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM | OPT_NETMASK),
 80         T(HASH,   NET,  UNSPEC, UNSPEC, 0,
 81           OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
 82         T(HASH,   IP,   PORT,   UNSPEC, 0,
 83           OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
 84         T(HASH,   NET,  PORT,   UNSPEC, 0,
 85           OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
 86         T(HASH,   IP,   PORT,   IP,     0,
 87           OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
 88         T(HASH,   IP,   PORT,   NET,    0,
 89           OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
 90 
 91         T(LIST,   SET,  UNSPEC, UNSPEC, 0, OPT_MAXELEM),
 92 };
 93 
 94 
 95 static bool
 96 check_types(struct uci_element *e, struct fw3_ipset *ipset)
 97 {
 98         int i = 0;
 99         uint32_t typelist = 0;
100         struct fw3_ipset_datatype *type;
101 
102         list_for_each_entry(type, &ipset->datatypes, list)
103         {
104                 if (i >= 3)
105                 {
106                         warn_section("ipset", ipset, e, "must not have more than 3 datatypes assigned");
107                         return false;
108                 }
109 
110                 typelist |= (type->type << (i++ * 8));
111         }
112 
113         /* find a suitable storage method if none specified */
114         if (ipset->method == FW3_IPSET_METHOD_UNSPEC)
115         {
116                 for (i = 0; i < ARRAY_SIZE(ipset_types); i++)
117                 {
118                         /* skip type for v6 if it does not support family */
119                         if (ipset->family != FW3_FAMILY_V4 &&
120                             !(ipset_types[i].optional & OPT_FAMILY))
121                                 continue;
122 
123                         if (ipset_types[i].types == typelist)
124                         {
125                                 ipset->method = ipset_types[i].method;
126 
127                                 warn_section("ipset", ipset, e, "defines no storage method, assuming '%s'",
128                                         fw3_ipset_method_names[ipset->method]);
129 
130                                 break;
131                         }
132                 }
133         }
134 
135         //typelist |= ipset->method;
136 
137         for (i = 0; i < ARRAY_SIZE(ipset_types); i++)
138         {
139                 if (ipset_types[i].method == ipset->method &&
140                     ipset_types[i].types == typelist)
141                 {
142                         if (!ipset->external)
143                         {
144                                 if ((ipset_types[i].required & OPT_IPRANGE) &&
145                                         !ipset->iprange.set)
146                                 {
147                                         warn_section("ipset", ipset, e, "requires an ip range");
148                                         return false;
149                                 }
150 
151                                 if ((ipset_types[i].required & OPT_PORTRANGE) &&
152                                     !ipset->portrange.set)
153                                 {
154                                         warn_section("ipset", ipset, e, "requires a port range");
155                                         return false;
156                                 }
157 
158                                 if (!(ipset_types[i].required & OPT_IPRANGE) &&
159                                     ipset->iprange.set)
160                                 {
161                                         warn_section("ipset", ipset, e, "iprange ignored");
162                                         ipset->iprange.set = false;
163                                 }
164 
165                                 if (!(ipset_types[i].required & OPT_PORTRANGE) &&
166                                     ipset->portrange.set)
167                                 {
168                                         warn_section("ipset", ipset, e, "portrange ignored");
169                                         ipset->portrange.set = false;
170                                 }
171 
172                                 if (!(ipset_types[i].optional & OPT_NETMASK) &&
173                                     ipset->netmask > 0)
174                                 {
175                                         warn_section("ipset", ipset, e, "netmask ignored");
176                                         ipset->netmask = 0;
177                                 }
178 
179                                 if (!(ipset_types[i].optional & OPT_HASHSIZE) &&
180                                     ipset->hashsize > 0)
181                                 {
182                                         warn_section("ipset", ipset, e, "hashsize ignored");
183                                         ipset->hashsize = 0;
184                                 }
185 
186                                 if (!(ipset_types[i].optional & OPT_MAXELEM) &&
187                                     ipset->maxelem > 0)
188                                 {
189                                         warn_section("ipset", ipset, e, "maxelem ignored");
190                                         ipset->maxelem = 0;
191                                 }
192 
193                                 if (!(ipset_types[i].optional & OPT_FAMILY) &&
194                                     ipset->family != FW3_FAMILY_V4)
195                                 {
196                                         warn_section("ipset", ipset, e, "family ignored");
197                                         ipset->family = FW3_FAMILY_V4;
198                                 }
199                         }
200 
201                         return true;
202                 }
203         }
204 
205         warn_section("ipset", ipset, e, "has an invalid combination of storage method and matches");
206         return false;
207 }
208 
209 static bool
210 check_ipset(struct fw3_state *state, struct fw3_ipset *ipset, struct uci_element *e)
211 {
212         if (!ipset->enabled) {
213                 return false;
214         }
215 
216         if (ipset->external)
217         {
218                 if (!*ipset->external)
219                         ipset->external = NULL;
220                 else if (!ipset->name)
221                         ipset->name = ipset->external;
222         }
223 
224         if (!ipset->name || !*ipset->name)
225         {
226                 warn_section("ipset", ipset, e, "ipset must have a name assigned");
227         }
228         //else if (fw3_lookup_ipset(state, ipset->name) != NULL)
229         //{
230         //      warn_section("ipset", ipset, e, "has duplicated set name", ipset->name);
231         //}
232         else if (ipset->family == FW3_FAMILY_ANY)
233         {
234                 warn_section("ipset", ipset, e, "must not have family 'any'");
235         }
236         else if (ipset->iprange.set && ipset->family != ipset->iprange.family)
237         {
238                 warn_section("ipset", ipset, e, "has iprange of wrong address family");
239         }
240         else if (list_empty(&ipset->datatypes))
241         {
242                 warn_section("ipset", ipset, e, "has no datatypes assigned");
243         }
244         else if (check_types(e, ipset))
245         {
246                 return true;
247         }
248 
249         return false;
250 }
251 
252 static struct fw3_ipset *
253 fw3_alloc_ipset(struct fw3_state *state)
254 {
255         struct fw3_ipset *ipset;
256 
257         ipset = calloc(1, sizeof(*ipset));
258         if (!ipset)
259                 return NULL;
260 
261         INIT_LIST_HEAD(&ipset->datatypes);
262         INIT_LIST_HEAD(&ipset->entries);
263 
264         ipset->comment    = false;
265         ipset->counters   = false;
266         ipset->enabled    = true;
267         ipset->family     = FW3_FAMILY_V4;
268         ipset->reload_set = false;
269         ipset->timeout    = -1; /* no timeout by default */
270 
271         list_add_tail(&ipset->list, &state->ipsets);
272 
273         return ipset;
274 }
275 
276 void
277 fw3_load_ipsets(struct fw3_state *state, struct uci_package *p,
278                 struct blob_attr *a)
279 {
280         struct uci_section *s;
281         struct uci_element *e;
282         struct fw3_ipset *ipset;
283         struct blob_attr *entry;
284         unsigned rem;
285 
286         INIT_LIST_HEAD(&state->ipsets);
287 
288         if (state->disable_ipsets)
289                 return;
290 
291         blob_for_each_attr(entry, a, rem)
292         {
293                 const char *type;
294                 const char *name = "ubus ipset";
295 
296                 if (!fw3_attr_parse_name_type(entry, &name, &type))
297                         continue;
298 
299                 if (strcmp(type, "ipset"))
300                         continue;
301 
302                 ipset = fw3_alloc_ipset(state);
303                 if (!ipset)
304                         continue;
305 
306                 if (!fw3_parse_blob_options(ipset, fw3_ipset_opts, entry, name))
307                 {
308                         warn_section("ipset", ipset, NULL, "skipped due to invalid options");
309                         fw3_free_ipset(ipset);
310                         continue;
311                 }
312 
313                 if (!check_ipset(state, ipset, NULL))
314                         fw3_free_ipset(ipset);
315         }
316 
317         uci_foreach_element(&p->sections, e)
318         {
319                 s = uci_to_section(e);
320 
321                 if (strcmp(s->type, "ipset"))
322                         continue;
323 
324                 ipset = fw3_alloc_ipset(state);
325 
326                 if (!ipset)
327                         continue;
328 
329                 if (!fw3_parse_options(ipset, fw3_ipset_opts, s))
330                         warn_elem(e, "has invalid options");
331 
332                 if (!check_ipset(state, ipset, e))
333                         fw3_free_ipset(ipset);
334         }
335 }
336 
337 
338 static void
339 load_file(struct fw3_ipset *ipset)
340 {
341         FILE *f;
342         char line[128];
343         char *p;
344 
345         if (!ipset->loadfile)
346                 return;
347 
348         info("   * Loading file %s", ipset->loadfile);
349 
350         f = fopen(ipset->loadfile, "r");
351 
352         if (!f) {
353                 info("     ! Skipping due to open error: %s", strerror(errno));
354                 return;
355         }
356 
357         while (fgets(line, sizeof(line), f)) {
358                 p = line;
359                 while (isspace(*p))
360                         p++;
361                 if (*p && *p != '#')
362                         fw3_pr("add %s %s", ipset->name, line);
363         }
364 
365         fclose(f);
366 }
367 
368 static void
369 create_ipset(struct fw3_ipset *ipset, struct fw3_state *state)
370 {
371         bool first = true;
372         struct fw3_setentry *entry;
373         struct fw3_ipset_datatype *type;
374 
375         info(" * Creating ipset %s", ipset->name);
376 
377         first = true;
378         fw3_pr("create %s %s", ipset->name, fw3_ipset_method_names[ipset->method]);
379 
380         list_for_each_entry(type, &ipset->datatypes, list)
381         {
382                 fw3_pr("%c%s", first ? ':' : ',', fw3_ipset_type_names[type->type]);
383                 first = false;
384         }
385 
386         if (ipset->method == FW3_IPSET_METHOD_HASH)
387                 fw3_pr(" family inet%s", (ipset->family == FW3_FAMILY_V4) ? "" : "6");
388 
389         if (ipset->iprange.set)
390         {
391                 fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false, true));
392         }
393         else if (ipset->portrange.set)
394         {
395                 fw3_pr(" range %u-%u",
396                        ipset->portrange.port_min, ipset->portrange.port_max);
397         }
398 
399         if (ipset->timeout >= 0)
400                 fw3_pr(" timeout %u", ipset->timeout);
401 
402         if (ipset->maxelem > 0)
403                 fw3_pr(" maxelem %u", ipset->maxelem);
404 
405         if (ipset->netmask > 0)
406                 fw3_pr(" netmask %u", ipset->netmask);
407 
408         if (ipset->hashsize > 0)
409                 fw3_pr(" hashsize %u", ipset->hashsize);
410 
411         if (ipset->counters)
412                 fw3_pr(" counters");
413 
414         if (ipset->comment)
415                 fw3_pr(" comment");
416 
417         fw3_pr("\n");
418 
419         list_for_each_entry(entry, &ipset->entries, list)
420                 fw3_pr("add %s %s\n", ipset->name, entry->value);
421 
422         load_file(ipset);
423 }
424 
425 void
426 fw3_create_ipsets(struct fw3_state *state, enum fw3_family family,
427                   bool reload_set)
428 {
429         unsigned int delay, tries;
430         bool exec = false;
431         struct fw3_ipset *ipset;
432 
433         if (state->disable_ipsets)
434                 return;
435 
436         /* spawn ipsets */
437         list_for_each_entry(ipset, &state->ipsets, list)
438         {
439                 if (ipset->family != family)
440                         continue;
441 
442                 if (ipset->external)
443                         continue;
444 
445                 if (fw3_check_ipset(ipset) &&
446                     (reload_set && !ipset->reload_set))
447                         continue;
448 
449                 if (!exec)
450                 {
451                         exec = fw3_command_pipe(false, "ipset", "-exist", "-");
452 
453                         if (!exec)
454                                 return;
455                 }
456 
457                 create_ipset(ipset, state);
458         }
459 
460         if (exec)
461         {
462                 fw3_pr("quit\n");
463                 fw3_command_close();
464         }
465 
466         /* wait a little expontially for ipsets to appear */
467         list_for_each_entry(ipset, &state->ipsets, list)
468         {
469                 if (ipset->external)
470                         continue;
471 
472                 delay = 5;
473                 for (tries = 0; !fw3_check_ipset(ipset) && tries < 10; tries++)
474                         usleep(delay<<1);
475         }
476 }
477 
478 void
479 fw3_destroy_ipsets(struct fw3_state *state, enum fw3_family family,
480                    bool reload_set)
481 {
482         unsigned int delay, tries;
483         bool exec = false;
484         struct fw3_ipset *ipset;
485 
486         if (state->disable_ipsets)
487                 return;
488 
489         /* destroy ipsets */
490         list_for_each_entry(ipset, &state->ipsets, list)
491         {
492                 if (ipset->family != family ||
493                     (reload_set && !ipset->reload_set))
494                         continue;
495 
496                 if (!exec)
497                 {
498                         exec = fw3_command_pipe(false, "ipset", "-exist", "-");
499 
500                         if (!exec)
501                                 return;
502                 }
503 
504                 info(" * Deleting ipset %s", ipset->name);
505 
506                 fw3_pr("flush %s\n", ipset->name);
507                 fw3_pr("destroy %s\n", ipset->name);
508         }
509 
510         if (exec)
511         {
512                 fw3_pr("quit\n");
513                 fw3_command_close();
514         }
515 
516         /* wait for ipsets to disappear */
517         list_for_each_entry(ipset, &state->ipsets, list)
518         {
519                 if (ipset->external)
520                         continue;
521 
522                 delay = 5;
523                 for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++)
524                         usleep(delay<<1);
525         }
526 }
527 
528 struct fw3_ipset *
529 fw3_lookup_ipset(struct fw3_state *state, const char *name)
530 {
531         struct fw3_ipset *s;
532 
533         if (list_empty(&state->ipsets))
534                 return NULL;
535 
536         list_for_each_entry(s, &state->ipsets, list)
537         {
538                 if (strcmp(s->name, name))
539                         continue;
540 
541                 return s;
542         }
543 
544         return NULL;
545 }
546 
547 bool
548 fw3_check_ipset(struct fw3_ipset *set)
549 {
550         bool rv = false;
551 
552         socklen_t sz;
553         int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
554         struct ip_set_req_version req_ver;
555         struct ip_set_req_get_set req_name;
556 
557         if (s < 0 || fcntl(s, F_SETFD, FD_CLOEXEC))
558                 goto out;
559 
560         sz = sizeof(req_ver);
561         req_ver.op = IP_SET_OP_VERSION;
562 
563         if (getsockopt(s, SOL_IP, SO_IP_SET, &req_ver, &sz))
564                 goto out;
565 
566         sz = sizeof(req_name);
567         req_name.op = IP_SET_OP_GET_BYNAME;
568         req_name.version = req_ver.version;
569         snprintf(req_name.set.name, IPSET_MAXNAMELEN - 1, "%s",
570                  set->external ? set->external : set->name);
571 
572         if (getsockopt(s, SOL_IP, SO_IP_SET, &req_name, &sz))
573                 goto out;
574 
575         rv = ((sz == sizeof(req_name)) && (req_name.set.index != IPSET_INVALID_ID));
576 
577 out:
578         if (s >= 0)
579                 close(s);
580 
581         return rv;
582 }
583 
584 void
585 fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state *run_state,
586                             struct fw3_state *cfg_state)
587 {
588         struct fw3_ipset *ipset_run, *ipset_cfg;
589         bool in_cfg;
590 
591         list_for_each_entry(ipset_run, &run_state->ipsets, list) {
592                 if (ipset_run->family != family)
593                         continue;
594 
595                 in_cfg = false;
596 
597                 list_for_each_entry(ipset_cfg, &cfg_state->ipsets, list) {
598                         if (ipset_cfg->family != family)
599                                 continue;
600 
601                         if (strlen(ipset_run->name) ==
602                             strlen(ipset_cfg->name) &&
603                             !strcmp(ipset_run->name, ipset_cfg->name)) {
604                                 in_cfg = true;
605                                 break;
606                         }
607                 }
608 
609                 /* If a set is found in run_state, but not in cfg_state then the
610                  * set has been deleted/renamed. Set reload_set to true to force
611                  * the old set to be destroyed in the "stop" fase of the reload.
612                  * If the set is found, then copy the reload_set value from the
613                  * configuration state. This ensures that the elements are
614                  * always updated according to the configuration, and not the
615                  * runtime state (which the user might have forgotten).
616                  */
617                 if (!in_cfg)
618                         ipset_run->reload_set = true;
619                 else
620                         ipset_run->reload_set = ipset_cfg->reload_set;
621         }
622 }
623 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt