aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Chapman <jchapman@katalix.com>2014-01-06 05:17:08 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2014-01-09 15:36:39 -0500
commit74f77a6b2b1c98d3f14364dccdd2353b99ecfeda (patch)
tree904a08be6af43df01ac205c71885a038d35b8bfc
parentd0eb1f7e66dd53355746cd6a8e7e56c465dc6cde (diff)
netfilter: introduce l2tp match extension
Introduce an xtables add-on for matching L2TP packets. Supports L2TPv2 and L2TPv3 over IPv4 and IPv6. As well as filtering on L2TP tunnel-id and session-id, the filtering decision can also include the L2TP packet type (control or data), protocol version (2 or 3) and encapsulation type (UDP or IP). The most common use for this will likely be to filter L2TP data packets of individual L2TP tunnels or sessions. While a u32 match can be used, the L2TP protocol headers are such that field offsets differ depending on bits set in the header, making rules for matching generic L2TP connections cumbersome. This match extension takes care of all that. Signed-off-by: James Chapman <jchapman@katalix.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/uapi/linux/netfilter/Kbuild1
-rw-r--r--include/uapi/linux/netfilter/xt_l2tp.h27
-rw-r--r--net/netfilter/Kconfig10
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/xt_l2tp.c354
5 files changed, 393 insertions, 0 deletions
diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild
index 2344f5a319fc..1d973d2ba417 100644
--- a/include/uapi/linux/netfilter/Kbuild
+++ b/include/uapi/linux/netfilter/Kbuild
@@ -58,6 +58,7 @@ header-y += xt_helper.h
58header-y += xt_ipcomp.h 58header-y += xt_ipcomp.h
59header-y += xt_iprange.h 59header-y += xt_iprange.h
60header-y += xt_ipvs.h 60header-y += xt_ipvs.h
61header-y += xt_l2tp.h
61header-y += xt_length.h 62header-y += xt_length.h
62header-y += xt_limit.h 63header-y += xt_limit.h
63header-y += xt_mac.h 64header-y += xt_mac.h
diff --git a/include/uapi/linux/netfilter/xt_l2tp.h b/include/uapi/linux/netfilter/xt_l2tp.h
new file mode 100644
index 000000000000..7dccfa0acbfa
--- /dev/null
+++ b/include/uapi/linux/netfilter/xt_l2tp.h
@@ -0,0 +1,27 @@
1#ifndef _LINUX_NETFILTER_XT_L2TP_H
2#define _LINUX_NETFILTER_XT_L2TP_H
3
4#include <linux/types.h>
5
6enum xt_l2tp_type {
7 XT_L2TP_TYPE_CONTROL,
8 XT_L2TP_TYPE_DATA,
9};
10
11/* L2TP matching stuff */
12struct xt_l2tp_info {
13 __u32 tid; /* tunnel id */
14 __u32 sid; /* session id */
15 __u8 version; /* L2TP protocol version */
16 __u8 type; /* L2TP packet type */
17 __u8 flags; /* which fields to match */
18};
19
20enum {
21 XT_L2TP_TID = (1 << 0), /* match L2TP tunnel id */
22 XT_L2TP_SID = (1 << 1), /* match L2TP session id */
23 XT_L2TP_VERSION = (1 << 2), /* match L2TP protocol version */
24 XT_L2TP_TYPE = (1 << 3), /* match L2TP packet type */
25};
26
27#endif /* _LINUX_NETFILTER_XT_L2TP_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index c3b3b26c4c4e..a1be47be0ad7 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -1131,6 +1131,16 @@ config NETFILTER_XT_MATCH_IPVS
1131 1131
1132 If unsure, say N. 1132 If unsure, say N.
1133 1133
1134config NETFILTER_XT_MATCH_L2TP
1135 tristate '"l2tp" match support'
1136 depends on NETFILTER_ADVANCED
1137 default L2TP
1138 ---help---
1139 This option adds an "L2TP" match, which allows you to match against
1140 L2TP protocol header fields.
1141
1142 To compile it as a module, choose M here. If unsure, say N.
1143
1134config NETFILTER_XT_MATCH_LENGTH 1144config NETFILTER_XT_MATCH_LENGTH
1135 tristate '"length" match support' 1145 tristate '"length" match support'
1136 depends on NETFILTER_ADVANCED 1146 depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 78b4e1c9c595..b8bc5b85c728 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -137,6 +137,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_HL) += xt_hl.o
137obj-$(CONFIG_NETFILTER_XT_MATCH_IPCOMP) += xt_ipcomp.o 137obj-$(CONFIG_NETFILTER_XT_MATCH_IPCOMP) += xt_ipcomp.o
138obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o 138obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o
139obj-$(CONFIG_NETFILTER_XT_MATCH_IPVS) += xt_ipvs.o 139obj-$(CONFIG_NETFILTER_XT_MATCH_IPVS) += xt_ipvs.o
140obj-$(CONFIG_NETFILTER_XT_MATCH_L2TP) += xt_l2tp.o
140obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o 141obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o
141obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o 142obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
142obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o 143obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
diff --git a/net/netfilter/xt_l2tp.c b/net/netfilter/xt_l2tp.c
new file mode 100644
index 000000000000..8aee572771f2
--- /dev/null
+++ b/net/netfilter/xt_l2tp.c
@@ -0,0 +1,354 @@
1/* Kernel module to match L2TP header parameters. */
2
3/* (C) 2013 James Chapman <jchapman@katalix.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11#include <linux/module.h>
12#include <linux/skbuff.h>
13#include <linux/if_ether.h>
14#include <net/ip.h>
15#include <linux/ipv6.h>
16#include <net/ipv6.h>
17#include <net/udp.h>
18#include <linux/l2tp.h>
19
20#include <linux/netfilter_ipv4.h>
21#include <linux/netfilter_ipv6.h>
22#include <linux/netfilter_ipv4/ip_tables.h>
23#include <linux/netfilter_ipv6/ip6_tables.h>
24#include <linux/netfilter/x_tables.h>
25#include <linux/netfilter/xt_tcpudp.h>
26#include <linux/netfilter/xt_l2tp.h>
27
28/* L2TP header masks */
29#define L2TP_HDR_T_BIT 0x8000
30#define L2TP_HDR_L_BIT 0x4000
31#define L2TP_HDR_VER 0x000f
32
33MODULE_LICENSE("GPL");
34MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
35MODULE_DESCRIPTION("Xtables: L2TP header match");
36MODULE_ALIAS("ipt_l2tp");
37MODULE_ALIAS("ip6t_l2tp");
38
39/* The L2TP fields that can be matched */
40struct l2tp_data {
41 u32 tid;
42 u32 sid;
43 u8 type;
44 u8 version;
45};
46
47union l2tp_val {
48 __be16 val16[2];
49 __be32 val32;
50};
51
52static bool l2tp_match(const struct xt_l2tp_info *info, struct l2tp_data *data)
53{
54 if ((info->flags & XT_L2TP_TYPE) && (info->type != data->type))
55 return false;
56
57 if ((info->flags & XT_L2TP_VERSION) && (info->version != data->version))
58 return false;
59
60 /* Check tid only for L2TPv3 control or any L2TPv2 packets */
61 if ((info->flags & XT_L2TP_TID) &&
62 ((data->type == XT_L2TP_TYPE_CONTROL) || (data->version == 2)) &&
63 (info->tid != data->tid))
64 return false;
65
66 /* Check sid only for L2TP data packets */
67 if ((info->flags & XT_L2TP_SID) && (data->type == XT_L2TP_TYPE_DATA) &&
68 (info->sid != data->sid))
69 return false;
70
71 return true;
72}
73
74/* Parse L2TP header fields when UDP encapsulation is used. Handles
75 * L2TPv2 and L2TPv3. Note the L2TPv3 control and data packets have a
76 * different format. See
77 * RFC2661, Section 3.1, L2TPv2 Header Format
78 * RFC3931, Section 3.2.1, L2TPv3 Control Message Header
79 * RFC3931, Section 3.2.2, L2TPv3 Data Message Header
80 * RFC3931, Section 4.1.2.1, L2TPv3 Session Header over UDP
81 */
82static bool l2tp_udp_mt(const struct sk_buff *skb, struct xt_action_param *par, u16 thoff)
83{
84 const struct xt_l2tp_info *info = par->matchinfo;
85 int uhlen = sizeof(struct udphdr);
86 int offs = thoff + uhlen;
87 union l2tp_val *lh;
88 union l2tp_val lhbuf;
89 u16 flags;
90 struct l2tp_data data = { 0, };
91
92 if (par->fragoff != 0)
93 return false;
94
95 /* Extract L2TP header fields. The flags in the first 16 bits
96 * tell us where the other fields are.
97 */
98 lh = skb_header_pointer(skb, offs, 2, &lhbuf);
99 if (lh == NULL)
100 return false;
101
102 flags = ntohs(lh->val16[0]);
103 if (flags & L2TP_HDR_T_BIT)
104 data.type = XT_L2TP_TYPE_CONTROL;
105 else
106 data.type = XT_L2TP_TYPE_DATA;
107 data.version = (u8) flags & L2TP_HDR_VER;
108
109 /* Now extract the L2TP tid/sid. These are in different places
110 * for L2TPv2 (rfc2661) and L2TPv3 (rfc3931). For L2TPv2, we
111 * must also check to see if the length field is present,
112 * since this affects the offsets into the packet of the
113 * tid/sid fields.
114 */
115 if (data.version == 3) {
116 lh = skb_header_pointer(skb, offs + 4, 4, &lhbuf);
117 if (lh == NULL)
118 return false;
119 if (data.type == XT_L2TP_TYPE_CONTROL)
120 data.tid = ntohl(lh->val32);
121 else
122 data.sid = ntohl(lh->val32);
123 } else if (data.version == 2) {
124 if (flags & L2TP_HDR_L_BIT)
125 offs += 2;
126 lh = skb_header_pointer(skb, offs + 2, 4, &lhbuf);
127 if (lh == NULL)
128 return false;
129 data.tid = (u32) ntohs(lh->val16[0]);
130 data.sid = (u32) ntohs(lh->val16[1]);
131 } else
132 return false;
133
134 return l2tp_match(info, &data);
135}
136
137/* Parse L2TP header fields for IP encapsulation (no UDP header).
138 * L2TPv3 data packets have a different form with IP encap. See
139 * RC3931, Section 4.1.1.1, L2TPv3 Session Header over IP.
140 * RC3931, Section 4.1.1.2, L2TPv3 Control and Data Traffic over IP.
141 */
142static bool l2tp_ip_mt(const struct sk_buff *skb, struct xt_action_param *par, u16 thoff)
143{
144 const struct xt_l2tp_info *info = par->matchinfo;
145 union l2tp_val *lh;
146 union l2tp_val lhbuf;
147 struct l2tp_data data = { 0, };
148
149 /* For IP encap, the L2TP sid is the first 32-bits. */
150 lh = skb_header_pointer(skb, thoff, sizeof(lhbuf), &lhbuf);
151 if (lh == NULL)
152 return false;
153 if (lh->val32 == 0) {
154 /* Must be a control packet. The L2TP tid is further
155 * into the packet.
156 */
157 data.type = XT_L2TP_TYPE_CONTROL;
158 lh = skb_header_pointer(skb, thoff + 8, sizeof(lhbuf),
159 &lhbuf);
160 if (lh == NULL)
161 return false;
162 data.tid = ntohl(lh->val32);
163 } else {
164 data.sid = ntohl(lh->val32);
165 data.type = XT_L2TP_TYPE_DATA;
166 }
167
168 data.version = 3;
169
170 return l2tp_match(info, &data);
171}
172
173static bool l2tp_mt4(const struct sk_buff *skb, struct xt_action_param *par)
174{
175 struct iphdr *iph = ip_hdr(skb);
176 u8 ipproto = iph->protocol;
177
178 /* l2tp_mt_check4 already restricts the transport protocol */
179 switch (ipproto) {
180 case IPPROTO_UDP:
181 return l2tp_udp_mt(skb, par, par->thoff);
182 case IPPROTO_L2TP:
183 return l2tp_ip_mt(skb, par, par->thoff);
184 }
185
186 return false;
187}
188
189#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
190static bool l2tp_mt6(const struct sk_buff *skb, struct xt_action_param *par)
191{
192 unsigned int thoff = 0;
193 unsigned short fragoff = 0;
194 int ipproto;
195
196 ipproto = ipv6_find_hdr(skb, &thoff, -1, &fragoff, NULL);
197 if (fragoff != 0)
198 return false;
199
200 /* l2tp_mt_check6 already restricts the transport protocol */
201 switch (ipproto) {
202 case IPPROTO_UDP:
203 return l2tp_udp_mt(skb, par, thoff);
204 case IPPROTO_L2TP:
205 return l2tp_ip_mt(skb, par, thoff);
206 }
207
208 return false;
209}
210#endif
211
212static int l2tp_mt_check(const struct xt_mtchk_param *par)
213{
214 const struct xt_l2tp_info *info = par->matchinfo;
215
216 /* Check for invalid flags */
217 if (info->flags & ~(XT_L2TP_TID | XT_L2TP_SID | XT_L2TP_VERSION |
218 XT_L2TP_TYPE)) {
219 pr_info("unknown flags: %x\n", info->flags);
220 return -EINVAL;
221 }
222
223 /* At least one of tid, sid or type=control must be specified */
224 if ((!(info->flags & XT_L2TP_TID)) &&
225 (!(info->flags & XT_L2TP_SID)) &&
226 ((!(info->flags & XT_L2TP_TYPE)) ||
227 (info->type != XT_L2TP_TYPE_CONTROL))) {
228 pr_info("invalid flags combination: %x\n", info->flags);
229 return -EINVAL;
230 }
231
232 /* If version 2 is specified, check that incompatible params
233 * are not supplied
234 */
235 if (info->flags & XT_L2TP_VERSION) {
236 if ((info->version < 2) || (info->version > 3)) {
237 pr_info("wrong L2TP version: %u\n", info->version);
238 return -EINVAL;
239 }
240
241 if (info->version == 2) {
242 if ((info->flags & XT_L2TP_TID) &&
243 (info->tid > 0xffff)) {
244 pr_info("v2 tid > 0xffff: %u\n", info->tid);
245 return -EINVAL;
246 }
247 if ((info->flags & XT_L2TP_SID) &&
248 (info->sid > 0xffff)) {
249 pr_info("v2 sid > 0xffff: %u\n", info->sid);
250 return -EINVAL;
251 }
252 }
253 }
254
255 return 0;
256}
257
258static int l2tp_mt_check4(const struct xt_mtchk_param *par)
259{
260 const struct xt_l2tp_info *info = par->matchinfo;
261 const struct ipt_entry *e = par->entryinfo;
262 const struct ipt_ip *ip = &e->ip;
263 int ret;
264
265 ret = l2tp_mt_check(par);
266 if (ret != 0)
267 return ret;
268
269 if ((ip->proto != IPPROTO_UDP) &&
270 (ip->proto != IPPROTO_L2TP)) {
271 pr_info("missing protocol rule (udp|l2tpip)\n");
272 return -EINVAL;
273 }
274
275 if ((ip->proto == IPPROTO_L2TP) &&
276 (info->version == 2)) {
277 pr_info("v2 doesn't support IP mode\n");
278 return -EINVAL;
279 }
280
281 return 0;
282}
283
284#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
285static int l2tp_mt_check6(const struct xt_mtchk_param *par)
286{
287 const struct xt_l2tp_info *info = par->matchinfo;
288 const struct ip6t_entry *e = par->entryinfo;
289 const struct ip6t_ip6 *ip = &e->ipv6;
290 int ret;
291
292 ret = l2tp_mt_check(par);
293 if (ret != 0)
294 return ret;
295
296 if ((ip->proto != IPPROTO_UDP) &&
297 (ip->proto != IPPROTO_L2TP)) {
298 pr_info("missing protocol rule (udp|l2tpip)\n");
299 return -EINVAL;
300 }
301
302 if ((ip->proto == IPPROTO_L2TP) &&
303 (info->version == 2)) {
304 pr_info("v2 doesn't support IP mode\n");
305 return -EINVAL;
306 }
307
308 return 0;
309}
310#endif
311
312static struct xt_match l2tp_mt_reg[] __read_mostly = {
313 {
314 .name = "l2tp",
315 .revision = 0,
316 .family = NFPROTO_IPV4,
317 .match = l2tp_mt4,
318 .matchsize = XT_ALIGN(sizeof(struct xt_l2tp_info)),
319 .checkentry = l2tp_mt_check4,
320 .hooks = ((1 << NF_INET_PRE_ROUTING) |
321 (1 << NF_INET_LOCAL_IN) |
322 (1 << NF_INET_LOCAL_OUT) |
323 (1 << NF_INET_FORWARD)),
324 .me = THIS_MODULE,
325 },
326#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
327 {
328 .name = "l2tp",
329 .revision = 0,
330 .family = NFPROTO_IPV6,
331 .match = l2tp_mt6,
332 .matchsize = XT_ALIGN(sizeof(struct xt_l2tp_info)),
333 .checkentry = l2tp_mt_check6,
334 .hooks = ((1 << NF_INET_PRE_ROUTING) |
335 (1 << NF_INET_LOCAL_IN) |
336 (1 << NF_INET_LOCAL_OUT) |
337 (1 << NF_INET_FORWARD)),
338 .me = THIS_MODULE,
339 },
340#endif
341};
342
343static int __init l2tp_mt_init(void)
344{
345 return xt_register_matches(&l2tp_mt_reg[0], ARRAY_SIZE(l2tp_mt_reg));
346}
347
348static void __exit l2tp_mt_exit(void)
349{
350 xt_unregister_matches(&l2tp_mt_reg[0], ARRAY_SIZE(l2tp_mt_reg));
351}
352
353module_init(l2tp_mt_init);
354module_exit(l2tp_mt_exit);