diff mbox

[v5] iptables: add support for l2tp match

Message ID 1388757742-13747-1-git-send-email-jchapman@katalix.com
State Superseded
Headers show

Commit Message

James Chapman Jan. 3, 2014, 2:02 p.m. UTC
This patch adds an iptables extension to support the l2tp xtables
add-on. The matcher lets users filter on one or more fields of L2TP
packets: tunnel-id, session-id, protocol version, encapsulation type
(IP or UDP), packet type (control or data).

Requires corresponding netfilter xt_l2tp kernel support, submitted
separately.

Signed-off-by: James Chapman <jchapman@katalix.com>

---
Changes in v2:
Change check() function to be consistent with kernel

Changes in v3:
Add local copy of xt_l2tp.h

Changes in v4:
Update check() again to be consistent with kernel.

Changes in v5:
Derive encap=udp setting if user specifies v2.
Check that encap is specified.
Update man page.

Tested the argument checking using the trivial script below.

#!/bin/sh
IPTABLES=./iptables

check_ok()
{
    echo $*
    $*
    if [ $? -ne 0 ]; then
	echo "FAIL: $*"
	exit 1
    fi
}

check_fail()
{
    echo $*
    $*
    if [ $? -eq 0 ]; then
	echo "FAIL: $*"
	exit 1
    fi
}

# Rejected commands
check_fail $IPTABLES -I OUTPUT -m l2tp
check_fail $IPTABLES -I OUTPUT -m l2tp --tid 42
check_fail $IPTABLES -I OUTPUT -m l2tp --sid 42
check_fail $IPTABLES -I OUTPUT -m l2tp --type control
check_fail $IPTABLES -I OUTPUT -m l2tp --type data
check_fail $IPTABLES -I OUTPUT -m l2tp --encap udp --type invalid
check_fail $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 1
check_fail $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 3
check_fail $IPTABLES -I OUTPUT -m l2tp --tid 65536 --pversion 2
check_fail $IPTABLES -I OUTPUT -m l2tp --tid 42 --sid 65536 --pversion 2

# Good commands
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 2
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --pversion 2
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 2 --sid 42
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --pversion 2 --sid 42
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --encap udp
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --encap udp
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 3 --encap udp
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --pversion 3 --encap udp
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 3 --encap ip
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --pversion 3 --encap ip
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 3 --encap udp --type control
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --pversion 3 --encap udp --type control
check_ok $IPTABLES -I OUTPUT -m l2tp --tid 42 --pversion 3 --encap udp --sid 42
check_ok $IPTABLES -D OUTPUT -m l2tp --tid 42 --pversion 3 --encap udp --sid 42

---
:000000 100644 0000000... 85f6c54... A	extensions/libxt_l2tp.c
:000000 100644 0000000... b3969ea... A	extensions/libxt_l2tp.man
:000000 100644 0000000... ba0a3ed... A	include/linux/netfilter/xt_l2tp.h
 extensions/libxt_l2tp.c           |  225 +++++++++++++++++++++++++++++++++++++
 extensions/libxt_l2tp.man         |   39 +++++++
 include/linux/netfilter/xt_l2tp.h |   36 ++++++
 3 files changed, 300 insertions(+), 0 deletions(-)
diff mbox

Patch

