Blame view

net/netfilter/xt_socket.c 9.53 KB
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * Transparent proxy support for Linux/iptables
   *
   * Copyright (C) 2007-2008 BalaBit IT Ltd.
   * Author: Krisztian Kovacs
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
ff67e4e42   Jan Engelhardt   netfilter: xt ext...
12
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
13
14
15
16
17
18
19
20
21
22
23
  #include <linux/module.h>
  #include <linux/skbuff.h>
  #include <linux/netfilter/x_tables.h>
  #include <linux/netfilter_ipv4/ip_tables.h>
  #include <net/tcp.h>
  #include <net/udp.h>
  #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>
f6318e558   KOVACS Krisztian   netfilter: fix mo...
24

c0cd11566   Igor Maravić   net:netfilter: us...
25
  #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
f6318e558   KOVACS Krisztian   netfilter: fix mo...
26
27
  #define XT_SOCKET_HAVE_IPV6 1
  #include <linux/netfilter_ipv6/ip6_tables.h>
b64c9256a   Balazs Scheidler   tproxy: added IPv...
28
  #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
f6318e558   KOVACS Krisztian   netfilter: fix mo...
29
  #endif
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
30

a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
31
  #include <linux/netfilter/xt_socket.h>
c0cd11566   Igor Maravić   net:netfilter: us...
32
  #if IS_ENABLED(CONFIG_NF_CONNTRACK)
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
33
34
35
  #define XT_SOCKET_HAVE_CONNTRACK 1
  #include <net/netfilter/nf_conntrack.h>
  #endif
d503b30bd   Florian Westphal   netfilter: tproxy...
36
37
38
39
40
41
42
43
  static void
  xt_socket_put_sk(struct sock *sk)
  {
  	if (sk->sk_state == TCP_TIME_WAIT)
  		inet_twsk_put(inet_twsk(sk));
  	else
  		sock_put(sk);
  }
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
44
  static int
b64c9256a   Balazs Scheidler   tproxy: added IPv...
45
  extract_icmp4_fields(const struct sk_buff *skb,
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  		    u8 *protocol,
  		    __be32 *raddr,
  		    __be32 *laddr,
  		    __be16 *rport,
  		    __be16 *lport)
  {
  	unsigned int outside_hdrlen = ip_hdrlen(skb);
  	struct iphdr *inside_iph, _inside_iph;
  	struct icmphdr *icmph, _icmph;
  	__be16 *ports, _ports[2];
  
  	icmph = skb_header_pointer(skb, outside_hdrlen,
  				   sizeof(_icmph), &_icmph);
  	if (icmph == NULL)
  		return 1;
  
  	switch (icmph->type) {
  	case ICMP_DEST_UNREACH:
  	case ICMP_SOURCE_QUENCH:
  	case ICMP_REDIRECT:
  	case ICMP_TIME_EXCEEDED:
  	case ICMP_PARAMETERPROB:
  		break;
  	default:
  		return 1;
  	}
  
  	inside_iph = skb_header_pointer(skb, outside_hdrlen +
  					sizeof(struct icmphdr),
  					sizeof(_inside_iph), &_inside_iph);
  	if (inside_iph == NULL)
  		return 1;
  
  	if (inside_iph->protocol != IPPROTO_TCP &&
  	    inside_iph->protocol != IPPROTO_UDP)
  		return 1;
  
  	ports = skb_header_pointer(skb, outside_hdrlen +
  				   sizeof(struct icmphdr) +
  				   (inside_iph->ihl << 2),
  				   sizeof(_ports), &_ports);
  	if (ports == NULL)
  		return 1;
  
  	/* the inside IP packet is the one quoted from our side, thus
  	 * its saddr is the local address */
  	*protocol = inside_iph->protocol;
  	*laddr = inside_iph->saddr;
  	*lport = ports[0];
  	*raddr = inside_iph->daddr;
  	*rport = ports[1];
  
  	return 0;
  }
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
100
  static bool
62fc80510   Jan Engelhardt   netfilter: xtable...
101
  socket_match(const struct sk_buff *skb, struct xt_action_param *par,
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
102
  	     const struct xt_socket_mtinfo1 *info)
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  {
  	const struct iphdr *iph = ip_hdr(skb);
  	struct udphdr _hdr, *hp = NULL;
  	struct sock *sk;
  	__be32 daddr, saddr;
  	__be16 dport, sport;
  	u8 protocol;
  #ifdef XT_SOCKET_HAVE_CONNTRACK
  	struct nf_conn const *ct;
  	enum ip_conntrack_info ctinfo;
  #endif
  
  	if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
  		hp = skb_header_pointer(skb, ip_hdrlen(skb),
  					sizeof(_hdr), &_hdr);
  		if (hp == NULL)
  			return false;
  
  		protocol = iph->protocol;
  		saddr = iph->saddr;
  		sport = hp->source;
  		daddr = iph->daddr;
  		dport = hp->dest;
  
  	} else if (iph->protocol == IPPROTO_ICMP) {
b64c9256a   Balazs Scheidler   tproxy: added IPv...
128
  		if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
129
130
131
132
133
134
135
136
137
138
139
  					&sport, &dport))
  			return false;
  	} else {
  		return false;
  	}
  
  #ifdef XT_SOCKET_HAVE_CONNTRACK
  	/* Do the lookup with the original socket address in case this is a
  	 * reply packet of an established SNAT-ted connection. */
  
  	ct = nf_ct_get(skb, &ctinfo);
