aboutsummaryrefslogtreecommitdiff
path: root/net/netfilter/xt_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/netfilter/xt_socket.c')
-rw-r--r--net/netfilter/xt_socket.c192
1 files changed, 157 insertions, 35 deletions
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index 00d6ae83830..1ba67931eb1 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -19,18 +19,18 @@
#include <net/icmp.h>
#include <net/sock.h>
#include <net/inet_sock.h>
-#include <net/netfilter/nf_tproxy_core.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
#define XT_SOCKET_HAVE_IPV6 1
#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/inet6_hashtables.h>
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
#endif
#include <linux/netfilter/xt_socket.h>
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#define XT_SOCKET_HAVE_CONNTRACK 1
#include <net/netfilter/nf_conntrack.h>
#endif
@@ -92,16 +92,53 @@ extract_icmp4_fields(const struct sk_buff *skb,
return 0;
}
+/* "socket" match based redirection (no specific rule)
+ * ===================================================
+ *
+ * There are connections with dynamic endpoints (e.g. FTP data
+ * connection) that the user is unable to add explicit rules
+ * for. These are taken care of by a generic "socket" rule. It is
+ * assumed that the proxy application is trusted to open such
+ * connections without explicit iptables rule (except of course the
+ * generic 'socket' rule). In this case the following sockets are
+ * matched in preference order:
+ *
+ * - match: if there's a fully established connection matching the
+ * _packet_ tuple
+ *
+ * - match: if there's a non-zero bound listener (possibly with a
+ * non-local address) We don't accept zero-bound listeners, since
+ * then local services could intercept traffic going through the
+ * box.
+ */
+static struct sock *
+xt_socket_get_sock_v4(struct net *net, const u8 protocol,
+ const __be32 saddr, const __be32 daddr,
+ const __be16 sport, const __be16 dport,
+ const struct net_device *in)
+{
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return __inet_lookup(net, &tcp_hashinfo,
+ saddr, sport, daddr, dport,
+ in->ifindex);
+ case IPPROTO_UDP:
+ return udp4_lib_lookup(net, saddr, sport, daddr, dport,
+ in->ifindex);
+ }
+ return NULL;
+}
+
static bool
socket_match(const struct sk_buff *skb, struct xt_action_param *par,
const struct xt_socket_mtinfo1 *info)
{
const struct iphdr *iph = ip_hdr(skb);
struct udphdr _hdr, *hp = NULL;
- struct sock *sk;
- __be32 daddr, saddr;
- __be16 dport, sport;
- u8 protocol;
+ struct sock *sk = skb->sk;
+ __be32 uninitialized_var(daddr), uninitialized_var(saddr);
+ __be16 uninitialized_var(dport), uninitialized_var(sport);
+ u8 uninitialized_var(protocol);
#ifdef XT_SOCKET_HAVE_CONNTRACK
struct nf_conn const *ct;
enum ip_conntrack_info ctinfo;
@@ -134,9 +171,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
ct = nf_ct_get(skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct) &&
((iph->protocol != IPPROTO_ICMP &&
- ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) ||
+ ctinfo == IP_CT_ESTABLISHED_REPLY) ||
(iph->protocol == IPPROTO_ICMP &&
- ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) &&
+ ctinfo == IP_CT_RELATED_REPLY)) &&
(ct->status & IPS_SRC_NAT_DONE)) {
daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
@@ -146,25 +183,31 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
}
#endif
- sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
- saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY);
- if (sk != NULL) {
+ if (!sk)
+ sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol,
+ saddr, daddr, sport, dport,
+ par->in);
+ if (sk) {
bool wildcard;
bool transparent = true;
- /* Ignore sockets listening on INADDR_ANY */
- wildcard = (sk->sk_state != TCP_TIME_WAIT &&
+ /* Ignore sockets listening on INADDR_ANY,
+ * unless XT_SOCKET_NOWILDCARD is set
+ */
+ wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
+ sk->sk_state != TCP_TIME_WAIT &&
inet_sk(sk)->inet_rcv_saddr == 0);
/* Ignore non-transparent sockets,
if XT_SOCKET_TRANSPARENT is used */
- if (info && info->flags & XT_SOCKET_TRANSPARENT)
+ if (info->flags & XT_SOCKET_TRANSPARENT)
transparent = ((sk->sk_state != TCP_TIME_WAIT &&
inet_sk(sk)->transparent) ||
(sk->sk_state == TCP_TIME_WAIT &&
inet_twsk(sk)->tw_transparent));
- nf_tproxy_put_sock(sk);
+ if (sk != skb->sk)
+ sock_gen_put(sk);
if (wildcard || !transparent)
sk = NULL;
@@ -181,11 +224,15 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
static bool
socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par)
{
- return socket_match(skb, par, NULL);
+ static struct xt_socket_mtinfo1 xt_info_v0 = {
+ .flags = 0,
+ };
+
+ return socket_match(skb, par, &xt_info_v0);
}
static bool
-socket_mt4_v1(const struct sk_buff *skb, struct xt_action_param *par)
+socket_mt4_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
{
return socket_match(skb, par, par->matchinfo);
}
@@ -205,6 +252,7 @@ extract_icmp6_fields(const struct sk_buff *skb,
struct icmp6hdr *icmph, _icmph;
__be16 *ports, _ports[2];
u8 inside_nexthdr;
+ __be16 inside_fragoff;
int inside_hdrlen;
icmph = skb_header_pointer(skb, outside_hdrlen,
@@ -220,7 +268,8 @@ extract_icmp6_fields(const struct sk_buff *skb,
return 1;
inside_nexthdr = inside_iph->nexthdr;
- inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), &inside_nexthdr);
+ inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph),
+ &inside_nexthdr, &inside_fragoff);
if (inside_hdrlen < 0)
return 1; /* hjm: Packet has no/incomplete transport layer headers. */
@@ -244,18 +293,37 @@ extract_icmp6_fields(const struct sk_buff *skb,
return 0;
}
+static struct sock *
+xt_socket_get_sock_v6(struct net *net, const u8 protocol,
+ const struct in6_addr *saddr, const struct in6_addr *daddr,
+ const __be16 sport, const __be16 dport,
+ const struct net_device *in)
+{
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return inet6_lookup(net, &tcp_hashinfo,
+ saddr, sport, daddr, dport,
+ in->ifindex);
+ case IPPROTO_UDP:
+ return udp6_lib_lookup(net, saddr, sport, daddr, dport,
+ in->ifindex);
+ }
+
+ return NULL;
+}
+
static bool
-socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
+socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
struct udphdr _hdr, *hp = NULL;
- struct sock *sk;
- struct in6_addr *daddr, *saddr;
- __be16 dport, sport;
- int thoff, tproto;
+ struct sock *sk = skb->sk;
+ struct in6_addr *daddr = NULL, *saddr = NULL;
+ __be16 uninitialized_var(dport), uninitialized_var(sport);
+ int thoff = 0, uninitialized_var(tproto);
const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
- tproto = ipv6_find_hdr(skb, &thoff, -1, NULL);
+ tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
if (tproto < 0) {
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
return NF_DROP;
@@ -280,25 +348,31 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
return false;
}
- sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
- saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY);
- if (sk != NULL) {
+ if (!sk)
+ sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto,
+ saddr, daddr, sport, dport,
+ par->in);
+ if (sk) {
bool wildcard;
bool transparent = true;
- /* Ignore sockets listening on INADDR_ANY */
- wildcard = (sk->sk_state != TCP_TIME_WAIT &&
- ipv6_addr_any(&inet6_sk(sk)->rcv_saddr));
+ /* Ignore sockets listening on INADDR_ANY
+ * unless XT_SOCKET_NOWILDCARD is set
+ */
+ wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
+ sk->sk_state != TCP_TIME_WAIT &&
+ ipv6_addr_any(&sk->sk_v6_rcv_saddr));
/* Ignore non-transparent sockets,
if XT_SOCKET_TRANSPARENT is used */
- if (info && info->flags & XT_SOCKET_TRANSPARENT)
+ if (info->flags & XT_SOCKET_TRANSPARENT)
transparent = ((sk->sk_state != TCP_TIME_WAIT &&
inet_sk(sk)->transparent) ||
(sk->sk_state == TCP_TIME_WAIT &&
inet_twsk(sk)->tw_transparent));
- nf_tproxy_put_sock(sk);
+ if (sk != skb->sk)
+ sock_gen_put(sk);
if (wildcard || !transparent)
sk = NULL;
@@ -314,6 +388,28 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
}
#endif
+static int socket_mt_v1_check(const struct xt_mtchk_param *par)
+{
+ const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+
+ if (info->flags & ~XT_SOCKET_FLAGS_V1) {
+ pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int socket_mt_v2_check(const struct xt_mtchk_param *par)
+{
+ const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo;
+
+ if (info->flags & ~XT_SOCKET_FLAGS_V2) {
+ pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct xt_match socket_mt_reg[] __read_mostly = {
{
.name = "socket",
@@ -328,7 +424,8 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.name = "socket",
.revision = 1,
.family = NFPROTO_IPV4,
- .match = socket_mt4_v1,
+ .match = socket_mt4_v1_v2,
+ .checkentry = socket_mt_v1_check,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
@@ -339,7 +436,32 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.name = "socket",
.revision = 1,
.family = NFPROTO_IPV6,
- .match = socket_mt6_v1,
+ .match = socket_mt6_v1_v2,
+ .checkentry = socket_mt_v1_check,
+ .matchsize = sizeof(struct xt_socket_mtinfo1),
+ .hooks = (1 << NF_INET_PRE_ROUTING) |
+ (1 << NF_INET_LOCAL_IN),
+ .me = THIS_MODULE,
+ },
+#endif
+ {
+ .name = "socket",
+ .revision = 2,
+ .family = NFPROTO_IPV4,
+ .match = socket_mt4_v1_v2,
+ .checkentry = socket_mt_v2_check,
+ .matchsize = sizeof(struct xt_socket_mtinfo1),
+ .hooks = (1 << NF_INET_PRE_ROUTING) |
+ (1 << NF_INET_LOCAL_IN),
+ .me = THIS_MODULE,
+ },
+#ifdef XT_SOCKET_HAVE_IPV6
+ {
+ .name = "socket",
+ .revision = 2,
+ .family = NFPROTO_IPV6,
+ .match = socket_mt6_v1_v2,
+ .checkentry = socket_mt_v2_check,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),