summaryrefslogtreecommitdiffstats
path: root/src/helpers/tftp.c
blob: 45591c617e768930682d4e7e9a6ec8c82cb07700 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
100
101
102
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
128
129
130
131
132
133
134
135
136
137
138
/*
 * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
 *
 * Adapted from:
 *
 * (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
 * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
 * 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.
 */

#include "conntrackd.h"
#include "helper.h"
#include "myct.h"
#include "log.h"
#include <errno.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_udp.h>
#include <libnetfilter_queue/pktbuff.h>
#include <linux/netfilter.h>

struct tftphdr {
	uint16_t opcode;
};

#define TFTP_OPCODE_READ	1
#define TFTP_OPCODE_WRITE	2
#define TFTP_OPCODE_DATA	3
#define TFTP_OPCODE_ACK		4
#define TFTP_OPCODE_ERROR	5

static unsigned int nat_tftp(struct pkt_buff *pkt, uint32_t ctinfo,
			     struct nf_conntrack *ct, struct nf_expect *exp)
{
	struct nf_conntrack *nat_tuple;
	static uint32_t zero[4] = { 0, 0, 0, 0 };

	nat_tuple = nfct_new();
	if (nat_tuple == NULL)
		return NF_ACCEPT;

	switch (nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
	case AF_INET:
		nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
		nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
		nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
		break;
	case AF_INET6:
		nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET6);
		nfct_set_attr(nat_tuple, ATTR_IPV6_SRC, &zero);
		nfct_set_attr(nat_tuple, ATTR_IPV6_DST, &zero);
		break;
	}
	nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_UDP);
	nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC,
			  nfct_get_attr_u16(ct, ATTR_PORT_SRC));
	nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);

	nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, MYCT_DIR_REPL);
	nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
	nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);

	return NF_ACCEPT;
}

static int
tftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
	       struct myct *myct, uint32_t ctinfo)
{
	const struct tftphdr *tfh;
	struct nf_expect *exp;
	unsigned int ret = NF_ACCEPT;
	union nfct_attr_grp_addr saddr, daddr;
	uint16_t dport;

	tfh = (struct tftphdr *)(pktb_network_header(pkt) + protoff + sizeof(struct udphdr));

	switch (ntohs(tfh->opcode)) {
	case TFTP_OPCODE_READ:
	case TFTP_OPCODE_WRITE:
		/* RRQ and WRQ works the same way */
		exp = nfexp_new();
		if (exp == NULL) {
			pr_debug("cannot alloc expectation\n");
			return NF_DROP;
		}

		cthelper_get_addr_src(myct->ct, MYCT_DIR_REPL, &saddr);
		cthelper_get_addr_dst(myct->ct, MYCT_DIR_REPL, &daddr);
		cthelper_get_port_dst(myct->ct, MYCT_DIR_REPL, &dport);

		if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
					 IPPROTO_UDP, NULL, &dport, 0)) {
			nfexp_destroy(exp);
			return NF_DROP;
		}

		if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK)
			ret = nat_tftp(pkt, ctinfo, myct->ct, exp);

		myct->exp = exp;
		break;
	case TFTP_OPCODE_DATA:
	case TFTP_OPCODE_ACK:
		pr_debug("Data/ACK opcode\n");
		break;
	case TFTP_OPCODE_ERROR:
		pr_debug("Error opcode\n");
		break;
	default:
		pr_debug("Unknown opcode\n");
	}
	return ret;
}

static struct ctd_helper tftp_helper = {
	.name		= "tftp",
	.l4proto	= IPPROTO_UDP,
	.cb		= tftp_helper_cb,
	.policy		= {
		[0] = {
			.name			= "tftp",
			.expect_max		= 1,
			.expect_timeout		= 5 * 60,
		},
	},
};

static void __attribute__ ((constructor)) tftp_init(void)
{
	helper_register(&tftp_helper);
}