From 2807cc26b8e46eef5f23c06534a853dd48183331 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 6 Aug 2014 19:00:18 +0200 Subject: [PATCH] Selectively flush conntrack Record active IP addresses in firewall state file and trigger conntrack flush for changed IP addresses on firewall reload. Additionally trigger a complete flush on the first firewall start in order to clear out streams which might have bypassed the masquerading rules. Signed-off-by: Jo-Philipp Wich --- main.c | 14 +++--- options.h | 2 + utils.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- utils.h | 4 ++ zones.c | 4 ++ 5 files changed, 142 insertions(+), 12 deletions(-) diff --git a/main.c b/main.c index 455c049..71463ae 100644 --- a/main.c +++ b/main.c @@ -167,8 +167,6 @@ family_set(struct fw3_state *state, enum fw3_family family, bool set) static int stop(bool complete) { - FILE *ct; - int rv = 1; enum fw3_family family; enum fw3_table table; @@ -224,13 +222,8 @@ stop(bool complete) if (run_state) fw3_destroy_ipsets(run_state); - if (complete && (ct = fopen("/proc/net/nf_conntrack", "w")) != NULL) - { - info(" * Flushing conntrack table ..."); - - fwrite("f\n", 2, 1, ct); - fclose(ct); - } + if (complete) + fw3_flush_conntrack(NULL); if (!rv && run_state) fw3_write_statefile(run_state); @@ -304,6 +297,7 @@ start(void) if (!rv) { + fw3_flush_conntrack(run_state); fw3_set_defaults(cfg_state); if (!print_family) @@ -395,6 +389,8 @@ start: if (!rv) { + fw3_flush_conntrack(run_state); + fw3_set_defaults(cfg_state); fw3_run_includes(cfg_state, true); fw3_hotplug_zones(cfg_state, true); diff --git a/options.h b/options.h index 28de48e..8489b8b 100644 --- a/options.h +++ b/options.h @@ -314,6 +314,8 @@ struct fw3_zone bool custom_chains; uint32_t flags[2]; + + struct list_head old_addrs; }; struct fw3_rule diff --git a/utils.c b/utils.c index fa4a73f..d8a881c 100644 --- a/utils.c +++ b/utils.c @@ -414,13 +414,14 @@ write_defaults_uci(struct uci_context *ctx, struct fw3_defaults *d, static void write_zone_uci(struct uci_context *ctx, struct fw3_zone *z, - struct uci_package *dest) + struct uci_package *dest, struct ifaddrs *ifaddr) { struct fw3_device *dev; struct fw3_address *sub; + struct ifaddrs *ifa; enum fw3_family fam = FW3_FAMILY_ANY; - char *p, buf[34]; + char *p, buf[INET6_ADDRSTRLEN]; struct uci_ptr ptr = { .p = dest }; @@ -518,6 +519,35 @@ write_zone_uci(struct uci_context *ctx, struct fw3_zone *z, uci_add_list(ctx, &ptr); } + ptr.o = NULL; + ptr.option = "__addrs"; + + fw3_foreach(dev, &z->devices) + { + if (!dev) + continue; + + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) + { + if (strcmp(dev->name, ifa->ifa_name)) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) + inet_ntop(AF_INET, + &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + buf, sizeof(buf)); + else if (ifa->ifa_addr->sa_family == AF_INET6) + inet_ntop(AF_INET6, + &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, + buf, sizeof(buf)); + else + continue; + + ptr.value = buf; + uci_add_list(ctx, &ptr); + } + } + sprintf(buf, "0x%x", z->flags[0]); ptr.o = NULL; ptr.option = "__flags_v4"; @@ -590,6 +620,7 @@ fw3_write_statefile(void *state) struct fw3_state *s = state; struct fw3_zone *z; struct fw3_ipset *i; + struct ifaddrs *ifaddr; struct uci_package *p; @@ -608,6 +639,12 @@ fw3_write_statefile(void *state) return; } + if (getifaddrs(&ifaddr)) + { + warn("Cannot get interface addresses: %s", strerror(errno)); + ifaddr = NULL; + } + if ((p = uci_lookup_package(s->uci, "fw3_state")) != NULL) uci_unload(s->uci, p); @@ -618,7 +655,7 @@ fw3_write_statefile(void *state) write_defaults_uci(s->uci, &s->defaults, p); list_for_each_entry(z, &s->zones, list) - write_zone_uci(s->uci, z, p); + write_zone_uci(s->uci, z, p, ifaddr); list_for_each_entry(i, &s->ipsets, list) write_ipset_uci(s->uci, i, p); @@ -629,6 +666,9 @@ fw3_write_statefile(void *state) fsync(fileno(sf)); fclose(sf); + + if (ifaddr) + freeifaddrs(ifaddr); } } @@ -767,3 +807,87 @@ fw3_bitlen2netmask(int family, int bits, void *mask) return true; } + +void +fw3_flush_conntrack(void *state) +{ + bool found; + struct fw3_state *s = state; + struct fw3_address *addr; + struct fw3_device *dev; + struct fw3_zone *zone; + struct ifaddrs *ifaddr, *ifa; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + char buf[INET6_ADDRSTRLEN]; + FILE *ct; + + if (!state) + { + if ((ct = fopen("/proc/net/nf_conntrack", "w")) != NULL) + { + info(" * Flushing conntrack table ..."); + + fwrite("f\n", 1, 2, ct); + fclose(ct); + } + + return; + } + + if (getifaddrs(&ifaddr)) + { + warn("Cannot get interface addresses: %s", strerror(errno)); + return; + } + + if ((ct = fopen("/proc/net/nf_conntrack", "w")) != NULL) + { + list_for_each_entry(zone, &s->zones, list) + list_for_each_entry(addr, &zone->old_addrs, list) + { + found = false; + + list_for_each_entry(dev, &zone->devices, list) + { + for (ifa = ifaddr; ifa && !found; ifa = ifa->ifa_next) + { + if (strcmp(dev->name, ifa->ifa_name)) + continue; + + sin = (struct sockaddr_in *)ifa->ifa_addr; + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + + if (addr->family == FW3_FAMILY_V4 && + sin->sin_family == AF_INET) + { + found = !memcmp(&addr->address.v4, &sin->sin_addr, + sizeof(sin->sin_addr)); + } + else if (addr->family == FW3_FAMILY_V6 && + sin6->sin6_family == AF_INET6) + { + found = !memcmp(&addr->address.v6, &sin6->sin6_addr, + sizeof(sin6->sin6_addr)); + } + } + + if (found) + break; + } + + if (!found) + { + inet_ntop(addr->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, + &addr->address.v4, buf, sizeof(buf)); + + info(" * Flushing conntrack: %s", buf); + fprintf(ct, "%s\n", buf); + } + } + + fclose(ct); + } + + freeifaddrs(ifaddr); +} diff --git a/utils.h b/utils.h index d2e1aa6..834d979 100644 --- a/utils.h +++ b/utils.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -99,4 +101,6 @@ int fw3_netmask2bitlen(int family, void *mask); bool fw3_bitlen2netmask(int family, int bits, void *mask); +void fw3_flush_conntrack(void *zone); + #endif diff --git a/zones.c b/zones.c index ebc4a2a..0e17d68 100644 --- a/zones.c +++ b/zones.c @@ -83,6 +83,8 @@ const struct fw3_option fw3_zone_opts[] = { FW3_OPT("__flags_v4", int, zone, flags[0]), FW3_OPT("__flags_v6", int, zone, flags[1]), + FW3_LIST("__addrs", address, zone, old_addrs), + { } }; @@ -138,6 +140,8 @@ fw3_alloc_zone(void) INIT_LIST_HEAD(&zone->masq_src); INIT_LIST_HEAD(&zone->masq_dest); + INIT_LIST_HEAD(&zone->old_addrs); + zone->enabled = true; zone->custom_chains = true; zone->log_limit.rate = 10; -- 2.25.1