summaryrefslogtreecommitdiff
path: root/net/bridge/netfilter
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/bridge/netfilter
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'net/bridge/netfilter')
-rw-r--r--net/bridge/netfilter/Kconfig211
-rw-r--r--net/bridge/netfilter/Makefile32
-rw-r--r--net/bridge/netfilter/ebt_802_3.c73
-rw-r--r--net/bridge/netfilter/ebt_among.c228
-rw-r--r--net/bridge/netfilter/ebt_arp.c140
-rw-r--r--net/bridge/netfilter/ebt_arpreply.c97
-rw-r--r--net/bridge/netfilter/ebt_dnat.c76
-rw-r--r--net/bridge/netfilter/ebt_ip.c122
-rw-r--r--net/bridge/netfilter/ebt_limit.c113
-rw-r--r--net/bridge/netfilter/ebt_log.c171
-rw-r--r--net/bridge/netfilter/ebt_mark.c68
-rw-r--r--net/bridge/netfilter/ebt_mark_m.c62
-rw-r--r--net/bridge/netfilter/ebt_pkttype.c59
-rw-r--r--net/bridge/netfilter/ebt_redirect.c81
-rw-r--r--net/bridge/netfilter/ebt_snat.c76
-rw-r--r--net/bridge/netfilter/ebt_stp.c194
-rw-r--r--net/bridge/netfilter/ebt_ulog.c295
-rw-r--r--net/bridge/netfilter/ebt_vlan.c195
-rw-r--r--net/bridge/netfilter/ebtable_broute.c86
-rw-r--r--net/bridge/netfilter/ebtable_filter.c123
-rw-r--r--net/bridge/netfilter/ebtable_nat.c130
-rw-r--r--net/bridge/netfilter/ebtables.c1507
22 files changed, 4139 insertions, 0 deletions
diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig
new file mode 100644
index 00000000000..68ccef507b4
--- /dev/null
+++ b/net/bridge/netfilter/Kconfig
@@ -0,0 +1,211 @@
+#
+# Bridge netfilter configuration
+#
+
+menu "Bridge: Netfilter Configuration"
+ depends on BRIDGE && NETFILTER
+
+config BRIDGE_NF_EBTABLES
+ tristate "Ethernet Bridge tables (ebtables) support"
+ help
+ ebtables is a general, extensible frame/packet identification
+ framework. Say 'Y' or 'M' here if you want to do Ethernet
+ filtering/NAT/brouting on the Ethernet bridge.
+#
+# tables
+#
+config BRIDGE_EBT_BROUTE
+ tristate "ebt: broute table support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ The ebtables broute table is used to define rules that decide between
+ bridging and routing frames, giving Linux the functionality of a
+ brouter. See the man page for ebtables(8) and examples on the ebtables
+ website.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_T_FILTER
+ tristate "ebt: filter table support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ The ebtables filter table is used to define frame filtering rules at
+ local input, forwarding and local output. See the man page for
+ ebtables(8).
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_T_NAT
+ tristate "ebt: nat table support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ The ebtables nat table is used to define rules that alter the MAC
+ source address (MAC SNAT) or the MAC destination address (MAC DNAT).
+ See the man page for ebtables(8).
+
+ To compile it as a module, choose M here. If unsure, say N.
+#
+# matches
+#
+config BRIDGE_EBT_802_3
+ tristate "ebt: 802.3 filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds matching support for 802.3 Ethernet frames.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_AMONG
+ tristate "ebt: among filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the among match, which allows matching the MAC source
+ and/or destination address on a list of addresses. Optionally,
+ MAC/IP address pairs can be matched, f.e. for anti-spoofing rules.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_ARP
+ tristate "ebt: ARP filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the ARP match, which allows ARP and RARP header field
+ filtering.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_IP
+ tristate "ebt: IP filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the IP match, which allows basic IP header field
+ filtering.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_LIMIT
+ tristate "ebt: limit match support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the limit match, which allows you to control
+ the rate at which a rule can be matched. This match is the
+ equivalent of the iptables limit match.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+
+config BRIDGE_EBT_MARK
+ tristate "ebt: mark filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the mark match, which allows matching frames based on
+ the 'nfmark' value in the frame. This can be set by the mark target.
+ This value is the same as the one used in the iptables mark match and
+ target.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_PKTTYPE
+ tristate "ebt: packet type filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the packet type match, which allows matching on the
+ type of packet based on its Ethernet "class" (as determined by
+ the generic networking code): broadcast, multicast,
+ for this host alone or for another host.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_STP
+ tristate "ebt: STP filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the Spanning Tree Protocol match, which
+ allows STP header field filtering.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_VLAN
+ tristate "ebt: 802.1Q VLAN filter support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the 802.1Q vlan match, which allows the filtering of
+ 802.1Q vlan fields.
+
+ To compile it as a module, choose M here. If unsure, say N.
+#
+# targets
+#
+config BRIDGE_EBT_ARPREPLY
+ tristate "ebt: arp reply target support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the arp reply target, which allows
+ automatically sending arp replies to arp requests.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_DNAT
+ tristate "ebt: dnat target support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the MAC DNAT target, which allows altering the MAC
+ destination address of frames.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_MARK_T
+ tristate "ebt: mark target support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the mark target, which allows marking frames by
+ setting the 'nfmark' value in the frame.
+ This value is the same as the one used in the iptables mark match and
+ target.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_REDIRECT
+ tristate "ebt: redirect target support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the MAC redirect target, which allows altering the MAC
+ destination address of a frame to that of the device it arrived on.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_SNAT
+ tristate "ebt: snat target support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the MAC SNAT target, which allows altering the MAC
+ source address of frames.
+
+ To compile it as a module, choose M here. If unsure, say N.
+#
+# watchers
+#
+config BRIDGE_EBT_LOG
+ tristate "ebt: log support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the log watcher, that you can use in any rule
+ in any ebtables table. It records info about the frame header
+ to the syslog.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config BRIDGE_EBT_ULOG
+ tristate "ebt: ulog support"
+ depends on BRIDGE_NF_EBTABLES
+ help
+ This option adds the ulog watcher, that you can use in any rule
+ in any ebtables table. The packet is passed to a userspace
+ logging daemon using netlink multicast sockets. This differs
+ from the log watcher in the sense that the complete packet is
+ sent to userspace instead of a descriptive text and that
+ netlink multicast sockets are used instead of the syslog.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+endmenu
diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile
new file mode 100644
index 00000000000..8bf6d9f6e9d
--- /dev/null
+++ b/net/bridge/netfilter/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for the netfilter modules for Link Layer filtering on a bridge.
+#
+
+obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
+
+# tables
+obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+
+#matches
+obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
+obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
+obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o
+obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o
+obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
+obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o
+obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
+obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o
+obj-$(CONFIG_BRIDGE_EBT_VLAN) += ebt_vlan.o
+
+# targets
+obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
+obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
+obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
+
+# watchers
+obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_ulog.o
diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c
new file mode 100644
index 00000000000..468ebdf4bc1
--- /dev/null
+++ b/net/bridge/netfilter/ebt_802_3.c
@@ -0,0 +1,73 @@
+/*
+ * 802_3
+ *
+ * Author:
+ * Chris Vitale csv@bluetail.com
+ *
+ * May 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_802_3.h>
+#include <linux/module.h>
+
+static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *data, unsigned int datalen)
+{
+ struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
+ struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb);
+ uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
+
+ if (info->bitmask & EBT_802_3_SAP) {
+ if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP))
+ return EBT_NOMATCH;
+ if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
+ return EBT_NOMATCH;
+ }
+
+ if (info->bitmask & EBT_802_3_TYPE) {
+ if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
+ return EBT_NOMATCH;
+ if (FWINV(info->type != type, EBT_802_3_TYPE))
+ return EBT_NOMATCH;
+ }
+
+ return EBT_MATCH;
+}
+
+static struct ebt_match filter_802_3;
+static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
+
+ if (datalen < sizeof(struct ebt_802_3_info))
+ return -EINVAL;
+ if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct ebt_match filter_802_3 =
+{
+ .name = EBT_802_3_MATCH,
+ .match = ebt_filter_802_3,
+ .check = ebt_802_3_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_802_3);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_802_3);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c
new file mode 100644
index 00000000000..5a1f5e3bff1
--- /dev/null
+++ b/net/bridge/netfilter/ebt_among.c
@@ -0,0 +1,228 @@
+/*
+ * ebt_among
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * August, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+
+static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
+ const char *mac, uint32_t ip)
+{
+ /* You may be puzzled as to how this code works.
+ * Some tricks were used, refer to
+ * include/linux/netfilter_bridge/ebt_among.h
+ * as there you can find a solution of this mystery.
+ */
+ const struct ebt_mac_wormhash_tuple *p;
+ int start, limit, i;
+ uint32_t cmp[2] = { 0, 0 };
+ int key = (const unsigned char) mac[5];
+
+ memcpy(((char *) cmp) + 2, mac, 6);
+ start = wh->table[key];
+ limit = wh->table[key + 1];
+ if (ip) {
+ for (i = start; i < limit; i++) {
+ p = &wh->pool[i];
+ if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+ if (p->ip == 0 || p->ip == ip) {
+ return 1;
+ }
+ }
+ }
+ } else {
+ for (i = start; i < limit; i++) {
+ p = &wh->pool[i];
+ if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+ if (p->ip == 0) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
+ *wh)
+{
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ if (wh->table[i] > wh->table[i + 1])
+ return -0x100 - i;
+ if (wh->table[i] < 0)
+ return -0x200 - i;
+ if (wh->table[i] > wh->poolsize)
+ return -0x300 - i;
+ }
+ if (wh->table[256] > wh->poolsize)
+ return -0xc00;
+ return 0;
+}
+
+static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
+{
+ if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
+ struct iphdr _iph, *ih;
+
+ ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+ if (ih == NULL)
+ return -1;
+ *addr = ih->daddr;
+ } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
+ struct arphdr _arph, *ah;
+ uint32_t buf, *bp;
+
+ ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+ if (ah == NULL ||
+ ah->ar_pln != sizeof(uint32_t) ||
+ ah->ar_hln != ETH_ALEN)
+ return -1;
+ bp = skb_header_pointer(skb, sizeof(struct arphdr) +
+ 2 * ETH_ALEN + sizeof(uint32_t),
+ sizeof(uint32_t), &buf);
+ if (bp == NULL)
+ return -1;
+ *addr = *bp;
+ }
+ return 0;
+}
+
+static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
+{
+ if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
+ struct iphdr _iph, *ih;
+
+ ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+ if (ih == NULL)
+ return -1;
+ *addr = ih->saddr;
+ } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
+ struct arphdr _arph, *ah;
+ uint32_t buf, *bp;
+
+ ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+ if (ah == NULL ||
+ ah->ar_pln != sizeof(uint32_t) ||
+ ah->ar_hln != ETH_ALEN)
+ return -1;
+ bp = skb_header_pointer(skb, sizeof(struct arphdr) +
+ ETH_ALEN, sizeof(uint32_t), &buf);
+ if (bp == NULL)
+ return -1;
+ *addr = *bp;
+ }
+ return 0;
+}
+
+static int ebt_filter_among(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out, const void *data,
+ unsigned int datalen)
+{
+ struct ebt_among_info *info = (struct ebt_among_info *) data;
+ const char *dmac, *smac;
+ const struct ebt_mac_wormhash *wh_dst, *wh_src;
+ uint32_t dip = 0, sip = 0;
+
+ wh_dst = ebt_among_wh_dst(info);
+ wh_src = ebt_among_wh_src(info);
+
+ if (wh_src) {
+ smac = eth_hdr(skb)->h_source;
+ if (get_ip_src(skb, &sip))
+ return EBT_NOMATCH;
+ if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
+ /* we match only if it contains */
+ if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
+ return EBT_NOMATCH;
+ } else {
+ /* we match only if it DOES NOT contain */
+ if (ebt_mac_wormhash_contains(wh_src, smac, sip))
+ return EBT_NOMATCH;
+ }
+ }
+
+ if (wh_dst) {
+ dmac = eth_hdr(skb)->h_dest;
+ if (get_ip_dst(skb, &dip))
+ return EBT_NOMATCH;
+ if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
+ /* we match only if it contains */
+ if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+ return EBT_NOMATCH;
+ } else {
+ /* we match only if it DOES NOT contain */
+ if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+ return EBT_NOMATCH;
+ }
+ }
+
+ return EBT_MATCH;
+}
+
+static int ebt_among_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data,
+ unsigned int datalen)
+{
+ struct ebt_among_info *info = (struct ebt_among_info *) data;
+ int expected_length = sizeof(struct ebt_among_info);
+ const struct ebt_mac_wormhash *wh_dst, *wh_src;
+ int err;
+
+ wh_dst = ebt_among_wh_dst(info);
+ wh_src = ebt_among_wh_src(info);
+ expected_length += ebt_mac_wormhash_size(wh_dst);
+ expected_length += ebt_mac_wormhash_size(wh_src);
+
+ if (datalen != EBT_ALIGN(expected_length)) {
+ printk(KERN_WARNING
+ "ebtables: among: wrong size: %d"
+ "against expected %d, rounded to %Zd\n",
+ datalen, expected_length,
+ EBT_ALIGN(expected_length));
+ return -EINVAL;
+ }
+ if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
+ printk(KERN_WARNING
+ "ebtables: among: dst integrity fail: %x\n", -err);
+ return -EINVAL;
+ }
+ if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
+ printk(KERN_WARNING
+ "ebtables: among: src integrity fail: %x\n", -err);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct ebt_match filter_among = {
+ .name = EBT_AMONG_MATCH,
+ .match = ebt_filter_among,
+ .check = ebt_among_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_among);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_among);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c
new file mode 100644
index 00000000000..b94c48cb6e4
--- /dev/null
+++ b/net/bridge/netfilter/ebt_arp.c
@@ -0,0 +1,140 @@
+/*
+ * ebt_arp
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * Tim Gardner <timg@tpi.com>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_arp.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/module.h>
+
+static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *data, unsigned int datalen)
+{
+ struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+ struct arphdr _arph, *ah;
+
+ ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+ if (ah == NULL)
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
+ ah->ar_op, EBT_ARP_OPCODE))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
+ ah->ar_hrd, EBT_ARP_HTYPE))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
+ ah->ar_pro, EBT_ARP_PTYPE))
+ return EBT_NOMATCH;
+
+ if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) {
+ uint32_t _addr, *ap;
+
+ /* IPv4 addresses are always 4 bytes */
+ if (ah->ar_pln != sizeof(uint32_t))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_SRC_IP) {
+ ap = skb_header_pointer(skb, sizeof(struct arphdr) +
+ ah->ar_hln, sizeof(_addr),
+ &_addr);
+ if (ap == NULL)
+ return EBT_NOMATCH;
+ if (FWINV(info->saddr != (*ap & info->smsk),
+ EBT_ARP_SRC_IP))
+ return EBT_NOMATCH;
+ }
+
+ if (info->bitmask & EBT_ARP_DST_IP) {
+ ap = skb_header_pointer(skb, sizeof(struct arphdr) +
+ 2*ah->ar_hln+sizeof(uint32_t),
+ sizeof(_addr), &_addr);
+ if (ap == NULL)
+ return EBT_NOMATCH;
+ if (FWINV(info->daddr != (*ap & info->dmsk),
+ EBT_ARP_DST_IP))
+ return EBT_NOMATCH;
+ }
+ }
+
+ if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) {
+ unsigned char _mac[ETH_ALEN], *mp;
+ uint8_t verdict, i;
+
+ /* MAC addresses are 6 bytes */
+ if (ah->ar_hln != ETH_ALEN)
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_SRC_MAC) {
+ mp = skb_header_pointer(skb, sizeof(struct arphdr),
+ sizeof(_mac), &_mac);
+ if (mp == NULL)
+ return EBT_NOMATCH;
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (mp[i] ^ info->smaddr[i]) &
+ info->smmsk[i];
+ if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
+ return EBT_NOMATCH;
+ }
+
+ if (info->bitmask & EBT_ARP_DST_MAC) {
+ mp = skb_header_pointer(skb, sizeof(struct arphdr) +
+ ah->ar_hln + ah->ar_pln,
+ sizeof(_mac), &_mac);
+ if (mp == NULL)
+ return EBT_NOMATCH;
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (mp[i] ^ info->dmaddr[i]) &
+ info->dmmsk[i];
+ if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
+ return EBT_NOMATCH;
+ }
+ }
+
+ return EBT_MATCH;
+}
+
+static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
+ return -EINVAL;
+ if ((e->ethproto != htons(ETH_P_ARP) &&
+ e->ethproto != htons(ETH_P_RARP)) ||
+ e->invflags & EBT_IPROTO)
+ return -EINVAL;
+ if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_match filter_arp =
+{
+ .name = EBT_ARP_MATCH,
+ .match = ebt_filter_arp,
+ .check = ebt_arp_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_arp);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_arp);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c
new file mode 100644
index 00000000000..b934de90f7c
--- /dev/null
+++ b/net/bridge/netfilter/ebt_arpreply.c
@@ -0,0 +1,97 @@
+/*
+ * ebt_arpreply
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * August, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_arpreply.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/module.h>
+
+static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
+ u32 _sip, *siptr, _dip, *diptr;
+ struct arphdr _ah, *ap;
+ unsigned char _sha[ETH_ALEN], *shp;
+ struct sk_buff *skb = *pskb;
+
+ ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
+ if (ap == NULL)
+ return EBT_DROP;
+
+ if (ap->ar_op != htons(ARPOP_REQUEST) ||
+ ap->ar_hln != ETH_ALEN ||
+ ap->ar_pro != htons(ETH_P_IP) ||
+ ap->ar_pln != 4)
+ return EBT_CONTINUE;
+
+ shp = skb_header_pointer(skb, sizeof(_ah), ETH_ALEN, &_sha);
+ if (shp == NULL)
+ return EBT_DROP;
+
+ siptr = skb_header_pointer(skb, sizeof(_ah) + ETH_ALEN,
+ sizeof(_sip), &_sip);
+ if (siptr == NULL)
+ return EBT_DROP;
+
+ diptr = skb_header_pointer(skb,
+ sizeof(_ah) + 2 * ETH_ALEN + sizeof(_sip),
+ sizeof(_dip), &_dip);
+ if (diptr == NULL)
+ return EBT_DROP;
+
+ arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)in,
+ *diptr, shp, info->mac, shp);
+
+ return info->target;
+}
+
+static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ if (e->ethproto != htons(ETH_P_ARP) ||
+ e->invflags & EBT_IPROTO)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target reply_target =
+{
+ .name = EBT_ARPREPLY_TARGET,
+ .target = ebt_target_reply,
+ .check = ebt_target_reply_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&reply_target);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&reply_target);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c
new file mode 100644
index 00000000000..f5463086c7b
--- /dev/null
+++ b/net/bridge/netfilter/ebt_dnat.c
@@ -0,0 +1,76 @@
+/*
+ * ebt_dnat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+#include <net/sock.h>
+
+static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+ if (skb_shared(*pskb) || skb_cloned(*pskb)) {
+ struct sk_buff *nskb;
+
+ nskb = skb_copy(*pskb, GFP_ATOMIC);
+ if (!nskb)
+ return NF_DROP;
+ if ((*pskb)->sk)
+ skb_set_owner_w(nskb, (*pskb)->sk);
+ kfree_skb(*pskb);
+ *pskb = nskb;
+ }
+ memcpy(eth_hdr(*pskb)->h_dest, info->mac, ETH_ALEN);
+ return info->target;
+}
+
+static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if ( (strcmp(tablename, "nat") ||
+ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target dnat =
+{
+ .name = EBT_DNAT_TARGET,
+ .target = ebt_target_dnat,
+ .check = ebt_target_dnat_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&dnat);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&dnat);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c
new file mode 100644
index 00000000000..7323805b972
--- /dev/null
+++ b/net/bridge/netfilter/ebt_ip.c
@@ -0,0 +1,122 @@
+/*
+ * ebt_ip
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ *
+ * Changes:
+ * added ip-sport and ip-dport
+ * Innominate Security Technologies AG <mhopf@innominate.com>
+ * September, 2002
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ip.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/module.h>
+
+struct tcpudphdr {
+ uint16_t src;
+ uint16_t dst;
+};
+
+static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *data,
+ unsigned int datalen)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+ struct iphdr _iph, *ih;
+ struct tcpudphdr _ports, *pptr;
+
+ ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+ if (ih == NULL)
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_IP_TOS &&
+ FWINV(info->tos != ih->tos, EBT_IP_TOS))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_IP_SOURCE &&
+ FWINV((ih->saddr & info->smsk) !=
+ info->saddr, EBT_IP_SOURCE))
+ return EBT_NOMATCH;
+ if ((info->bitmask & EBT_IP_DEST) &&
+ FWINV((ih->daddr & info->dmsk) !=
+ info->daddr, EBT_IP_DEST))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_IP_PROTO) {
+ if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO))
+ return EBT_NOMATCH;
+ if (!(info->bitmask & EBT_IP_DPORT) &&
+ !(info->bitmask & EBT_IP_SPORT))
+ return EBT_MATCH;
+ pptr = skb_header_pointer(skb, ih->ihl*4,
+ sizeof(_ports), &_ports);
+ if (pptr == NULL)
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_IP_DPORT) {
+ u32 dst = ntohs(pptr->dst);
+ if (FWINV(dst < info->dport[0] ||
+ dst > info->dport[1],
+ EBT_IP_DPORT))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_IP_SPORT) {
+ u32 src = ntohs(pptr->src);
+ if (FWINV(src < info->sport[0] ||
+ src > info->sport[1],
+ EBT_IP_SPORT))
+ return EBT_NOMATCH;
+ }
+ }
+ return EBT_MATCH;
+}
+
+static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
+ return -EINVAL;
+ if (e->ethproto != htons(ETH_P_IP) ||
+ e->invflags & EBT_IPROTO)
+ return -EINVAL;
+ if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+ return -EINVAL;
+ if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
+ if (info->invflags & EBT_IP_PROTO)
+ return -EINVAL;
+ if (info->protocol != IPPROTO_TCP &&
+ info->protocol != IPPROTO_UDP)
+ return -EINVAL;
+ }
+ if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
+ return -EINVAL;
+ if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_match filter_ip =
+{
+ .name = EBT_IP_MATCH,
+ .match = ebt_filter_ip,
+ .check = ebt_ip_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_ip);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_ip);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c
new file mode 100644
index 00000000000..637c8844cd5
--- /dev/null
+++ b/net/bridge/netfilter/ebt_limit.c
@@ -0,0 +1,113 @@
+/*
+ * ebt_limit
+ *
+ * Authors:
+ * Tom Marshall <tommy@home.tig-grr.com>
+ *
+ * Mostly copied from netfilter's ipt_limit.c, see that file for
+ * more explanation
+ *
+ * September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_limit.h>
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(limit_lock);
+
+#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24))
+
+#define _POW2_BELOW2(x) ((x)|((x)>>1))
+#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2))
+#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4))
+#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8))
+#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16))
+#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1)
+
+#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
+
+static int ebt_limit_match(const struct sk_buff *skb,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_limit_info *info = (struct ebt_limit_info *)data;
+ unsigned long now = jiffies;
+
+ spin_lock_bh(&limit_lock);
+ info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
+ if (info->credit > info->credit_cap)
+ info->credit = info->credit_cap;
+
+ if (info->credit >= info->cost) {
+ /* We're not limited. */
+ info->credit -= info->cost;
+ spin_unlock_bh(&limit_lock);
+ return EBT_MATCH;
+ }
+
+ spin_unlock_bh(&limit_lock);
+ return EBT_NOMATCH;
+}
+
+/* Precision saver. */
+static u_int32_t
+user2credits(u_int32_t user)
+{
+ /* If multiplying would overflow... */
+ if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
+ /* Divide first. */
+ return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
+
+ return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
+}
+
+static int ebt_limit_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_limit_info *info = (struct ebt_limit_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
+ return -EINVAL;
+
+ /* Check for overflow. */
+ if (info->burst == 0 ||
+ user2credits(info->avg * info->burst) < user2credits(info->avg)) {
+ printk("Overflow in ebt_limit, try lower: %u/%u\n",
+ info->avg, info->burst);
+ return -EINVAL;
+ }
+
+ /* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
+ info->prev = jiffies;
+ info->credit = user2credits(info->avg * info->burst);
+ info->credit_cap = user2credits(info->avg * info->burst);
+ info->cost = user2credits(info->avg);
+ return 0;
+}
+
+static struct ebt_match ebt_limit_reg =
+{
+ .name = EBT_LIMIT_MATCH,
+ .match = ebt_limit_match,
+ .check = ebt_limit_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&ebt_limit_reg);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&ebt_limit_reg);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
new file mode 100644
index 00000000000..e4ae34b8892
--- /dev/null
+++ b/net/bridge/netfilter/ebt_log.c
@@ -0,0 +1,171 @@
+/*
+ * ebt_log
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_log.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(ebt_log_lock);
+
+static int ebt_log_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_log_info *info = (struct ebt_log_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
+ return -EINVAL;
+ if (info->bitmask & ~EBT_LOG_MASK)
+ return -EINVAL;
+ if (info->loglevel >= 8)
+ return -EINVAL;
+ info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
+ return 0;
+}
+
+struct tcpudphdr
+{
+ uint16_t src;
+ uint16_t dst;
+};
+
+struct arppayload
+{
+ unsigned char mac_src[ETH_ALEN];
+ unsigned char ip_src[4];
+ unsigned char mac_dst[ETH_ALEN];
+ unsigned char ip_dst[4];
+};
+
+static void print_MAC(unsigned char *p)
+{
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++, p++)
+ printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+}
+
+#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
+static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_log_info *info = (struct ebt_log_info *)data;
+ char level_string[4] = "< >";
+ union {struct iphdr iph; struct tcpudphdr ports;
+ struct arphdr arph; struct arppayload arpp;} u;
+
+ level_string[1] = '0' + info->loglevel;
+ spin_lock_bh(&ebt_log_lock);
+ printk(level_string);
+ printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
+ out ? out->name : "");
+
+ printk("MAC source = ");
+ print_MAC(eth_hdr(skb)->h_source);
+ printk("MAC dest = ");
+ print_MAC(eth_hdr(skb)->h_dest);
+
+ printk("proto = 0x%04x", ntohs(eth_hdr(skb)->h_proto));
+
+ if ((info->bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto ==
+ htons(ETH_P_IP)){
+ struct iphdr _iph, *ih;
+
+ ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+ if (ih == NULL) {
+ printk(" INCOMPLETE IP header");
+ goto out;
+ }
+ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
+ NIPQUAD(ih->saddr), NIPQUAD(ih->daddr));
+ printk(" IP tos=0x%02X, IP proto=%d", u.iph.tos,
+ ih->protocol);
+ if (ih->protocol == IPPROTO_TCP ||
+ ih->protocol == IPPROTO_UDP) {
+ struct tcpudphdr _ports, *pptr;
+
+ pptr = skb_header_pointer(skb, ih->ihl*4,
+ sizeof(_ports), &_ports);
+ if (pptr == NULL) {
+ printk(" INCOMPLETE TCP/UDP header");
+ goto out;
+ }
+ printk(" SPT=%u DPT=%u", ntohs(pptr->src),
+ ntohs(pptr->dst));
+ }
+ goto out;
+ }
+
+ if ((info->bitmask & EBT_LOG_ARP) &&
+ ((eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) ||
+ (eth_hdr(skb)->h_proto == htons(ETH_P_RARP)))) {
+ struct arphdr _arph, *ah;
+
+ ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+ if (ah == NULL) {
+ printk(" INCOMPLETE ARP header");
+ goto out;
+ }
+ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
+ ntohs(ah->ar_hrd), ntohs(ah->ar_pro),
+ ntohs(ah->ar_op));
+
+ /* If it's for Ethernet and the lengths are OK,
+ * then log the ARP payload */
+ if (ah->ar_hrd == htons(1) &&
+ ah->ar_hln == ETH_ALEN &&
+ ah->ar_pln == sizeof(uint32_t)) {
+ struct arppayload _arpp, *ap;
+
+ ap = skb_header_pointer(skb, sizeof(u.arph),
+ sizeof(_arpp), &_arpp);
+ if (ap == NULL) {
+ printk(" INCOMPLETE ARP payload");
+ goto out;
+ }
+ printk(" ARP MAC SRC=");
+ print_MAC(ap->mac_src);
+ printk(" ARP IP SRC=%u.%u.%u.%u",
+ myNIPQUAD(ap->ip_src));
+ printk(" ARP MAC DST=");
+ print_MAC(ap->mac_dst);
+ printk(" ARP IP DST=%u.%u.%u.%u",
+ myNIPQUAD(ap->ip_dst));
+ }
+ }
+out:
+ printk("\n");
+ spin_unlock_bh(&ebt_log_lock);
+}
+
+static struct ebt_watcher log =
+{
+ .name = EBT_LOG_WATCHER,
+ .watcher = ebt_log,
+ .check = ebt_log_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_watcher(&log);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_watcher(&log);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c
new file mode 100644
index 00000000000..02c632b4d32
--- /dev/null
+++ b/net/bridge/netfilter/ebt_mark.c
@@ -0,0 +1,68 @@
+/*
+ * ebt_mark
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002
+ *
+ */
+
+/* The mark target can be used in any chain,
+ * I believe adding a mangle table just for marking is total overkill.
+ * Marking a frame doesn't really change anything in the frame anyway.
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+#include <linux/module.h>
+
+static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+ if ((*pskb)->nfmark != info->mark) {
+ (*pskb)->nfmark = info->mark;
+ (*pskb)->nfcache |= NFC_ALTERED;
+ }
+ return info->target;
+}
+
+static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target mark_target =
+{
+ .name = EBT_MARK_TARGET,
+ .target = ebt_target_mark,
+ .check = ebt_target_mark_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&mark_target);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&mark_target);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c
new file mode 100644
index 00000000000..625102de149
--- /dev/null
+++ b/net/bridge/netfilter/ebt_mark_m.c
@@ -0,0 +1,62 @@
+/*
+ * ebt_mark_m
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+#include <linux/module.h>
+
+static int ebt_filter_mark(const struct sk_buff *skb,
+ const struct net_device *in, const struct net_device *out, const void *data,
+ unsigned int datalen)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+ if (info->bitmask & EBT_MARK_OR)
+ return !(!!(skb->nfmark & info->mask) ^ info->invert);
+ return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
+}
+
+static int ebt_mark_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
+ return -EINVAL;
+ if (info->bitmask & ~EBT_MARK_MASK)
+ return -EINVAL;
+ if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
+ return -EINVAL;
+ if (!info->bitmask)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_match filter_mark =
+{
+ .name = EBT_MARK_MATCH,
+ .match = ebt_filter_mark,
+ .check = ebt_mark_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_mark);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_mark);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c
new file mode 100644
index 00000000000..ecd3b42b19b
--- /dev/null
+++ b/net/bridge/netfilter/ebt_pkttype.c
@@ -0,0 +1,59 @@
+/*
+ * ebt_pkttype
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_pkttype.h>
+#include <linux/module.h>
+
+static int ebt_filter_pkttype(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *data,
+ unsigned int datalen)
+{
+ struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
+
+ return (skb->pkt_type != info->pkt_type) ^ info->invert;
+}
+
+static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
+ return -EINVAL;
+ if (info->invert != 0 && info->invert != 1)
+ return -EINVAL;
+ /* Allow any pkt_type value */
+ return 0;
+}
+
+static struct ebt_match filter_pkttype =
+{
+ .name = EBT_PKTTYPE_MATCH,
+ .match = ebt_filter_pkttype,
+ .check = ebt_pkttype_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_pkttype);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_pkttype);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c
new file mode 100644
index 00000000000..1538b438666
--- /dev/null
+++ b/net/bridge/netfilter/ebt_redirect.c
@@ -0,0 +1,81 @@
+/*
+ * ebt_redirect
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_redirect.h>
+#include <linux/module.h>
+#include <net/sock.h>
+#include "../br_private.h"
+
+static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+ if (skb_shared(*pskb) || skb_cloned(*pskb)) {
+ struct sk_buff *nskb;
+
+ nskb = skb_copy(*pskb, GFP_ATOMIC);
+ if (!nskb)
+ return NF_DROP;
+ if ((*pskb)->sk)
+ skb_set_owner_w(nskb, (*pskb)->sk);
+ kfree_skb(*pskb);
+ *pskb = nskb;
+ }
+ if (hooknr != NF_BR_BROUTING)
+ memcpy(eth_hdr(*pskb)->h_dest,
+ in->br_port->br->dev->dev_addr, ETH_ALEN);
+ else
+ memcpy(eth_hdr(*pskb)->h_dest, in->dev_addr, ETH_ALEN);
+ (*pskb)->pkt_type = PACKET_HOST;
+ return info->target;
+}
+
+static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target redirect_target =
+{
+ .name = EBT_REDIRECT_TARGET,
+ .target = ebt_target_redirect,
+ .check = ebt_target_redirect_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&redirect_target);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&redirect_target);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c
new file mode 100644
index 00000000000..1529bdcb9a4
--- /dev/null
+++ b/net/bridge/netfilter/ebt_snat.c
@@ -0,0 +1,76 @@
+/*
+ * ebt_snat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+#include <net/sock.h>
+
+static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+ if (skb_shared(*pskb) || skb_cloned(*pskb)) {
+ struct sk_buff *nskb;
+
+ nskb = skb_copy(*pskb, GFP_ATOMIC);
+ if (!nskb)
+ return NF_DROP;
+ if ((*pskb)->sk)
+ skb_set_owner_w(nskb, (*pskb)->sk);
+ kfree_skb(*pskb);
+ *pskb = nskb;
+ }
+ memcpy(eth_hdr(*pskb)->h_source, info->mac, ETH_ALEN);
+ return info->target;
+}
+
+static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if (strcmp(tablename, "nat"))
+ return -EINVAL;
+ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target snat =
+{
+ .name = EBT_SNAT_TARGET,
+ .target = ebt_target_snat,
+ .check = ebt_target_snat_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&snat);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&snat);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c
new file mode 100644
index 00000000000..f8a8cdec16e
--- /dev/null
+++ b/net/bridge/netfilter/ebt_stp.c
@@ -0,0 +1,194 @@
+/*
+ * ebt_stp
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * Stephen Hemminger <shemminger@osdl.org>
+ *
+ * July, 2003
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_stp.h>
+#include <linux/module.h>
+
+#define BPDU_TYPE_CONFIG 0
+#define BPDU_TYPE_TCN 0x80
+
+struct stp_header {
+ uint8_t dsap;
+ uint8_t ssap;
+ uint8_t ctrl;
+ uint8_t pid;
+ uint8_t vers;
+ uint8_t type;
+};
+
+struct stp_config_pdu {
+ uint8_t flags;
+ uint8_t root[8];
+ uint8_t root_cost[4];
+ uint8_t sender[8];
+ uint8_t port[2];
+ uint8_t msg_age[2];
+ uint8_t max_age[2];
+ uint8_t hello_time[2];
+ uint8_t forward_delay[2];
+};
+
+#define NR16(p) (p[0] << 8 | p[1])
+#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
+
+static int ebt_filter_config(struct ebt_stp_info *info,
+ struct stp_config_pdu *stpc)
+{
+ struct ebt_stp_config_info *c;
+ uint16_t v16;
+ uint32_t v32;
+ int verdict, i;
+
+ c = &info->config;
+ if ((info->bitmask & EBT_STP_FLAGS) &&
+ FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_STP_ROOTPRIO) {
+ v16 = NR16(stpc->root);
+ if (FWINV(v16 < c->root_priol ||
+ v16 > c->root_priou, EBT_STP_ROOTPRIO))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_ROOTADDR) {
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
+ c->root_addrmsk[i];
+ if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_ROOTCOST) {
+ v32 = NR32(stpc->root_cost);
+ if (FWINV(v32 < c->root_costl ||
+ v32 > c->root_costu, EBT_STP_ROOTCOST))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_SENDERPRIO) {
+ v16 = NR16(stpc->sender);
+ if (FWINV(v16 < c->sender_priol ||
+ v16 > c->sender_priou, EBT_STP_SENDERPRIO))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_SENDERADDR) {
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
+ c->sender_addrmsk[i];
+ if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_PORT) {
+ v16 = NR16(stpc->port);
+ if (FWINV(v16 < c->portl ||
+ v16 > c->portu, EBT_STP_PORT))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_MSGAGE) {
+ v16 = NR16(stpc->msg_age);
+ if (FWINV(v16 < c->msg_agel ||
+ v16 > c->msg_ageu, EBT_STP_MSGAGE))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_MAXAGE) {
+ v16 = NR16(stpc->max_age);
+ if (FWINV(v16 < c->max_agel ||
+ v16 > c->max_ageu, EBT_STP_MAXAGE))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_HELLOTIME) {
+ v16 = NR16(stpc->hello_time);
+ if (FWINV(v16 < c->hello_timel ||
+ v16 > c->hello_timeu, EBT_STP_HELLOTIME))
+ return EBT_NOMATCH;
+ }
+ if (info->bitmask & EBT_STP_FWDD) {
+ v16 = NR16(stpc->forward_delay);
+ if (FWINV(v16 < c->forward_delayl ||
+ v16 > c->forward_delayu, EBT_STP_FWDD))
+ return EBT_NOMATCH;
+ }
+ return EBT_MATCH;
+}
+
+static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *data, unsigned int datalen)
+{
+ struct ebt_stp_info *info = (struct ebt_stp_info *)data;
+ struct stp_header _stph, *sp;
+ uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
+
+ sp = skb_header_pointer(skb, 0, sizeof(_stph), &_stph);
+ if (sp == NULL)
+ return EBT_NOMATCH;
+
+ /* The stp code only considers these */
+ if (memcmp(sp, header, sizeof(header)))
+ return EBT_NOMATCH;
+
+ if (info->bitmask & EBT_STP_TYPE
+ && FWINV(info->type != sp->type, EBT_STP_TYPE))
+ return EBT_NOMATCH;
+
+ if (sp->type == BPDU_TYPE_CONFIG &&
+ info->bitmask & EBT_STP_CONFIG_MASK) {
+ struct stp_config_pdu _stpc, *st;
+
+ st = skb_header_pointer(skb, sizeof(_stph),
+ sizeof(_stpc), &_stpc);
+ if (st == NULL)
+ return EBT_NOMATCH;
+ return ebt_filter_config(info, st);
+ }
+ return EBT_MATCH;
+}
+
+static int ebt_stp_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_stp_info *info = (struct ebt_stp_info *)data;
+ int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
+ uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
+ !(info->bitmask & EBT_STP_MASK))
+ return -EINVAL;
+ if (datalen != len)
+ return -EINVAL;
+ /* Make sure the match only receives stp frames */
+ if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
+ memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct ebt_match filter_stp =
+{
+ .name = EBT_STP_MATCH,
+ .match = ebt_filter_stp,
+ .check = ebt_stp_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_stp);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_stp);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
new file mode 100644
index 00000000000..01af4fcef26
--- /dev/null
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -0,0 +1,295 @@
+/*
+ * netfilter module for userspace bridged Ethernet frames logging daemons
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * November, 2004
+ *
+ * Based on ipt_ULOG.c, which is
+ * (C) 2000-2002 by Harald Welte <laforge@netfilter.org>
+ *
+ * This module accepts two parameters:
+ *
+ * nlbufsiz:
+ * The parameter specifies how big the buffer for each netlink multicast
+ * group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will
+ * get accumulated in the kernel until they are sent to userspace. It is
+ * NOT possible to allocate more than 128kB, and it is strongly discouraged,
+ * because atomically allocating 128kB inside the network rx softirq is not
+ * reliable. Please also keep in mind that this buffer size is allocated for
+ * each nlgroup you are using, so the total kernel memory usage increases
+ * by that factor.
+ *
+ * flushtimeout:
+ * Specify, after how many hundredths of a second the queue should be
+ * flushed even if it is not full yet.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/netlink.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ulog.h>
+#include <net/sock.h>
+#include "../br_private.h"
+
+#define PRINTR(format, args...) do { if (net_ratelimit()) \
+ printk(format , ## args); } while (0)
+
+static unsigned int nlbufsiz = 4096;
+module_param(nlbufsiz, uint, 0600);
+MODULE_PARM_DESC(nlbufsiz, "netlink buffer size (number of bytes) "
+ "(defaults to 4096)");
+
+static unsigned int flushtimeout = 10;
+module_param(flushtimeout, uint, 0600);
+MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths ofa second) "
+ "(defaults to 10)");
+
+typedef struct {
+ unsigned int qlen; /* number of nlmsgs' in the skb */
+ struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */
+ struct sk_buff *skb; /* the pre-allocated skb */
+ struct timer_list timer; /* the timer function */
+ spinlock_t lock; /* the per-queue lock */
+} ebt_ulog_buff_t;
+
+static ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS];
+static struct sock *ebtulognl;
+
+/* send one ulog_buff_t to userspace */
+static void ulog_send(unsigned int nlgroup)
+{
+ ebt_ulog_buff_t *ub = &ulog_buffers[nlgroup];
+
+ if (timer_pending(&ub->timer))
+ del_timer(&ub->timer);
+
+ /* last nlmsg needs NLMSG_DONE */
+ if (ub->qlen > 1)
+ ub->lastnlh->nlmsg_type = NLMSG_DONE;
+
+ NETLINK_CB(ub->skb).dst_groups = 1 << nlgroup;
+ netlink_broadcast(ebtulognl, ub->skb, 0, 1 << nlgroup, GFP_ATOMIC);
+
+ ub->qlen = 0;
+ ub->skb = NULL;
+}
+
+/* timer function to flush queue in flushtimeout time */
+static void ulog_timer(unsigned long data)
+{
+ spin_lock_bh(&ulog_buffers[data].lock);
+ if (ulog_buffers[data].skb)
+ ulog_send(data);
+ spin_unlock_bh(&ulog_buffers[data].lock);
+}
+
+static struct sk_buff *ulog_alloc_skb(unsigned int size)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(nlbufsiz, GFP_ATOMIC);
+ if (!skb) {
+ PRINTR(KERN_ERR "ebt_ulog: can't alloc whole buffer "
+ "of size %ub!\n", nlbufsiz);
+ if (size < nlbufsiz) {
+ /* try to allocate only as much as we need for
+ * current packet */
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ PRINTR(KERN_ERR "ebt_ulog: can't even allocate "
+ "buffer of size %ub\n", size);
+ }
+ }
+
+ return skb;
+}
+
+static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ ebt_ulog_packet_msg_t *pm;
+ size_t size, copy_len;
+ struct nlmsghdr *nlh;
+ struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
+ unsigned int group = uloginfo->nlgroup;
+ ebt_ulog_buff_t *ub = &ulog_buffers[group];
+ spinlock_t *lock = &ub->lock;
+
+ if ((uloginfo->cprange == 0) ||
+ (uloginfo->cprange > skb->len + ETH_HLEN))
+ copy_len = skb->len + ETH_HLEN;
+ else
+ copy_len = uloginfo->cprange;
+
+ size = NLMSG_SPACE(sizeof(*pm) + copy_len);
+ if (size > nlbufsiz) {
+ PRINTR("ebt_ulog: Size %Zd needed, but nlbufsiz=%d\n",
+ size, nlbufsiz);
+ return;
+ }
+
+ spin_lock_bh(lock);
+
+ if (!ub->skb) {
+ if (!(ub->skb = ulog_alloc_skb(size)))
+ goto alloc_failure;
+ } else if (size > skb_tailroom(ub->skb)) {
+ ulog_send(group);
+
+ if (!(ub->skb = ulog_alloc_skb(size)))
+ goto alloc_failure;
+ }
+
+ nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, 0,
+ size - NLMSG_ALIGN(sizeof(*nlh)));
+ ub->qlen++;
+
+ pm = NLMSG_DATA(nlh);
+
+ /* Fill in the ulog data */
+ pm->version = EBT_ULOG_VERSION;
+ do_gettimeofday(&pm->stamp);
+ if (ub->qlen == 1)
+ ub->skb->stamp = pm->stamp;
+ pm->data_len = copy_len;
+ pm->mark = skb->nfmark;
+ pm->hook = hooknr;
+ if (uloginfo->prefix != NULL)
+ strcpy(pm->prefix, uloginfo->prefix);
+ else
+ *(pm->prefix) = '\0';
+
+ if (in) {
+ strcpy(pm->physindev, in->name);
+ /* If in isn't a bridge, then physindev==indev */
+ if (in->br_port)
+ strcpy(pm->indev, in->br_port->br->dev->name);
+ else
+ strcpy(pm->indev, in->name);
+ } else
+ pm->indev[0] = pm->physindev[0] = '\0';
+
+ if (out) {
+ /* If out exists, then out is a bridge port */
+ strcpy(pm->physoutdev, out->name);
+ strcpy(pm->outdev, out->br_port->br->dev->name);
+ } else
+ pm->outdev[0] = pm->physoutdev[0] = '\0';
+
+ if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
+ BUG();
+
+ if (ub->qlen > 1)
+ ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;
+
+ ub->lastnlh = nlh;
+
+ if (ub->qlen >= uloginfo->qthreshold)
+ ulog_send(group);
+ else if (!timer_pending(&ub->timer)) {
+ ub->timer.expires = jiffies + flushtimeout * HZ / 100;
+ add_timer(&ub->timer);
+ }
+
+unlock:
+ spin_unlock_bh(lock);
+
+ return;
+
+nlmsg_failure:
+ printk(KERN_CRIT "ebt_ulog: error during NLMSG_PUT. This should "
+ "not happen, please report to author.\n");
+ goto unlock;
+alloc_failure:
+ goto unlock;
+}
+
+static int ebt_ulog_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
+
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_ulog_info)) ||
+ uloginfo->nlgroup > 31)
+ return -EINVAL;
+
+ uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0';
+
+ if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN)
+ uloginfo->qthreshold = EBT_ULOG_MAX_QLEN;
+
+ return 0;
+}
+
+static struct ebt_watcher ulog = {
+ .name = EBT_ULOG_WATCHER,
+ .watcher = ebt_ulog,
+ .check = ebt_ulog_check,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ int i, ret = 0;
+
+ if (nlbufsiz >= 128*1024) {
+ printk(KERN_NOTICE "ebt_ulog: Netlink buffer has to be <= 128kB,"
+ " please try a smaller nlbufsiz parameter.\n");
+ return -EINVAL;
+ }
+
+ /* initialize ulog_buffers */
+ for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
+ init_timer(&ulog_buffers[i].timer);
+ ulog_buffers[i].timer.function = ulog_timer;
+ ulog_buffers[i].timer.data = i;
+ spin_lock_init(&ulog_buffers[i].lock);
+ }
+
+ ebtulognl = netlink_kernel_create(NETLINK_NFLOG, NULL);
+ if (!ebtulognl)
+ ret = -ENOMEM;
+ else if ((ret = ebt_register_watcher(&ulog)))
+ sock_release(ebtulognl->sk_socket);
+
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ ebt_ulog_buff_t *ub;
+ int i;
+
+ ebt_unregister_watcher(&ulog);
+ for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
+ ub = &ulog_buffers[i];
+ if (timer_pending(&ub->timer))
+ del_timer(&ub->timer);
+ spin_lock_bh(&ub->lock);
+ if (ub->skb) {
+ kfree_skb(ub->skb);
+ ub->skb = NULL;
+ }
+ spin_unlock_bh(&ub->lock);
+ }
+ sock_release(ebtulognl->sk_socket);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>");
+MODULE_DESCRIPTION("ebtables userspace logging module for bridged Ethernet"
+ " frames");
diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c
new file mode 100644
index 00000000000..db60d734908
--- /dev/null
+++ b/net/bridge/netfilter/ebt_vlan.c
@@ -0,0 +1,195 @@
+/*
+ * Description: EBTables 802.1Q match extension kernelspace module.
+ * Authors: Nick Fedchik <nick@fedchik.org.ua>
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_vlan.h>
+
+static int debug;
+#define MODULE_VERS "0.6"
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
+MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
+MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
+ MODULE_VERS);
+MODULE_LICENSE("GPL");
+
+
+#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
+#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
+#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
+#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
+#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return EBT_NOMATCH;}
+
+static int
+ebt_filter_vlan(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+ struct vlan_hdr _frame, *fp;
+
+ unsigned short TCI; /* Whole TCI, given from parsed frame */
+ unsigned short id; /* VLAN ID, given from frame TCI */
+ unsigned char prio; /* user_priority, given from frame TCI */
+ /* VLAN encapsulated Type/Length field, given from orig frame */
+ unsigned short encap;
+
+ fp = skb_header_pointer(skb, 0, sizeof(_frame), &_frame);
+ if (fp == NULL)
+ return EBT_NOMATCH;
+
+ /* Tag Control Information (TCI) consists of the following elements:
+ * - User_priority. The user_priority field is three bits in length,
+ * interpreted as a binary number.
+ * - Canonical Format Indicator (CFI). The Canonical Format Indicator
+ * (CFI) is a single bit flag value. Currently ignored.
+ * - VLAN Identifier (VID). The VID is encoded as
+ * an unsigned binary number. */
+ TCI = ntohs(fp->h_vlan_TCI);
+ id = TCI & VLAN_VID_MASK;
+ prio = (TCI >> 13) & 0x7;
+ encap = fp->h_vlan_encapsulated_proto;
+
+ /* Checking VLAN Identifier (VID) */
+ if (GET_BITMASK(EBT_VLAN_ID))
+ EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
+
+ /* Checking user_priority */
+ if (GET_BITMASK(EBT_VLAN_PRIO))
+ EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
+
+ /* Checking Encapsulated Proto (Length/Type) field */
+ if (GET_BITMASK(EBT_VLAN_ENCAP))
+ EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
+
+ return EBT_MATCH;
+}
+
+static int
+ebt_check_vlan(const char *tablename,
+ unsigned int hooknr,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+
+ /* Parameters buffer overflow check */
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
+ DEBUG_MSG
+ ("passed size %d is not eq to ebt_vlan_info (%Zd)\n",
+ datalen, sizeof(struct ebt_vlan_info));
+ return -EINVAL;
+ }
+
+ /* Is it 802.1Q frame checked? */
+ if (e->ethproto != htons(ETH_P_8021Q)) {
+ DEBUG_MSG
+ ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+ (unsigned short) ntohs(e->ethproto));
+ return -EINVAL;
+ }
+
+ /* Check for bitmask range
+ * True if even one bit is out of mask */
+ if (info->bitmask & ~EBT_VLAN_MASK) {
+ DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
+ info->bitmask, EBT_VLAN_MASK);
+ return -EINVAL;
+ }
+
+ /* Check for inversion flags range */
+ if (info->invflags & ~EBT_VLAN_MASK) {
+ DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
+ info->invflags, EBT_VLAN_MASK);
+ return -EINVAL;
+ }
+
+ /* Reserved VLAN ID (VID) values
+ * -----------------------------
+ * 0 - The null VLAN ID.
+ * 1 - The default Port VID (PVID)
+ * 0x0FFF - Reserved for implementation use.
+ * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096. */
+ if (GET_BITMASK(EBT_VLAN_ID)) {
+ if (!!info->id) { /* if id!=0 => check vid range */
+ if (info->id > VLAN_GROUP_ARRAY_LEN) {
+ DEBUG_MSG
+ ("id %d is out of range (1-4096)\n",
+ info->id);
+ return -EINVAL;
+ }
+ /* Note: This is valid VLAN-tagged frame point.
+ * Any value of user_priority are acceptable,
+ * but should be ignored according to 802.1Q Std.
+ * So we just drop the prio flag. */
+ info->bitmask &= ~EBT_VLAN_PRIO;
+ }
+ /* Else, id=0 (null VLAN ID) => user_priority range (any?) */
+ }
+
+ if (GET_BITMASK(EBT_VLAN_PRIO)) {
+ if ((unsigned char) info->prio > 7) {
+ DEBUG_MSG("prio %d is out of range (0-7)\n",
+ info->prio);
+ return -EINVAL;
+ }
+ }
+ /* Check for encapsulated proto range - it is possible to be
+ * any value for u_short range.
+ * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS */
+ if (GET_BITMASK(EBT_VLAN_ENCAP)) {
+ if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
+ DEBUG_MSG
+ ("encap frame length %d is less than minimal\n",
+ ntohs(info->encap));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct ebt_match filter_vlan = {
+ .name = EBT_VLAN_MATCH,
+ .match = ebt_filter_vlan,
+ .check = ebt_check_vlan,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ DEBUG_MSG("ebtables 802.1Q extension module v"
+ MODULE_VERS "\n");
+ DEBUG_MSG("module debug=%d\n", !!debug);
+ return ebt_register_match(&filter_vlan);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_vlan);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
new file mode 100644
index 00000000000..1767c94cd3d
--- /dev/null
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -0,0 +1,86 @@
+/*
+ * ebtable_broute
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ *
+ * This table lets you choose between routing and bridging for frames
+ * entering on a bridge enslaved nic. This table is traversed before any
+ * other ebtables table. See net/bridge/br_input.c.
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+#include <linux/if_bridge.h>
+
+/* EBT_ACCEPT means the frame will be bridged
+ * EBT_DROP means the frame will be routed
+ */
+static struct ebt_entries initial_chain = {
+ .name = "BROUTING",
+ .policy = EBT_ACCEPT,
+};
+
+static struct ebt_replace initial_table =
+{
+ .name = "broute",
+ .valid_hooks = 1 << NF_BR_BROUTING,
+ .entries_size = sizeof(struct ebt_entries),
+ .hook_entry = {
+ [NF_BR_BROUTING] = &initial_chain,
+ },
+ .entries = (char *)&initial_chain,
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+ if (valid_hooks & ~(1 << NF_BR_BROUTING))
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_table broute_table =
+{
+ .name = "broute",
+ .table = &initial_table,
+ .valid_hooks = 1 << NF_BR_BROUTING,
+ .lock = RW_LOCK_UNLOCKED,
+ .check = check,
+ .me = THIS_MODULE,
+};
+
+static int ebt_broute(struct sk_buff **pskb)
+{
+ int ret;
+
+ ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
+ &broute_table);
+ if (ret == NF_DROP)
+ return 1; /* route it */
+ return 0; /* bridge it */
+}
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = ebt_register_table(&broute_table);
+ if (ret < 0)
+ return ret;
+ /* see br_input.c */
+ br_should_route_hook = ebt_broute;
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ br_should_route_hook = NULL;
+ synchronize_net();
+ ebt_unregister_table(&broute_table);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
new file mode 100644
index 00000000000..c18666e0392
--- /dev/null
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -0,0 +1,123 @@
+/*
+ * ebtable_filter
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+ (1 << NF_BR_LOCAL_OUT))
+
+static struct ebt_entries initial_chains[] =
+{
+ {
+ .name = "INPUT",
+ .policy = EBT_ACCEPT,
+ },
+ {
+ .name = "FORWARD",
+ .policy = EBT_ACCEPT,
+ },
+ {
+ .name = "OUTPUT",
+ .policy = EBT_ACCEPT,
+ },
+};
+
+static struct ebt_replace initial_table =
+{
+ .name = "filter",
+ .valid_hooks = FILTER_VALID_HOOKS,
+ .entries_size = 3 * sizeof(struct ebt_entries),
+ .hook_entry = {
+ [NF_BR_LOCAL_IN] = &initial_chains[0],
+ [NF_BR_FORWARD] = &initial_chains[1],
+ [NF_BR_LOCAL_OUT] = &initial_chains[2],
+ },
+ .entries = (char *)initial_chains,
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+ if (valid_hooks & ~FILTER_VALID_HOOKS)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_table frame_filter =
+{
+ .name = "filter",
+ .table = &initial_table,
+ .valid_hooks = FILTER_VALID_HOOKS,
+ .lock = RW_LOCK_UNLOCKED,
+ .check = check,
+ .me = THIS_MODULE,
+};
+
+static unsigned int
+ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
+ const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &frame_filter);
+}
+
+static struct nf_hook_ops ebt_ops_filter[] = {
+ {
+ .hook = ebt_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_LOCAL_IN,
+ .priority = NF_BR_PRI_FILTER_BRIDGED,
+ },
+ {
+ .hook = ebt_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_FORWARD,
+ .priority = NF_BR_PRI_FILTER_BRIDGED,
+ },
+ {
+ .hook = ebt_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_LOCAL_OUT,
+ .priority = NF_BR_PRI_FILTER_OTHER,
+ },
+};
+
+static int __init init(void)
+{
+ int i, j, ret;
+
+ ret = ebt_register_table(&frame_filter);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
+ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
+ goto cleanup;
+ return ret;
+cleanup:
+ for (j = 0; j < i; j++)
+ nf_unregister_hook(&ebt_ops_filter[j]);
+ ebt_unregister_table(&frame_filter);
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
+ nf_unregister_hook(&ebt_ops_filter[i]);
+ ebt_unregister_table(&frame_filter);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
new file mode 100644
index 00000000000..828cac2cc4a
--- /dev/null
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -0,0 +1,130 @@
+/*
+ * ebtable_nat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+
+#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+ (1 << NF_BR_POST_ROUTING))
+
+static struct ebt_entries initial_chains[] =
+{
+ {
+ .name = "PREROUTING",
+ .policy = EBT_ACCEPT,
+ },
+ {
+ .name = "OUTPUT",
+ .policy = EBT_ACCEPT,
+ },
+ {
+ .name = "POSTROUTING",
+ .policy = EBT_ACCEPT,
+ }
+};
+
+static struct ebt_replace initial_table =
+{
+ .name = "nat",
+ .valid_hooks = NAT_VALID_HOOKS,
+ .entries_size = 3 * sizeof(struct ebt_entries),
+ .hook_entry = {
+ [NF_BR_PRE_ROUTING] = &initial_chains[0],
+ [NF_BR_LOCAL_OUT] = &initial_chains[1],
+ [NF_BR_POST_ROUTING] = &initial_chains[2],
+ },
+ .entries = (char *)initial_chains,
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+ if (valid_hooks & ~NAT_VALID_HOOKS)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_table frame_nat =
+{
+ .name = "nat",
+ .table = &initial_table,
+ .valid_hooks = NAT_VALID_HOOKS,
+ .lock = RW_LOCK_UNLOCKED,
+ .check = check,
+ .me = THIS_MODULE,
+};
+
+static unsigned int
+ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
+ , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static unsigned int
+ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
+ , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static struct nf_hook_ops ebt_ops_nat[] = {
+ {
+ .hook = ebt_nat_dst,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_LOCAL_OUT,
+ .priority = NF_BR_PRI_NAT_DST_OTHER,
+ },
+ {
+ .hook = ebt_nat_src,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_POST_ROUTING,
+ .priority = NF_BR_PRI_NAT_SRC,
+ },
+ {
+ .hook = ebt_nat_dst,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_PRE_ROUTING,
+ .priority = NF_BR_PRI_NAT_DST_BRIDGED,
+ },
+};
+
+static int __init init(void)
+{
+ int i, ret, j;
+
+ ret = ebt_register_table(&frame_nat);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
+ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
+ goto cleanup;
+ return ret;
+cleanup:
+ for (j = 0; j < i; j++)
+ nf_unregister_hook(&ebt_ops_nat[j]);
+ ebt_unregister_table(&frame_nat);
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
+ nf_unregister_hook(&ebt_ops_nat[i]);
+ ebt_unregister_table(&frame_nat);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
new file mode 100644
index 00000000000..18ebc664769
--- /dev/null
+++ b/net/bridge/netfilter/ebtables.c
@@ -0,0 +1,1507 @@
+/*
+ * ebtables
+ *
+ * Author:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * ebtables.c,v 2.0, July, 2002
+ *
+ * This code is stongly inspired on the iptables code which is
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/* used for print_string */
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <net/sock.h>
+/* needed for logical [in,out]-dev filtering */
+#include "../br_private.h"
+
+/* list_named_find */
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+/* use this for remote debugging
+ * Copyright (C) 1998 by Ori Pomerantz
+ * Print the string to the appropriate tty, the one
+ * the current task uses
+ */
+static void print_string(char *str)
+{
+ struct tty_struct *my_tty;
+
+ /* The tty for the current task */
+ my_tty = current->signal->tty;
+ if (my_tty != NULL) {
+ my_tty->driver->write(my_tty, 0, str, strlen(str));
+ my_tty->driver->write(my_tty, 0, "\015\012", 2);
+ }
+}
+
+#define BUGPRINT(args) print_string(args);
+#else
+#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
+ "report to author: "format, ## args)
+/* #define BUGPRINT(format, args...) */
+#endif
+#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
+ ": out of memory: "format, ## args)
+/* #define MEMPRINT(format, args...) */
+
+
+
+/*
+ * Each cpu has its own set of counters, so there is no need for write_lock in
+ * the softirq
+ * For reading or updating the counters, the user context needs to
+ * get a write_lock
+ */
+
+/* The size of each set of counters is altered to get cache alignment */
+#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
+#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
+#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
+ COUNTER_OFFSET(n) * cpu))
+
+
+
+static DECLARE_MUTEX(ebt_mutex);
+static LIST_HEAD(ebt_tables);
+static LIST_HEAD(ebt_targets);
+static LIST_HEAD(ebt_matches);
+static LIST_HEAD(ebt_watchers);
+
+static struct ebt_target ebt_standard_target =
+{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
+
+static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+ const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in,
+ const struct net_device *out)
+{
+ w->u.watcher->watcher(skb, hooknr, in, out, w->data,
+ w->watcher_size);
+ /* watchers don't give a verdict */
+ return 0;
+}
+
+static inline int ebt_do_match (struct ebt_entry_match *m,
+ const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out)
+{
+ return m->u.match->match(skb, in, out, m->data,
+ m->match_size);
+}
+
+static inline int ebt_dev_check(char *entry, const struct net_device *device)
+{
+ int i = 0;
+ char *devname = device->name;
+
+ if (*entry == '\0')
+ return 0;
+ if (!device)
+ return 1;
+ /* 1 is the wildcard token */
+ while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i])
+ i++;
+ return (devname[i] != entry[i] && entry[i] != 1);
+}
+
+#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+/* process standard matches */
+static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
+ const struct net_device *in, const struct net_device *out)
+{
+ int verdict, i;
+
+ if (e->bitmask & EBT_802_3) {
+ if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+ return 1;
+ } else if (!(e->bitmask & EBT_NOPROTO) &&
+ FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+ return 1;
+
+ if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
+ return 1;
+ if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
+ return 1;
+ if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
+ e->logical_in, in->br_port->br->dev), EBT_ILOGICALIN))
+ return 1;
+ if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
+ e->logical_out, out->br_port->br->dev), EBT_ILOGICALOUT))
+ return 1;
+
+ if (e->bitmask & EBT_SOURCEMAC) {
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
+ e->sourcemsk[i];
+ if (FWINV2(verdict != 0, EBT_ISOURCE) )
+ return 1;
+ }
+ if (e->bitmask & EBT_DESTMAC) {
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (h->h_dest[i] ^ e->destmac[i]) &
+ e->destmsk[i];
+ if (FWINV2(verdict != 0, EBT_IDEST) )
+ return 1;
+ }
+ return 0;
+}
+
+/* Do some firewalling */
+unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ struct ebt_table *table)
+{
+ int i, nentries;
+ struct ebt_entry *point;
+ struct ebt_counter *counter_base, *cb_base;
+ struct ebt_entry_target *t;
+ int verdict, sp = 0;
+ struct ebt_chainstack *cs;
+ struct ebt_entries *chaininfo;
+ char *base;
+ struct ebt_table_info *private;
+
+ read_lock_bh(&table->lock);
+ private = table->private;
+ cb_base = COUNTER_BASE(private->counters, private->nentries,
+ smp_processor_id());
+ if (private->chainstack)
+ cs = private->chainstack[smp_processor_id()];
+ else
+ cs = NULL;
+ chaininfo = private->hook_entry[hook];
+ nentries = private->hook_entry[hook]->nentries;
+ point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+ counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+ /* base for chain jumps */
+ base = private->entries;
+ i = 0;
+ while (i < nentries) {
+ if (ebt_basic_match(point, eth_hdr(*pskb), in, out))
+ goto letscontinue;
+
+ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
+ goto letscontinue;
+
+ /* increase counter */
+ (*(counter_base + i)).pcnt++;
+ (*(counter_base + i)).bcnt+=(**pskb).len;
+
+ /* these should only watch: not modify, nor tell us
+ what to do with the packet */
+ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, hook, in,
+ out);
+
+ t = (struct ebt_entry_target *)
+ (((char *)point) + point->target_offset);
+ /* standard target */
+ if (!t->u.target->target)
+ verdict = ((struct ebt_standard_target *)t)->verdict;
+ else
+ verdict = t->u.target->target(pskb, hook,
+ in, out, t->data, t->target_size);
+ if (verdict == EBT_ACCEPT) {
+ read_unlock_bh(&table->lock);
+ return NF_ACCEPT;
+ }
+ if (verdict == EBT_DROP) {
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+ if (verdict == EBT_RETURN) {
+letsreturn:
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (sp == 0) {
+ BUGPRINT("RETURN on base chain");
+ /* act like this is EBT_CONTINUE */
+ goto letscontinue;
+ }
+#endif
+ sp--;
+ /* put all the local variables right */
+ i = cs[sp].n;
+ chaininfo = cs[sp].chaininfo;
+ nentries = chaininfo->nentries;
+ point = cs[sp].e;
+ counter_base = cb_base +
+ chaininfo->counter_offset;
+ continue;
+ }
+ if (verdict == EBT_CONTINUE)
+ goto letscontinue;
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (verdict < 0) {
+ BUGPRINT("bogus standard verdict\n");
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+#endif
+ /* jump to a udc */
+ cs[sp].n = i + 1;
+ cs[sp].chaininfo = chaininfo;
+ cs[sp].e = (struct ebt_entry *)
+ (((char *)point) + point->next_offset);
+ i = 0;
+ chaininfo = (struct ebt_entries *) (base + verdict);
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (chaininfo->distinguisher) {
+ BUGPRINT("jump to non-chain\n");
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+#endif
+ nentries = chaininfo->nentries;
+ point = (struct ebt_entry *)chaininfo->data;
+ counter_base = cb_base + chaininfo->counter_offset;
+ sp++;
+ continue;
+letscontinue:
+ point = (struct ebt_entry *)
+ (((char *)point) + point->next_offset);
+ i++;
+ }
+
+ /* I actually like this :) */
+ if (chaininfo->policy == EBT_RETURN)
+ goto letsreturn;
+ if (chaininfo->policy == EBT_ACCEPT) {
+ read_unlock_bh(&table->lock);
+ return NF_ACCEPT;
+ }
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+}
+
+/* If it succeeds, returns element and locks mutex */
+static inline void *
+find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
+ struct semaphore *mutex)
+{
+ void *ret;
+
+ *error = down_interruptible(mutex);
+ if (*error != 0)
+ return NULL;
+
+ ret = list_named_find(head, name);
+ if (!ret) {
+ *error = -ENOENT;
+ up(mutex);
+ }
+ return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
+ int *error, struct semaphore *mutex)
+{
+ void *ret;
+
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ if (!ret) {
+ request_module("%s%s", prefix, name);
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ }
+ return ret;
+}
+#endif
+
+static inline struct ebt_table *
+find_table_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
+}
+
+static inline struct ebt_match *
+find_match_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_watcher *
+find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_target *
+find_target_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
+}
+
+static inline int
+ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+ const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+ struct ebt_match *match;
+ int ret;
+
+ if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
+ ((char *)e) + e->watchers_offset)
+ return -EINVAL;
+ match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+ if (!match)
+ return ret;
+ m->u.match = match;
+ if (!try_module_get(match->me)) {
+ up(&ebt_mutex);
+ return -ENOENT;
+ }
+ up(&ebt_mutex);
+ if (match->check &&
+ match->check(name, hookmask, e, m->data, m->match_size) != 0) {
+ BUGPRINT("match->check failed\n");
+ module_put(match->me);
+ return -EINVAL;
+ }
+ (*cnt)++;
+ return 0;
+}
+
+static inline int
+ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
+ const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+ struct ebt_watcher *watcher;
+ int ret;
+
+ if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
+ ((char *)e) + e->target_offset)
+ return -EINVAL;
+ watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+ if (!watcher)
+ return ret;
+ w->u.watcher = watcher;
+ if (!try_module_get(watcher->me)) {
+ up(&ebt_mutex);
+ return -ENOENT;
+ }
+ up(&ebt_mutex);
+ if (watcher->check &&
+ watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
+ BUGPRINT("watcher->check failed\n");
+ module_put(watcher->me);
+ return -EINVAL;
+ }
+ (*cnt)++;
+ return 0;
+}
+
+/*
+ * this one is very careful, as it is the first function
+ * to parse the userspace data
+ */
+static inline int
+ebt_check_entry_size_and_hooks(struct ebt_entry *e,
+ struct ebt_table_info *newinfo, char *base, char *limit,
+ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
+ unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+{
+ int i;
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if ( (char *)hook_entries[i] - base ==
+ (char *)e - newinfo->entries)
+ break;
+ }
+ /* beginning of a new chain
+ if i == NF_BR_NUMHOOKS it must be a user defined chain */
+ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
+ /* we make userspace set this right,
+ so there is no misunderstanding */
+ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
+ "in distinguisher\n");
+ return -EINVAL;
+ }
+ /* this checks if the previous chain has as many entries
+ as it said it has */
+ if (*n != *cnt) {
+ BUGPRINT("nentries does not equal the nr of entries "
+ "in the chain\n");
+ return -EINVAL;
+ }
+ /* before we look at the struct, be sure it is not too big */
+ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
+ > limit) {
+ BUGPRINT("entries_size too small\n");
+ return -EINVAL;
+ }
+ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
+ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
+ /* only RETURN from udc */
+ if (i != NF_BR_NUMHOOKS ||
+ ((struct ebt_entries *)e)->policy != EBT_RETURN) {
+ BUGPRINT("bad policy\n");
+ return -EINVAL;
+ }
+ }
+ if (i == NF_BR_NUMHOOKS) /* it's a user defined chain */
+ (*udc_cnt)++;
+ else
+ newinfo->hook_entry[i] = (struct ebt_entries *)e;
+ if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
+ BUGPRINT("counter_offset != totalcnt");
+ return -EINVAL;
+ }
+ *n = ((struct ebt_entries *)e)->nentries;
+ *cnt = 0;
+ return 0;
+ }
+ /* a plain old entry, heh */
+ if (sizeof(struct ebt_entry) > e->watchers_offset ||
+ e->watchers_offset > e->target_offset ||
+ e->target_offset >= e->next_offset) {
+ BUGPRINT("entry offsets not in right order\n");
+ return -EINVAL;
+ }
+ /* this is not checked anywhere else */
+ if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
+ BUGPRINT("target size too small\n");
+ return -EINVAL;
+ }
+
+ (*cnt)++;
+ (*totalcnt)++;
+ return 0;
+}
+
+struct ebt_cl_stack
+{
+ struct ebt_chainstack cs;
+ int from;
+ unsigned int hookmask;
+};
+
+/*
+ * we need these positions to check that the jumps to a different part of the
+ * entries is a jump to the beginning of a new chain.
+ */
+static inline int
+ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
+ struct ebt_cl_stack *udc)
+{
+ int i;
+
+ /* we're only interested in chain starts */
+ if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+ return 0;
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
+ break;
+ }
+ /* only care about udc */
+ if (i != NF_BR_NUMHOOKS)
+ return 0;
+
+ udc[*n].cs.chaininfo = (struct ebt_entries *)e;
+ /* these initialisations are depended on later in check_chainloops() */
+ udc[*n].cs.n = 0;
+ udc[*n].hookmask = 0;
+
+ (*n)++;
+ return 0;
+}
+
+static inline int
+ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+{
+ if (i && (*i)-- == 0)
+ return 1;
+ if (m->u.match->destroy)
+ m->u.match->destroy(m->data, m->match_size);
+ module_put(m->u.match->me);
+
+ return 0;
+}
+
+static inline int
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+{
+ if (i && (*i)-- == 0)
+ return 1;
+ if (w->u.watcher->destroy)
+ w->u.watcher->destroy(w->data, w->watcher_size);
+ module_put(w->u.watcher->me);
+
+ return 0;
+}
+
+static inline int
+ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+{
+ struct ebt_entry_target *t;
+
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+ /* we're done */
+ if (cnt && (*cnt)-- == 0)
+ return 1;
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ if (t->u.target->destroy)
+ t->u.target->destroy(t->data, t->target_size);
+ module_put(t->u.target->me);
+
+ return 0;
+}
+
+static inline int
+ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ const char *name, unsigned int *cnt, unsigned int valid_hooks,
+ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+{
+ struct ebt_entry_target *t;
+ struct ebt_target *target;
+ unsigned int i, j, hook = 0, hookmask = 0;
+ int ret;
+
+ /* don't mess with the struct ebt_entries */
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+
+ if (e->bitmask & ~EBT_F_MASK) {
+ BUGPRINT("Unknown flag for bitmask\n");
+ return -EINVAL;
+ }
+ if (e->invflags & ~EBT_INV_MASK) {
+ BUGPRINT("Unknown flag for inv bitmask\n");
+ return -EINVAL;
+ }
+ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
+ BUGPRINT("NOPROTO & 802_3 not allowed\n");
+ return -EINVAL;
+ }
+ /* what hook do we belong to? */
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if ((char *)newinfo->hook_entry[i] < (char *)e)
+ hook = i;
+ else
+ break;
+ }
+ /* (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
+ a base chain */
+ if (i < NF_BR_NUMHOOKS)
+ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ else {
+ for (i = 0; i < udc_cnt; i++)
+ if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
+ break;
+ if (i == 0)
+ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ else
+ hookmask = cl_s[i - 1].hookmask;
+ }
+ i = 0;
+ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
+ if (ret != 0)
+ goto cleanup_matches;
+ j = 0;
+ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
+ if (ret != 0)
+ goto cleanup_watchers;
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+ if (!target)
+ goto cleanup_watchers;
+ if (!try_module_get(target->me)) {
+ up(&ebt_mutex);
+ ret = -ENOENT;
+ goto cleanup_watchers;
+ }
+ up(&ebt_mutex);
+
+ t->u.target = target;
+ if (t->u.target == &ebt_standard_target) {
+ if (e->target_offset + sizeof(struct ebt_standard_target) >
+ e->next_offset) {
+ BUGPRINT("Standard target size too big\n");
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ if (((struct ebt_standard_target *)t)->verdict <
+ -NUM_STANDARD_TARGETS) {
+ BUGPRINT("Invalid standard target\n");
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ } else if ((e->target_offset + t->target_size +
+ sizeof(struct ebt_entry_target) > e->next_offset) ||
+ (t->u.target->check &&
+ t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
+ module_put(t->u.target->me);
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ (*cnt)++;
+ return 0;
+cleanup_watchers:
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+cleanup_matches:
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+ return ret;
+}
+
+/*
+ * checks for loops and sets the hook mask for udc
+ * the hook mask for udc tells us from which base chains the udc can be
+ * accessed. This mask is a parameter to the check() functions of the extensions
+ */
+static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
+ unsigned int udc_cnt, unsigned int hooknr, char *base)
+{
+ int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
+ struct ebt_entry *e = (struct ebt_entry *)chain->data;
+ struct ebt_entry_target *t;
+
+ while (pos < nentries || chain_nr != -1) {
+ /* end of udc, go back one 'recursion' step */
+ if (pos == nentries) {
+ /* put back values of the time when this chain was called */
+ e = cl_s[chain_nr].cs.e;
+ if (cl_s[chain_nr].from != -1)
+ nentries =
+ cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
+ else
+ nentries = chain->nentries;
+ pos = cl_s[chain_nr].cs.n;
+ /* make sure we won't see a loop that isn't one */
+ cl_s[chain_nr].cs.n = 0;
+ chain_nr = cl_s[chain_nr].from;
+ if (pos == nentries)
+ continue;
+ }
+ t = (struct ebt_entry_target *)
+ (((char *)e) + e->target_offset);
+ if (strcmp(t->u.name, EBT_STANDARD_TARGET))
+ goto letscontinue;
+ if (e->target_offset + sizeof(struct ebt_standard_target) >
+ e->next_offset) {
+ BUGPRINT("Standard target size too big\n");
+ return -1;
+ }
+ verdict = ((struct ebt_standard_target *)t)->verdict;
+ if (verdict >= 0) { /* jump to another chain */
+ struct ebt_entries *hlp2 =
+ (struct ebt_entries *)(base + verdict);
+ for (i = 0; i < udc_cnt; i++)
+ if (hlp2 == cl_s[i].cs.chaininfo)
+ break;
+ /* bad destination or loop */
+ if (i == udc_cnt) {
+ BUGPRINT("bad destination\n");
+ return -1;
+ }
+ if (cl_s[i].cs.n) {
+ BUGPRINT("loop\n");
+ return -1;
+ }
+ /* this can't be 0, so the above test is correct */
+ cl_s[i].cs.n = pos + 1;
+ pos = 0;
+ cl_s[i].cs.e = ((void *)e + e->next_offset);
+ e = (struct ebt_entry *)(hlp2->data);
+ nentries = hlp2->nentries;
+ cl_s[i].from = chain_nr;
+ chain_nr = i;
+ /* this udc is accessible from the base chain for hooknr */
+ cl_s[i].hookmask |= (1 << hooknr);
+ continue;
+ }
+letscontinue:
+ e = (void *)e + e->next_offset;
+ pos++;
+ }
+ return 0;
+}
+
+/* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
+static int translate_table(struct ebt_replace *repl,
+ struct ebt_table_info *newinfo)
+{
+ unsigned int i, j, k, udc_cnt;
+ int ret;
+ struct ebt_cl_stack *cl_s = NULL; /* used in the checking for chain loops */
+
+ i = 0;
+ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+ i++;
+ if (i == NF_BR_NUMHOOKS) {
+ BUGPRINT("No valid hooks specified\n");
+ return -EINVAL;
+ }
+ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
+ BUGPRINT("Chains don't start at beginning\n");
+ return -EINVAL;
+ }
+ /* make sure chains are ordered after each other in same order
+ as their corresponding hooks */
+ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
+ if (!(repl->valid_hooks & (1 << j)))
+ continue;
+ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
+ BUGPRINT("Hook order must be followed\n");
+ return -EINVAL;
+ }
+ i = j;
+ }
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ newinfo->hook_entry[i] = NULL;
+
+ newinfo->entries_size = repl->entries_size;
+ newinfo->nentries = repl->nentries;
+
+ /* do some early checkings and initialize some things */
+ i = 0; /* holds the expected nr. of entries for the chain */
+ j = 0; /* holds the up to now counted entries for the chain */
+ k = 0; /* holds the total nr. of entries, should equal
+ newinfo->nentries afterwards */
+ udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */
+ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
+ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
+ &udc_cnt, repl->valid_hooks);
+
+ if (ret != 0)
+ return ret;
+
+ if (i != j) {
+ BUGPRINT("nentries does not equal the nr of entries in the "
+ "(last) chain\n");
+ return -EINVAL;
+ }
+ if (k != newinfo->nentries) {
+ BUGPRINT("Total nentries is wrong\n");
+ return -EINVAL;
+ }
+
+ /* check if all valid hooks have a chain */
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (newinfo->hook_entry[i] == NULL &&
+ (repl->valid_hooks & (1 << i))) {
+ BUGPRINT("Valid hook without chain\n");
+ return -EINVAL;
+ }
+ }
+
+ /* get the location of the udc, put them in an array
+ while we're at it, allocate the chainstack */
+ if (udc_cnt) {
+ /* this will get free'd in do_replace()/ebt_register_table()
+ if an error occurs */
+ newinfo->chainstack = (struct ebt_chainstack **)
+ vmalloc(num_possible_cpus() * sizeof(struct ebt_chainstack));
+ if (!newinfo->chainstack)
+ return -ENOMEM;
+ for (i = 0; i < num_possible_cpus(); i++) {
+ newinfo->chainstack[i] =
+ vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
+ if (!newinfo->chainstack[i]) {
+ while (i)
+ vfree(newinfo->chainstack[--i]);
+ vfree(newinfo->chainstack);
+ newinfo->chainstack = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ cl_s = (struct ebt_cl_stack *)
+ vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
+ if (!cl_s)
+ return -ENOMEM;
+ i = 0; /* the i'th udc */
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
+ repl->valid_hooks, cl_s);
+ /* sanity check */
+ if (i != udc_cnt) {
+ BUGPRINT("i != udc_cnt\n");
+ vfree(cl_s);
+ return -EFAULT;
+ }
+ }
+
+ /* Check for loops */
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ if (repl->valid_hooks & (1 << i))
+ if (check_chainloops(newinfo->hook_entry[i],
+ cl_s, udc_cnt, i, newinfo->entries)) {
+ if (cl_s)
+ vfree(cl_s);
+ return -EINVAL;
+ }
+
+ /* we now know the following (along with E=mc˛):
+ - the nr of entries in each chain is right
+ - the size of the allocated space is right
+ - all valid hooks have a corresponding chain
+ - there are no loops
+ - wrong data can still be on the level of a single entry
+ - could be there are jumps to places that are not the
+ beginning of a chain. This can only occur in chains that
+ are not accessible from any base chains, so we don't care. */
+
+ /* used to know what we need to clean up if something goes wrong */
+ i = 0;
+ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
+ cl_s, udc_cnt);
+ if (ret != 0) {
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_cleanup_entry, &i);
+ }
+ if (cl_s)
+ vfree(cl_s);
+ return ret;
+}
+
+/* called under write_lock */
+static void get_counters(struct ebt_counter *oldcounters,
+ struct ebt_counter *counters, unsigned int nentries)
+{
+ int i, cpu;
+ struct ebt_counter *counter_base;
+
+ /* counters of cpu 0 */
+ memcpy(counters, oldcounters,
+ sizeof(struct ebt_counter) * nentries);
+ /* add other counters to those of cpu 0 */
+ for (cpu = 1; cpu < num_possible_cpus(); cpu++) {
+ counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+ for (i = 0; i < nentries; i++) {
+ counters[i].pcnt += counter_base[i].pcnt;
+ counters[i].bcnt += counter_base[i].bcnt;
+ }
+ }
+}
+
+/* replace the table */
+static int do_replace(void __user *user, unsigned int len)
+{
+ int ret, i, countersize;
+ struct ebt_table_info *newinfo;
+ struct ebt_replace tmp;
+ struct ebt_table *t;
+ struct ebt_counter *counterstmp = NULL;
+ /* used to be able to unlock earlier */
+ struct ebt_table_info *table;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ return -EFAULT;
+
+ if (len != sizeof(tmp) + tmp.entries_size) {
+ BUGPRINT("Wrong len argument\n");
+ return -EINVAL;
+ }
+
+ if (tmp.entries_size == 0) {
+ BUGPRINT("Entries_size never zero\n");
+ return -EINVAL;
+ }
+ countersize = COUNTER_OFFSET(tmp.nentries) * num_possible_cpus();
+ newinfo = (struct ebt_table_info *)
+ vmalloc(sizeof(struct ebt_table_info) + countersize);
+ if (!newinfo)
+ return -ENOMEM;
+
+ if (countersize)
+ memset(newinfo->counters, 0, countersize);
+
+ newinfo->entries = (char *)vmalloc(tmp.entries_size);
+ if (!newinfo->entries) {
+ ret = -ENOMEM;
+ goto free_newinfo;
+ }
+ if (copy_from_user(
+ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+ BUGPRINT("Couldn't copy entries from userspace\n");
+ ret = -EFAULT;
+ goto free_entries;
+ }
+
+ /* the user wants counters back
+ the check on the size is done later, when we have the lock */
+ if (tmp.num_counters) {
+ counterstmp = (struct ebt_counter *)
+ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
+ if (!counterstmp) {
+ ret = -ENOMEM;
+ goto free_entries;
+ }
+ }
+ else
+ counterstmp = NULL;
+
+ /* this can get initialized by translate_table() */
+ newinfo->chainstack = NULL;
+ ret = translate_table(&tmp, newinfo);
+
+ if (ret != 0)
+ goto free_counterstmp;
+
+ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ if (!t) {
+ ret = -ENOENT;
+ goto free_iterate;
+ }
+
+ /* the table doesn't like it */
+ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+ goto free_unlock;
+
+ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+ BUGPRINT("Wrong nr. of counters requested\n");
+ ret = -EINVAL;
+ goto free_unlock;
+ }
+
+ /* we have the mutex lock, so no danger in reading this pointer */
+ table = t->private;
+ /* make sure the table can only be rmmod'ed if it contains no rules */
+ if (!table->nentries && newinfo->nentries && !try_module_get(t->me)) {
+ ret = -ENOENT;
+ goto free_unlock;
+ } else if (table->nentries && !newinfo->nentries)
+ module_put(t->me);
+ /* we need an atomic snapshot of the counters */
+ write_lock_bh(&t->lock);
+ if (tmp.num_counters)
+ get_counters(t->private->counters, counterstmp,
+ t->private->nentries);
+
+ t->private = newinfo;
+ write_unlock_bh(&t->lock);
+ up(&ebt_mutex);
+ /* so, a user can change the chains while having messed up her counter
+ allocation. Only reason why this is done is because this way the lock
+ is held only once, while this doesn't bring the kernel into a
+ dangerous state. */
+ if (tmp.num_counters &&
+ copy_to_user(tmp.counters, counterstmp,
+ tmp.num_counters * sizeof(struct ebt_counter))) {
+ BUGPRINT("Couldn't copy counters to userspace\n");
+ ret = -EFAULT;
+ }
+ else
+ ret = 0;
+
+ /* decrease module count and free resources */
+ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
+ ebt_cleanup_entry, NULL);
+
+ vfree(table->entries);
+ if (table->chainstack) {
+ for (i = 0; i < num_possible_cpus(); i++)
+ vfree(table->chainstack[i]);
+ vfree(table->chainstack);
+ }
+ vfree(table);
+
+ if (counterstmp)
+ vfree(counterstmp);
+ return ret;
+
+free_unlock:
+ up(&ebt_mutex);
+free_iterate:
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_cleanup_entry, NULL);
+free_counterstmp:
+ if (counterstmp)
+ vfree(counterstmp);
+ /* can be initialized in translate_table() */
+ if (newinfo->chainstack) {
+ for (i = 0; i < num_possible_cpus(); i++)
+ vfree(newinfo->chainstack[i]);
+ vfree(newinfo->chainstack);
+ }
+free_entries:
+ if (newinfo->entries)
+ vfree(newinfo->entries);
+free_newinfo:
+ if (newinfo)
+ vfree(newinfo);
+ return ret;
+}
+
+int ebt_register_target(struct ebt_target *target)
+{
+ int ret;
+
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ return ret;
+ if (!list_named_insert(&ebt_targets, target)) {
+ up(&ebt_mutex);
+ return -EEXIST;
+ }
+ up(&ebt_mutex);
+
+ return 0;
+}
+
+void ebt_unregister_target(struct ebt_target *target)
+{
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_targets, target);
+ up(&ebt_mutex);
+}
+
+int ebt_register_match(struct ebt_match *match)
+{
+ int ret;
+
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ return ret;
+ if (!list_named_insert(&ebt_matches, match)) {
+ up(&ebt_mutex);
+ return -EEXIST;
+ }
+ up(&ebt_mutex);
+
+ return 0;
+}
+
+void ebt_unregister_match(struct ebt_match *match)
+{
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_matches, match);
+ up(&ebt_mutex);
+}
+
+int ebt_register_watcher(struct ebt_watcher *watcher)
+{
+ int ret;
+
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ return ret;
+ if (!list_named_insert(&ebt_watchers, watcher)) {
+ up(&ebt_mutex);
+ return -EEXIST;
+ }
+ up(&ebt_mutex);
+
+ return 0;
+}
+
+void ebt_unregister_watcher(struct ebt_watcher *watcher)
+{
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_watchers, watcher);
+ up(&ebt_mutex);
+}
+
+int ebt_register_table(struct ebt_table *table)
+{
+ struct ebt_table_info *newinfo;
+ int ret, i, countersize;
+
+ if (!table || !table->table ||!table->table->entries ||
+ table->table->entries_size == 0 ||
+ table->table->counters || table->private) {
+ BUGPRINT("Bad table data for ebt_register_table!!!\n");
+ return -EINVAL;
+ }
+
+ countersize = COUNTER_OFFSET(table->table->nentries) * num_possible_cpus();
+ newinfo = (struct ebt_table_info *)
+ vmalloc(sizeof(struct ebt_table_info) + countersize);
+ ret = -ENOMEM;
+ if (!newinfo)
+ return -ENOMEM;
+
+ newinfo->entries = (char *)vmalloc(table->table->entries_size);
+ if (!(newinfo->entries))
+ goto free_newinfo;
+
+ memcpy(newinfo->entries, table->table->entries,
+ table->table->entries_size);
+
+ if (countersize)
+ memset(newinfo->counters, 0, countersize);
+
+ /* fill in newinfo and parse the entries */
+ newinfo->chainstack = NULL;
+ ret = translate_table(table->table, newinfo);
+ if (ret != 0) {
+ BUGPRINT("Translate_table failed\n");
+ goto free_chainstack;
+ }
+
+ if (table->check && table->check(newinfo, table->valid_hooks)) {
+ BUGPRINT("The table doesn't like its own initial data, lol\n");
+ return -EINVAL;
+ }
+
+ table->private = newinfo;
+ rwlock_init(&table->lock);
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ goto free_chainstack;
+
+ if (list_named_find(&ebt_tables, table->name)) {
+ ret = -EEXIST;
+ BUGPRINT("Table name already exists\n");
+ goto free_unlock;
+ }
+
+ /* Hold a reference count if the chains aren't empty */
+ if (newinfo->nentries && !try_module_get(table->me)) {
+ ret = -ENOENT;
+ goto free_unlock;
+ }
+ list_prepend(&ebt_tables, table);
+ up(&ebt_mutex);
+ return 0;
+free_unlock:
+ up(&ebt_mutex);
+free_chainstack:
+ if (newinfo->chainstack) {
+ for (i = 0; i < num_possible_cpus(); i++)
+ vfree(newinfo->chainstack[i]);
+ vfree(newinfo->chainstack);
+ }
+ vfree(newinfo->entries);
+free_newinfo:
+ vfree(newinfo);
+ return ret;
+}
+
+void ebt_unregister_table(struct ebt_table *table)
+{
+ int i;
+
+ if (!table) {
+ BUGPRINT("Request to unregister NULL table!!!\n");
+ return;
+ }
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_tables, table);
+ up(&ebt_mutex);
+ if (table->private->entries)
+ vfree(table->private->entries);
+ if (table->private->chainstack) {
+ for (i = 0; i < num_possible_cpus(); i++)
+ vfree(table->private->chainstack[i]);
+ vfree(table->private->chainstack);
+ }
+ vfree(table->private);
+}
+
+/* userspace just supplied us with counters */
+static int update_counters(void __user *user, unsigned int len)
+{
+ int i, ret;
+ struct ebt_counter *tmp;
+ struct ebt_replace hlp;
+ struct ebt_table *t;
+
+ if (copy_from_user(&hlp, user, sizeof(hlp)))
+ return -EFAULT;
+
+ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
+ return -EINVAL;
+ if (hlp.num_counters == 0)
+ return -EINVAL;
+
+ if ( !(tmp = (struct ebt_counter *)
+ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
+ MEMPRINT("Update_counters && nomemory\n");
+ return -ENOMEM;
+ }
+
+ t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ if (!t)
+ goto free_tmp;
+
+ if (hlp.num_counters != t->private->nentries) {
+ BUGPRINT("Wrong nr of counters\n");
+ ret = -EINVAL;
+ goto unlock_mutex;
+ }
+
+ if ( copy_from_user(tmp, hlp.counters,
+ hlp.num_counters * sizeof(struct ebt_counter)) ) {
+ BUGPRINT("Updata_counters && !cfu\n");
+ ret = -EFAULT;
+ goto unlock_mutex;
+ }
+
+ /* we want an atomic add of the counters */
+ write_lock_bh(&t->lock);
+
+ /* we add to the counters of the first cpu */
+ for (i = 0; i < hlp.num_counters; i++) {
+ t->private->counters[i].pcnt += tmp[i].pcnt;
+ t->private->counters[i].bcnt += tmp[i].bcnt;
+ }
+
+ write_unlock_bh(&t->lock);
+ ret = 0;
+unlock_mutex:
+ up(&ebt_mutex);
+free_tmp:
+ vfree(tmp);
+ return ret;
+}
+
+static inline int ebt_make_matchname(struct ebt_entry_match *m,
+ char *base, char *ubase)
+{
+ char *hlp = ubase - base + (char *)m;
+ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
+ char *base, char *ubase)
+{
+ char *hlp = ubase - base + (char *)w;
+ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+{
+ int ret;
+ char *hlp;
+ struct ebt_entry_target *t;
+
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+
+ hlp = ubase - base + (char *)e + e->target_offset;
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+
+ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
+ if (ret != 0)
+ return ret;
+ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
+ if (ret != 0)
+ return ret;
+ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
+ return -EFAULT;
+ return 0;
+}
+
+/* called with ebt_mutex down */
+static int copy_everything_to_user(struct ebt_table *t, void __user *user,
+ int *len, int cmd)
+{
+ struct ebt_replace tmp;
+ struct ebt_counter *counterstmp, *oldcounters;
+ unsigned int entries_size, nentries;
+ char *entries;
+
+ if (cmd == EBT_SO_GET_ENTRIES) {
+ entries_size = t->private->entries_size;
+ nentries = t->private->nentries;
+ entries = t->private->entries;
+ oldcounters = t->private->counters;
+ } else {
+ entries_size = t->table->entries_size;
+ nentries = t->table->nentries;
+ entries = t->table->entries;
+ oldcounters = t->table->counters;
+ }
+
+ if (copy_from_user(&tmp, user, sizeof(tmp))) {
+ BUGPRINT("Cfu didn't work\n");
+ return -EFAULT;
+ }
+
+ if (*len != sizeof(struct ebt_replace) + entries_size +
+ (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
+ BUGPRINT("Wrong size\n");
+ return -EINVAL;
+ }
+
+ if (tmp.nentries != nentries) {
+ BUGPRINT("Nentries wrong\n");
+ return -EINVAL;
+ }
+
+ if (tmp.entries_size != entries_size) {
+ BUGPRINT("Wrong size\n");
+ return -EINVAL;
+ }
+
+ /* userspace might not need the counters */
+ if (tmp.num_counters) {
+ if (tmp.num_counters != nentries) {
+ BUGPRINT("Num_counters wrong\n");
+ return -EINVAL;
+ }
+ counterstmp = (struct ebt_counter *)
+ vmalloc(nentries * sizeof(struct ebt_counter));
+ if (!counterstmp) {
+ MEMPRINT("Couldn't copy counters, out of memory\n");
+ return -ENOMEM;
+ }
+ write_lock_bh(&t->lock);
+ get_counters(oldcounters, counterstmp, nentries);
+ write_unlock_bh(&t->lock);
+
+ if (copy_to_user(tmp.counters, counterstmp,
+ nentries * sizeof(struct ebt_counter))) {
+ BUGPRINT("Couldn't copy counters to userspace\n");
+ vfree(counterstmp);
+ return -EFAULT;
+ }
+ vfree(counterstmp);
+ }
+
+ if (copy_to_user(tmp.entries, entries, entries_size)) {
+ BUGPRINT("Couldn't copy entries to userspace\n");
+ return -EFAULT;
+ }
+ /* set the match/watcher/target names right */
+ return EBT_ENTRY_ITERATE(entries, entries_size,
+ ebt_make_names, entries, tmp.entries);
+}
+
+static int do_ebt_set_ctl(struct sock *sk,
+ int cmd, void __user *user, unsigned int len)
+{
+ int ret;
+
+ switch(cmd) {
+ case EBT_SO_SET_ENTRIES:
+ ret = do_replace(user, len);
+ break;
+ case EBT_SO_SET_COUNTERS:
+ ret = update_counters(user, len);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
+{
+ int ret;
+ struct ebt_replace tmp;
+ struct ebt_table *t;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)))
+ return -EFAULT;
+
+ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ if (!t)
+ return ret;
+
+ switch(cmd) {
+ case EBT_SO_GET_INFO:
+ case EBT_SO_GET_INIT_INFO:
+ if (*len != sizeof(struct ebt_replace)){
+ ret = -EINVAL;
+ up(&ebt_mutex);
+ break;
+ }
+ if (cmd == EBT_SO_GET_INFO) {
+ tmp.nentries = t->private->nentries;
+ tmp.entries_size = t->private->entries_size;
+ tmp.valid_hooks = t->valid_hooks;
+ } else {
+ tmp.nentries = t->table->nentries;
+ tmp.entries_size = t->table->entries_size;
+ tmp.valid_hooks = t->table->valid_hooks;
+ }
+ up(&ebt_mutex);
+ if (copy_to_user(user, &tmp, *len) != 0){
+ BUGPRINT("c2u Didn't work\n");
+ ret = -EFAULT;
+ break;
+ }
+ ret = 0;
+ break;
+
+ case EBT_SO_GET_ENTRIES:
+ case EBT_SO_GET_INIT_ENTRIES:
+ ret = copy_everything_to_user(t, user, len, cmd);
+ up(&ebt_mutex);
+ break;
+
+ default:
+ up(&ebt_mutex);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct nf_sockopt_ops ebt_sockopts =
+{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
+ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+};
+
+static int __init init(void)
+{
+ int ret;
+
+ down(&ebt_mutex);
+ list_named_insert(&ebt_targets, &ebt_standard_target);
+ up(&ebt_mutex);
+ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
+ return ret;
+
+ printk(KERN_NOTICE "Ebtables v2.0 registered\n");
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ nf_unregister_sockopt(&ebt_sockopts);
+ printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
+}
+
+EXPORT_SYMBOL(ebt_register_table);
+EXPORT_SYMBOL(ebt_unregister_table);
+EXPORT_SYMBOL(ebt_register_match);
+EXPORT_SYMBOL(ebt_unregister_match);
+EXPORT_SYMBOL(ebt_register_watcher);
+EXPORT_SYMBOL(ebt_unregister_watcher);
+EXPORT_SYMBOL(ebt_register_target);
+EXPORT_SYMBOL(ebt_unregister_target);
+EXPORT_SYMBOL(ebt_do_table);
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");