• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/conntrack-tools/conntrack-tools-1.4.0/src/helpers/
1/*
2 * (C) 2012 by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
3 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
4 *
5 * Based on: RPC extension for conntrack.
6 *
7 * This port has been sponsored by Vyatta Inc. <http://www.vyatta.com>
8 *
9 * Original copyright notice:
10 *
11 * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
12 * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
13 * (C) 2002,2003 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
14 * (C) 2004,2005 by David Stes <stes@pandora.be>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License version 2 as
18 * published by the Free Software Foundation.
19 */
20
21#include "conntrackd.h"
22#include "network.h"	/* for before and after */
23#include "helper.h"
24#include "myct.h"
25#include "log.h"
26
27#include <errno.h>
28
29#include <rpc/rpc_msg.h>
30#include <rpc/pmap_prot.h>
31#include <netinet/tcp.h>
32#include <netinet/udp.h>
33
34#include <libmnl/libmnl.h>
35#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
36#include <libnetfilter_queue/libnetfilter_queue.h>
37#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
38#include <libnetfilter_queue/pktbuff.h>
39#include <linux/netfilter.h>
40
41/* RFC 1050: RPC: Remote Procedure Call Protocol Specification Version 2 */
42/* RFC 1014: XDR: External Data Representation Standard */
43#define SUPPORTED_RPC_VERSION	2
44
45struct rpc_info {
46	/* XID */
47	uint32_t xid;
48	/* program */
49	uint32_t pm_prog;
50	/* program version */
51	uint32_t pm_vers;
52	/* transport protocol: TCP|UDP */
53	uint32_t pm_prot;
54};
55
56/* So, this packet has hit the connection tracking matching code.
57   Mangle it, and change the expectation to match the new version. */
58static unsigned int
59nf_nat_rpc(struct pkt_buff *pkt, int dir, struct nf_expect *exp,
60	   uint8_t proto, uint32_t *port_ptr)
61{
62	const struct nf_conntrack *expected;
63	struct nf_conntrack *nat_tuple;
64	uint16_t initial_port, port;
65
66	expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED);
67
68	nat_tuple = nfct_new();
69	if (nat_tuple == NULL)
70		return NF_ACCEPT;
71
72	initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST);
73
74	nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir);
75
76	/* libnetfilter_conntrack needs this */
77	nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
78	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
79	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
80	nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, proto);
81	nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
82
83	/* When you see the packet, we need to NAT it the same as the
84	 * this one. */
85	nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
86
87	/* Try to get same port: if not, try to change it. */
88	for (port = ntohs(initial_port); port != 0; port++) {
89		int ret;
90
91		nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port));
92		nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
93
94		ret = cthelper_add_expect(exp);
95		if (ret == 0)
96			break;
97		else if (ret != -EBUSY) {
98			port = 0;
99			break;
100		}
101	}
102	nfct_destroy(nat_tuple);
103
104	if (port == 0)
105		return NF_DROP;
106
107	*port_ptr = htonl(port);
108
109	return NF_ACCEPT;
110}
111
112#define OFFSET(o, n)	((o) += n)
113#define ROUNDUP(n)	((((n) + 3)/4)*4)
114
115static int
116rpc_call(const uint32_t *data, uint32_t offset, uint32_t datalen,
117	 struct rpc_info *rpc_info)
118{
119	uint32_t p, r;
120
121	/* RPC CALL message body */
122
123	/* call_body {
124	 *	rpcvers
125	 *	prog
126	 *	vers
127	 *	proc
128	 *	opaque_auth cred
129	 *	opaque_auth verf
130	 *	pmap
131	 * }
132	 *
133	 * opaque_auth {
134	 *	flavour
135	 *	opaque[len] <= MAX_AUTH_BYTES
136	 * }
137	 */
138	if (datalen < OFFSET(offset, 4*4 + 2*2*4)) {
139		pr_debug("RPC CALL: too short packet: %u < %u\n",
140			 datalen, offset);
141		return -1;
142	}
143	/* Check rpcversion */
144	p = IXDR_GET_INT32(data);
145	if (p != SUPPORTED_RPC_VERSION) {
146		pr_debug("RPC CALL: wrong rpcvers %u != %u\n",
147			 p, SUPPORTED_RPC_VERSION);
148		return -1;
149	}
150	/* Skip non-portmap requests */
151	p = IXDR_GET_INT32(data);
152	if (p != PMAPPROG) {
153		pr_debug("RPC CALL: not portmap %u != %lu\n",
154			 p, PMAPPROG);
155		return -1;
156	}
157	/* Check portmap version */
158	p = IXDR_GET_INT32(data);
159	if (p != PMAPVERS) {
160		pr_debug("RPC CALL: wrong portmap version %u != %lu\n",
161			 p, PMAPVERS);
162		return -1;
163	}
164	/* Skip non PMAPPROC_GETPORT requests */
165	p = IXDR_GET_INT32(data);
166	if (p != PMAPPROC_GETPORT) {
167		pr_debug("RPC CALL: not PMAPPROC_GETPORT %u != %lu\n",
168			 p, PMAPPROC_GETPORT);
169		return -1;
170	}
171	/* Check and skip credentials */
172	r = IXDR_GET_INT32(data);
173	p = IXDR_GET_INT32(data);
174	pr_debug("RPC CALL: cred: %u %u (%u, %u)\n",
175		 r, p, datalen, offset);
176	if (p > MAX_AUTH_BYTES) {
177		pr_debug("RPC CALL: invalid sized cred %u > %u\n",
178			 p, MAX_AUTH_BYTES);
179		return -1;
180	}
181	r = ROUNDUP(p);
182	if (datalen < OFFSET(offset, r)) {
183		pr_debug("RPC CALL: too short to carry cred: %u < %u, %u\n",
184			 datalen, offset, r);
185		return -1;
186	}
187	data += r/4;
188	/* Check and skip verifier */
189	r = IXDR_GET_INT32(data);
190	p = IXDR_GET_INT32(data);
191	pr_debug("RPC CALL: verf: %u %u (%u, %u)\n",
192		 r, p, datalen, offset);
193	if (p > MAX_AUTH_BYTES) {
194		pr_debug("RPC CALL: invalid sized verf %u > %u\n",
195			 p, MAX_AUTH_BYTES);
196		return -1;
197	}
198	r = ROUNDUP(p);
199	if (datalen < OFFSET(offset, r)) {
200		pr_debug("RPC CALL: too short to carry verf: %u < %u, %u\n",
201			 datalen, offset, r);
202		return -1;
203	}
204	data += r/4;
205	/* pmap {
206	 *	prog
207	 *	vers
208	 *	prot
209	 *	port
210	 * }
211	 */
212	/* Check CALL size */
213	if (datalen != offset + 4*4) {
214		pr_debug("RPC CALL: invalid size to carry pmap: %u != %u\n",
215			 datalen, offset + 4*4);
216		return -1;
217	}
218	rpc_info->pm_prog = IXDR_GET_INT32(data);
219	rpc_info->pm_vers = IXDR_GET_INT32(data);
220	rpc_info->pm_prot = IXDR_GET_INT32(data);
221	/* Check supported protocols */
222	if (!(rpc_info->pm_prot == IPPROTO_TCP
223	      || rpc_info->pm_prot == IPPROTO_UDP)) {
224		pr_debug("RPC CALL: unsupported protocol %u",
225			 rpc_info->pm_prot);
226		return -1;
227	}
228	p = IXDR_GET_INT32(data);
229	/* Check port: must be zero */
230	if (p != 0) {
231		pr_debug("RPC CALL: port is nonzero %u\n",
232			 ntohl(p));
233		return -1;
234	}
235	pr_debug("RPC CALL: processed: xid %u, prog %u, vers %u, prot %u\n",
236		 rpc_info->xid, rpc_info->pm_prog,
237		 rpc_info->pm_vers, rpc_info->pm_prot);
238
239	return 0;
240}
241
242static int
243rpc_reply(uint32_t *data, uint32_t offset, uint32_t datalen,
244	  struct rpc_info *rpc_info, uint32_t **port_ptr)
245{
246	uint16_t port;
247	uint32_t p, r;
248
249	/* RPC REPLY message body */
250
251	/* reply_body {
252	 *	reply_stat
253	 *	xdr_union {
254	 *		accepted_reply
255	 *		rejected_reply
256	 *	}
257	 * }
258	 * accepted_reply {
259	 *	opaque_auth verf
260	 *	accept_stat
261	 *	xdr_union {
262	 *		port
263	 *		struct mismatch_info
264	 *	}
265	 * }
266	 */
267
268	/* Check size: reply status */
269	if (datalen < OFFSET(offset, 4)) {
270		pr_debug("RPC REPL: too short, missing rp_stat: %u < %u\n",
271			 datalen, offset);
272		return -1;
273	}
274	p = IXDR_GET_INT32(data);
275	/* Check accepted request */
276	if (p != MSG_ACCEPTED) {
277		pr_debug("RPC REPL: not accepted %u != %u\n",
278			 p, MSG_ACCEPTED);
279		return -1;
280	}
281	/* Check and skip verifier */
282	if (datalen < OFFSET(offset, 2*4)) {
283		pr_debug("RPC REPL: too short, missing verf: %u < %u\n",
284			 datalen, offset);
285		return -1;
286	}
287	r = IXDR_GET_INT32(data);
288	p = IXDR_GET_INT32(data);
289	pr_debug("RPC REPL: verf: %u %u (%u, %u)\n",
290		 r, p, datalen, offset);
291	if (p > MAX_AUTH_BYTES) {
292		pr_debug("RPC REPL: invalid sized verf %u > %u\n",
293			 p, MAX_AUTH_BYTES);
294		return -1;
295	}
296	r = ROUNDUP(p);
297	/* verifier + ac_stat + port */
298	if (datalen != OFFSET(offset, r) + 2*4) {
299		pr_debug("RPC REPL: invalid size to carry verf and "
300			 "success: %u != %u\n",
301			 datalen, offset + 2*4);
302		return -1;
303	}
304	data += r/4;
305	/* Check success */
306	p = IXDR_GET_INT32(data);
307	if (p != SUCCESS) {
308		pr_debug("RPC REPL: not success %u != %u\n",
309			 p, SUCCESS);
310		return -1;
311	}
312	/* Get port */
313	*port_ptr = data;
314	port = IXDR_GET_INT32(data); /* -Wunused-but-set-parameter */
315	if (port == 0) {
316		pr_debug("RPC REPL: port is zero\n");
317		return -1;
318	}
319	pr_debug("RPC REPL: processed: xid %u, prog %u, vers %u, "
320		 "prot %u, port %u\n",
321		 rpc_info->xid, rpc_info->pm_prog, rpc_info->pm_vers,
322		 rpc_info->pm_prot, port);
323	return 0;
324}
325
326static int
327rpc_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
328	      struct myct *myct, uint32_t ctinfo)
329{
330	int dir = CTINFO2DIR(ctinfo);
331	unsigned int offset = protoff, datalen;
332	uint32_t *data, *port_ptr = NULL, xid;
333	uint16_t port;
334	uint8_t proto = nfct_get_attr_u8(myct->ct, ATTR_L4PROTO);
335	enum msg_type rm_dir;
336	struct rpc_info *rpc_info = myct->priv_data;
337	union nfct_attr_grp_addr addr, daddr;
338	struct nf_expect *exp = NULL;
339	int ret = NF_ACCEPT;
340
341	/* Until there's been traffic both ways, don't look into TCP packets. */
342	if (proto == IPPROTO_TCP
343	    && ctinfo != IP_CT_ESTABLISHED
344	    && ctinfo != IP_CT_ESTABLISHED_REPLY) {
345		pr_debug("TCP RPC: Conntrackinfo = %u\n", ctinfo);
346		return ret;
347	}
348	if (proto == IPPROTO_TCP) {
349		struct tcphdr *th =
350			(struct tcphdr *) (pktb_network_header(pkt) + protoff);
351		offset += th->doff * 4;
352	} else {
353		offset += sizeof(struct udphdr);
354	}
355	/* Skip broken headers */
356	if (offset % 4) {
357		pr_debug("RPC: broken header: offset %u%%4 != 0\n", offset);
358		return ret;
359	}
360
361	/* Take into Record Fragment header */
362	if (proto == IPPROTO_TCP)
363		offset += 4;
364
365	datalen = pktb_len(pkt);
366	data = (uint32_t *)(pktb_network_header(pkt) + offset);
367
368	/* rpc_msg {
369	 *	xid
370	 *	direction
371	 *	xdr_union {
372	 *		call_body
373	 *		reply_body
374	 *	}
375	 * }
376	 */
377
378	 /* Check minimal msg size: xid + direction */
379	if (datalen < OFFSET(offset, 2*4)) {
380		pr_debug("RPC: too short packet: %u < %u\n",
381			 datalen, offset);
382		return ret;
383	}
384	xid = IXDR_GET_INT32(data);
385	rm_dir = IXDR_GET_INT32(data);
386
387	/* Check direction */
388	if (!((rm_dir == CALL && dir == MYCT_DIR_ORIG)
389	      || (rm_dir == REPLY && dir == MYCT_DIR_REPL))) {
390		pr_debug("RPC: rm_dir != dir %u != %u\n", rm_dir, dir);
391		goto out;
392	}
393
394	if (rm_dir == CALL) {
395		if (rpc_call(data, offset, datalen, rpc_info) < 0)
396			goto out;
397
398		rpc_info->xid = xid;
399
400		return ret;
401	} else {
402		/* Check XID */
403		if (xid != rpc_info->xid) {
404			pr_debug("RPC REPL: XID does not match: %u != %u\n",
405				 xid, rpc_info->xid);
406			goto out;
407		}
408		if (rpc_reply(data, offset, datalen, rpc_info, &port_ptr) < 0)
409			goto out;
410
411		port = IXDR_GET_INT32(port_ptr);
412		port = htons(port);
413
414		/* We refer to the reverse direction ("!dir") tuples here,
415		 * because we're expecting something in the other direction.
416		 * Doesn't matter unless NAT is happening.  */
417		cthelper_get_addr_dst(myct->ct, !dir, &daddr);
418		cthelper_get_addr_src(myct->ct, !dir, &addr);
419
420		exp = nfexp_new();
421		if (exp == NULL)
422			goto out;
423
424		if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr,
425					 rpc_info->pm_prot,
426					 NULL, &port, NF_CT_EXPECT_PERMANENT)) {
427			pr_debug("RPC: failed to init expectation\n");
428			goto out_exp;
429		}
430
431		/* Now, NAT might want to mangle the packet, and register the
432		 * (possibly changed) expectation itself. */
433		if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) {
434			ret = nf_nat_rpc(pkt, dir, exp, rpc_info->pm_prot,
435					 port_ptr);
436			goto out_exp;
437		}
438
439		/* Can't expect this?  Best to drop packet now. */
440		if (cthelper_add_expect(exp) < 0) {
441			pr_debug("RPC: cannot add expectation: %s\n",
442				 strerror(errno));
443			ret = NF_DROP;
444		}
445	}
446
447out_exp:
448	nfexp_destroy(exp);
449out:
450	rpc_info->xid = 0;
451	return ret;
452}
453
454static struct ctd_helper rpc_helper_tcp = {
455	.name		= "rpc",
456	.l4proto	= IPPROTO_TCP,
457	.cb		= rpc_helper_cb,
458	.priv_data_len	= sizeof(struct rpc_info),
459	.policy		= {
460		{
461			.name			= "rpc",
462			.expect_max		= 1,
463			.expect_timeout		= 300,
464		},
465	},
466};
467
468static struct ctd_helper rpc_helper_udp = {
469	.name		= "rpc",
470	.l4proto	= IPPROTO_UDP,
471	.cb		= rpc_helper_cb,
472	.priv_data_len	= sizeof(struct rpc_info),
473	.policy		= {
474		{
475			.name			= "rpc",
476			.expect_max		= 1,
477			.expect_timeout		= 300,
478		},
479	},
480};
481
482void __attribute__ ((constructor)) rpc_init(void);
483
484void rpc_init(void)
485{
486	helper_register(&rpc_helper_tcp);
487	helper_register(&rpc_helper_udp);
488}
489