Skip to content

Commit 8db4c5b

Browse files
committed
netfilter: move socket lookup infrastructure to nf_socket_ipv{4,6}.c
We need this split to reuse existing codebase for the upcoming nf_tables socket expression. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 1fddf4b commit 8db4c5b

File tree

9 files changed

+369
-296
lines changed

9 files changed

+369
-296
lines changed

include/net/netfilter/nf_socket.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef _NF_SOCK_H_
2+
#define _NF_SOCK_H_
3+
4+
struct net_device;
5+
struct sk_buff;
6+
struct sock;
7+
struct net;
8+
9+
static inline bool nf_sk_is_transparent(struct sock *sk)
10+
{
11+
switch (sk->sk_state) {
12+
case TCP_TIME_WAIT:
13+
return inet_twsk(sk)->tw_transparent;
14+
case TCP_NEW_SYN_RECV:
15+
return inet_rsk(inet_reqsk(sk))->no_srccheck;
16+
default:
17+
return inet_sk(sk)->transparent;
18+
}
19+
}
20+
21+
struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb,
22+
const struct net_device *indev);
23+
24+
struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb,
25+
const struct net_device *indev);
26+
27+
#endif

net/ipv4/netfilter/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ config NF_CONNTRACK_IPV4
2525

2626
To compile it as a module, choose M here. If unsure, say N.
2727

28+
config NF_SOCKET_IPV4
29+
tristate "IPv4 socket lookup support"
30+
help
31+
This option enables the IPv4 socket lookup infrastructure. This is
32+
is required by the iptables socket match.
33+
2834
if NF_TABLES
2935

3036
config NF_TABLES_IPV4

net/ipv4/netfilter/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o
1414
# defrag
1515
obj-$(CONFIG_NF_DEFRAG_IPV4) += nf_defrag_ipv4.o
1616

17+
obj-$(CONFIG_NF_SOCKET_IPV4) += nf_socket_ipv4.o
18+
1719
# logging
1820
obj-$(CONFIG_NF_LOG_ARP) += nf_log_arp.o
1921
obj-$(CONFIG_NF_LOG_IPV4) += nf_log_ipv4.o

