Matching broadcast packets

Peter Benie pjb1008@cam.ac.uk
Sat, 02 Oct 1999 14:07:58 +0100


One of the 'features' of ipchains was that you ended up reimplementing
the routing table in the chains rules. Most of that has gone away now
that the forward rules can match on the names of both the incoming and
outgoing interfaces, however, it's can still necessary to wire in all
your broadcast addresses into the input/output rules.

This module will solve that by allowing you to match on 
a) packet type (eg. for detecting incoming link-layer broadcasts)
b) routing flags (for spotting broadcast addresses)

This enables you to conveniently filter broadcasts into a different
chain from unicast packets.

Peter

1999-10-02  Peter Benie <pjb1008@cam.ac.uk>

	* packet-filter/extentions/ipt_pkttype: Added module to match
	packet types and route flags in order to match broadcasts.

	* packet-filter/extentions/libipt_pkttype: Shared object for iptables
	to control the above module.

diff -Pur netfilter-0.1.9.orig/packet-filter/extentions/Makefile netfilter-0.1.9/packet-filter/extentions/Makefile
--- netfilter-0.1.9.orig/packet-filter/extentions/Makefile	Wed Sep 29 19:58:49 1999
+++ netfilter-0.1.9/packet-filter/extentions/Makefile	Sat Oct  2 02:01:41 1999
@@ -1,7 +1,7 @@
 #! /usr/bin/make
 
-PF_EXT_KERN:=tcp udp icmp mac limit REJECT LOG unclean QUEUE state
-PF_EXT_SLIB:=tcp udp icmp mac limit standard LOG unclean QUEUE state
+PF_EXT_KERN:=tcp udp icmp mac limit REJECT LOG unclean QUEUE state pkttype
+PF_EXT_SLIB:=tcp udp icmp mac limit standard LOG unclean QUEUE state pkttype
 KERN_TARGETS+=$(foreach T,$(PF_EXT_KERN),packet-filter/extentions/ipt_$(T).o)
 SHARED_LIBS+=$(foreach T,$(PF_EXT_SLIB),packet-filter/extentions/libipt_$(T).so)
 EXTRA_INSTALLS+=$(foreach T, $(PF_EXT_SLIB), $(LIBDIR)/iptables/libipt_$(T).so)