5bfddbd46   Eric Dumazet   netfilter: nf_con...
140
  	if (ct && !nf_ct_is_untracked(ct) &&
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
141
  	    ((iph->protocol != IPPROTO_ICMP &&
fb0488337   Eric Dumazet   netfilter: add mo...
142
  	      ctinfo == IP_CT_ESTABLISHED_REPLY) ||
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
143
  	     (iph->protocol == IPPROTO_ICMP &&
fb0488337   Eric Dumazet   netfilter: add mo...
144
  	      ctinfo == IP_CT_RELATED_REPLY)) &&
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
145
146
147
148
149
150
151
152
153
154
  	    (ct->status & IPS_SRC_NAT_DONE)) {
  
  		daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
  		dport = (iph->protocol == IPPROTO_TCP) ?
  			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port :
  			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
  	}
  #endif
  
  	sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
106e4c26b   Balazs Scheidler   tproxy: kick out ...
155
  				   saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY);
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
156
  	if (sk != NULL) {
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
157
158
159
160
161
  		bool wildcard;
  		bool transparent = true;
  
  		/* Ignore sockets listening on INADDR_ANY */
  		wildcard = (sk->sk_state != TCP_TIME_WAIT &&
c720c7e83   Eric Dumazet   inet: rename some...
162
  			    inet_sk(sk)->inet_rcv_saddr == 0);
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
163
164
165
166
167
168
169
170
  
  		/* Ignore non-transparent sockets,
  		   if XT_SOCKET_TRANSPARENT is used */
  		if (info && 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));
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
171

d503b30bd   Florian Westphal   netfilter: tproxy...
172
  		xt_socket_put_sk(sk);
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
173
174
  
  		if (wildcard || !transparent)
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
175
176
  			sk = NULL;
  	}
b64c9256a   Balazs Scheidler   tproxy: added IPv...
177
178
179
180
181
  	pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p
  ",
  		 protocol, &saddr, ntohs(sport),
  		 &daddr, ntohs(dport),
  		 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
182
183
184
  
  	return (sk != NULL);
  }
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
185
  static bool
b64c9256a   Balazs Scheidler   tproxy: added IPv...
186
  socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par)
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
187
188
189
190
191
  {
  	return socket_match(skb, par, NULL);
  }
  
  static bool
b64c9256a   Balazs Scheidler   tproxy: added IPv...
192
  socket_mt4_v1(const struct sk_buff *skb, struct xt_action_param *par)
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
193
194
195
  {
  	return socket_match(skb, par, par->matchinfo);
  }
f6318e558   KOVACS Krisztian   netfilter: fix mo...
196
  #ifdef XT_SOCKET_HAVE_IPV6
b64c9256a   Balazs Scheidler   tproxy: added IPv...
197
198
199
200
  
  static int
  extract_icmp6_fields(const struct sk_buff *skb,
  		     unsigned int outside_hdrlen,
089282fb0   David S. Miller   netfilter: xt_soc...
201
  		     int *protocol,
b64c9256a   Balazs Scheidler   tproxy: added IPv...
202
203
204
205
206
207
208
209
210
  		     struct in6_addr **raddr,
  		     struct in6_addr **laddr,
  		     __be16 *rport,
  		     __be16 *lport)
  {
  	struct ipv6hdr *inside_iph, _inside_iph;
  	struct icmp6hdr *icmph, _icmph;
  	__be16 *ports, _ports[2];
  	u8 inside_nexthdr;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
211
  	__be16 inside_fragoff;
b64c9256a   Balazs Scheidler   tproxy: added IPv...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  	int inside_hdrlen;
  
  	icmph = skb_header_pointer(skb, outside_hdrlen,
  				   sizeof(_icmph), &_icmph);
  	if (icmph == NULL)
  		return 1;
  
  	if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK)
  		return 1;
  
  	inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph);
  	if (inside_iph == NULL)
  		return 1;
  	inside_nexthdr = inside_iph->nexthdr;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
226
227
  	inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph),
  					 &inside_nexthdr, &inside_fragoff);