net/ipv4/netfilter/nf_socket_ipv4.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright (C) 2007-2008 BalaBit IT Ltd.
3+
* Author: Krisztian Kovacs
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 2 as
7+
* published by the Free Software Foundation.
8+
*
9+
*/
10+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11+
#include <linux/module.h>
12+
#include <linux/skbuff.h>
13+
#include <net/tcp.h>
14+
#include <net/udp.h>
15+
#include <net/icmp.h>
16+
#include <net/sock.h>
17+
#include <net/inet_sock.h>
18+
#include <net/netfilter/nf_socket.h>
19+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
20+
#include <net/netfilter/nf_conntrack.h>
21+
#endif
22+
23+
static int
24+
extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol,
25+
__be32 *raddr, __be32 *laddr,
26+
__be16 *rport, __be16 *lport)
27+
{
28+
unsigned int outside_hdrlen = ip_hdrlen(skb);
29+
struct iphdr *inside_iph, _inside_iph;
30+
struct icmphdr *icmph, _icmph;
31+
__be16 *ports, _ports[2];
32+
33+
icmph = skb_header_pointer(skb, outside_hdrlen,
34+
sizeof(_icmph), &_icmph);
35+
if (icmph == NULL)
36+
return 1;
37+
38+
switch (icmph->type) {
39+
case ICMP_DEST_UNREACH:
40+
case ICMP_SOURCE_QUENCH:
41+
case ICMP_REDIRECT:
42+
case ICMP_TIME_EXCEEDED:
43+
case ICMP_PARAMETERPROB:
44+
break;
45+
default:
46+
return 1;
47+
}
48+
49+
inside_iph = skb_header_pointer(skb, outside_hdrlen +
50+
sizeof(struct icmphdr),
51+
sizeof(_inside_iph), &_inside_iph);
52+
if (inside_iph == NULL)
53+
return 1;
54+
55+
if (inside_iph->protocol != IPPROTO_TCP &&
56+
inside_iph->protocol != IPPROTO_UDP)
57+
return 1;
58+
59+
ports = skb_header_pointer(skb, outside_hdrlen +
60+
sizeof(struct icmphdr) +
61+
(inside_iph->ihl << 2),
62+
sizeof(_ports), &_ports);
63+
if (ports == NULL)
64+
return 1;
65+
66+
/* the inside IP packet is the one quoted from our side, thus
67+
* its saddr is the local address */
68+
*protocol = inside_iph->protocol;
69+
*laddr = inside_iph->saddr;
70+
*lport = ports[0];
71+
*raddr = inside_iph->daddr;
72+
*rport = ports[1];
73+
74+
return 0;
75+
}
76+
77+
static struct sock *
78+
nf_socket_get_sock_v4(struct net *net, struct sk_buff *skb, const int doff,
79+
const u8 protocol,
80+
const __be32 saddr, const __be32 daddr,
81+
const __be16 sport, const __be16 dport,
82+
const struct net_device *in)
83+
{
84+
switch (protocol) {
85+
case IPPROTO_TCP:
86+
return inet_lookup(net, &tcp_hashinfo, skb, doff,
87+
saddr, sport, daddr, dport,
88+
in->ifindex);
89+
case IPPROTO_UDP:
90+
return udp4_lib_lookup(net, saddr, sport, daddr, dport,
91+
in->ifindex);
92+
}
93+
return NULL;
94+
}
95+
96+
struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb,
97+
const struct net_device *indev)
98+
{
99+
__be32 uninitialized_var(daddr), uninitialized_var(saddr);
100+
__be16 uninitialized_var(dport), uninitialized_var(sport);
101+
const struct iphdr *iph = ip_hdr(skb);
102+
struct sk_buff *data_skb = NULL;
103+
u8 uninitialized_var(protocol);
104+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
105+
enum ip_conntrack_info ctinfo;
106+
struct nf_conn const *ct;
107+
#endif
108+
int doff = 0;
109+
110+
if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
111+
struct udphdr _hdr, *hp;
112+
113+
hp = skb_header_pointer(skb, ip_hdrlen(skb),
114+
sizeof(_hdr), &_hdr);
115+
if (hp == NULL)
116+
return NULL;
117+
118+
protocol = iph->protocol;
119+
saddr = iph->saddr;
120+
sport = hp->source;
121+
daddr = iph->daddr;
122+
dport = hp->dest;
123+
data_skb = (struct sk_buff *)skb;
124+
doff = iph->protocol == IPPROTO_TCP ?
125+
ip_hdrlen(skb) + __tcp_hdrlen((struct tcphdr *)hp) :
126+
ip_hdrlen(skb) + sizeof(*hp);
127+
128+
} else if (iph->protocol == IPPROTO_ICMP) {
129+
if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
130+
&sport, &dport))
131+
return NULL;
132+
} else {
133+
return NULL;
134+
}
135+
136+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
137+
/* Do the lookup with the original socket address in
138+
* case this is a reply packet of an established
139+
* SNAT-ted connection.
140+
*/
141+
ct = nf_ct_get(skb, &ctinfo);
142+
if (ct && !nf_ct_is_untracked(ct) &&
143+
((iph->protocol != IPPROTO_ICMP &&
144+
ctinfo == IP_CT_ESTABLISHED_REPLY) ||
145+
(iph->protocol == IPPROTO_ICMP &&
146+
ctinfo == IP_CT_RELATED_REPLY)) &&
147+
(ct->status & IPS_SRC_NAT_DONE)) {
148+
149+
daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
150+
dport = (iph->protocol == IPPROTO_TCP) ?
151+
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port :
152+
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
153+
}
154+
#endif
155+
156+
return nf_socket_get_sock_v4(net, data_skb, doff, protocol, saddr,
157+
daddr, sport, dport, indev);
158+
}
159+
EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v4);
160+
161+
MODULE_LICENSE("GPL");
162+
MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
163+
MODULE_DESCRIPTION("Netfilter IPv4 socket lookup infrastructure");

net/ipv6/netfilter/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ config NF_CONNTRACK_IPV6
2525

2626
To compile it as a module, choose M here. If unsure, say N.
2727

28+
config NF_SOCKET_IPV6
29+
tristate "IPv6 socket lookup support"
30+
help
31+
This option enables the IPv6 socket lookup infrastructure. This
32+
is used by the ip6tables socket match.
33+
2834
if NF_TABLES
2935

3036
config NF_TABLES_IPV6

net/ipv6/netfilter/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ obj-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o
2424
nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
2525
obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o
2626

27+
obj-$(CONFIG_NF_SOCKET_IPV6) += nf_socket_ipv6.o
28+
2729
# logging
2830
obj-$(CONFIG_NF_LOG_IPV6) += nf_log_ipv6.o
2931

