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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt