1// SPDX-License-Identifier: GPL-2.0-only
2/* SANE connection tracking helper
3 * (SANE = Scanner Access Now Easy)
4 * For documentation about the SANE network protocol see
5 * http://www.sane-project.org/html/doc015.html
6 */
7
8/* Copyright (C) 2007 Red Hat, Inc.
9 * Author: Michal Schmidt <mschmidt@redhat.com>
10 * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c):
11 * (C) 1999-2001 Paul `Rusty' Russell
12 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
13 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
14 * (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
15 */
16
17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19#include <linux/module.h>
20#include <linux/moduleparam.h>
21#include <linux/netfilter.h>
22#include <linux/slab.h>
23#include <linux/in.h>
24#include <linux/tcp.h>
25#include <net/netfilter/nf_conntrack.h>
26#include <net/netfilter/nf_conntrack_helper.h>
27#include <net/netfilter/nf_conntrack_expect.h>
28#include <linux/netfilter/nf_conntrack_sane.h>
29
30#define HELPER_NAME "sane"
31
32MODULE_LICENSE("GPL");
33MODULE_AUTHOR("Michal Schmidt <mschmidt@redhat.com>");
34MODULE_DESCRIPTION("SANE connection tracking helper");
35MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
36
37#define MAX_PORTS 8
38static u_int16_t ports[MAX_PORTS];
39static unsigned int ports_c;
40module_param_array(ports, ushort, &ports_c, 0400);
41
42struct sane_request {
43 __be32 RPC_code;
44#define SANE_NET_START 7 /* RPC code */
45
46 __be32 handle;
47};
48
49struct sane_reply_net_start {
50 __be32 status;
51#define SANE_STATUS_SUCCESS 0
52
53 __be16 zero;
54 __be16 port;
55 /* other fields aren't interesting for conntrack */
56};
57
58static int help(struct sk_buff *skb,
59 unsigned int protoff,
60 struct nf_conn *ct,
61 enum ip_conntrack_info ctinfo)
62{
63 unsigned int dataoff, datalen;
64 const struct tcphdr *th;
65 struct tcphdr _tcph;
66 int ret = NF_ACCEPT;
67 int dir = CTINFO2DIR(ctinfo);
68 struct nf_ct_sane_master *ct_sane_info = nfct_help_data(ct);
69 struct nf_conntrack_expect *exp;
70 struct nf_conntrack_tuple *tuple;
71 struct sane_reply_net_start *reply;
72 union {
73 struct sane_request req;
74 struct sane_reply_net_start repl;
75 } buf;
76
77 /* Until there's been traffic both ways, don't look in packets. */
78 if (ctinfo != IP_CT_ESTABLISHED &&
79 ctinfo != IP_CT_ESTABLISHED_REPLY)
80 return NF_ACCEPT;
81
82 /* Not a full tcp header? */
83 th = skb_header_pointer(skb, offset: protoff, len: sizeof(_tcph), buffer: &_tcph);
84 if (th == NULL)
85 return NF_ACCEPT;
86
87 /* No data? */
88 dataoff = protoff + th->doff * 4;
89 if (dataoff >= skb->len)
90 return NF_ACCEPT;
91
92 datalen = skb->len - dataoff;
93 if (dir == IP_CT_DIR_ORIGINAL) {
94 const struct sane_request *req;
95
96 if (datalen != sizeof(struct sane_request))
97 return NF_ACCEPT;
98
99 req = skb_header_pointer(skb, offset: dataoff, len: datalen, buffer: &buf.req);
100 if (!req)
101 return NF_ACCEPT;
102
103 if (req->RPC_code != htonl(SANE_NET_START)) {
104 /* Not an interesting command */
105 WRITE_ONCE(ct_sane_info->state, SANE_STATE_NORMAL);
106 return NF_ACCEPT;
107 }
108
109 /* We're interested in the next reply */
110 WRITE_ONCE(ct_sane_info->state, SANE_STATE_START_REQUESTED);
111 return NF_ACCEPT;
112 }
113
114 /* IP_CT_DIR_REPLY */
115
116 /* Is it a reply to an uninteresting command? */
117 if (READ_ONCE(ct_sane_info->state) != SANE_STATE_START_REQUESTED)
118 return NF_ACCEPT;
119
120 /* It's a reply to SANE_NET_START. */
121 WRITE_ONCE(ct_sane_info->state, SANE_STATE_NORMAL);
122
123 if (datalen < sizeof(struct sane_reply_net_start)) {
124 pr_debug("NET_START reply too short\n");
125 return NF_ACCEPT;
126 }
127
128 datalen = sizeof(struct sane_reply_net_start);
129
130 reply = skb_header_pointer(skb, offset: dataoff, len: datalen, buffer: &buf.repl);
131 if (!reply)
132 return NF_ACCEPT;
133
134 if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
135 /* saned refused the command */
136 pr_debug("unsuccessful SANE_STATUS = %u\n",
137 ntohl(reply->status));
138 return NF_ACCEPT;
139 }
140
141 /* Invalid saned reply? Ignore it. */
142 if (reply->zero != 0)
143 return NF_ACCEPT;
144
145 exp = nf_ct_expect_alloc(me: ct);
146 if (exp == NULL) {
147 nf_ct_helper_log(skb, ct, fmt: "cannot alloc expectation");
148 return NF_DROP;
149 }
150
151 tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
152 nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
153 &tuple->src.u3, &tuple->dst.u3,
154 IPPROTO_TCP, NULL, &reply->port);
155
156 pr_debug("expect: ");
157 nf_ct_dump_tuple(t: &exp->tuple);
158
159 /* Can't expect this? Best to drop packet now. */
160 if (nf_ct_expect_related(expect: exp, flags: 0) != 0) {
161 nf_ct_helper_log(skb, ct, fmt: "cannot add expectation");
162 ret = NF_DROP;
163 }
164
165 nf_ct_expect_put(exp);
166 return ret;
167}
168
169static struct nf_conntrack_helper sane[MAX_PORTS * 2] __read_mostly;
170
171static const struct nf_conntrack_expect_policy sane_exp_policy = {
172 .max_expected = 1,
173 .timeout = 5 * 60,
174};
175
176static void __exit nf_conntrack_sane_fini(void)
177{
178 nf_conntrack_helpers_unregister(sane, ports_c * 2);
179}
180
181static int __init nf_conntrack_sane_init(void)
182{
183 int i, ret = 0;
184
185 NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sane_master));
186
187 if (ports_c == 0)
188 ports[ports_c++] = SANE_PORT;
189
190 /* FIXME should be configurable whether IPv4 and IPv6 connections
191 are tracked or not - YK */
192 for (i = 0; i < ports_c; i++) {
193 nf_ct_helper_init(helper: &sane[2 * i], AF_INET, IPPROTO_TCP,
194 HELPER_NAME, SANE_PORT, spec_port: ports[i], id: ports[i],
195 exp_pol: &sane_exp_policy, expect_class_max: 0, help, NULL,
196 THIS_MODULE);
197 nf_ct_helper_init(helper: &sane[2 * i + 1], AF_INET6, IPPROTO_TCP,
198 HELPER_NAME, SANE_PORT, spec_port: ports[i], id: ports[i],
199 exp_pol: &sane_exp_policy, expect_class_max: 0, help, NULL,
200 THIS_MODULE);
201 }
202
203 ret = nf_conntrack_helpers_register(sane, ports_c * 2);
204 if (ret < 0) {
205 pr_err("failed to register helpers\n");
206 return ret;
207 }
208
209 return 0;
210}
211
212module_init(nf_conntrack_sane_init);
213module_exit(nf_conntrack_sane_fini);
214

source code of linux/net/netfilter/nf_conntrack_sane.c