net/ipv6/netfilter/nf_socket_ipv6.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright (C) 2007-2008 BalaBit IT Ltd.
3+
* Author: Krisztian Kovacs
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 2 as
7+
* published by the Free Software Foundation.
8+
*
9+
*/
10+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11+
#include <linux/module.h>
12+
#include <linux/skbuff.h>
13+
#include <net/tcp.h>
14+
#include <net/udp.h>
15+
#include <net/icmp.h>
16+
#include <net/sock.h>
17+
#include <net/inet_sock.h>
18+
#include <net/inet6_hashtables.h>
19+
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
20+
#include <net/netfilter/nf_socket.h>
21+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
22+
#include <net/netfilter/nf_conntrack.h>
23+
#endif
24+
25+
static int
26+
extract_icmp6_fields(const struct sk_buff *skb,
27+
unsigned int outside_hdrlen,
28+
int *protocol,
29+
const struct in6_addr **raddr,
30+
const struct in6_addr **laddr,
31+
__be16 *rport,
32+
__be16 *lport,
33+
struct ipv6hdr *ipv6_var)
34+
{
35+
const struct ipv6hdr *inside_iph;
36+
struct icmp6hdr *icmph, _icmph;
37+
__be16 *ports, _ports[2];
38+
u8 inside_nexthdr;
39+
__be16 inside_fragoff;
40+
int inside_hdrlen;
41+
42+
icmph = skb_header_pointer(skb, outside_hdrlen,
43+
sizeof(_icmph), &_icmph);
44+
if (icmph == NULL)
45+
return 1;
46+
47+
if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK)
48+
return 1;
49+
50+
inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph),
51+
sizeof(*ipv6_var), ipv6_var);
52+
if (inside_iph == NULL)
53+
return 1;
54+
inside_nexthdr = inside_iph->nexthdr;
55+
56+
inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) +
57+
sizeof(*ipv6_var),
58+
&inside_nexthdr, &inside_fragoff);
59+
if (inside_hdrlen < 0)
60+
return 1; /* hjm: Packet has no/incomplete transport layer headers. */
61+
62+
if (inside_nexthdr != IPPROTO_TCP &&
63+
inside_nexthdr != IPPROTO_UDP)
64+
return 1;
65+
66+
ports = skb_header_pointer(skb, inside_hdrlen,
67+
sizeof(_ports), &_ports);
68+
if (ports == NULL)
69+
return 1;
70+
71+
/* the inside IP packet is the one quoted from our side, thus
72+
* its saddr is the local address */
73+
*protocol = inside_nexthdr;
74+
*laddr = &inside_iph->saddr;
75+
*lport = ports[0];
76+
*raddr = &inside_iph->daddr;
77+
*rport = ports[1];
78+
79+
return 0;
80+
}
81+
82+
static struct sock *
83+
nf_socket_get_sock_v6(struct net *net, struct sk_buff *skb, int doff,
84+
const u8 protocol,
85+
const struct in6_addr *saddr, const struct in6_addr *daddr,
86+
const __be16 sport, const __be16 dport,
87+
const struct net_device *in)
88+
{
89+
switch (protocol) {
90+
case IPPROTO_TCP:
91+
return inet6_lookup(net, &tcp_hashinfo, skb, doff,
92+
saddr, sport, daddr, dport,
93+
in->ifindex);
94+
case IPPROTO_UDP:
95+
return udp6_lib_lookup(net, saddr, sport, daddr, dport,
96+
in->ifindex);
97+
}
98+
99+
return NULL;
100+
}
101+
102+
struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb,
103+
const struct net_device *indev)
104+
{
105+
__be16 uninitialized_var(dport), uninitialized_var(sport);
106+
const struct in6_addr *daddr = NULL, *saddr = NULL;
107+
struct ipv6hdr *iph = ipv6_hdr(skb);
108+
struct sk_buff *data_skb = NULL;
109+
int doff = 0;
110+
int thoff = 0, tproto;
111+
112+
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
113+
if (tproto < 0) {
114+
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
115+
return NULL;
116+
}
117+
118+
if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
119+
struct udphdr _hdr, *hp;
120+
121+
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
122+
if (hp == NULL)
123+
return NULL;
124+
125+
saddr = &iph->saddr;
126+
sport = hp->source;
127+
daddr = &iph->daddr;
128+
dport = hp->dest;
129+
data_skb = (struct sk_buff *)skb;
130+
doff = tproto == IPPROTO_TCP ?
131+
thoff + __tcp_hdrlen((struct tcphdr *)hp) :
132+
thoff + sizeof(*hp);
133+
134+
} else if (tproto == IPPROTO_ICMPV6) {
135+
struct ipv6hdr ipv6_var;
136+
137+
if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
138+
&sport, &dport, &ipv6_var))
139+
return NULL;
140+
} else {
141+
return NULL;
142+
}
143+
144+
return nf_socket_get_sock_v6(net, data_skb, doff, tproto, saddr, daddr,
145+
sport, dport, indev);
146+
}
147+
EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v6);
148+
149+
MODULE_LICENSE("GPL");
150+
MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
151+
MODULE_DESCRIPTION("Netfilter IPv6 socket lookup infrastructure");

net/netfilter/Kconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1426,9 +1426,10 @@ config NETFILTER_XT_MATCH_SOCKET
14261426
tristate '"socket" match support'
14271427
depends on NETFILTER_XTABLES
14281428
depends on NETFILTER_ADVANCED
1429-
depends on !NF_CONNTRACK || NF_CONNTRACK
14301429
depends on IPV6 || IPV6=n
14311430
depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n
1431+
depends on NF_SOCKET_IPV4
1432+
depends on NF_SOCKET_IPV6
14321433
select NF_DEFRAG_IPV4
14331434
select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n
14341435
help

0 commit comments

Comments
 (0)