diff -Pur netfilter-0.1.9.orig/include/packet-filter/extentions/ipt_pkttype.h netfilter-0.1.9/include/packet-filter/extentions/ipt_pkttype.h
--- netfilter-0.1.9.orig/include/packet-filter/extentions/ipt_pkttype.h	Thu Jan  1 01:00:00 1970
+++ netfilter-0.1.9/include/packet-filter/extentions/ipt_pkttype.h	Sat Oct  2 03:29:12 1999
@@ -0,0 +1,15 @@
+#ifndef _IPT_PKTTYPE_H
+#define _IPT_PKTTYPE_H
+
+#define IPT_PKTTYPE_PACKET_TYPE	0x01	/* Test packet type. */
+#define IPT_PKTTYPE_MASK	0x01	/* All possible flags. */
+
+struct ipt_pktinfo
+{
+	u_int8_t flags, invflags;
+	u_int16_t packet_type;
+	u_int32_t route_flags;
+	u_int32_t route_invflags;
+};
+
+#endif /*_IPT_PKTTYPE_H*/
diff -Pur netfilter-0.1.9.orig/packet-filter/extentions/ipt_pkttype.c netfilter-0.1.9/packet-filter/extentions/ipt_pkttype.c
--- netfilter-0.1.9.orig/packet-filter/extentions/ipt_pkttype.c	Thu Jan  1 01:00:00 1970
+++ netfilter-0.1.9/packet-filter/extentions/ipt_pkttype.c	Sat Oct  2 11:58:09 1999
@@ -0,0 +1,67 @@
+/* Kernel module to match on packet type */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/if_packet.h>
+#include <net/ip.h>
+#include <net/route.h>
+
+#include "packet-filter/extentions/ipt_pkttype.h"
+#include "packet-filter/kernel/ip_tables.h"
+
+EXPORT_NO_SYMBOLS;
+
+#define FWINVPKT(bool,invflg) ((bool) ^ !!(pktinfo->invflags & invflg))
+#define FWBITS(value,flags,invflags) ((((value)^(invflags))&(flags))==(flags))
+
+static int
+match(struct sk_buff *skb,
+      const char *indev,
+      const char *outdev,
+      const union ipt_matchinfo *matchinfo,
+      int offset,
+      const void *hdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	const struct ipt_pktinfo *pktinfo=
+		(const struct ipt_pktinfo *)matchinfo;
+	struct rtable *rt = (struct rtable *)skb->dst;
+
+	if (pktinfo->route_flags)
+	{
+		if (!rt
+		    || !FWBITS(rt->rt_flags & pktinfo->route_flags,
+			       pktinfo->route_flags, pktinfo->route_invflags))
+			return 0;
+	}
+	return (!(pktinfo->flags & IPT_PKTTYPE_PACKET_TYPE))
+		|| FWINVPKT(skb->pkt_type==pktinfo->packet_type,
+			    IPT_PKTTYPE_PACKET_TYPE);
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const struct ipt_ip *ip,
+	   const union ipt_matchinfo *matchinfo)
+{
+	const struct ipt_pktinfo *pktinfo=
+		(const struct ipt_pktinfo *)matchinfo;
+	return !(pktinfo->flags & ~IPT_PKTTYPE_MASK)
+		&& !(pktinfo->invflags & ~IPT_PKTTYPE_MASK);
+}
+
+static struct ipt_match pkttype_match
+= { { NULL, NULL }, "pkttype", &match, &checkentry, NULL, THIS_MODULE };
+
+int __init init(void)
+{
+	return ipt_register_match(&pkttype_match);
+}
+
+void __exit cleanup(void)
+{
+	ipt_unregister_match(&pkttype_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -Pur netfilter-0.1.9.orig/packet-filter/extentions/libipt_pkttype.c netfilter-0.1.9/packet-filter/extentions/libipt_pkttype.c
--- netfilter-0.1.9.orig/packet-filter/extentions/libipt_pkttype.c	Thu Jan  1 01:00:00 1970
+++ netfilter-0.1.9/packet-filter/extentions/libipt_pkttype.c	Sat Oct  2 11:47:22 1999
@@ -0,0 +1,233 @@
+/* Match on packet type. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <linux/if_packet.h>
+#include <linux/in_route.h>
+#include "packet-filter/userspace/iptables.h"
+#include "packet-filter/extentions/ipt_pkttype.h"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+	printf(
+"pkttype v%s options:\n"
+" --packet [host|broadcast|multicast|
+            otherhost|outgoing|loopback|fastroute]	match packet type\n"
+" --route [local,directsrc,
+           broadcast,multicast]				match route flags\n"
+" ('packet' gives information about the link layer and is meaningful only"
+" for incoming packets.)\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+	{ "packet", 1, 0, '1' },
+	{ "route", 1, 0, '2' },
+	{0}
+};
+
+static void
+init(union ipt_matchinfo *matchinfo, unsigned int *nfcache)
+{
+	struct ipt_pktinfo *pktinfo=
+		(struct ipt_pktinfo *)matchinfo;
+	*pktinfo = ((struct ipt_pktinfo) { 0, 0 });
+}
+
+struct packettypelist { const char *name; int value; } packettypelist[] =
+{
+	{"host",	PACKET_HOST},
+	{"broadcast",	PACKET_BROADCAST},
+	{"multicast",	PACKET_MULTICAST},
+	{"otherhost",	PACKET_OTHERHOST},
+	{"outgoing",	PACKET_OUTGOING},
+	{"loopback",	PACKET_LOOPBACK},
+	{"fastroute",	PACKET_FASTROUTE},
+	{NULL,		0}
+};
+
+struct routetypelist { const char *name; u_int32_t value; } routetypelist[] =
+{
+	{"local",	RTCF_LOCAL},
+	{"directsrc",	RTCF_DIRECTSRC},
+	{"broadcast",	RTCF_BROADCAST},
+	{"multicast",	RTCF_MULTICAST},
+	{NULL,		0}
+};
+
+static int
+parse_packet_type(const char *arg)
+{
+	struct packettypelist *type;
+	for (type=packettypelist; type->name; type++)
+		if (!strcasecmp(type->name, arg)) return type->value;
+	exit_error(PARAMETER_PROBLEM, "Unknown packet type `%s'", arg);
+}
+
+static int
+parse_route_type(const char *arg)
+{
+	struct routetypelist *type;
+	for (type=routetypelist; type->name; type++)
+		if (!strcasecmp(type->name, arg)) return type->value;
+	exit_error(PARAMETER_PROBLEM, "Unknown route type `%s'", arg);
+}
+
+static void
+parse_route_types(const char *arg, u_int32_t *flagsp, u_int32_t *invflagsp)
+{
+	char *buffer, *cp, *next;
+	u_int32_t flags=0, invflags=0;
+
+	buffer = strdup(arg);
+	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+	for (cp=buffer; cp; cp=next)
+	{
+		int flag, invert=0;
+		if ((next=strchr(cp, ','))) *next++='\0';
+		if (*cp=='!') invert=1,cp++;
+		flag=parse_route_type(cp);
+		if (flag & flags) 
+			exit_error(PARAMETER_PROBLEM, 
+				   "flag `%s' repeated", cp);
+		flags|=flag;
+		if (invert) invflags|=flag;
+	}
+	free(buffer);
+	*flagsp=flags, *invflagsp=invflags;
+}
+
+#define PKTTYPE_PACKET_TYPE		0x01
+#define PKTTYPE_ROUTE_TYPE		0x02
+
+/* Function which parses command options; returns true if it
+   ate an option. */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      struct ipt_entry *entry)
+{
+	struct ipt_pktinfo *pktinfo=
+		(struct ipt_pktinfo *)&entry->matchinfo;
+
+	switch (c) {
+	case '1':
+		if (*flags & PKTTYPE_PACKET_TYPE)
+			exit_error(PARAMETER_PROBLEM,
+				   "Only one `--packet-type' allowed");
+		if (check_inverse(optarg, &invert))
+			optind++;
+		pktinfo->packet_type=parse_packet_type(argv[optind-1]);
+		pktinfo->flags |= IPT_PKTTYPE_PACKET_TYPE;
+		if (invert)
+			pktinfo->invflags |= IPT_PKTTYPE_PACKET_TYPE;
+		*flags |= PKTTYPE_PACKET_TYPE;
+		break;
+
+	case '2':
+		if (*flags & PKTTYPE_ROUTE_TYPE)
+			exit_error(PARAMETER_PROBLEM,
+				   "Only one `--route-type' allowed");
+		parse_route_types(argv[optind-1], &pktinfo->route_flags,
+				 &pktinfo->route_invflags);
+		*flags |= PKTTYPE_ROUTE_TYPE;
+		break;
+		
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_packet_type(int value)
+{
+	struct packettypelist *type;
+	for (type=packettypelist; type->name; type++)
+		if (type->value==value) { printf(type->name); return; }
+	printf("unknown");
+}
+
+static void
+print_route_type(u_int32_t value)
+{
+	struct routetypelist *type;
+	for (type=routetypelist; type->name; type++)
+		if (type->value==value) { printf(type->name); return; }
+	printf("unknown");
+}
+
+static void
+print_route_types(u_int32_t value, u_int32_t inv)
+{
+	int i, first=1;
+	u_int32_t mask;
+
+	for (i=0,mask=1; i<32; i++,mask<<=1)
+	{
+		if (!(value & mask)) continue;
+		if (first) 
+			first=0;
+		else
+			printf(",");
+		if (inv & mask) printf("!");
+		print_route_type(value & mask);
+	}
+}
+
+/* Prints ipt_matchinfo. */
+static void
+print(const union ipt_matchinfo *matchinfo, int numeric)
+{
+	struct ipt_pktinfo *pktinfo=
+		(struct ipt_pktinfo *)matchinfo;
+
+	if (pktinfo->flags & IPT_PKTTYPE_PACKET_TYPE)
+	{
+		printf("packet ");
+		if (pktinfo->invflags & IPT_PKTTYPE_PACKET_TYPE)
+			printf("!");
+		print_packet_type(pktinfo->packet_type);
+		printf(" ");
+	}
+	if ((pktinfo->flags|pktinfo->invflags) & ~IPT_PKTTYPE_MASK)
+	{
+		printf("Unknown flags: 0x%2X/0x%2X ",
+		       pktinfo->flags & ~IPT_PKTTYPE_MASK,
+		       pktinfo->invflags & ~IPT_PKTTYPE_MASK);
+	}
+
+	if (pktinfo->route_flags)
+	{
+		printf("route ");
+		print_route_types(pktinfo->route_flags, pktinfo->route_invflags);
+		printf(" ");
+	}
+}
+
+struct iptables_match pkttype
+= { NULL,
+    "pkttype",
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    opts };
+
+void
+_init(void)
+{
+	register_match(&pkttype);
+}
diff -Pur netfilter-0.1.9.orig/packet-filter/userspace/iptables.8 netfilter-0.1.9/packet-filter/userspace/iptables.8
--- netfilter-0.1.9.orig/packet-filter/userspace/iptables.8	Sat Sep  4 19:35:04 1999
+++ netfilter-0.1.9/packet-filter/userspace/iptables.8	Sat Oct  2 11:57:58 1999
@@ -399,7 +399,22 @@
 The maximum initial number of packets to match: this number gets
 recharged by one every time the limit specified above is not reached,
 up to this number; the default is 5.
+.SS pkttpye
+This module matches routing properties of packets.
 .TP
+.BR "--packet " "[!] \fIpacket-type\fR"
+Packet-type may be one of host, broadcast, multicast, otherhost,
+outgoing, loopback or fastroute]. It gives information about the link
+layer and is meaningful only for incoming packets. `--packet
+broadcast' will match ethernet broadcasts, regardless of the
+destination IP address is set to. Some packet types, such as
+`otherhost', should never be seen by iptables.
+.TP
+.BR "--route " "[!]\fIflag\fR,[!]\fIflag\fR..."
+Routing flags may be local, directsrc, broadcast or multicast. Local
+is set if the packet is to be processed by the local
+machine. Directsrc indicates that the source address is that of a
+machine on the same network. Broadcast and multicast are obvious.
 .SH DIAGNOSTICS
 Various error messages are printed to standard error.  The exit code
 is 0 for correct functioning.  Errors which appear to be caused by