diff options
author | David S. Miller <davem@davemloft.net> | 2015-05-22 14:25:45 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-22 14:25:45 -0400 |
commit | 572152adfba711f770fc7ec5d140070a91d8e045 (patch) | |
tree | f674c351988d101c686503b9c4af56a2b28fd167 /net | |
parent | 381c759d9916c42959515ad34a6d467e24a88e93 (diff) | |
parent | faecbb45ebefb20260ad4a631e011e93c896cb73 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
Pablo Neira Ayuso says:
====================
Netfilter fixes for net
The following patchset contain Netfilter fixes for your net tree, they are:
1) Fix a race in nfnetlink_log and nfnetlink_queue that can lead to a crash.
This problem is due to wrong order in the per-net registration and netlink
socket events. Patch from Francesco Ruggeri.
2) Make sure that counters that userspace pass us are higher than 0 in all the
x_tables frontends. Discovered via Trinity, patch from Dave Jones.
3) Revert a patch for br_netfilter to rely on the conntrack status bits. This
breaks stateless IPv6 NAT transformations. Patch from Florian Westphal.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_netfilter.c | 27 | ||||
-rw-r--r-- | net/bridge/netfilter/ebtables.c | 4 | ||||
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 6 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 6 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_log.c | 19 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue_core.c | 18 |
7 files changed, 50 insertions, 36 deletions
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index ab55e2472beb..60ddfbeb47f5 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c | |||
@@ -37,10 +37,6 @@ | |||
37 | #include <net/route.h> | 37 | #include <net/route.h> |
38 | #include <net/netfilter/br_netfilter.h> | 38 | #include <net/netfilter/br_netfilter.h> |
39 | 39 | ||
40 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) | ||
41 | #include <net/netfilter/nf_conntrack.h> | ||
42 | #endif | ||
43 | |||
44 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
45 | #include "br_private.h" | 41 | #include "br_private.h" |
46 | #ifdef CONFIG_SYSCTL | 42 | #ifdef CONFIG_SYSCTL |
@@ -350,24 +346,15 @@ free_skb: | |||
350 | return 0; | 346 | return 0; |
351 | } | 347 | } |
352 | 348 | ||
353 | static bool dnat_took_place(const struct sk_buff *skb) | 349 | static bool daddr_was_changed(const struct sk_buff *skb, |
350 | const struct nf_bridge_info *nf_bridge) | ||
354 | { | 351 | { |
355 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) | 352 | return ip_hdr(skb)->daddr != nf_bridge->ipv4_daddr; |
356 | enum ip_conntrack_info ctinfo; | ||
357 | struct nf_conn *ct; | ||
358 | |||
359 | ct = nf_ct_get(skb, &ctinfo); | ||
360 | if (!ct || nf_ct_is_untracked(ct)) | ||
361 | return false; | ||
362 | |||
363 | return test_bit(IPS_DST_NAT_BIT, &ct->status); | ||
364 | #else | ||
365 | return false; | ||
366 | #endif | ||
367 | } | 353 | } |
368 | 354 | ||
369 | /* This requires some explaining. If DNAT has taken place, | 355 | /* This requires some explaining. If DNAT has taken place, |
370 | * we will need to fix up the destination Ethernet address. | 356 | * we will need to fix up the destination Ethernet address. |
357 | * This is also true when SNAT takes place (for the reply direction). | ||
371 | * | 358 | * |
372 | * There are two cases to consider: | 359 | * There are two cases to consider: |
373 | * 1. The packet was DNAT'ed to a device in the same bridge | 360 | * 1. The packet was DNAT'ed to a device in the same bridge |
@@ -421,7 +408,7 @@ static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb) | |||
421 | nf_bridge->pkt_otherhost = false; | 408 | nf_bridge->pkt_otherhost = false; |
422 | } | 409 | } |
423 | nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; | 410 | nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; |
424 | if (dnat_took_place(skb)) { | 411 | if (daddr_was_changed(skb, nf_bridge)) { |
425 | if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) { | 412 | if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) { |
426 | struct in_device *in_dev = __in_dev_get_rcu(dev); | 413 | struct in_device *in_dev = __in_dev_get_rcu(dev); |
427 | 414 | ||
@@ -632,6 +619,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, | |||
632 | struct sk_buff *skb, | 619 | struct sk_buff *skb, |
633 | const struct nf_hook_state *state) | 620 | const struct nf_hook_state *state) |
634 | { | 621 | { |
622 | struct nf_bridge_info *nf_bridge; | ||
635 | struct net_bridge_port *p; | 623 | struct net_bridge_port *p; |
636 | struct net_bridge *br; | 624 | struct net_bridge *br; |
637 | __u32 len = nf_bridge_encap_header_len(skb); | 625 | __u32 len = nf_bridge_encap_header_len(skb); |
@@ -669,6 +657,9 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, | |||
669 | if (!setup_pre_routing(skb)) | 657 | if (!setup_pre_routing(skb)) |
670 | return NF_DROP; | 658 | return NF_DROP; |
671 | 659 | ||
660 | nf_bridge = nf_bridge_info_get(skb); | ||
661 | nf_bridge->ipv4_daddr = ip_hdr(skb)->daddr; | ||
662 | |||
672 | skb->protocol = htons(ETH_P_IP); | 663 | skb->protocol = htons(ETH_P_IP); |
673 | 664 | ||
674 | NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb, | 665 | NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb, |
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 91180a7fc943..24c7c96bf5f8 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c | |||
@@ -1117,6 +1117,8 @@ static int do_replace(struct net *net, const void __user *user, | |||
1117 | return -ENOMEM; | 1117 | return -ENOMEM; |
1118 | if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) | 1118 | if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) |
1119 | return -ENOMEM; | 1119 | return -ENOMEM; |
1120 | if (tmp.num_counters == 0) | ||
1121 | return -EINVAL; | ||
1120 | 1122 | ||
1121 | tmp.name[sizeof(tmp.name) - 1] = 0; | 1123 | tmp.name[sizeof(tmp.name) - 1] = 0; |
1122 | 1124 | ||
@@ -2159,6 +2161,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl, | |||
2159 | return -ENOMEM; | 2161 | return -ENOMEM; |
2160 | if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) | 2162 | if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) |
2161 | return -ENOMEM; | 2163 | return -ENOMEM; |
2164 | if (tmp.num_counters == 0) | ||
2165 | return -EINVAL; | ||
2162 | 2166 | ||
2163 | memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); | 2167 | memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); |
2164 | 2168 | ||
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 13bfe84bf3ca..a61200754f4b 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c | |||
@@ -1075,6 +1075,9 @@ static int do_replace(struct net *net, const void __user *user, | |||
1075 | /* overflow check */ | 1075 | /* overflow check */ |
1076 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | 1076 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) |
1077 | return -ENOMEM; | 1077 | return -ENOMEM; |
1078 | if (tmp.num_counters == 0) | ||
1079 | return -EINVAL; | ||
1080 | |||
1078 | tmp.name[sizeof(tmp.name)-1] = 0; | 1081 | tmp.name[sizeof(tmp.name)-1] = 0; |
1079 | 1082 | ||
1080 | newinfo = xt_alloc_table_info(tmp.size); | 1083 | newinfo = xt_alloc_table_info(tmp.size); |
@@ -1499,6 +1502,9 @@ static int compat_do_replace(struct net *net, void __user *user, | |||
1499 | return -ENOMEM; | 1502 | return -ENOMEM; |
1500 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | 1503 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) |
1501 | return -ENOMEM; | 1504 | return -ENOMEM; |
1505 | if (tmp.num_counters == 0) | ||
1506 | return -EINVAL; | ||
1507 | |||
1502 | tmp.name[sizeof(tmp.name)-1] = 0; | 1508 | tmp.name[sizeof(tmp.name)-1] = 0; |
1503 | 1509 | ||
1504 | newinfo = xt_alloc_table_info(tmp.size); | 1510 | newinfo = xt_alloc_table_info(tmp.size); |
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index c69db7fa25ee..2d0e265fef6e 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -1262,6 +1262,9 @@ do_replace(struct net *net, const void __user *user, unsigned int len) | |||
1262 | /* overflow check */ | 1262 | /* overflow check */ |
1263 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | 1263 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) |
1264 | return -ENOMEM; | 1264 | return -ENOMEM; |
1265 | if (tmp.num_counters == 0) | ||
1266 | return -EINVAL; | ||
1267 | |||
1265 | tmp.name[sizeof(tmp.name)-1] = 0; | 1268 | tmp.name[sizeof(tmp.name)-1] = 0; |
1266 | 1269 | ||
1267 | newinfo = xt_alloc_table_info(tmp.size); | 1270 | newinfo = xt_alloc_table_info(tmp.size); |
@@ -1809,6 +1812,9 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len) | |||
1809 | return -ENOMEM; | 1812 | return -ENOMEM; |
1810 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | 1813 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) |
1811 | return -ENOMEM; | 1814 | return -ENOMEM; |
1815 | if (tmp.num_counters == 0) | ||
1816 | return -EINVAL; | ||
1817 | |||
1812 | tmp.name[sizeof(tmp.name)-1] = 0; | 1818 | tmp.name[sizeof(tmp.name)-1] = 0; |
1813 | 1819 | ||
1814 | newinfo = xt_alloc_table_info(tmp.size); | 1820 | newinfo = xt_alloc_table_info(tmp.size); |
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 1a732a1d3c8e..62f5b0d0bc9b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -1275,6 +1275,9 @@ do_replace(struct net *net, const void __user *user, unsigned int len) | |||
1275 | /* overflow check */ | 1275 | /* overflow check */ |
1276 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | 1276 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) |
1277 | return -ENOMEM; | 1277 | return -ENOMEM; |
1278 | if (tmp.num_counters == 0) | ||
1279 | return -EINVAL; | ||
1280 | |||
1278 | tmp.name[sizeof(tmp.name)-1] = 0; | 1281 | tmp.name[sizeof(tmp.name)-1] = 0; |
1279 | 1282 | ||
1280 | newinfo = xt_alloc_table_info(tmp.size); | 1283 | newinfo = xt_alloc_table_info(tmp.size); |
@@ -1822,6 +1825,9 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len) | |||
1822 | return -ENOMEM; | 1825 | return -ENOMEM; |
1823 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | 1826 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) |
1824 | return -ENOMEM; | 1827 | return -ENOMEM; |
1828 | if (tmp.num_counters == 0) | ||
1829 | return -EINVAL; | ||
1830 | |||
1825 | tmp.name[sizeof(tmp.name)-1] = 0; | 1831 | tmp.name[sizeof(tmp.name)-1] = 0; |
1826 | 1832 | ||
1827 | newinfo = xt_alloc_table_info(tmp.size); | 1833 | newinfo = xt_alloc_table_info(tmp.size); |
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 3ad91266c821..4ef1fae8445e 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c | |||
@@ -1073,7 +1073,13 @@ static struct pernet_operations nfnl_log_net_ops = { | |||
1073 | 1073 | ||
1074 | static int __init nfnetlink_log_init(void) | 1074 | static int __init nfnetlink_log_init(void) |
1075 | { | 1075 | { |
1076 | int status = -ENOMEM; | 1076 | int status; |
1077 | |||
1078 | status = register_pernet_subsys(&nfnl_log_net_ops); | ||
1079 | if (status < 0) { | ||
1080 | pr_err("failed to register pernet ops\n"); | ||
1081 | goto out; | ||
1082 | } | ||
1077 | 1083 | ||
1078 | netlink_register_notifier(&nfulnl_rtnl_notifier); | 1084 | netlink_register_notifier(&nfulnl_rtnl_notifier); |
1079 | status = nfnetlink_subsys_register(&nfulnl_subsys); | 1085 | status = nfnetlink_subsys_register(&nfulnl_subsys); |
@@ -1088,28 +1094,23 @@ static int __init nfnetlink_log_init(void) | |||
1088 | goto cleanup_subsys; | 1094 | goto cleanup_subsys; |
1089 | } | 1095 | } |
1090 | 1096 | ||
1091 | status = register_pernet_subsys(&nfnl_log_net_ops); | ||
1092 | if (status < 0) { | ||
1093 | pr_err("failed to register pernet ops\n"); | ||
1094 | goto cleanup_logger; | ||
1095 | } | ||
1096 | return status; | 1097 | return status; |
1097 | 1098 | ||
1098 | cleanup_logger: | ||
1099 | nf_log_unregister(&nfulnl_logger); | ||
1100 | cleanup_subsys: | 1099 | cleanup_subsys: |
1101 | nfnetlink_subsys_unregister(&nfulnl_subsys); | 1100 | nfnetlink_subsys_unregister(&nfulnl_subsys); |
1102 | cleanup_netlink_notifier: | 1101 | cleanup_netlink_notifier: |
1103 | netlink_unregister_notifier(&nfulnl_rtnl_notifier); | 1102 | netlink_unregister_notifier(&nfulnl_rtnl_notifier); |
1103 | unregister_pernet_subsys(&nfnl_log_net_ops); | ||
1104 | out: | ||
1104 | return status; | 1105 | return status; |
1105 | } | 1106 | } |
1106 | 1107 | ||
1107 | static void __exit nfnetlink_log_fini(void) | 1108 | static void __exit nfnetlink_log_fini(void) |
1108 | { | 1109 | { |
1109 | unregister_pernet_subsys(&nfnl_log_net_ops); | ||
1110 | nf_log_unregister(&nfulnl_logger); | 1110 | nf_log_unregister(&nfulnl_logger); |
1111 | nfnetlink_subsys_unregister(&nfulnl_subsys); | 1111 | nfnetlink_subsys_unregister(&nfulnl_subsys); |
1112 | netlink_unregister_notifier(&nfulnl_rtnl_notifier); | 1112 | netlink_unregister_notifier(&nfulnl_rtnl_notifier); |
1113 | unregister_pernet_subsys(&nfnl_log_net_ops); | ||
1113 | } | 1114 | } |
1114 | 1115 | ||
1115 | MODULE_DESCRIPTION("netfilter userspace logging"); | 1116 | MODULE_DESCRIPTION("netfilter userspace logging"); |
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 0b98c7420239..11c7682fa0ea 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c | |||
@@ -1317,7 +1317,13 @@ static struct pernet_operations nfnl_queue_net_ops = { | |||
1317 | 1317 | ||
1318 | static int __init nfnetlink_queue_init(void) | 1318 | static int __init nfnetlink_queue_init(void) |
1319 | { | 1319 | { |
1320 | int status = -ENOMEM; | 1320 | int status; |
1321 | |||
1322 | status = register_pernet_subsys(&nfnl_queue_net_ops); | ||
1323 | if (status < 0) { | ||
1324 | pr_err("nf_queue: failed to register pernet ops\n"); | ||
1325 | goto out; | ||
1326 | } | ||
1321 | 1327 | ||
1322 | netlink_register_notifier(&nfqnl_rtnl_notifier); | 1328 | netlink_register_notifier(&nfqnl_rtnl_notifier); |
1323 | status = nfnetlink_subsys_register(&nfqnl_subsys); | 1329 | status = nfnetlink_subsys_register(&nfqnl_subsys); |
@@ -1326,19 +1332,13 @@ static int __init nfnetlink_queue_init(void) | |||
1326 | goto cleanup_netlink_notifier; | 1332 | goto cleanup_netlink_notifier; |
1327 | } | 1333 | } |
1328 | 1334 | ||
1329 | status = register_pernet_subsys(&nfnl_queue_net_ops); | ||
1330 | if (status < 0) { | ||
1331 | pr_err("nf_queue: failed to register pernet ops\n"); | ||
1332 | goto cleanup_subsys; | ||
1333 | } | ||
1334 | register_netdevice_notifier(&nfqnl_dev_notifier); | 1335 | register_netdevice_notifier(&nfqnl_dev_notifier); |
1335 | nf_register_queue_handler(&nfqh); | 1336 | nf_register_queue_handler(&nfqh); |
1336 | return status; | 1337 | return status; |
1337 | 1338 | ||
1338 | cleanup_subsys: | ||
1339 | nfnetlink_subsys_unregister(&nfqnl_subsys); | ||
1340 | cleanup_netlink_notifier: | 1339 | cleanup_netlink_notifier: |
1341 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); | 1340 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); |
1341 | out: | ||
1342 | return status; | 1342 | return status; |
1343 | } | 1343 | } |
1344 | 1344 | ||
@@ -1346,9 +1346,9 @@ static void __exit nfnetlink_queue_fini(void) | |||
1346 | { | 1346 | { |
1347 | nf_unregister_queue_handler(); | 1347 | nf_unregister_queue_handler(); |
1348 | unregister_netdevice_notifier(&nfqnl_dev_notifier); | 1348 | unregister_netdevice_notifier(&nfqnl_dev_notifier); |
1349 | unregister_pernet_subsys(&nfnl_queue_net_ops); | ||
1350 | nfnetlink_subsys_unregister(&nfqnl_subsys); | 1349 | nfnetlink_subsys_unregister(&nfqnl_subsys); |
1351 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); | 1350 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); |
1351 | unregister_pernet_subsys(&nfnl_queue_net_ops); | ||
1352 | 1352 | ||
1353 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ | 1353 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ |
1354 | } | 1354 | } |