diff options
author | KOVACS Krisztian <hidden@sch.bme.hu> | 2008-10-08 05:35:12 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2008-10-08 05:35:12 -0400 |
commit | 136cdc71fd54e77463e570643ac76e2b696e48a0 (patch) | |
tree | 89006778803d098a8f991ebeacc2dd86a2782340 /net | |
parent | 9ad2d745a23853927a19789b034d9eb2e62d78ee (diff) |
netfilter: iptables socket match
Add iptables 'socket' match, which matches packets for which a TCP/UDP
socket lookup succeeds.
Signed-off-by: KOVACS Krisztian <hidden@sch.bme.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Kconfig | 15 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_socket.c | 192 |
3 files changed, 208 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ed1dcfb61e12..f6c807299487 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -778,6 +778,21 @@ config NETFILTER_XT_MATCH_SCTP | |||
778 | If you want to compile it as a module, say M here and read | 778 | If you want to compile it as a module, say M here and read |
779 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. | 779 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. |
780 | 780 | ||
781 | config NETFILTER_XT_MATCH_SOCKET | ||
782 | tristate '"socket" match support (EXPERIMENTAL)' | ||
783 | depends on EXPERIMENTAL | ||
784 | depends on NETFILTER_TPROXY | ||
785 | depends on NETFILTER_XTABLES | ||
786 | depends on NETFILTER_ADVANCED | ||
787 | select NF_DEFRAG_IPV4 | ||
788 | help | ||
789 | This option adds a `socket' match, which can be used to match | ||
790 | packets for which a TCP or UDP socket lookup finds a valid socket. | ||
791 | It can be used in combination with the MARK target and policy | ||
792 | routing to implement full featured non-locally bound sockets. | ||
793 | |||
794 | To compile it as a module, choose M here. If unsure, say N. | ||
795 | |||
781 | config NETFILTER_XT_MATCH_STATE | 796 | config NETFILTER_XT_MATCH_STATE |
782 | tristate '"state" match support' | 797 | tristate '"state" match support' |
783 | depends on NETFILTER_XTABLES | 798 | depends on NETFILTER_XTABLES |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index fc8bbb48d383..1cdc3a13d01f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -81,6 +81,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o | |||
81 | obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o | 81 | obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o |
82 | obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o | 82 | obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o |
83 | obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o | 83 | obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o |
84 | obj-$(CONFIG_NETFILTER_XT_MATCH_SOCKET) += xt_socket.o | ||
84 | obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o | 85 | obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o |
85 | obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o | 86 | obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o |
86 | obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o | 87 | obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o |
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c new file mode 100644 index 000000000000..ac9db17c7b9c --- /dev/null +++ b/net/netfilter/xt_socket.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * Transparent proxy support for Linux/iptables | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 BalaBit IT Ltd. | ||
5 | * Author: Krisztian Kovacs | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/netfilter/x_tables.h> | ||
16 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
17 | #include <net/tcp.h> | ||
18 | #include <net/udp.h> | ||
19 | #include <net/icmp.h> | ||
20 | #include <net/sock.h> | ||
21 | #include <net/inet_sock.h> | ||
22 | #include <net/netfilter/nf_tproxy_core.h> | ||
23 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | ||
24 | |||
25 | #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) | ||
26 | #define XT_SOCKET_HAVE_CONNTRACK 1 | ||
27 | #include <net/netfilter/nf_conntrack.h> | ||
28 | #endif | ||
29 | |||
30 | static int | ||
31 | extract_icmp_fields(const struct sk_buff *skb, | ||
32 | u8 *protocol, | ||
33 | __be32 *raddr, | ||
34 | __be32 *laddr, | ||
35 | __be16 *rport, | ||
36 | __be16 *lport) | ||
37 | { | ||
38 | unsigned int outside_hdrlen = ip_hdrlen(skb); | ||
39 | struct iphdr *inside_iph, _inside_iph; | ||
40 | struct icmphdr *icmph, _icmph; | ||
41 | __be16 *ports, _ports[2]; | ||
42 | |||
43 | icmph = skb_header_pointer(skb, outside_hdrlen, | ||
44 | sizeof(_icmph), &_icmph); | ||
45 | if (icmph == NULL) | ||
46 | return 1; | ||
47 | |||
48 | switch (icmph->type) { | ||
49 | case ICMP_DEST_UNREACH: | ||
50 | case ICMP_SOURCE_QUENCH: | ||
51 | case ICMP_REDIRECT: | ||
52 | case ICMP_TIME_EXCEEDED: | ||
53 | case ICMP_PARAMETERPROB: | ||
54 | break; | ||
55 | default: | ||
56 | return 1; | ||
57 | } | ||
58 | |||
59 | inside_iph = skb_header_pointer(skb, outside_hdrlen + | ||
60 | sizeof(struct icmphdr), | ||
61 | sizeof(_inside_iph), &_inside_iph); | ||
62 | if (inside_iph == NULL) | ||
63 | return 1; | ||
64 | |||
65 | if (inside_iph->protocol != IPPROTO_TCP && | ||
66 | inside_iph->protocol != IPPROTO_UDP) | ||
67 | return 1; | ||
68 | |||
69 | ports = skb_header_pointer(skb, outside_hdrlen + | ||
70 | sizeof(struct icmphdr) + | ||
71 | (inside_iph->ihl << 2), | ||
72 | sizeof(_ports), &_ports); | ||
73 | if (ports == NULL) | ||
74 | return 1; | ||
75 | |||
76 | /* the inside IP packet is the one quoted from our side, thus | ||
77 | * its saddr is the local address */ | ||
78 | *protocol = inside_iph->protocol; | ||
79 | *laddr = inside_iph->saddr; | ||
80 | *lport = ports[0]; | ||
81 | *raddr = inside_iph->daddr; | ||
82 | *rport = ports[1]; | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | |||
88 | static bool | ||
89 | socket_mt(const struct sk_buff *skb, | ||
90 | const struct net_device *in, | ||
91 | const struct net_device *out, | ||
92 | const struct xt_match *match, | ||
93 | const void *matchinfo, | ||
94 | int offset, | ||
95 | unsigned int protoff, | ||
96 | bool *hotdrop) | ||
97 | { | ||
98 | const struct iphdr *iph = ip_hdr(skb); | ||
99 | struct udphdr _hdr, *hp = NULL; | ||
100 | struct sock *sk; | ||
101 | __be32 daddr, saddr; | ||
102 | __be16 dport, sport; | ||
103 | u8 protocol; | ||
104 | #ifdef XT_SOCKET_HAVE_CONNTRACK | ||
105 | struct nf_conn const *ct; | ||
106 | enum ip_conntrack_info ctinfo; | ||
107 | #endif | ||
108 | |||
109 | if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { | ||
110 | hp = skb_header_pointer(skb, ip_hdrlen(skb), | ||
111 | sizeof(_hdr), &_hdr); | ||
112 | if (hp == NULL) | ||
113 | return false; | ||
114 | |||
115 | protocol = iph->protocol; | ||
116 | saddr = iph->saddr; | ||
117 | sport = hp->source; | ||
118 | daddr = iph->daddr; | ||
119 | dport = hp->dest; | ||
120 | |||
121 | } else if (iph->protocol == IPPROTO_ICMP) { | ||
122 | if (extract_icmp_fields(skb, &protocol, &saddr, &daddr, | ||
123 | &sport, &dport)) | ||
124 | return false; | ||
125 | } else { | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | #ifdef XT_SOCKET_HAVE_CONNTRACK | ||
130 | /* Do the lookup with the original socket address in case this is a | ||
131 | * reply packet of an established SNAT-ted connection. */ | ||
132 | |||
133 | ct = nf_ct_get(skb, &ctinfo); | ||
134 | if (ct && (ct != &nf_conntrack_untracked) && | ||
135 | ((iph->protocol != IPPROTO_ICMP && | ||
136 | ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) || | ||
137 | (iph->protocol == IPPROTO_ICMP && | ||
138 | ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) && | ||
139 | (ct->status & IPS_SRC_NAT_DONE)) { | ||
140 | |||
141 | daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; | ||
142 | dport = (iph->protocol == IPPROTO_TCP) ? | ||
143 | ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port : | ||
144 | ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; | ||
145 | } | ||
146 | #endif | ||
147 | |||
148 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, | ||
149 | saddr, daddr, sport, dport, in, false); | ||
150 | if (sk != NULL) { | ||
151 | bool wildcard = (inet_sk(sk)->rcv_saddr == 0); | ||
152 | |||
153 | nf_tproxy_put_sock(sk); | ||
154 | if (wildcard) | ||
155 | sk = NULL; | ||
156 | } | ||
157 | |||
158 | pr_debug("socket match: proto %u %08x:%u -> %08x:%u " | ||
159 | "(orig %08x:%u) sock %p\n", | ||
160 | protocol, ntohl(saddr), ntohs(sport), | ||
161 | ntohl(daddr), ntohs(dport), | ||
162 | ntohl(iph->daddr), hp ? ntohs(hp->dest) : 0, sk); | ||
163 | |||
164 | return (sk != NULL); | ||
165 | } | ||
166 | |||
167 | static struct xt_match socket_mt_reg __read_mostly = { | ||
168 | .name = "socket", | ||
169 | .family = AF_INET, | ||
170 | .match = socket_mt, | ||
171 | .hooks = 1 << NF_INET_PRE_ROUTING, | ||
172 | .me = THIS_MODULE, | ||
173 | }; | ||
174 | |||
175 | static int __init socket_mt_init(void) | ||
176 | { | ||
177 | nf_defrag_ipv4_enable(); | ||
178 | return xt_register_match(&socket_mt_reg); | ||
179 | } | ||
180 | |||
181 | static void __exit socket_mt_exit(void) | ||
182 | { | ||
183 | xt_unregister_match(&socket_mt_reg); | ||
184 | } | ||
185 | |||
186 | module_init(socket_mt_init); | ||
187 | module_exit(socket_mt_exit); | ||
188 | |||
189 | MODULE_LICENSE("GPL"); | ||
190 | MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); | ||
191 | MODULE_DESCRIPTION("x_tables socket match module"); | ||
192 | MODULE_ALIAS("ipt_socket"); | ||