b64c9256a   Balazs Scheidler   tproxy: added IPv...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  	if (inside_hdrlen < 0)
  		return 1; /* hjm: Packet has no/incomplete transport layer headers. */
  
  	if (inside_nexthdr != IPPROTO_TCP &&
  	    inside_nexthdr != IPPROTO_UDP)
  		return 1;
  
  	ports = skb_header_pointer(skb, inside_hdrlen,
  				   sizeof(_ports), &_ports);
  	if (ports == NULL)
  		return 1;
  
  	/* the inside IP packet is the one quoted from our side, thus
  	 * its saddr is the local address */
  	*protocol = inside_nexthdr;
  	*laddr = &inside_iph->saddr;
  	*lport = ports[0];
  	*raddr = &inside_iph->daddr;
  	*rport = ports[1];
  
  	return 0;
  }
  
  static bool
  socket_mt6_v1(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;
089282fb0   David S. Miller   netfilter: xt_soc...
259
  	int thoff, tproto;
b64c9256a   Balazs Scheidler   tproxy: added IPv...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
  	const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
  
  	tproto = ipv6_find_hdr(skb, &thoff, -1, NULL);
  	if (tproto < 0) {
  		pr_debug("unable to find transport header in IPv6 packet, dropping
  ");
  		return NF_DROP;
  	}
  
  	if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
  		hp = skb_header_pointer(skb, thoff,
  					sizeof(_hdr), &_hdr);
  		if (hp == NULL)
  			return false;
  
  		saddr = &iph->saddr;
  		sport = hp->source;
  		daddr = &iph->daddr;
  		dport = hp->dest;
  
  	} else if (tproto == IPPROTO_ICMPV6) {
  		if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
  					 &sport, &dport))
  			return false;
  	} else {
  		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) {
  		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 non-transparent sockets,
  		   if XT_SOCKET_TRANSPARENT is used */
  		if (info && 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));
d503b30bd   Florian Westphal   netfilter: tproxy...
305
  		xt_socket_put_sk(sk);
b64c9256a   Balazs Scheidler   tproxy: added IPv...
306
307
308
309
  
  		if (wildcard || !transparent)
  			sk = NULL;
  	}
089282fb0   David S. Miller   netfilter: xt_soc...
310
  	pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
b64c9256a   Balazs Scheidler   tproxy: added IPv...
311
312
313
314
315
316
317
318
319
  		 "(orig %pI6:%hu) sock %p
  ",
  		 tproto, saddr, ntohs(sport),
  		 daddr, ntohs(dport),
  		 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
  
  	return (sk != NULL);
  }
  #endif
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
320
321
322
323
324
  static struct xt_match socket_mt_reg[] __read_mostly = {
  	{
  		.name		= "socket",
  		.revision	= 0,
  		.family		= NFPROTO_IPV4,
b64c9256a   Balazs Scheidler   tproxy: added IPv...
325
  		.match		= socket_mt4_v0,
aa3c487f3   Jan Engelhardt   netfilter: xt_soc...
326
327
  		.hooks		= (1 << NF_INET_PRE_ROUTING) |
  				  (1 << NF_INET_LOCAL_IN),
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
328
329
330
331
332
333
  		.me		= THIS_MODULE,
  	},
  	{
  		.name		= "socket",
  		.revision	= 1,
  		.family		= NFPROTO_IPV4,
b64c9256a   Balazs Scheidler   tproxy: added IPv...
334
  		.match		= socket_mt4_v1,
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
335
  		.matchsize	= sizeof(struct xt_socket_mtinfo1),
aa3c487f3   Jan Engelhardt   netfilter: xt_soc...
336
337
  		.hooks		= (1 << NF_INET_PRE_ROUTING) |
  				  (1 << NF_INET_LOCAL_IN),
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
338
339
  		.me		= THIS_MODULE,
  	},
f6318e558   KOVACS Krisztian   netfilter: fix mo...
340
  #ifdef XT_SOCKET_HAVE_IPV6
b64c9256a   Balazs Scheidler   tproxy: added IPv...
341
342
343
344
345
346
347
348
349
350
351
  	{
  		.name		= "socket",
  		.revision	= 1,
  		.family		= NFPROTO_IPV6,
  		.match		= socket_mt6_v1,
  		.matchsize	= sizeof(struct xt_socket_mtinfo1),
  		.hooks		= (1 << NF_INET_PRE_ROUTING) |
  				  (1 << NF_INET_LOCAL_IN),
  		.me		= THIS_MODULE,
  	},
  #endif
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
352
353
354
355
356
  };
  
  static int __init socket_mt_init(void)
  {
  	nf_defrag_ipv4_enable();
f6318e558   KOVACS Krisztian   netfilter: fix mo...
357
  #ifdef XT_SOCKET_HAVE_IPV6
b64c9256a   Balazs Scheidler   tproxy: added IPv...
358
359
  	nf_defrag_ipv6_enable();
  #endif
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
360
  	return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
361
362
363
364
  }
  
  static void __exit socket_mt_exit(void)
  {
a31e1ffd2   Laszlo Attila Toth   netfilter: xt_soc...
365
  	xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
136cdc71f   KOVACS Krisztian   netfilter: iptabl...
366
367
368
369
370
371
372
373
374
  }
  
  module_init(socket_mt_init);
  module_exit(socket_mt_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
  MODULE_DESCRIPTION("x_tables socket match module");
  MODULE_ALIAS("ipt_socket");
b64c9256a   Balazs Scheidler   tproxy: added IPv...
375
  MODULE_ALIAS("ip6t_socket");