diff --git a/extensions/libxt_l2tp.c b/extensions/libxt_l2tp.c
new file mode 100644
index 0000000..85f6c54
--- /dev/null
+++ b/extensions/libxt_l2tp.c
@@ -0,0 +1,225 @@ 
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_l2tp.h>
+
+enum {
+	O_TID = 0,
+	O_SID,
+	O_VERSION,
+	O_ENCAP,
+	O_TYPE,
+};
+
+static void l2tp_help(void)
+{
+	printf(
+"l2tp match options:\n"
+" --tunnel-id id\n"
+" --tid ...\n"
+"				match tunnel-id\n"
+" --session-id id\n"
+" --sid ...\n"
+"				match session-id\n"
+" --pversion {2|3}\n"
+"				match version\n"
+" --encap {udp|ip}\n"
+"				match encapsulation\n"
+" --type {control|data}\n"
+"				match packet type\n");
+}
+
+#define s struct xt_l2tp_info
+static const struct xt_option_entry l2tp_opts[] = {
+	{.name = "tunnel-id", .id = O_TID, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, tid)},
+	{.name = "tid", .id = O_TID, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, tid)},
+	{.name = "session-id", .id = O_SID, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, sid)},
+	{.name = "sid", .id = O_SID, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, sid)},
+	{.name = "pversion", .id = O_VERSION, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, version)},
+	{.name = "encap", .id = O_ENCAP, .type = XTTYPE_STRING },
+	{.name = "type", .id = O_TYPE, .type = XTTYPE_STRING },
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static int parse_encap(const char *s)
+{
+	if (strcmp(s, "udp") == 0)
+		return XT_L2TP_ENCAP_UDP;
+	if (strcmp(s, "ip") == 0)
+		return XT_L2TP_ENCAP_IP;
+	xtables_error(PARAMETER_PROBLEM, "l2tp: invalid encap \"%s\"", s);
+}
+
+static int parse_type(const char *s)
+{
+	if (strcmp(s, "control") == 0)
+		return XT_L2TP_TYPE_CONTROL;
+	if (strcmp(s, "data") == 0)
+		return XT_L2TP_TYPE_DATA;
+	xtables_error(PARAMETER_PROBLEM, "l2tp: invalid type \"%s\"", s);
+}
+
+static void l2tp_parse(struct xt_option_call *cb)
+{
+	struct xt_l2tp_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TID:
+		info->flags |= XT_L2TP_TID;
+		break;
+	case O_SID:
+		info->flags |= XT_L2TP_SID;
+		break;
+	case O_VERSION:
+		info->flags |= XT_L2TP_VERSION;
+		break;
+	case O_ENCAP:
+		info->encap = parse_encap(cb->arg);
+		info->flags |= XT_L2TP_ENCAP;
+		break;
+	case O_TYPE:
+		info->type = parse_type(cb->arg);
+		info->flags |= XT_L2TP_TYPE;
+		break;
+	}
+}
+
+static void l2tp_check(struct xt_fcheck_call *cb)
+{
+	struct xt_l2tp_info *info = cb->data;
+
+	if ((!(info->flags & XT_L2TP_TID)) &&
+	    (!(info->flags & XT_L2TP_SID)) &&
+	    ((!(info->flags & XT_L2TP_TYPE)) || (info->type != XT_L2TP_TYPE_CONTROL)))
+		xtables_error(PARAMETER_PROBLEM,
+			      "l2tp tid, sid or control type not specified");
+
+	if (info->flags & XT_L2TP_VERSION) {
+		if ((info->version < 2) || (info->version > 3))
+			xtables_error(PARAMETER_PROBLEM, "l2tp: invalid version \"%d\"",
+				      info->version);
+
+		if (info->version == 2) {
+			if ((info->flags & XT_L2TP_TID) &&
+			    (info->tid > 0xffff))
+				xtables_error(PARAMETER_PROBLEM,
+					      "l2tp tid is 16 bits for L2TPv2");
+			if ((info->flags & XT_L2TP_SID) &&
+			    (info->sid > 0xffff))
+				xtables_error(PARAMETER_PROBLEM,
+					      "l2tp sid is 16 bits for L2TPv2");
+			if ((info->flags & XT_L2TP_ENCAP) &&
+			    (info->encap == XT_L2TP_ENCAP_IP))
+				xtables_error(PARAMETER_PROBLEM,
+					      "l2tp encap ip invalid for L2TPv2");
+
+			/* v2 is always UDP encap */
+			if (!(info->flags & XT_L2TP_ENCAP)) {
+				info->encap = XT_L2TP_ENCAP_UDP;
+				info->flags |= XT_L2TP_ENCAP;
+			}
+		}
+	}
+
+	if (!(info->flags & XT_L2TP_ENCAP))
+		xtables_error(PARAMETER_PROBLEM,
+			      "l2tp: encap not specified");
+}
+
+static void print_encap(uint8_t encap, const char *prefix)
+{
+	printf(" %sencap ", prefix);
+
+	switch (encap) {
+	case XT_L2TP_ENCAP_UDP:
+		printf("udp");
+		break;
+	case XT_L2TP_ENCAP_IP:
+		printf("ip");
+		break;
+	default:
+		printf("???");
+		break;
+	}
+}
+
+static void print_type(uint8_t type, const char *prefix)
+{
+	printf(" %stype ", prefix);
+
+	switch (type) {
+	case XT_L2TP_TYPE_CONTROL:
+		printf("control");
+		break;
+	case XT_L2TP_TYPE_DATA:
+		printf("data");
+		break;
+	default:
+		printf("???");
+		break;
+	}
+}
+
+static void l2tp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_l2tp_info *info = (struct xt_l2tp_info *)match->data;
+
+	printf(" l2tp");
+	if (info->flags & XT_L2TP_TID)
+		printf(" tid %u", info->tid);
+	if (info->flags & XT_L2TP_SID)
+		printf(" sid %u", info->sid);
+	if (info->flags & XT_L2TP_VERSION)
+		printf(" pver %hd", info->version);
+	if (info->flags & XT_L2TP_ENCAP)
+		print_encap(info->encap, "");
+	if (info->flags & XT_L2TP_TYPE)
+		print_type(info->type, "");
+}
+
+static void l2tp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_l2tp_info *info = (struct xt_l2tp_info *)match->data;
+
+	if (info->flags & XT_L2TP_TID)
+		printf(" --tid %u", info->tid);
+	if (info->flags & XT_L2TP_SID)
+		printf(" --sid %u", info->sid);
+	if (info->flags & XT_L2TP_VERSION)
+		printf(" --pversion %hd", info->version);
+	if (info->flags & XT_L2TP_ENCAP)
+		print_encap(info->encap, "--");
+	if (info->flags & XT_L2TP_TYPE)
+		print_type(info->type, "--");
+}
+
+static struct xtables_match l2tp_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "l2tp",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_l2tp_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_l2tp_info)),
+	.help		= l2tp_help,
+	.print		= l2tp_print,
+	.save		= l2tp_save,
+	.x6_fcheck	= l2tp_check,
+	.x6_parse	= l2tp_parse,
+	.x6_options	= l2tp_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&l2tp_match);
+}
diff --git a/extensions/libxt_l2tp.man b/extensions/libxt_l2tp.man
new file mode 100644
index 0000000..b3969ea
--- /dev/null
+++ b/extensions/libxt_l2tp.man
@@ -0,0 +1,39 @@ 
+This is used to match L2TP packets. L2TP tunnels are identified by
+tunnel-id and session-id fields in the L2TP header. L2TPv2 and L2TPv3
+are supported. 
+.TP
+\fB\-\-tunnel\-id\fP,\fB\-\-tid\fP \fIid\fP
+.TP
+\fB\-\-session\-id\fP,\fB\-\-sid\fP \fIid\fP
+.TP
+\fB\-\-pversion\fP {\fB2\fP|\fB3\fP}
+.TP
+\fB\-\-encap\fP {\fBudp\fP|\fBip\fP}
+.TP
+\fB\-\-type\fP {\fBcontrol\fP|\fBdata\fP}
+.PP
+At least one of \fB\-\-tunnel\-id\fP, \fB\-\-session\-id\fP or
+\fB\-\-type\fP must be
+specified.
+.PP
+\fB\-\-encap\fP must also be specified, either explicitely, or
+implicitely by specifying \fB\-\-pversion 2.
+.PP
+L2TP match rules should always be used together with IP address
+specifiers since the tunnel-id and session-id fields are scoped by the
+L2TP sender or receiver.
+.PP
+To match L2TPv3 packets which use 32-bit tunnel and session ids, the
+\fBpversion\fP option must be used to set the protocol version to 3.
+.PP
+Examples:
+.IP
+iptables \-A INPUT \-s 1.2.3.4 \-m l2tp
+\-\-tid 42 \-\-pversion 2 \-j DROP
+.IP
+iptables \-A OUTPUT \-d 1.2.3.5 \-m l2tp
+\-\-tid 100043 \-\-sid 100042
+\-\-pversion 3 \-\-encap ip \-j DROP
+.IP
+iptables \-A INPUT \-s 1.2.3.4 \-m l2tp
+\-\-encap udp \-\-type control \-j DROP
diff --git a/include/linux/netfilter/xt_l2tp.h b/include/linux/netfilter/xt_l2tp.h
new file mode 100644
index 0000000..ba0a3ed
--- /dev/null
+++ b/include/linux/netfilter/xt_l2tp.h
@@ -0,0 +1,36 @@ 
+#ifndef _LINUX_NETFILTER_XT_L2TP_H
+#define _LINUX_NETFILTER_XT_L2TP_H
+
+#include <linux/types.h>
+
+enum xt_l2tp_encap {
+	XT_L2TP_ENCAP_UDP,
+	XT_L2TP_ENCAP_IP,
+};
+
+enum xt_l2tp_type {
+	XT_L2TP_TYPE_CONTROL,
+	XT_L2TP_TYPE_DATA,
+};
+
+/* L2TP matching stuff */
+struct xt_l2tp_info {
+	__u32 tid;			/* tunnel id */
+	__u32 sid;			/* session id */
+	__u8 version;			/* L2TP protocol version */
+	__u8 encap;			/* L2TP encapsulation type */
+	__u8 type;			/* L2TP packet type */
+	__u8 flags;			/* which fields to match */
+};
+
+enum {
+	XT_L2TP_TID	= (1 << 0),	/* match L2TP tunnel id */
+	XT_L2TP_SID	= (1 << 1),	/* match L2TP session id */
+	XT_L2TP_VERSION	= (1 << 2),	/* match L2TP protocol version */
+	XT_L2TP_ENCAP	= (1 << 3),	/* match L2TP encapsulation type */
+	XT_L2TP_TYPE	= (1 << 4),	/* match L2TP packet type */
+};
+
+
+#endif /* _LINUX_NETFILTER_XT_L2TP_H */
+