• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/netfilter/
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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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#include <linux/netfilter/xt_socket.h>
26
27#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
28#define XT_SOCKET_HAVE_CONNTRACK 1
29#include <net/netfilter/nf_conntrack.h>
30#endif
31
32static int
33extract_icmp_fields(const struct sk_buff *skb,
34		    u8 *protocol,
35		    __be32 *raddr,
36		    __be32 *laddr,
37		    __be16 *rport,
38		    __be16 *lport)
39{
40	unsigned int outside_hdrlen = ip_hdrlen(skb);
41	struct iphdr *inside_iph, _inside_iph;
42	struct icmphdr *icmph, _icmph;
43	__be16 *ports, _ports[2];
44
45	icmph = skb_header_pointer(skb, outside_hdrlen,
46				   sizeof(_icmph), &_icmph);
47	if (icmph == NULL)
48		return 1;
49
50	switch (icmph->type) {
51	case ICMP_DEST_UNREACH:
52	case ICMP_SOURCE_QUENCH:
53	case ICMP_REDIRECT:
54	case ICMP_TIME_EXCEEDED:
55	case ICMP_PARAMETERPROB:
56		break;
57	default:
58		return 1;
59	}
60
61	inside_iph = skb_header_pointer(skb, outside_hdrlen +
62					sizeof(struct icmphdr),
63					sizeof(_inside_iph), &_inside_iph);
64	if (inside_iph == NULL)
65		return 1;
66
67	if (inside_iph->protocol != IPPROTO_TCP &&
68	    inside_iph->protocol != IPPROTO_UDP)
69		return 1;
70
71	ports = skb_header_pointer(skb, outside_hdrlen +
72				   sizeof(struct icmphdr) +
73				   (inside_iph->ihl << 2),
74				   sizeof(_ports), &_ports);
75	if (ports == NULL)
76		return 1;
77
78	/* the inside IP packet is the one quoted from our side, thus
79	 * its saddr is the local address */
80	*protocol = inside_iph->protocol;
81	*laddr = inside_iph->saddr;
82	*lport = ports[0];
83	*raddr = inside_iph->daddr;
84	*rport = ports[1];
85
86	return 0;
87}
88
89
90static bool
91socket_match(const struct sk_buff *skb, struct xt_action_param *par,
92	     const struct xt_socket_mtinfo1 *info)
93{
94	const struct iphdr *iph = ip_hdr(skb);
95	struct udphdr _hdr, *hp = NULL;
96	struct sock *sk;
97	__be32 daddr, saddr;
98	__be16 dport, sport;
99	u8 protocol;
100#ifdef XT_SOCKET_HAVE_CONNTRACK
101	struct nf_conn const *ct;
102	enum ip_conntrack_info ctinfo;
103#endif
104
105	if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
106		hp = skb_header_pointer(skb, ip_hdrlen(skb),
107					sizeof(_hdr), &_hdr);
108		if (hp == NULL)
109			return false;
110
111		protocol = iph->protocol;
112		saddr = iph->saddr;
113		sport = hp->source;
114		daddr = iph->daddr;
115		dport = hp->dest;
116
117	} else if (iph->protocol == IPPROTO_ICMP) {
118		if (extract_icmp_fields(skb, &protocol, &saddr, &daddr,
119					&sport, &dport))
120			return false;
121	} else {
122		return false;
123	}
124
125#ifdef XT_SOCKET_HAVE_CONNTRACK
126	/* Do the lookup with the original socket address in case this is a
127	 * reply packet of an established SNAT-ted connection. */
128
129	ct = nf_ct_get(skb, &ctinfo);
130	if (ct && !nf_ct_is_untracked(ct) &&
131	    ((iph->protocol != IPPROTO_ICMP &&
132	      ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) ||
133	     (iph->protocol == IPPROTO_ICMP &&
134	      ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) &&
135	    (ct->status & IPS_SRC_NAT_DONE)) {
136
137		daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
138		dport = (iph->protocol == IPPROTO_TCP) ?
139			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port :
140			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
141	}
142#endif
143
144	sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
145				   saddr, daddr, sport, dport, par->in, false);
146	if (sk != NULL) {
147		bool wildcard;
148		bool transparent = true;
149
150		/* Ignore sockets listening on INADDR_ANY */
151		wildcard = (sk->sk_state != TCP_TIME_WAIT &&
152			    inet_sk(sk)->inet_rcv_saddr == 0);
153
154		/* Ignore non-transparent sockets,
155		   if XT_SOCKET_TRANSPARENT is used */
156		if (info && info->flags & XT_SOCKET_TRANSPARENT)
157			transparent = ((sk->sk_state != TCP_TIME_WAIT &&
158					inet_sk(sk)->transparent) ||
159				       (sk->sk_state == TCP_TIME_WAIT &&
160					inet_twsk(sk)->tw_transparent));
161
162		nf_tproxy_put_sock(sk);
163
164		if (wildcard || !transparent)
165			sk = NULL;
166	}
167
168	pr_debug("proto %u %08x:%u -> %08x:%u (orig %08x:%u) sock %p\n",
169		 protocol, ntohl(saddr), ntohs(sport),
170		 ntohl(daddr), ntohs(dport),
171		 ntohl(iph->daddr), hp ? ntohs(hp->dest) : 0, sk);
172
173	return (sk != NULL);
174}
175
176static bool
177socket_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
178{
179	return socket_match(skb, par, NULL);
180}
181
182static bool
183socket_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
184{
185	return socket_match(skb, par, par->matchinfo);
186}
187
188static struct xt_match socket_mt_reg[] __read_mostly = {
189	{
190		.name		= "socket",
191		.revision	= 0,
192		.family		= NFPROTO_IPV4,
193		.match		= socket_mt_v0,
194		.hooks		= (1 << NF_INET_PRE_ROUTING) |
195				  (1 << NF_INET_LOCAL_IN),
196		.me		= THIS_MODULE,
197	},
198	{
199		.name		= "socket",
200		.revision	= 1,
201		.family		= NFPROTO_IPV4,
202		.match		= socket_mt_v1,
203		.matchsize	= sizeof(struct xt_socket_mtinfo1),
204		.hooks		= (1 << NF_INET_PRE_ROUTING) |
205				  (1 << NF_INET_LOCAL_IN),
206		.me		= THIS_MODULE,
207	},
208};
209
210static int __init socket_mt_init(void)
211{
212	nf_defrag_ipv4_enable();
213	return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
214}
215
216static void __exit socket_mt_exit(void)
217{
218	xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
219}
220
221module_init(socket_mt_init);
222module_exit(socket_mt_exit);
223
224MODULE_LICENSE("GPL");
225MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
226MODULE_DESCRIPTION("x_tables socket match module");
227MODULE_ALIAS("ipt_socket");
228