aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/csource/linux_common.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-01-29 13:44:57 +0100
committerDmitry Vyukov <dvyukov@google.com>2018-02-09 20:14:33 +0100
commit033b610ec91096a5791c90761df1289fd33280e6 (patch)
tree57863e9e73f2d664640d7c889cd052138017f4a5 /pkg/csource/linux_common.go
parentbb826eb26c510f5faa418b8a37302be43d535088 (diff)
sys/linux: improve netfilter descriptions
Put the underflow entry at the end. Entries must end on an unconditional, non-goto entry, otherwise fallthrough from the last entry is invalid. Add arp tables support. Split unspec matches/targets to unspec and inet. Reset ipv6 and arp tables in executor. Fix number of counters in tables. Plus a bunch of assorted fixes for matches/targets.
Diffstat (limited to 'pkg/csource/linux_common.go')
-rw-r--r--pkg/csource/linux_common.go230
1 files changed, 193 insertions, 37 deletions
diff --git a/pkg/csource/linux_common.go b/pkg/csource/linux_common.go
index b02e61188..37557447a 100644
--- a/pkg/csource/linux_common.go
+++ b/pkg/csource/linux_common.go
@@ -1991,6 +1991,13 @@ static int do_sandbox_namespace(int executor_pid, bool enable_tun)
#if defined(SYZ_EXECUTOR) || defined(SYZ_RESET_NET_NAMESPACE)
+#define XT_TABLE_SIZE 1536
+#define XT_MAX_ENTRIES 10
+
+struct xt_counters {
+ uint64 pcnt, bcnt;
+};
+
struct ipt_getinfo {
char name[32];
unsigned int valid_hooks;
@@ -2003,11 +2010,7 @@ struct ipt_getinfo {
struct ipt_get_entries {
char name[32];
unsigned int size;
- void* entrytable[1024 / sizeof(void*)];
-};
-
-struct xt_counters {
- uint64 pcnt, bcnt;
+ void* entrytable[XT_TABLE_SIZE / sizeof(void*)];
};
struct ipt_replace {
@@ -2019,15 +2022,13 @@ struct ipt_replace {
unsigned int underflow[5];
unsigned int num_counters;
struct xt_counters* counters;
- char entrytable[1024];
+ char entrytable[XT_TABLE_SIZE];
};
struct ipt_table_desc {
const char* name;
struct ipt_getinfo info;
- struct ipt_get_entries entries;
struct ipt_replace replace;
- struct xt_counters counters[10];
};
static struct ipt_table_desc ipv4_tables[] = {
@@ -2038,27 +2039,76 @@ static struct ipt_table_desc ipv4_tables[] = {
{.name = "security"},
};
+static struct ipt_table_desc ipv6_tables[] = {
+ {.name = "filter"},
+ {.name = "nat"},
+ {.name = "mangle"},
+ {.name = "raw"},
+ {.name = "security"},
+};
+
#define IPT_BASE_CTL 64
#define IPT_SO_SET_REPLACE (IPT_BASE_CTL)
#define IPT_SO_GET_INFO (IPT_BASE_CTL)
#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1)
-static void checkpoint_net_namespace(void)
+struct arpt_getinfo {
+ char name[32];
+ unsigned int valid_hooks;
+ unsigned int hook_entry[3];
+ unsigned int underflow[3];
+ unsigned int num_entries;
+ unsigned int size;
+};
+
+struct arpt_get_entries {
+ char name[32];
+ unsigned int size;
+ void* entrytable[XT_TABLE_SIZE / sizeof(void*)];
+};
+
+struct arpt_replace {
+ char name[32];
+ unsigned int valid_hooks;
+ unsigned int num_entries;
+ unsigned int size;
+ unsigned int hook_entry[3];
+ unsigned int underflow[3];
+ unsigned int num_counters;
+ struct xt_counters* counters;
+ char entrytable[XT_TABLE_SIZE];
+};
+
+struct arpt_table_desc {
+ const char* name;
+ struct arpt_getinfo info;
+ struct arpt_replace replace;
+};
+
+static struct arpt_table_desc arpt_tables[] = {
+ {.name = "filter"},
+};
+
+#define ARPT_BASE_CTL 96
+#define ARPT_SO_SET_REPLACE (ARPT_BASE_CTL)
+#define ARPT_SO_GET_INFO (ARPT_BASE_CTL)
+#define ARPT_SO_GET_ENTRIES (ARPT_BASE_CTL + 1)
+
+static void checkpoint_iptables(struct ipt_table_desc* tables, int num_tables, int family, int level)
{
+ struct ipt_get_entries entries;
socklen_t optlen;
- unsigned i;
- int fd;
+ int fd, i;
- fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ fd = socket(family, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1)
- fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)");
- for (i = 0; i < sizeof(ipv4_tables) / sizeof(ipv4_tables[0]); i++) {
- struct ipt_table_desc* table = &ipv4_tables[i];
+ fail("socket(%d, SOCK_STREAM, IPPROTO_TCP)", family);
+ for (i = 0; i < num_tables; i++) {
+ struct ipt_table_desc* table = &tables[i];
strcpy(table->info.name, table->name);
- strcpy(table->entries.name, table->name);
strcpy(table->replace.name, table->name);
optlen = sizeof(table->info);
- if (getsockopt(fd, SOL_IP, IPT_SO_GET_INFO, &table->info, &optlen)) {
+ if (getsockopt(fd, level, IPT_SO_GET_INFO, &table->info, &optlen)) {
switch (errno) {
case EPERM:
case ENOENT:
@@ -2067,63 +2117,169 @@ static void checkpoint_net_namespace(void)
}
fail("getsockopt(IPT_SO_GET_INFO)");
}
- if (table->info.size > sizeof(table->entries.entrytable))
+ debug("checkpoint iptable %s/%d: entries=%d hooks=%x size=%d\n",
+ table->name, family, table->info.num_entries, table->info.valid_hooks,
+ table->info.size);
+ if (table->info.size > sizeof(table->replace.entrytable))
fail("table size is too large: %u", table->info.size);
- if (table->info.num_entries > sizeof(table->counters) / sizeof(table->counters[0]))
+ if (table->info.num_entries > XT_MAX_ENTRIES)
fail("too many counters: %u", table->info.num_entries);
- table->entries.size = table->info.size;
- optlen = sizeof(table->entries) - sizeof(table->entries.entrytable) + table->info.size;
- if (getsockopt(fd, SOL_IP, IPT_SO_GET_ENTRIES, &table->entries, &optlen))
+ memset(&entries, 0, sizeof(entries));
+ strcpy(entries.name, table->name);
+ entries.size = table->info.size;
+ optlen = sizeof(entries) - sizeof(entries.entrytable) + table->info.size;
+ if (getsockopt(fd, level, IPT_SO_GET_ENTRIES, &entries, &optlen))
fail("getsockopt(IPT_SO_GET_ENTRIES)");
table->replace.valid_hooks = table->info.valid_hooks;
table->replace.num_entries = table->info.num_entries;
- table->replace.counters = table->counters;
table->replace.size = table->info.size;
memcpy(table->replace.hook_entry, table->info.hook_entry, sizeof(table->replace.hook_entry));
memcpy(table->replace.underflow, table->info.underflow, sizeof(table->replace.underflow));
- memcpy(table->replace.entrytable, table->entries.entrytable, table->info.size);
+ memcpy(table->replace.entrytable, entries.entrytable, table->info.size);
}
close(fd);
}
-static void reset_net_namespace(void)
+static void reset_iptables(struct ipt_table_desc* tables, int num_tables, int family, int level)
{
+ struct xt_counters counters[XT_MAX_ENTRIES];
struct ipt_get_entries entries;
struct ipt_getinfo info;
socklen_t optlen;
- unsigned i;
- int fd;
+ int fd, i;
- memset(&info, 0, sizeof(info));
- memset(&entries, 0, sizeof(entries));
- fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ fd = socket(family, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1)
- fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)");
- for (i = 0; i < sizeof(ipv4_tables) / sizeof(ipv4_tables[0]); i++) {
- struct ipt_table_desc* table = &ipv4_tables[i];
+ fail("socket(%d, SOCK_STREAM, IPPROTO_TCP)", family);
+ for (i = 0; i < num_tables; i++) {
+ struct ipt_table_desc* table = &tables[i];
if (table->info.valid_hooks == 0)
continue;
+ memset(&info, 0, sizeof(info));
strcpy(info.name, table->name);
optlen = sizeof(info);
- if (getsockopt(fd, SOL_IP, IPT_SO_GET_INFO, &info, &optlen))
+ if (getsockopt(fd, level, IPT_SO_GET_INFO, &info, &optlen))
fail("getsockopt(IPT_SO_GET_INFO)");
if (memcmp(&table->info, &info, sizeof(table->info)) == 0) {
+ memset(&entries, 0, sizeof(entries));
strcpy(entries.name, table->name);
entries.size = table->info.size;
optlen = sizeof(entries) - sizeof(entries.entrytable) + entries.size;
- if (getsockopt(fd, SOL_IP, IPT_SO_GET_ENTRIES, &entries, &optlen))
+ if (getsockopt(fd, level, IPT_SO_GET_ENTRIES, &entries, &optlen))
fail("getsockopt(IPT_SO_GET_ENTRIES)");
- if (memcmp(&table->entries, &entries, optlen) == 0)
+ if (memcmp(table->replace.entrytable, entries.entrytable, table->info.size) == 0)
continue;
}
debug("resetting iptable %s\n", table->name);
table->replace.num_counters = info.num_entries;
+ table->replace.counters = counters;
optlen = sizeof(table->replace) - sizeof(table->replace.entrytable) + table->replace.size;
- if (setsockopt(fd, SOL_IP, IPT_SO_SET_REPLACE, &table->replace, optlen))
+ if (setsockopt(fd, level, IPT_SO_SET_REPLACE, &table->replace, optlen))
fail("setsockopt(IPT_SO_SET_REPLACE)");
}
close(fd);
}
+
+static void checkpoint_arptables(void)
+{
+ struct arpt_get_entries entries;
+ socklen_t optlen;
+ unsigned i;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)");
+ for (i = 0; i < sizeof(arpt_tables) / sizeof(arpt_tables[0]); i++) {
+ struct arpt_table_desc* table = &arpt_tables[i];
+ strcpy(table->info.name, table->name);
+ strcpy(table->replace.name, table->name);
+ optlen = sizeof(table->info);
+ if (getsockopt(fd, SOL_IP, ARPT_SO_GET_INFO, &table->info, &optlen)) {
+ switch (errno) {
+ case EPERM:
+ case ENOENT:
+ case ENOPROTOOPT:
+ continue;
+ }
+ fail("getsockopt(ARPT_SO_GET_INFO)");
+ }
+ debug("checkpoint arptable %s: entries=%d hooks=%x size=%d\n",
+ table->name, table->info.num_entries, table->info.valid_hooks, table->info.size);
+ if (table->info.size > sizeof(table->replace.entrytable))
+ fail("table size is too large: %u", table->info.size);
+ if (table->info.num_entries > XT_MAX_ENTRIES)
+ fail("too many counters: %u", table->info.num_entries);
+ memset(&entries, 0, sizeof(entries));
+ strcpy(entries.name, table->name);
+ entries.size = table->info.size;
+ optlen = sizeof(entries) - sizeof(entries.entrytable) + table->info.size;
+ if (getsockopt(fd, SOL_IP, ARPT_SO_GET_ENTRIES, &entries, &optlen))
+ fail("getsockopt(ARPT_SO_GET_ENTRIES)");
+ table->replace.valid_hooks = table->info.valid_hooks;
+ table->replace.num_entries = table->info.num_entries;
+ table->replace.size = table->info.size;
+ memcpy(table->replace.hook_entry, table->info.hook_entry, sizeof(table->replace.hook_entry));
+ memcpy(table->replace.underflow, table->info.underflow, sizeof(table->replace.underflow));
+ memcpy(table->replace.entrytable, entries.entrytable, table->info.size);
+ }
+ close(fd);
+}
+
+static void reset_arptables()
+{
+ struct xt_counters counters[XT_MAX_ENTRIES];
+ struct arpt_get_entries entries;
+ struct arpt_getinfo info;
+ socklen_t optlen;
+ unsigned i;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)");
+ for (i = 0; i < sizeof(arpt_tables) / sizeof(arpt_tables[0]); i++) {
+ struct arpt_table_desc* table = &arpt_tables[i];
+ if (table->info.valid_hooks == 0)
+ continue;
+ memset(&info, 0, sizeof(info));
+ strcpy(info.name, table->name);
+ optlen = sizeof(info);
+ if (getsockopt(fd, SOL_IP, ARPT_SO_GET_INFO, &info, &optlen))
+ fail("getsockopt(ARPT_SO_GET_INFO)");
+ if (memcmp(&table->info, &info, sizeof(table->info)) == 0) {
+ memset(&entries, 0, sizeof(entries));
+ strcpy(entries.name, table->name);
+ entries.size = table->info.size;
+ optlen = sizeof(entries) - sizeof(entries.entrytable) + entries.size;
+ if (getsockopt(fd, SOL_IP, ARPT_SO_GET_ENTRIES, &entries, &optlen))
+ fail("getsockopt(ARPT_SO_GET_ENTRIES)");
+ if (memcmp(table->replace.entrytable, entries.entrytable, table->info.size) == 0)
+ continue;
+ }
+ debug("resetting arptable %s\n", table->name);
+ table->replace.num_counters = info.num_entries;
+ table->replace.counters = counters;
+ optlen = sizeof(table->replace) - sizeof(table->replace.entrytable) + table->replace.size;
+ if (setsockopt(fd, SOL_IP, ARPT_SO_SET_REPLACE, &table->replace, optlen))
+ fail("setsockopt(ARPT_SO_SET_REPLACE)");
+ }
+ close(fd);
+}
+
+static void checkpoint_net_namespace(void)
+{
+ checkpoint_arptables();
+ checkpoint_iptables(ipv4_tables, sizeof(ipv4_tables) / sizeof(ipv4_tables[0]), AF_INET, SOL_IP);
+ checkpoint_iptables(ipv6_tables, sizeof(ipv6_tables) / sizeof(ipv6_tables[0]), AF_INET6, SOL_IPV6);
+}
+
+static void reset_net_namespace(void)
+{
+ reset_arptables();
+ reset_iptables(ipv4_tables, sizeof(ipv4_tables) / sizeof(ipv4_tables[0]), AF_INET, SOL_IP);
+ reset_iptables(ipv6_tables, sizeof(ipv6_tables) / sizeof(ipv6_tables[0]), AF_INET6, SOL_IPV6);
+}
#endif
#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR))