From d891e9e5bc309d5aeb2ab774c76b34a92085b3e7 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Sat, 1 Jun 2002 19:23:47 +0000 Subject: Initial revision --- kernel/README | 6 + kernel/linux/include/linux/br_db.h | 53 + kernel/linux/include/linux/if_bridge.h | 114 + kernel/linux/include/linux/netfilter_bridge.h | 36 + .../linux/include/linux/netfilter_bridge/ebt_arp.h | 26 + .../linux/include/linux/netfilter_bridge/ebt_ip.h | 24 + .../linux/include/linux/netfilter_bridge/ebt_log.h | 17 + .../linux/include/linux/netfilter_bridge/ebt_nat.h | 13 + .../include/linux/netfilter_bridge/ebt_redirect.h | 11 + .../include/linux/netfilter_bridge/ebt_vlan.h | 18 + .../include/linux/netfilter_bridge/ebtables.h | 332 ++ kernel/linux/net/Config.in | 98 + kernel/linux/net/Makefile | 63 + kernel/linux/net/bridge/br_input.c | 178 ++ kernel/linux/net/bridge/br_private.h | 212 ++ kernel/linux/net/bridge/netfilter/Config.in | 15 + kernel/linux/net/bridge/netfilter/Makefile | 25 + kernel/linux/net/bridge/netfilter/br_db.c | 357 +++ kernel/linux/net/bridge/netfilter/ebt_arp.c | 107 + kernel/linux/net/bridge/netfilter/ebt_ip.c | 81 + kernel/linux/net/bridge/netfilter/ebt_log.c | 111 + kernel/linux/net/bridge/netfilter/ebt_nat.c | 106 + kernel/linux/net/bridge/netfilter/ebt_redirect.c | 65 + kernel/linux/net/bridge/netfilter/ebt_vlan.c | 124 + kernel/linux/net/bridge/netfilter/ebtable_broute.c | 80 + kernel/linux/net/bridge/netfilter/ebtable_filter.c | 93 + kernel/linux/net/bridge/netfilter/ebtable_nat.c | 156 + kernel/linux/net/bridge/netfilter/ebtables.c | 1189 +++++++ kernel/linux/net/netsyms.c | 595 ++++ .../base-patches/ebtables-v2.0pre1_vs_2.4.18.diff | 2621 ++++++++++++++++ .../base-patches/ebtables-v2.0pre2_vs_2.4.18.diff | 2752 ++++++++++++++++ .../base-patches/ebtables-v2.0pre3_vs_2.4.18.diff | 3108 ++++++++++++++++++ .../base-patches/ebtables-v2.0pre4_vs_2.4.18.diff | 3122 +++++++++++++++++++ .../base-patches/ebtables-v2.0pre5_vs_2.4.18.diff | 3133 +++++++++++++++++++ .../base-patches/ebtables-v2.0pre6_vs_2.4.18.diff | 3135 +++++++++++++++++++ .../base-patches/ebtables-v2.0pre7_vs_2.4.18.diff | 3285 ++++++++++++++++++++ .../ebtables-v2.0_vs_2.4.18.pre2.001.diff | 11 + .../ebtables-v2.0_vs_2.4.18.pre3.001.diff | 166 + .../ebtables-v2.0_vs_2.4.18.pre3.002.diff | 66 + .../ebtables-v2.0_vs_2.4.18.pre3.003.diff | 367 +++ .../ebtables-v2.0_vs_2.4.18.pre3.004.diff | 252 ++ .../ebtables-v2.0_vs_2.4.18.pre3.005.diff | 22 + .../ebtables-v2.0_vs_2.4.18.pre4.001.diff | 14 + .../ebtables-v2.0_vs_2.4.18.pre5.001.diff | 51 + .../ebtables-v2.0_vs_2.4.18.pre6.001.diff | 12 + .../ebtables-v2.0_vs_2.4.18.pre7.001.diff | 172 + 46 files changed, 26594 insertions(+) create mode 100644 kernel/README create mode 100644 kernel/linux/include/linux/br_db.h create mode 100644 kernel/linux/include/linux/if_bridge.h create mode 100644 kernel/linux/include/linux/netfilter_bridge.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_arp.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_ip.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_log.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_nat.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebtables.h create mode 100644 kernel/linux/net/Config.in create mode 100644 kernel/linux/net/Makefile create mode 100644 kernel/linux/net/bridge/br_input.c create mode 100644 kernel/linux/net/bridge/br_private.h create mode 100644 kernel/linux/net/bridge/netfilter/Config.in create mode 100644 kernel/linux/net/bridge/netfilter/Makefile create mode 100644 kernel/linux/net/bridge/netfilter/br_db.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_arp.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_ip.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_log.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_nat.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_redirect.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_vlan.c create mode 100644 kernel/linux/net/bridge/netfilter/ebtable_broute.c create mode 100644 kernel/linux/net/bridge/netfilter/ebtable_filter.c create mode 100644 kernel/linux/net/bridge/netfilter/ebtable_nat.c create mode 100644 kernel/linux/net/bridge/netfilter/ebtables.c create mode 100644 kernel/linux/net/netsyms.c create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff create mode 100644 kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff create mode 100644 kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff (limited to 'kernel') diff --git a/kernel/README b/kernel/README new file mode 100644 index 0000000..617a201 --- /dev/null +++ b/kernel/README @@ -0,0 +1,6 @@ +Here are the source code and patches for the kernel files ebtables +changes/creates. + +Bart De Schuymer, +June 1, 2002 + diff --git a/kernel/linux/include/linux/br_db.h b/kernel/linux/include/linux/br_db.h new file mode 100644 index 0000000..fae1279 --- /dev/null +++ b/kernel/linux/include/linux/br_db.h @@ -0,0 +1,53 @@ +/* + * bridge ethernet protocol filter + * + * Authors: + * Bart De Schuymer + * + * br_db.h,v 1.1 2001/04/16 + * + * 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. + */ + +#ifndef __LINUX_BRIDGE_DB_H +#define __LINUX_BRIDGE_DB_H +#include /* IFNAMSIZ */ +#ifdef __KERNEL__ +#include +#include +#else +#include +#endif +#define BRDB_BASE_CTL 135 + +#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) +#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) + +#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) +#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) +#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) + +#define BRDB_NODB 0 +#define BRDB_DB 1 + +#define INITIAL_DBSIZE 10 +#define IDENTIFY802_3 46 + +struct brdb_dbinfo { + __u32 nentries; +}; + +struct brdb_dbentry { + __u8 in[IFNAMSIZ]; + __u8 out[IFNAMSIZ]; + __u16 ethproto; + __u32 hook; +}; + +#endif diff --git a/kernel/linux/include/linux/if_bridge.h b/kernel/linux/include/linux/if_bridge.h new file mode 100644 index 0000000..926b45f --- /dev/null +++ b/kernel/linux/include/linux/if_bridge.h @@ -0,0 +1,114 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id: if_bridge.h,v 1.1 2002/06/01 19:24:08 bdschuym Exp $ + * + * 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. + */ + +#ifndef _LINUX_IF_BRIDGE_H +#define _LINUX_IF_BRIDGE_H + +#include + +#define BRCTL_VERSION 1 + +#define BRCTL_GET_VERSION 0 +#define BRCTL_GET_BRIDGES 1 +#define BRCTL_ADD_BRIDGE 2 +#define BRCTL_DEL_BRIDGE 3 +#define BRCTL_ADD_IF 4 +#define BRCTL_DEL_IF 5 +#define BRCTL_GET_BRIDGE_INFO 6 +#define BRCTL_GET_PORT_LIST 7 +#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 +#define BRCTL_SET_BRIDGE_HELLO_TIME 9 +#define BRCTL_SET_BRIDGE_MAX_AGE 10 +#define BRCTL_SET_AGEING_TIME 11 +#define BRCTL_SET_GC_INTERVAL 12 +#define BRCTL_GET_PORT_INFO 13 +#define BRCTL_SET_BRIDGE_STP_STATE 14 +#define BRCTL_SET_BRIDGE_PRIORITY 15 +#define BRCTL_SET_PORT_PRIORITY 16 +#define BRCTL_SET_PATH_COST 17 +#define BRCTL_GET_FDB_ENTRIES 18 + +#define BR_STATE_DISABLED 0 +#define BR_STATE_LISTENING 1 +#define BR_STATE_LEARNING 2 +#define BR_STATE_FORWARDING 3 +#define BR_STATE_BLOCKING 4 + +struct __bridge_info +{ + __u64 designated_root; + __u64 bridge_id; + __u32 root_path_cost; + __u32 max_age; + __u32 hello_time; + __u32 forward_delay; + __u32 bridge_max_age; + __u32 bridge_hello_time; + __u32 bridge_forward_delay; + __u8 topology_change; + __u8 topology_change_detected; + __u8 root_port; + __u8 stp_enabled; + __u32 ageing_time; + __u32 gc_interval; + __u32 hello_timer_value; + __u32 tcn_timer_value; + __u32 topology_change_timer_value; + __u32 gc_timer_value; +}; + +struct __port_info +{ + __u64 designated_root; + __u64 designated_bridge; + __u16 port_id; + __u16 designated_port; + __u32 path_cost; + __u32 designated_cost; + __u8 state; + __u8 top_change_ack; + __u8 config_pending; + __u8 unused0; + __u32 message_age_timer_value; + __u32 forward_delay_timer_value; + __u32 hold_timer_value; +}; + +struct __fdb_entry +{ + __u8 mac_addr[6]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u32 unused; +}; + +#ifdef __KERNEL__ + +#include + +struct net_bridge; +struct net_bridge_port; + +extern int (*br_ioctl_hook)(unsigned long arg); +extern int (*br_handle_frame_hook)(struct sk_buff *skb); +#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ + defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) +extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)); +#endif +#endif + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge.h b/kernel/linux/include/linux/netfilter_bridge.h new file mode 100644 index 0000000..2c950c2 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge.h @@ -0,0 +1,36 @@ +#ifndef __LINUX_BRIDGE_NETFILTER_H +#define __LINUX_BRIDGE_NETFILTER_H + +/* bridge-specific defines for netfilter. + */ + +#include +#include + +/* Bridge Hooks */ +/* After promisc drops, checksum checks. */ +#define NF_BR_PRE_ROUTING 0 +/* If the packet is destined for this box. */ +#define NF_BR_LOCAL_IN 1 +/* If the packet is destined for another interface. */ +#define NF_BR_FORWARD 2 +/* Packets coming from a local process. */ +#define NF_BR_LOCAL_OUT 3 +/* Packets about to hit the wire. */ +#define NF_BR_POST_ROUTING 4 +/* Not really a hook, but used for the ebtables broute table */ +#define NF_BR_BROUTING 5 +#define NF_BR_NUMHOOKS 6 + +enum nf_br_hook_priorities { + NF_BR_PRI_FIRST = INT_MIN, + NF_BR_PRI_FILTER_BRIDGED = -200, + NF_BR_PRI_FILTER_OTHER = 200, + NF_BR_PRI_NAT_DST_BRIDGED = -300, + NF_BR_PRI_NAT_DST_OTHER = 100, + NF_BR_PRI_NAT_SRC_BRIDGED = -100, + NF_BR_PRI_NAT_SRC_OTHER = 300, + NF_BR_PRI_LAST = INT_MAX, +}; + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h b/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h new file mode 100644 index 0000000..a29f926 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h @@ -0,0 +1,26 @@ +#ifndef __LINUX_BRIDGE_EBT_ARP_H +#define __LINUX_BRIDGE_EBT_ARP_H + +#define EBT_ARP_OPCODE 0x01 +#define EBT_ARP_HTYPE 0x02 +#define EBT_ARP_PTYPE 0x04 +#define EBT_ARP_SRC_IP 0x08 +#define EBT_ARP_DST_IP 0x10 +#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ + EBT_ARP_SRC_IP | EBT_ARP_DST_IP) +#define EBT_ARP_MATCH "arp" + +struct ebt_arp_info +{ + __u16 htype; + __u16 ptype; + __u16 opcode; + __u32 saddr; + __u32 smsk; + __u32 daddr; + __u32 dmsk; + __u8 bitmask; + __u8 invflags; +}; + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h b/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h new file mode 100644 index 0000000..f4f9ed1 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h @@ -0,0 +1,24 @@ +#ifndef __LINUX_BRIDGE_EBT_IP_H +#define __LINUX_BRIDGE_EBT_IP_H + +#define EBT_IP_SOURCE 0x01 +#define EBT_IP_DEST 0x02 +#define EBT_IP_TOS 0x04 +#define EBT_IP_PROTO 0x08 +#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) +#define EBT_IP_MATCH "ip" + +// the same values are used for the invflags +struct ebt_ip_info +{ + __u32 saddr; + __u32 daddr; + __u32 smsk; + __u32 dmsk; + __u8 tos; + __u8 protocol; + __u8 bitmask; + __u8 invflags; +}; + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_log.h b/kernel/linux/include/linux/netfilter_bridge/ebt_log.h new file mode 100644 index 0000000..9343d11 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebt_log.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_BRIDGE_EBT_LOG_H +#define __LINUX_BRIDGE_EBT_LOG_H + +#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information +#define EBT_LOG_ARP 0x02 +#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) +#define EBT_LOG_PREFIX_SIZE 30 +#define EBT_LOG_WATCHER "log" + +struct ebt_log_info +{ + __u8 loglevel; + __u8 prefix[EBT_LOG_PREFIX_SIZE]; + __u32 bitmask; +}; + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h b/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h new file mode 100644 index 0000000..53c81d2 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h @@ -0,0 +1,13 @@ +#ifndef __LINUX_BRIDGE_EBT_NAT_H +#define __LINUX_BRIDGE_EBT_NAT_H + +struct ebt_nat_info +{ + unsigned char mac[ETH_ALEN]; + // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE + __u8 target; +}; +#define EBT_SNAT_TARGET "snat" +#define EBT_DNAT_TARGET "dnat" + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h b/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h new file mode 100644 index 0000000..82cd309 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h @@ -0,0 +1,11 @@ +#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H +#define __LINUX_BRIDGE_EBT_REDIRECT_H + +struct ebt_redirect_info +{ + // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE + __u8 target; +}; +#define EBT_REDIRECT_TARGET "redirect" + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h b/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h new file mode 100644 index 0000000..079112b --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h @@ -0,0 +1,18 @@ +#ifndef __LINUX_BRIDGE_EBT_VLAN_H +#define __LINUX_BRIDGE_EBT_VLAN_H + +#define EBT_VLAN_ID 0x01 +#define EBT_VLAN_PRIO 0x02 +#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO) +#define EBT_VLAN_MATCH "vlan" + +struct ebt_vlan_info { + __u16 id; /* VLAN ID {1-4095} */ + __u16 prio; /* VLAN Priority {0-7} */ + __u8 bitmask; /* Args bitmask bit 1=1 - ID arg, + bit 2=1 - Pirority arg */ + __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, + bit 2=1 - inversed Pirority arg */ +}; + +#endif diff --git a/kernel/linux/include/linux/netfilter_bridge/ebtables.h b/kernel/linux/include/linux/netfilter_bridge/ebtables.h new file mode 100644 index 0000000..f4f9e90 --- /dev/null +++ b/kernel/linux/include/linux/netfilter_bridge/ebtables.h @@ -0,0 +1,332 @@ +/* + * ebtables + * + * Authors: + * Bart De Schuymer + * + * ebtables.c,v 2.0, April, 2002 + * + * This code is stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + */ + +#ifndef __LINUX_BRIDGE_EFF_H +#define __LINUX_BRIDGE_EFF_H +#include // IFNAMSIZ +#include +#include // ETH_ALEN + +#define EBT_TABLE_MAXNAMELEN 32 +#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN + +// [gs]etsockopt numbers +#define EBT_BASE_CTL 128 + +#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) +#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) +#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) + +#define EBT_SO_GET_INFO (EBT_BASE_CTL) +#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) +#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) + +#define EBT_ACCEPT 0 +#define EBT_DROP 1 +#define EBT_CONTINUE 2 +#define NUM_STANDARD_TARGETS 3 + +struct ebt_entries { + // this field is always set to zero (including userspace). + // See EBT_ENTRY_OR_ENTRIES. + // Must be same size as ebt_entry.bitmask + __u32 distinguisher; + // one standard (accept or drop) per hook + __u8 policy; + // nr. of entries + __u32 nentries; + // entry list + __u8 data[0]; +}; + +// used for the bitmask of struct ebt_entry + +// This is a hack to make a difference between an ebt_entry struct and an +// ebt_entries struct when traversing the entries from start to end. +// Using this simplifies the code alot, while still being able to use +// ebt_entries. +// Contrary, iptables doesn't use something like ebt_entries and therefore uses +// different techniques for naming the policy and such. So, iptables doesn't +// need a hack like this. +#define EBT_ENTRY_OR_ENTRIES 0x01 +// these are the normal masks +#define EBT_NOPROTO 0x02 +#define EBT_802_3 0x04 +#define EBT_SOURCEMAC 0x08 +#define EBT_DESTMAC 0x10 +#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ + | EBT_ENTRY_OR_ENTRIES) + +#define EBT_IPROTO 0x01 +#define EBT_IIN 0x02 +#define EBT_IOUT 0x04 +#define EBT_ISOURCE 0x8 +#define EBT_IDEST 0x10 +#define EBT_ILOGICALIN 0x20 +#define EBT_ILOGICALOUT 0x40 +#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ + | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) + +struct ebt_counter +{ + __u64 pcnt; +}; + +struct ebt_entry_match +{ + union { + char name[EBT_FUNCTION_MAXNAMELEN]; + struct ebt_match *match; + } u; + // size of data + unsigned int match_size; + unsigned char data[0]; +}; + +struct ebt_entry_watcher +{ + union { + char name[EBT_FUNCTION_MAXNAMELEN]; + struct ebt_watcher *watcher; + } u; + // size of data + unsigned int watcher_size; + unsigned char data[0]; +}; + +struct ebt_entry_target +{ + union { + char name[EBT_FUNCTION_MAXNAMELEN]; + struct ebt_target *target; + } u; + // size of data + unsigned int target_size; + unsigned char data[0]; +}; + +#define EBT_STANDARD_TARGET "standard" +struct ebt_standard_target +{ + struct ebt_entry_target target; + __u8 verdict; +}; + +// one entry +struct ebt_entry { + // this needs to be the first field + __u32 bitmask; + __u32 invflags; + __u16 ethproto; + // the physical in-dev + __u8 in[IFNAMSIZ]; + // the logical in-dev + __u8 logical_in[IFNAMSIZ]; + // the physical out-dev + __u8 out[IFNAMSIZ]; + // the logical out-dev + __u8 logical_out[IFNAMSIZ]; + __u8 sourcemac[ETH_ALEN]; + __u8 sourcemsk[ETH_ALEN]; + __u8 destmac[ETH_ALEN]; + __u8 destmsk[ETH_ALEN]; + // sizeof ebt_entry + matches + __u16 watchers_offset; + // sizeof ebt_entry + matches + watchers + __u16 target_offset; + // sizeof ebt_entry + matches + watchers + target + __u16 next_offset; + unsigned char elems[0]; +}; + +struct ebt_replace +{ + char name[EBT_TABLE_MAXNAMELEN]; + unsigned int valid_hooks; + // nr of rules in the table + unsigned int nentries; + // total size of the entries + unsigned int entries_size; + // start of the chains + struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; + // how many counters in front of it? + unsigned int counter_entry[NF_BR_NUMHOOKS]; + // nr of counters userspace expects back + unsigned int num_counters; + // where the kernel will put the old counters + struct ebt_counter *counters; + char *entries; +}; + +#ifdef __KERNEL__ + +struct ebt_match +{ + struct list_head list; + const char name[EBT_FUNCTION_MAXNAMELEN]; + // 0 == it matches + int (*match)(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchdata, + unsigned int datalen, const struct ebt_counter *c); + // 0 == let it in + int (*check)(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *matchdata, unsigned int datalen); + void (*destroy)(void *matchdata, unsigned int datalen); + struct module *me; +}; + +struct ebt_watcher +{ + struct list_head list; + const char name[EBT_FUNCTION_MAXNAMELEN]; + void (*watcher)(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *watcherdata, + unsigned int datalen, const struct ebt_counter *c); + // 0 == let it in + int (*check)(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *watcherdata, unsigned int datalen); + void (*destroy)(void *watcherdata, unsigned int datalen); + struct module *me; +}; + +struct ebt_target +{ + struct list_head list; + const char name[EBT_FUNCTION_MAXNAMELEN]; + // returns one of the standard verdicts + __u8 (*target)(struct sk_buff **pskb, + unsigned int hooknr, + const struct net_device *in, + const struct net_device *out, + const void *targetdata, + unsigned int datalen); + // 0 == let it in + int (*check)(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *targetdata, unsigned int datalen); + void (*destroy)(void *targetdata, unsigned int datalen); + struct module *me; +}; + +struct ebt_table_info +{ + // total size of the entries + unsigned int entries_size; + unsigned int nentries; + // pointers to the start of the chains + struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; + // how many counters in front of the counters bolonging to a chain + unsigned int counter_entry[NF_BR_NUMHOOKS]; + struct ebt_counter *counters; + char *entries; +}; + +struct ebt_table +{ + struct list_head list; + char name[EBT_TABLE_MAXNAMELEN]; + struct ebt_replace *table; + unsigned int valid_hooks; + rwlock_t lock; + // e.g. could be the table explicitly only allows certain + // matches, targets, ... 0 == let it in + int (*check)(const struct ebt_table_info *info, + unsigned int valid_hooks); + // the data used by the kernel + struct ebt_table_info *private; +}; + +extern int ebt_register_table(struct ebt_table *table); +extern void ebt_unregister_table(struct ebt_table *table); +extern int ebt_register_match(struct ebt_match *match); +extern void ebt_unregister_match(struct ebt_match *match); +extern int ebt_register_watcher(struct ebt_watcher *watcher); +extern void ebt_unregister_watcher(struct ebt_watcher *watcher); +extern int ebt_register_target(struct ebt_target *target); +extern void ebt_unregister_target(struct ebt_target *target); +extern 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); + +#endif /* __KERNEL__ */ + +// blatently stolen from ip_tables.h +// fn returns 0 to continue iteration +#define EBT_MATCH_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ebt_entry_match *__match; \ + \ + for (__i = sizeof(struct ebt_entry); \ + __i < (e)->watchers_offset; \ + __i += __match->match_size + \ + sizeof(struct ebt_entry_match)) { \ + __match = (void *)(e) + __i; \ + \ + __ret = fn(__match , ## args); \ + if (__ret != 0) \ + break; \ + } \ + if (__ret == 0) { \ + if (__i != (e)->watchers_offset) \ + __ret = -EINVAL; \ + } \ + __ret; \ +}) + +#define EBT_WATCHER_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ebt_entry_watcher *__watcher; \ + \ + for (__i = e->watchers_offset; \ + __i < (e)->target_offset; \ + __i += __watcher->watcher_size + \ + sizeof(struct ebt_entry_watcher)) { \ + __watcher = (void *)(e) + __i; \ + \ + __ret = fn(__watcher , ## args); \ + if (__ret != 0) \ + break; \ + } \ + if (__ret == 0) { \ + if (__i != (e)->target_offset) \ + __ret = -EINVAL; \ + } \ + __ret; \ +}) + +#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ebt_entry *__entry; \ + \ + for (__i = 0; __i < (size);) { \ + __entry = (void *)(entries) + __i; \ + __ret = fn(__entry , ## args); \ + if (__ret != 0) \ + break; \ + if (__entry->bitmask != 0) \ + __i += __entry->next_offset; \ + else \ + __i += sizeof(struct ebt_entries); \ + } \ + if (__ret == 0) { \ + if (__i != (size)) \ + __ret = -EINVAL; \ + } \ + __ret; \ +}) + +#endif diff --git a/kernel/linux/net/Config.in b/kernel/linux/net/Config.in new file mode 100644 index 0000000..76b32b4 --- /dev/null +++ b/kernel/linux/net/Config.in @@ -0,0 +1,98 @@ +# +# Network configuration +# +mainmenu_option next_comment +comment 'Networking options' +tristate 'Packet socket' CONFIG_PACKET +if [ "$CONFIG_PACKET" != "n" ]; then + bool ' Packet socket: mmapped IO' CONFIG_PACKET_MMAP +fi + +tristate 'Netlink device emulation' CONFIG_NETLINK_DEV + +bool 'Network packet filtering (replaces ipchains)' CONFIG_NETFILTER +if [ "$CONFIG_NETFILTER" = "y" ]; then + bool ' Network packet filtering debugging' CONFIG_NETFILTER_DEBUG +fi +bool 'Socket Filtering' CONFIG_FILTER +tristate 'Unix domain sockets' CONFIG_UNIX +bool 'TCP/IP networking' CONFIG_INET +if [ "$CONFIG_INET" = "y" ]; then + source net/ipv4/Config.in + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# IPv6 as module will cause a CRASH if you try to unload it + tristate ' The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6 + if [ "$CONFIG_IPV6" != "n" ]; then + source net/ipv6/Config.in + fi + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + source net/khttpd/Config.in + fi +fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM + if [ "$CONFIG_ATM" = "y" ]; then + if [ "$CONFIG_INET" = "y" ]; then + bool ' Classical IP over ATM' CONFIG_ATM_CLIP + if [ "$CONFIG_ATM_CLIP" = "y" ]; then + bool ' Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP + fi + fi + tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE + if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then + tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA + fi + fi + + dep_tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_8021Q $CONFIG_EXPERIMENTAL + +fi + +comment ' ' +tristate 'The IPX protocol' CONFIG_IPX +if [ "$CONFIG_IPX" != "n" ]; then + source net/ipx/Config.in +fi +tristate 'Appletalk protocol support' CONFIG_ATALK +tristate 'DECnet Support' CONFIG_DECNET +if [ "$CONFIG_DECNET" != "n" ]; then + source net/decnet/Config.in +fi +dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET +if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + source net/bridge/netfilter/Config.in +fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF + fi + tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 + tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB + bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC + bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT +# if [ "$CONFIG_LLC" = "y" ]; then +# bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI +# fi + if [ "$CONFIG_INET" = "y" ]; then + tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET + fi + if [ "$CONFIG_ECONET" != "n" ]; then + bool ' AUN over UDP' CONFIG_ECONET_AUNUDP + bool ' Native Econet' CONFIG_ECONET_NATIVE + fi + tristate 'WAN router' CONFIG_WAN_ROUTER + bool 'Fast switching (read help!)' CONFIG_NET_FASTROUTE + bool 'Forwarding between high speed interfaces' CONFIG_NET_HW_FLOWCONTROL +fi + +mainmenu_option next_comment +comment 'QoS and/or fair queueing' +bool 'QoS and/or fair queueing' CONFIG_NET_SCHED +if [ "$CONFIG_NET_SCHED" = "y" ]; then + source net/sched/Config.in +fi +#bool 'Network code profiler' CONFIG_NET_PROFILE +endmenu + +endmenu diff --git a/kernel/linux/net/Makefile b/kernel/linux/net/Makefile new file mode 100644 index 0000000..25d02b2 --- /dev/null +++ b/kernel/linux/net/Makefile @@ -0,0 +1,63 @@ +# +# Makefile for the linux networking. +# +# 2 Sep 2000, Christoph Hellwig +# Rewritten to use lists instead of if-statements. +# + +O_TARGET := network.o + +mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ + bluetooth atm netlink sched +export-objs := netsyms.o + +subdir-y := core ethernet +subdir-m := ipv4 # hum? + + +subdir-$(CONFIG_NET) += 802 sched netlink +subdir-$(CONFIG_INET) += ipv4 +subdir-$(CONFIG_NETFILTER) += ipv4/netfilter +subdir-$(CONFIG_UNIX) += unix +subdir-$(CONFIG_IPV6) += ipv6 + +ifneq ($(CONFIG_IPV6),n) +ifneq ($(CONFIG_IPV6),) +subdir-$(CONFIG_NETFILTER) += ipv6/netfilter +endif +endif + +ifneq ($(CONFIG_BRIDGE),n) +ifneq ($CONFIG_BRIDGE),) +subdir-$(CONFIG_BRIDGE) += bridge/netfilter +endif +endif + +subdir-$(CONFIG_KHTTPD) += khttpd +subdir-$(CONFIG_PACKET) += packet +subdir-$(CONFIG_NET_SCHED) += sched +subdir-$(CONFIG_BRIDGE) += bridge +subdir-$(CONFIG_IPX) += ipx +subdir-$(CONFIG_ATALK) += appletalk +subdir-$(CONFIG_WAN_ROUTER) += wanrouter +subdir-$(CONFIG_X25) += x25 +subdir-$(CONFIG_LAPB) += lapb +subdir-$(CONFIG_NETROM) += netrom +subdir-$(CONFIG_ROSE) += rose +subdir-$(CONFIG_AX25) += ax25 +subdir-$(CONFIG_IRDA) += irda +subdir-$(CONFIG_BLUEZ) += bluetooth +subdir-$(CONFIG_SUNRPC) += sunrpc +subdir-$(CONFIG_ATM) += atm +subdir-$(CONFIG_DECNET) += decnet +subdir-$(CONFIG_ECONET) += econet +subdir-$(CONFIG_VLAN_8021Q) += 8021q + + +obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y)))) +ifeq ($(CONFIG_NET),y) +obj-$(CONFIG_MODULES) += netsyms.o +obj-$(CONFIG_SYSCTL) += sysctl_net.o +endif + +include $(TOPDIR)/Rules.make diff --git a/kernel/linux/net/bridge/br_input.c b/kernel/linux/net/bridge/br_input.c new file mode 100644 index 0000000..0a5d3eb --- /dev/null +++ b/kernel/linux/net/bridge/br_input.c @@ -0,0 +1,178 @@ +/* + * Handle incoming frames + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id: br_input.c,v 1.1 2002/06/01 19:24:03 bdschuym Exp $ + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "br_private.h" +#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ + defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) +#include +#endif +unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + +static int br_pass_frame_up_finish(struct sk_buff *skb) +{ + netif_rx(skb); + + return 0; +} + +static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) +{ + struct net_device *indev; + + br->statistics.rx_packets++; + br->statistics.rx_bytes += skb->len; + + indev = skb->dev; + skb->dev = &br->dev; + skb->pkt_type = PACKET_HOST; + skb_push(skb, ETH_HLEN); + skb->protocol = eth_type_trans(skb, &br->dev); + + NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, + br_pass_frame_up_finish); +} + +int br_handle_frame_finish(struct sk_buff *skb) +{ + struct net_bridge *br; + unsigned char *dest; + struct net_bridge_fdb_entry *dst; + struct net_bridge_port *p; + int passedup; + + dest = skb->mac.ethernet->h_dest; + + p = skb->dev->br_port; + if (p == NULL) + goto err_nolock; + + br = p->br; + read_lock(&br->lock); + if (skb->dev->br_port == NULL) + goto err; + + passedup = 0; + if (br->dev.flags & IFF_PROMISC) { + struct sk_buff *skb2; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 != NULL) { + passedup = 1; + br_pass_frame_up(br, skb2); + } + } + + if (dest[0] & 1) { + br_flood_forward(br, skb, !passedup); + if (!passedup) + br_pass_frame_up(br, skb); + goto out; + } + + dst = br_fdb_get(br, dest); + if (dst != NULL && dst->is_local) { + if (!passedup) + br_pass_frame_up(br, skb); + else + kfree_skb(skb); + br_fdb_put(dst); + goto out; + } + + if (dst != NULL) { + br_forward(dst->dst, skb); + br_fdb_put(dst); + goto out; + } + + br_flood_forward(br, skb, 0); + +out: + read_unlock(&br->lock); + return 0; + +err: + read_unlock(&br->lock); +err_nolock: + kfree_skb(skb); + return 0; +} + +int br_handle_frame(struct sk_buff *skb) +{ + struct net_bridge *br; + unsigned char *dest; + struct net_bridge_port *p; + + dest = skb->mac.ethernet->h_dest; + + p = skb->dev->br_port; + if (p == NULL) + goto err_nolock; + + br = p->br; + read_lock(&br->lock); + if (skb->dev->br_port == NULL) + goto err; + + if (!(br->dev.flags & IFF_UP) || + p->state == BR_STATE_DISABLED) + goto err; + + if (skb->mac.ethernet->h_source[0] & 1) + goto err; + + if (p->state == BR_STATE_LEARNING || + p->state == BR_STATE_FORWARDING) + br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); + + if (br->stp_enabled && + !memcmp(dest, bridge_ula, 5) && + !(dest[5] & 0xF0)) + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { +#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ + defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) + if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, + skb->dev, NULL, NULL) == NF_DROP) + return -1; +#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); + return 0; + } + +err: + read_unlock(&br->lock); +err_nolock: + kfree_skb(skb); + return 0; + +handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); + return 0; + } + + kfree_skb(skb); + return 0; +} diff --git a/kernel/linux/net/bridge/br_private.h b/kernel/linux/net/bridge/br_private.h new file mode 100644 index 0000000..5e33c9b --- /dev/null +++ b/kernel/linux/net/bridge/br_private.h @@ -0,0 +1,212 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id: br_private.h,v 1.1 2002/06/01 19:24:03 bdschuym Exp $ + * + * 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. + */ + +#ifndef _BR_PRIVATE_H +#define _BR_PRIVATE_H + +#include +#include +#include +#include "br_private_timer.h" + +#define BR_HASH_BITS 8 +#define BR_HASH_SIZE (1 << BR_HASH_BITS) + +#define BR_HOLD_TIME (1*HZ) + +typedef struct bridge_id bridge_id; +typedef struct mac_addr mac_addr; +typedef __u16 port_id; + +struct bridge_id +{ + unsigned char prio[2]; + unsigned char addr[6]; +}; + +struct mac_addr +{ + unsigned char addr[6]; + unsigned char pad[2]; +}; + +struct net_bridge_fdb_entry +{ + struct net_bridge_fdb_entry *next_hash; + struct net_bridge_fdb_entry **pprev_hash; + atomic_t use_count; + mac_addr addr; + struct net_bridge_port *dst; + unsigned long ageing_timer; + unsigned is_local:1; + unsigned is_static:1; +}; + +struct net_bridge_port +{ + struct net_bridge_port *next; + struct net_bridge *br; + struct net_device *dev; + int port_no; + + /* STP */ + port_id port_id; + int state; + int path_cost; + bridge_id designated_root; + int designated_cost; + bridge_id designated_bridge; + port_id designated_port; + unsigned topology_change_ack:1; + unsigned config_pending:1; + int priority; + + struct br_timer forward_delay_timer; + struct br_timer hold_timer; + struct br_timer message_age_timer; +}; + +struct net_bridge +{ + struct net_bridge *next; + rwlock_t lock; + struct net_bridge_port *port_list; + struct net_device dev; + struct net_device_stats statistics; + rwlock_t hash_lock; + struct net_bridge_fdb_entry *hash[BR_HASH_SIZE]; + struct timer_list tick; + + /* STP */ + bridge_id designated_root; + int root_path_cost; + int root_port; + int max_age; + int hello_time; + int forward_delay; + bridge_id bridge_id; + int bridge_max_age; + int bridge_hello_time; + int bridge_forward_delay; + unsigned stp_enabled:1; + unsigned topology_change:1; + unsigned topology_change_detected:1; + + struct br_timer hello_timer; + struct br_timer tcn_timer; + struct br_timer topology_change_timer; + struct br_timer gc_timer; + + int ageing_time; + int gc_interval; +}; + +extern struct notifier_block br_device_notifier; +extern unsigned char bridge_ula[6]; + +/* br.c */ +extern void br_dec_use_count(void); +extern void br_inc_use_count(void); + +/* br_device.c */ +extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); +extern void br_dev_setup(struct net_device *dev); +extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); + +/* br_fdb.c */ +extern void br_fdb_changeaddr(struct net_bridge_port *p, + unsigned char *newaddr); +extern void br_fdb_cleanup(struct net_bridge *br); +extern void br_fdb_delete_by_port(struct net_bridge *br, + struct net_bridge_port *p); +extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, + unsigned char *addr); +extern void br_fdb_put(struct net_bridge_fdb_entry *ent); +extern int br_fdb_get_entries(struct net_bridge *br, + unsigned char *_buf, + int maxnum, + int offset); +extern void br_fdb_insert(struct net_bridge *br, + struct net_bridge_port *source, + unsigned char *addr, + int is_local); + +/* br_forward.c */ +extern void br_deliver(struct net_bridge_port *to, + struct sk_buff *skb); +extern int br_dev_queue_push_xmit(struct sk_buff *skb); +extern void br_forward(struct net_bridge_port *to, + struct sk_buff *skb); +extern int br_forward_finish(struct sk_buff *skb); +extern void br_flood_deliver(struct net_bridge *br, + struct sk_buff *skb, + int clone); +extern void br_flood_forward(struct net_bridge *br, + struct sk_buff *skb, + int clone); + +/* br_if.c */ +extern int br_add_bridge(char *name); +extern int br_del_bridge(char *name); +extern int br_add_if(struct net_bridge *br, + struct net_device *dev); +extern int br_del_if(struct net_bridge *br, + struct net_device *dev); +extern int br_get_bridge_ifindices(int *indices, + int num); +extern void br_get_port_ifindices(struct net_bridge *br, + int *ifindices); + +/* br_input.c */ +extern int br_handle_frame_finish(struct sk_buff *skb); +extern int br_handle_frame(struct sk_buff *skb); + +/* br_ioctl.c */ +extern void br_call_ioctl_atomic(void (*fn)(void)); +extern int br_ioctl(struct net_bridge *br, + unsigned int cmd, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2); +extern int br_ioctl_deviceless_stub(unsigned long arg); + +/* br_netfilter.c */ +extern int br_netfilter_init(void); +extern void br_netfilter_fini(void); + +/* br_stp.c */ +extern int br_is_root_bridge(struct net_bridge *br); +extern struct net_bridge_port *br_get_port(struct net_bridge *br, + int port_no); +extern void br_init_port(struct net_bridge_port *p); +extern port_id br_make_port_id(struct net_bridge_port *p); +extern void br_become_designated_port(struct net_bridge_port *p); + +/* br_stp_if.c */ +extern void br_stp_enable_bridge(struct net_bridge *br); +extern void br_stp_disable_bridge(struct net_bridge *br); +extern void br_stp_enable_port(struct net_bridge_port *p); +extern void br_stp_disable_port(struct net_bridge_port *p); +extern void br_stp_recalculate_bridge_id(struct net_bridge *br); +extern void br_stp_set_bridge_priority(struct net_bridge *br, + int newprio); +extern void br_stp_set_port_priority(struct net_bridge_port *p, + int newprio); +extern void br_stp_set_path_cost(struct net_bridge_port *p, + int path_cost); + +/* br_stp_bpdu.c */ +extern void br_stp_handle_bpdu(struct sk_buff *skb); + +#endif diff --git a/kernel/linux/net/bridge/netfilter/Config.in b/kernel/linux/net/bridge/netfilter/Config.in new file mode 100644 index 0000000..69c176d --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/Config.in @@ -0,0 +1,15 @@ +# +# Bridge netfilter configuration +# +dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE +dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT +dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT +dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE + diff --git a/kernel/linux/net/bridge/netfilter/Makefile b/kernel/linux/net/bridge/netfilter/Makefile new file mode 100644 index 0000000..12eefaa --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/Makefile @@ -0,0 +1,25 @@ +# +# Makefile for the netfilter modules on top of bridging. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := netfilter.o + +export-objs = ebtables.o + +obj-$(CONFIG_BRIDGE_EBT) += ebtables.o +obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o +obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o +obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o +obj-$(CONFIG_BRIDGE_DB) += br_db.o +obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o +obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o +obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o +obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o +obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o +obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o +include $(TOPDIR)/Rules.make diff --git a/kernel/linux/net/bridge/netfilter/br_db.c b/kernel/linux/net/bridge/netfilter/br_db.c new file mode 100644 index 0000000..ad63647 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/br_db.c @@ -0,0 +1,357 @@ +/* + * bridge ethernet protocol database + * + * Authors: + * Bart De Schuymer + * + * br_db.c, April, 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. + */ + +#include +#include +#include +#include +#include +#include +#include /* PF_BRIDGE */ +#include /* rwlock_t */ +#include +#include /* copy_[to,from]_user */ +#include /* multiprocessors */ + +#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) +/*#define BUGPRINT(format, args...)*/ +#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) +/*#define MEMPRINT(format, args...)*/ + +/* database variables */ +static __u16 allowdb = BRDB_NODB; +static struct brdb_dbentry **flowdb = NULL; +static unsigned int *dbsize; +static unsigned int *dbnum; +/* database lock */ +static rwlock_t brdb_dblock; + +static inline int brdb_dev_check(char *entry, const struct net_device *device){ + if (*entry == '\0') return 0; + if (!device) return 1; + return strncmp(entry, device->name, IFNAMSIZ); +} + +static inline int brdb_proto_check(unsigned int a, unsigned int b){ + if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; + return 1; +} + +static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct brdb_dbentry *hlp; + int i, cpunr; + unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; + + cpunr = cpu_number_map(smp_processor_id()); + + read_lock_bh(&brdb_dblock); + + if (allowdb == BRDB_NODB) {// must be after readlock + read_unlock_bh(&brdb_dblock); + return NF_ACCEPT; + } + hlp = flowdb[cpunr]; + /* search for existing entry */ + for (i = 0; i < dbnum[cpunr]; i++) { + if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && + !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { + read_unlock_bh(&brdb_dblock); + return NF_ACCEPT; + } + hlp++; + } + /* add new entry to database */ + if (dbnum[cpunr] == dbsize[cpunr]) { + dbsize[cpunr] *= 2; + if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { + dbsize[cpunr] /= 2; + MEMPRINT("maintaindb && nomemory\n"); + read_unlock_bh(&brdb_dblock); + return NF_ACCEPT; + } + memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); + vfree(flowdb[cpunr]); + flowdb[cpunr] = hlp; + } + + hlp = flowdb[cpunr] + dbnum[cpunr]; + hlp->hook = hook; + if (in) + strncpy(hlp->in, in->name, IFNAMSIZ); + else + hlp->in[0] = '\0'; + if (out) + strncpy(hlp->out, out->name, IFNAMSIZ); + else + hlp->out[0] = '\0'; + if (ntohs(ethproto) < 1536) + hlp->ethproto = IDENTIFY802_3; + else + hlp->ethproto = ethproto; + dbnum[cpunr]++; + + read_unlock_bh(&brdb_dblock); + + return NF_ACCEPT; +} + +static int copy_db(void *user, int *len) +{ + int i, j, nentries = 0, ret; + struct brdb_dbentry *begin, *end1, *end2, *point, *point2; + + write_lock_bh(&brdb_dblock); + for (i = 0; i < smp_num_cpus; i++) + nentries += dbnum[i]; + if (*len > nentries) + return -EINVAL; + + if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) + return -ENOMEM; + memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); + end1 = begin + dbnum[0]; + for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ + point2 = flowdb[i]; + end2 = end1; + for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ + for (point = begin; point != end2; point++)/* cycle different entries we found so far */ + if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && + !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) + goto out;/* already exists in a database of another cpu */ + + memcpy(end1, point2, sizeof(struct brdb_dbentry)); + end1++; +out: + point2++; + } + } + write_unlock_bh(&brdb_dblock); + i = (int)( (char *)end1 - (char *)begin); + *len = i < *len ? i : *len; + if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) + ret = -EFAULT; + else + ret = 0; + vfree(begin); + return ret; +} + +static int switch_nodb(void){ + int i; + + if (!flowdb) + BUGPRINT("switch_nodb && !flowdb\n"); + for (i = 0; i < smp_num_cpus; i++) + vfree(flowdb[i]); + vfree(flowdb); + if (!dbsize) + BUGPRINT("switch_nodb && !dbsize\n"); + vfree(dbsize); + if (!dbnum) + BUGPRINT("switch_nodb && !dbnum\n"); + vfree(dbnum); + flowdb = NULL; + allowdb = BRDB_NODB; + return 0; +} + +static int switch_db(void) +{ + int i, j; + + if (flowdb) BUGPRINT("switch_db && flowdb\n"); + if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { + MEMPRINT("switch_db && nomemory\n"); + return -ENOMEM; + } + + for (i = 0; i < smp_num_cpus; i++) + if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) + goto sw_free1; + else + memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); + + if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) + goto sw_free2; + + if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) + goto sw_free3; + + for (i = 0; i < smp_num_cpus; i++) { + dbnum[i] = 0; + dbsize[i] = INITIAL_DBSIZE; + } + allowdb = BRDB_DB; + return 0; + +sw_free3: + MEMPRINT("switch_db && nomemory2\n"); + vfree(dbnum); + dbnum = NULL; +sw_free2: + MEMPRINT("switch_db && nomemory3\n"); +sw_free1: + MEMPRINT("switch_db && nomemory4\n"); + for (j = 0; j + * Tim Gardner + * + * April, 2002 + * + */ + +#include +#include +#include +#include + +#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) +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, const struct ebt_counter *c) +{ + struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; + + if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != + ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) + return 1; + if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != + ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) + return 1; + if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != + ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) + return 1; + + if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) + { + __u32 arp_len = sizeof(struct arphdr) + + (2*(((*skb).nh.arph)->ar_hln)) + + (2*(((*skb).nh.arph)->ar_pln)); + __u32 dst; + __u32 src; + + // Make sure the packet is long enough. + if ((((*skb).nh.raw) + arp_len) > (*skb).tail) + return 1; + // IPV4 addresses are always 4 bytes. + if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) + return 1; + + if (infostuff->bitmask & EBT_ARP_SRC_IP) { + memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + + ((*skb).nh.arph)->ar_hln, sizeof(__u32)); + if (FWINV2(infostuff->saddr != (src & infostuff->smsk), + EBT_ARP_SRC_IP)) + return 1; + } + + if (infostuff->bitmask & EBT_ARP_DST_IP) { + memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + + (2*(((*skb).nh.arph)->ar_hln)) + + (((*skb).nh.arph)->ar_pln), sizeof(__u32)); + if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), + EBT_ARP_DST_IP)) + return 1; + } + } + return 0; +} + +static int ebt_arp_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; + + if (datalen != sizeof(struct ebt_arp_info)) + return -EINVAL; + if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || + (e->ethproto != __constant_htons(ETH_P_ARP) && + e->ethproto != __constant_htons(ETH_P_RARP)) || + e->invflags & EBT_IPROTO) + return -EINVAL; + if (infostuff->bitmask & ~EBT_ARP_MASK) + return -EINVAL; + return 0; +} + +static struct ebt_match filter_arp = +{ + {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, + 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); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebt_ip.c b/kernel/linux/net/bridge/netfilter/ebt_ip.c new file mode 100644 index 0000000..c34b1b5 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebt_ip.c @@ -0,0 +1,81 @@ +/* + * ebt_ip + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + */ + +#include +#include +#include +#include + +#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) +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, const struct ebt_counter *c) +{ + struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; + + if (infostuff->bitmask & EBT_IP_TOS && + FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) + return 1; + if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != + ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) + return 1; + if (infostuff->bitmask & EBT_IP_SOURCE && + FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != + infostuff->saddr, EBT_IP_SOURCE)) + return 1; + if ((infostuff->bitmask & EBT_IP_DEST) && + FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != + infostuff->daddr, EBT_IP_DEST)) + return 1; + return 0; +} + +static int ebt_ip_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; + + if (datalen != sizeof(struct ebt_ip_info)) { + return -EINVAL; + } + if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || + e->ethproto != __constant_htons(ETH_P_IP) || + e->invflags & EBT_IPROTO) + { + return -EINVAL; + } + if (infostuff->bitmask & ~EBT_IP_MASK) { + return -EINVAL; + } + return 0; +} + +static struct ebt_match filter_ip = +{ + {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, + 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); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebt_log.c b/kernel/linux/net/bridge/netfilter/ebt_log.c new file mode 100644 index 0000000..e7f0506 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebt_log.c @@ -0,0 +1,111 @@ +/* + * ebt_log + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + */ + +#include +#include +#include +#include +#include +#include + +static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; + +static int ebt_log_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_log_info *loginfo = (struct ebt_log_info *)data; + + if (datalen != sizeof(struct ebt_log_info)) + return -EINVAL; + if (loginfo->bitmask & ~EBT_LOG_MASK) + return -EINVAL; + if (loginfo->loglevel >= 8) + return -EINVAL; + loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; + return 0; +} + +static void ebt_log(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *data, unsigned int datalen, + const struct ebt_counter *c) +{ + struct ebt_log_info *loginfo = (struct ebt_log_info *)data; + char level_string[4] = "< >"; + level_string[1] = '0' + loginfo->loglevel; + + spin_lock_bh(&ebt_log_lock); + printk(level_string); + // max length: 29 + 10 + 2 * 16 + printk("%s IN=%s OUT=%s ", + loginfo->prefix, + in ? in->name : "", + out ? out->name : ""); + + if (skb->dev->hard_header_len) { + int i; + unsigned char *p = (skb->mac.ethernet)->h_source; + printk("MAC source = "); + for (i = 0; i < ETH_ALEN; i++,p++) + printk("%02x%c", *p, + i == ETH_ALEN - 1 + ? ' ':':');// length: 31 + printk("MAC dest = "); + p = (skb->mac.ethernet)->h_dest; + for (i = 0; i < ETH_ALEN; i++,p++) + printk("%02x%c", *p, + i == ETH_ALEN - 1 + ? ' ':':');// length: 29 + } + // length: 14 + printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); + + if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == + htons(ETH_P_IP)){ + struct iphdr *iph = skb->nh.iph; + // max length: 46 + printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", + NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); + // max length: 26 + printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); + } + + if ((loginfo->bitmask & EBT_LOG_ARP) && + ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || + (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { + struct arphdr * arph = skb->nh.arph; + // max length: 40 + printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", + ntohs(arph->ar_hrd), ntohs(arph->ar_pro), + ntohs(arph->ar_op)); + } + printk("\n"); + spin_unlock_bh(&ebt_log_lock); +} + +struct ebt_watcher log = +{ + {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, + 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); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebt_nat.c b/kernel/linux/net/bridge/netfilter/ebt_nat.c new file mode 100644 index 0000000..16694c5 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebt_nat.c @@ -0,0 +1,106 @@ +/* + * ebt_nat + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + */ + +#include +#include +#include +#include +#include +#include + +static __u8 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 *infostuff = (struct ebt_nat_info *) data; + + memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, + ETH_ALEN * sizeof(unsigned char)); + return infostuff->target; +} + +static __u8 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 *infostuff = (struct ebt_nat_info *) data; + + memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, + ETH_ALEN * sizeof(unsigned char)); + return infostuff->target; +} + +static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; + + if (strcmp(tablename, "nat")) + return -EINVAL; + if (datalen != sizeof(struct ebt_nat_info)) + return -EINVAL; + if (hooknr != NF_BR_POST_ROUTING) + return -EINVAL; + if (infostuff->target >= NUM_STANDARD_TARGETS) + return -EINVAL; + return 0; +} + +static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; + + if ( (strcmp(tablename, "nat") || + (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && + (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) + return -EINVAL; + if (datalen != sizeof(struct ebt_nat_info)) + return -EINVAL; + if (infostuff->target >= NUM_STANDARD_TARGETS) + return -EINVAL; + return 0; +} + +static struct ebt_target snat = +{ + {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, + NULL, THIS_MODULE +}; + +static struct ebt_target dnat = +{ + {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, + NULL, THIS_MODULE +}; + +static int __init init(void) +{ + int ret; + ret = ebt_register_target(&snat); + if (ret != 0) + return ret; + ret = ebt_register_target(&dnat); + if (ret == 0) + return 0; + ebt_unregister_target(&snat); + return ret; +} + +static void __exit fini(void) +{ + ebt_unregister_target(&snat); + ebt_unregister_target(&dnat); +} + +module_init(init); +module_exit(fini); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebt_redirect.c b/kernel/linux/net/bridge/netfilter/ebt_redirect.c new file mode 100644 index 0000000..c26d57b --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebt_redirect.c @@ -0,0 +1,65 @@ +/* + * ebt_redirect + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + */ + +#include +#include +#include +#include +#include +#include +#include "../br_private.h" + +static __u8 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 *infostuff = (struct ebt_redirect_info *) data; + + memcpy((**pskb).mac.ethernet->h_dest, + in->br_port->br->dev.dev_addr, ETH_ALEN); + (*pskb)->pkt_type = PACKET_HOST; + return infostuff->target; +} + +static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; + + if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && + (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) + return -EINVAL; + if (datalen != sizeof(struct ebt_redirect_info)) + return -EINVAL; + if (infostuff->target >= NUM_STANDARD_TARGETS) + return -EINVAL; + return 0; +} + +static struct ebt_target redirect_target = +{ + {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, + ebt_target_redirect_check, NULL, 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); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebt_vlan.c b/kernel/linux/net/bridge/netfilter/ebt_vlan.c new file mode 100644 index 0000000..8ad921d --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebt_vlan.c @@ -0,0 +1,124 @@ +/* + * ebt_vlan kernelspace + * + * Authors: + * Bart De Schuymer + * Nick Fedchik + * + * May, 2002 + */ + +#include +#include +#include +#include +#include + +static unsigned char debug; +MODULE_PARM (debug, "0-1b"); +MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages"); + +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, + const struct ebt_counter *c) +{ + struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; + struct vlan_ethhdr *vlanethhdr = + (struct vlan_ethhdr *) skb->mac.raw; + unsigned short v_id; + unsigned short v_prio; + + /* + * Calculate 802.1Q VLAN ID and Priority + * Reserved one bit (13) for CFI + */ + v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF; + v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13; + + /* + * Checking VLANs + */ + if (infostuff->bitmask & EBT_VLAN_ID) { /* Is VLAN ID parsed? */ + if (!((infostuff->id == v_id) + ^ !!(infostuff->invflags & EBT_VLAN_ID))) + return 1; + if (debug) + printk (KERN_DEBUG + "ebt_vlan: matched ID=%s%d (mask=%X)\n", + (infostuff->invflags & EBT_VLAN_ID) ? "!" : "", + infostuff->id, + (unsigned char) infostuff->bitmask); + } + /* + * Checking Priority + */ + if (infostuff->bitmask & EBT_VLAN_PRIO) { /* Is VLAN Prio parsed? */ + if (!( (infostuff->prio == v_prio) + ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) + return 1; /* missed */ + if (debug) + printk (KERN_DEBUG + "ebt_vlan: matched Prio=%s%d (mask=%X)\n", + (infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "", + infostuff->prio, + (unsigned char) infostuff->bitmask); + } + /* + * rule matched + */ + return 0; +} + +/* + * ebt_vlan_check() is called when userspace delivers the table to the kernel, + * * it is called to check that userspace doesn't give a bad table. + */ +static int ebt_vlan_check (const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, + unsigned int datalen) +{ + struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; + + if (datalen != sizeof (struct ebt_vlan_info)) + return -EINVAL; + + if (e->ethproto != __constant_htons (ETH_P_8021Q)) + return -EINVAL; + + if (infostuff->bitmask & ~EBT_VLAN_MASK) { + return -EINVAL; + } + + return 0; +} + +static struct ebt_match filter_vlan = { + {NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check, + NULL, + THIS_MODULE +}; + +static int __init init (void) +{ + printk (KERN_INFO + "ebt_vlan: 802.1Q VLAN matching module for EBTables\n"); + if (debug) + printk (KERN_DEBUG + "ebt_vlan: 802.1Q matching debug is on\n"); + return ebt_register_match (&filter_vlan); +} + +static void __exit fini (void) +{ + ebt_unregister_match (&filter_vlan); +} + +module_init (init); +module_exit (fini); +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR ("Nick Fedchik "); +MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1"); +MODULE_LICENSE ("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebtable_broute.c b/kernel/linux/net/bridge/netfilter/ebtable_broute.c new file mode 100644 index 0000000..ce880a2 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebtable_broute.c @@ -0,0 +1,80 @@ +/* + * ebtable_broute + * + * Authors: + * Bart De Schuymer + * + * 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 +#include +#include +#include +#include +#include + +// EBT_ACCEPT means the frame will be bridged +// EBT_DROP means the frame will be routed +static struct ebt_entries initial_chain = + {0, EBT_ACCEPT, 0}; + +static struct ebt_replace initial_table = +{ + "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), + { [NF_BR_BROUTING]&initial_chain}, {}, + 0, NULL, (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 = +{ + {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, + RW_LOCK_UNLOCKED, check, NULL +}; + +static unsigned int +ebt_broute (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, &broute_table); +} + +static int __init init(void) +{ + int ret; + + ret = ebt_register_table(&broute_table); + if (ret < 0) + return ret; + br_write_lock_bh(BR_NETPROTO_LOCK); + // in br_input.c, br_handle_frame() wants to call broute_decision() + broute_decision = ebt_broute; + br_write_unlock_bh(BR_NETPROTO_LOCK); + return ret; +} + +static void __exit fini(void) +{ + br_write_lock_bh(BR_NETPROTO_LOCK); + broute_decision = NULL; + br_write_unlock_bh(BR_NETPROTO_LOCK); + ebt_unregister_table(&broute_table); +} + +module_init(init); +module_exit(fini); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebtable_filter.c b/kernel/linux/net/bridge/netfilter/ebtable_filter.c new file mode 100644 index 0000000..e16f696 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebtable_filter.c @@ -0,0 +1,93 @@ +/* + * ebtable_filter + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + */ + +#include +#include +#include + +#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ + (1 << NF_BR_LOCAL_OUT)) + +static struct ebt_entries initial_chains[] = +{ + {0, EBT_ACCEPT, 0}, + {0, EBT_ACCEPT, 0}, + {0, EBT_ACCEPT, 0} +}; + +static struct ebt_replace initial_table = +{ + "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), + { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], + [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, + 0, NULL, (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 = +{ + {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, + RW_LOCK_UNLOCKED, check, NULL +}; + +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[] = { + { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, + NF_BR_PRI_FILTER_BRIDGED}, + { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, + NF_BR_PRI_FILTER_BRIDGED}, + { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, + 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) + nf_unregister_hook(&ebt_ops_filter[i]); + ebt_unregister_table(&frame_filter); +} + +module_init(init); +module_exit(fini); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebtable_nat.c b/kernel/linux/net/bridge/netfilter/ebtable_nat.c new file mode 100644 index 0000000..b99db09 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebtable_nat.c @@ -0,0 +1,156 @@ +/* + * ebtable_nat + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + */ + +#include +#include +#include +#include +#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[] = +{ + {0, EBT_ACCEPT, 0}, + {0, EBT_ACCEPT, 0}, + {0, EBT_ACCEPT, 0} +}; + +static struct ebt_replace initial_table = +{ + "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), + { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], + [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, + 0, NULL, (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 = +{ + {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, + RW_LOCK_UNLOCKED, check, NULL +}; + +// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. +// needed because of the bridge-nf patch (that allows use of iptables +// on bridged traffic) +// if the packet is routed, we want the ebtables stuff on POSTROUTING +// to be executed _after_ the iptables stuff. when it's bridged, it's +// the way around +static struct net_device __fake_net_device = { + hard_header_len: ETH_HLEN +}; + +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); +} + +// let snat know this frame is routed +static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + (*pskb)->physindev = NULL; + return NF_ACCEPT; +} + +// let snat know this frame is bridged +static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + (*pskb)->physindev = &__fake_net_device; + return NF_ACCEPT; +} + +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 *)) +{ + // this is a routed packet + if ((*pskb)->physindev == NULL) + return NF_ACCEPT; + if ((*pskb)->physindev != &__fake_net_device) + printk("ebtables (br_nat_src): physindev hack " + "doesn't work - BUG\n"); + + return ebt_do_table(hook, pskb, in, out, &frame_nat); +} + +static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + // this is a bridged packet + if ((*pskb)->physindev == &__fake_net_device) + return NF_ACCEPT; + if ((*pskb)->physindev) + printk("ebtables (br_nat_src_route): physindev hack " + "doesn't work - BUG\n"); + + return ebt_do_table(hook, pskb, in, out, &frame_nat); +} + +static struct nf_hook_ops ebt_ops_nat[] = { + { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, + NF_BR_PRI_NAT_DST_OTHER}, + { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, + NF_BR_PRI_NAT_SRC_BRIDGED}, + { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, + NF_BR_PRI_NAT_SRC_OTHER}, + { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, + NF_BR_PRI_NAT_DST_BRIDGED}, + { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, + NF_BR_PRI_FILTER_OTHER + 1}, + { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, + NF_BR_PRI_FILTER_OTHER + 1} +}; + +static int __init init(void) +{ + int i, ret, j; + + ret = ebt_register_table(&frame_nat); + if (ret < 0) + return ret; + for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) + nf_unregister_hook(&ebt_ops_nat[i]); + ebt_unregister_table(&frame_nat); +} + +module_init(init); +module_exit(fini); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); diff --git a/kernel/linux/net/bridge/netfilter/ebtables.c b/kernel/linux/net/bridge/netfilter/ebtables.c new file mode 100644 index 0000000..3f8d550 --- /dev/null +++ b/kernel/linux/net/bridge/netfilter/ebtables.c @@ -0,0 +1,1189 @@ +/* + * ebtables + * + * Author: + * Bart De Schuymer + * + * ebtables.c,v 2.0, April, 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// 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 + +#if 0 // use this for remote debugging +#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...) + +static void print_string(char *str); + +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, + const struct net_device *in, + const struct net_device *out, + const struct ebt_counter *c) +{ + w->u.watcher->watcher(skb, in, out, w->data, + w->watcher_size, c); + // 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, + const struct ebt_counter *c) +{ + return m->u.match->match(skb, in, out, m->data, + m->match_size, c); +} + +static inline int ebt_dev_check(char *entry, const struct net_device *device) +{ + if (*entry == '\0') + return 0; + if (!device) + return 1; + return strncmp(entry, device->name, IFNAMSIZ); +} + +// 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; + struct ebt_entry_target *t; + __u8 verdict; + + read_lock_bh(&table->lock); + nentries = table->private->hook_entry[hook]->nentries; + point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); + counter_base = table->private->counters + + cpu_number_map(smp_processor_id()) * table->private->nentries + + table->private->counter_entry[hook]; + #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) + for (i = 0; i < nentries; i++) { + if ( ( point->bitmask & EBT_NOPROTO || + FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, + EBT_IPROTO) + || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && + (point->bitmask & EBT_802_3), EBT_IPROTO) ) + && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) + && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) + && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) + (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) + && ((!out || !out->br_port) ? 1 : + FWINV(!ebt_dev_check((char *) + (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) + + ) { + char hlpmac[6]; + int j; + + if (point->bitmask & EBT_SOURCEMAC) { + for (j = 0; j < 6; j++) + hlpmac[j] = ((**pskb).mac.ethernet)-> + h_source[j] & point->sourcemsk[j]; + if (FWINV(!!memcmp(point->sourcemac, hlpmac, + ETH_ALEN), EBT_ISOURCE) ) + goto letscontinue; + } + + if (point->bitmask & EBT_DESTMAC) { + for (j = 0; j < 6; j++) + hlpmac[j] = ((**pskb).mac.ethernet)-> + h_dest[j] & point->destmsk[j]; + if (FWINV(!!memcmp(point->destmac, hlpmac, + ETH_ALEN), EBT_IDEST) ) + goto letscontinue; + } + + if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, + out, counter_base + i) != 0) + goto letscontinue; + + // increase counter + (*(counter_base + i)).pcnt++; + + // these should only watch: not modify, nor tell us + // what to do with the packet + EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, + out, counter_base + i); + + 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_CONTINUE) { + read_unlock_bh(&table->lock); + BUGPRINT("Illegal target while " + "firewalling!!\n"); + // Try not to get oopsen + return NF_DROP; + } + } +letscontinue: + point = (struct ebt_entry *) + (((char *)point) + point->next_offset); + } + + if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { + read_unlock_bh(&table->lock); + return NF_ACCEPT; + } + read_unlock_bh(&table->lock); + return NF_DROP; +} + +static inline int +ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, + const char *name, unsigned int hook, unsigned int *cnt) +{ + struct ebt_match *match; + int ret; + + m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; + ret = down_interruptible(&ebt_mutex); + if (ret != 0) + return -EFAULT; + if (!(match = (struct ebt_match *) + list_named_find(&ebt_matches, m->u.name))) { + up(&ebt_mutex); + return -ENOENT; + } + m->u.match = match; + if (match->check && + match->check(name, hook, e, m->data, + m->match_size) != 0) { + BUGPRINT("match->check failed\n"); + up(&ebt_mutex); + return -EINVAL; + } + if (match->me) + __MOD_INC_USE_COUNT(match->me); + up(&ebt_mutex); + (*cnt)++; + return 0; +} + +static inline int +ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, + const char *name, unsigned int hook, unsigned int *cnt) +{ + struct ebt_watcher *watcher; + int ret; + + ret = down_interruptible(&ebt_mutex); + if (ret != 0) + return -EFAULT; + w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; + if (!(watcher = (struct ebt_watcher *) + list_named_find(&ebt_watchers, w->u.name))) { + up(&ebt_mutex); + return -ENOENT; + } + w->u.watcher = watcher; + if (watcher->check && + watcher->check(name, hook, e, w->data, + w->watcher_size) != 0) { + BUGPRINT("watcher->check failed\n"); + up(&ebt_mutex); + return -EINVAL; + } + if (watcher->me) + __MOD_INC_USE_COUNT(watcher->me); + up(&ebt_mutex); + (*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 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) { + 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) { + BUGPRINT("bad policy\n"); + return -EINVAL; + } + *n = ((struct ebt_entries *)e)->nentries; + *cnt = 0; + newinfo->hook_entry[i] = (struct ebt_entries *)e; + newinfo->counter_entry[i] = *totalcnt; + 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; + } + if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { + BUGPRINT("entry offsets point too far\n"); + return -EINVAL; + } + + if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { + BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " + "bitmask for an entry\n"); + return -EINVAL; + } + (*cnt)++; + (*totalcnt)++; + 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); + if (m->u.match->me) + __MOD_DEC_USE_COUNT(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); + if (w->u.watcher->me) + __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; + struct ebt_target *target; + unsigned int i, j, hook = 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; + } + e->in[IFNAMSIZ - 1] = '\0'; + e->out[IFNAMSIZ - 1] = '\0'; + e->logical_in[IFNAMSIZ - 1] = '\0'; + e->logical_out[IFNAMSIZ - 1] = '\0'; + // 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; + } + i = 0; + ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); + if (ret != 0) + goto cleanup_matches; + j = 0; + ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); + if (ret != 0) + goto cleanup_watchers; + t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); + ret = down_interruptible(&ebt_mutex); + if (ret != 0) + goto cleanup_watchers; + t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; + if (!(target = (struct ebt_target *) + list_named_find(&ebt_targets, t->u.name))) { + ret = -ENOENT; + up(&ebt_mutex); + goto cleanup_watchers; + } + if (target->me) + __MOD_INC_USE_COUNT(target->me); + 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 (t->u.target->check && + t->u.target->check(name, hook, e, t->data, + t->target_size) != 0) { + if (t->u.target->me) + __MOD_DEC_USE_COUNT(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; +} + +static inline int +ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) +{ + struct ebt_entry_target *t; + + if (e->bitmask == 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); + if (t->u.target->me) + __MOD_DEC_USE_COUNT(t->u.target->me); + + 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; + int ret; + + 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->counter_entry[i] = 0; + } + + 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 + 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, + 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; + } + } + + // we just don't trust anything + repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; + // 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); + if (ret != 0) { + BUGPRINT("ebt_check_entry gave fault back\n"); + EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); + } + return ret; +} + +// called under write_lock +static inline void get_counters(struct ebt_table_info *info, + struct ebt_counter *counters) +{ + int i, cpu, counter_base; + + // counters of cpu 0 + memcpy(counters, info->counters, + sizeof(struct ebt_counter) * info->nentries); + // add other counters to those of cpu 0 + for (cpu = 1; cpu < smp_num_cpus; cpu++) { + counter_base = cpu * info->nentries; + for (i = 0; i < info->nentries; i++) + counters[i].pcnt += + info->counters[counter_base + i].pcnt; + } +} + +// replace the table +static int do_replace(void *user, unsigned int len) +{ + int ret; + 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; + } + newinfo = (struct ebt_table_info *) + vmalloc(sizeof(struct ebt_table_info)); + if (!newinfo) + return -ENOMEM; + + if (tmp.nentries) { + newinfo->counters = (struct ebt_counter *)vmalloc( + sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); + if (!newinfo->counters) { + ret = -ENOMEM; + goto free_newinfo; + } + memset(newinfo->counters, 0, + sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); + } + else + newinfo->counters = NULL; + + newinfo->entries = (char *)vmalloc(tmp.entries_size); + if (!newinfo->entries) { + ret = -ENOMEM; + goto free_counters; + } + 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; + + ret = translate_table(&tmp, newinfo); + + if (ret != 0) + goto free_counterstmp; + + ret = down_interruptible(&ebt_mutex); + + if (ret != 0) + goto free_cleanup; + + if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { + ret = -ENOENT; + // give some help to the poor user + print_string("The table is not present, try insmod\n"); + goto free_unlock; + } + + // 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; + // we need an atomic snapshot of the counters + write_lock_bh(&t->lock); + if (tmp.num_counters) + get_counters(t->private, counterstmp); + + t->private = newinfo; + write_unlock_bh(&t->lock); + up(&ebt_mutex); + // So, a user can change the chains while having messed up his counter + // allocation. Only reason why I do this 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->counters) + vfree(table->counters); + vfree(table); + + if (counterstmp) + vfree(counterstmp); + return ret; + +free_unlock: + up(&ebt_mutex); +free_cleanup: + EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, + ebt_cleanup_entry, NULL); +free_counterstmp: + if (counterstmp) + vfree(counterstmp); +free_entries: + if (newinfo->entries) + vfree(newinfo->entries); +free_counters: + if (newinfo->counters) + vfree(newinfo->counters); +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); + MOD_INC_USE_COUNT; + + return 0; +} + +void ebt_unregister_target(struct ebt_target *target) +{ + down(&ebt_mutex); + LIST_DELETE(&ebt_targets, target); + up(&ebt_mutex); + MOD_DEC_USE_COUNT; +} + +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); + MOD_INC_USE_COUNT; + + return 0; +} + +void ebt_unregister_match(struct ebt_match *match) +{ + down(&ebt_mutex); + LIST_DELETE(&ebt_matches, match); + up(&ebt_mutex); + MOD_DEC_USE_COUNT; +} + +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); + MOD_INC_USE_COUNT; + + return 0; +} + +void ebt_unregister_watcher(struct ebt_watcher *watcher) +{ + down(&ebt_mutex); + LIST_DELETE(&ebt_watchers, watcher); + up(&ebt_mutex); + MOD_DEC_USE_COUNT; +} + +int ebt_register_table(struct ebt_table *table) +{ + struct ebt_table_info *newinfo; + int ret; + + 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; + } + + newinfo = (struct ebt_table_info *) + vmalloc(sizeof(struct ebt_table_info)); + 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 (table->table->nentries) { + newinfo->counters = (struct ebt_counter *) + vmalloc(table->table->nentries * + sizeof(struct ebt_counter) * smp_num_cpus); + if (!newinfo->counters) + goto free_entries; + memset(newinfo->counters, 0, table->table->nentries * + sizeof(struct ebt_counter) * smp_num_cpus); + } + else + newinfo->counters = NULL; + + // fill in newinfo and parse the entries + ret = translate_table(table->table, newinfo); + if (ret != 0) { + BUGPRINT("Translate_table failed\n"); + goto free_counters; + } + + 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; + table->lock = RW_LOCK_UNLOCKED; + ret = down_interruptible(&ebt_mutex); + if (ret != 0) + goto free_counters; + + if (list_named_find(&ebt_tables, table->name)) { + ret = -EEXIST; + BUGPRINT("Table name already exists\n"); + goto free_unlock; + } + + list_prepend(&ebt_tables, table); + up(&ebt_mutex); + MOD_INC_USE_COUNT; + return 0; +free_unlock: + up(&ebt_mutex); +free_counters: + if (newinfo->counters) + vfree(newinfo->counters); +free_entries: + vfree(newinfo->entries); +free_newinfo: + vfree(newinfo); + return ret; +} + +void ebt_unregister_table(struct ebt_table *table) +{ + if (!table) { + BUGPRINT("Request to unregister NULL table!!!\n"); + return; + } + down(&ebt_mutex); + LIST_DELETE(&ebt_tables, table); + up(&ebt_mutex); + EBT_ENTRY_ITERATE(table->private->entries, + table->private->entries_size, ebt_cleanup_entry, NULL); + if (table->private->counters) + vfree(table->private->counters); + if (table->private->entries) + vfree(table->private->entries); + vfree(table->private); + MOD_DEC_USE_COUNT; +} + +// userspace just supplied us with counters +static int update_counters(void *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("Updata_counters && nomemory\n"); + return -ENOMEM; + } + + hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; + ret = down_interruptible(&ebt_mutex); + if (ret != 0) + goto free_tmp; + + if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { + ret = -EINVAL; + goto unlock_mutex; + } + + 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; + + 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 = ubase - base + (char *)e + e->target_offset; + struct ebt_entry_target *t; + + if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) + return 0; + + 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, int *len) +{ + struct ebt_replace tmp; + struct ebt_table_info *info = t->private; + struct ebt_counter *counterstmp; + int i; + + if (copy_from_user(&tmp, user, sizeof(tmp))) { + BUGPRINT("Cfu didn't work\n"); + return -EFAULT; + } + + if (*len != sizeof(struct ebt_replace) + info->entries_size + + (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { + BUGPRINT("Wrong size\n"); + return -EINVAL; + } + + if (tmp.nentries != info->nentries) { + BUGPRINT("Nentries wrong\n"); + return -EINVAL; + } + + if (tmp.entries_size != info->entries_size) { + BUGPRINT("Wrong size\n"); + return -EINVAL; + } + + // userspace might not need the counters + if (tmp.num_counters) { + if (tmp.num_counters != info->nentries) { + BUGPRINT("Num_counters wrong\n"); + return -EINVAL; + } + counterstmp = (struct ebt_counter *) + vmalloc(info->nentries * sizeof(struct ebt_counter)); + if (!counterstmp) { + BUGPRINT("Couldn't copy counters, out of memory\n"); + return -ENOMEM; + } + write_lock_bh(&t->lock); + get_counters(info, counterstmp); + write_unlock_bh(&t->lock); + + if (copy_to_user(tmp.counters, counterstmp, + info->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, info->entries, info->entries_size)) { + BUGPRINT("Couldn't copy entries to userspace\n"); + return -EFAULT; + } + // make userspace's life easier + memcpy(tmp.counter_entry, info->counter_entry, + NF_BR_NUMHOOKS * sizeof(int)); + memcpy(tmp.hook_entry, info->hook_entry, + NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); + for (i = 0; i < NF_BR_NUMHOOKS; i++) + tmp.hook_entry[i] = (struct ebt_entries *)(((char *) + (info->hook_entry[i])) - info->entries + tmp.entries); + if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { + BUGPRINT("Couldn't copy ebt_replace to userspace\n"); + return -EFAULT; + } + // set the match/watcher/target names right + return EBT_ENTRY_ITERATE(info->entries, info->entries_size, + ebt_make_names, info->entries, tmp.entries); +} + +static int do_ebt_set_ctl(struct sock *sk, + int cmd, void *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, int *len) +{ + int ret; + struct ebt_replace tmp; + struct ebt_table *t; + + if (copy_from_user(&tmp, user, sizeof(tmp))) + return -EFAULT; + + ret = down_interruptible(&ebt_mutex); + if (ret != 0) + return ret; + + if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { + print_string("Table not found, try insmod\n"); + up(&ebt_mutex); + return -EINVAL; + } + + switch(cmd) { + case EBT_SO_GET_INFO: + if (*len != sizeof(struct ebt_replace)){ + ret = -EINVAL; + up(&ebt_mutex); + break; + } + tmp.nentries = t->private->nentries; + tmp.entries_size = t->private->entries_size; + // userspace needs this to check the chain names + tmp.valid_hooks = t->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: + ret = copy_everything_to_user(t, user, len); + 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 +}; + +// 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->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); + } +} + +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; + + print_string("Ebtables v2.0 registered"); + return 0; +} + +static void __exit fini(void) +{ + nf_unregister_sockopt(&ebt_sockopts); + print_string("Ebtables v2.0 unregistered"); +} + +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"); diff --git a/kernel/linux/net/netsyms.c b/kernel/linux/net/netsyms.c new file mode 100644 index 0000000..14fa1bd --- /dev/null +++ b/kernel/linux/net/netsyms.c @@ -0,0 +1,595 @@ +/* + * linux/net/netsyms.c + * + * Symbol table for the linux networking subsystem. Moved here to + * make life simpler in ksyms.c. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HIPPI +#include +#endif +#include +#include +#include +#include +#include +#ifdef CONFIG_NET_DIVERT +#include +#endif /* CONFIG_NET_DIVERT */ + +#ifdef CONFIG_NET +extern __u32 sysctl_wmem_max; +extern __u32 sysctl_rmem_max; +#endif + +#ifdef CONFIG_INET +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct net_proto_family inet_family_ops; + +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) +#include +#include +#include +#include +#include +#include + +extern int sysctl_local_port_range[2]; +extern int tcp_port_rover; +extern int udp_port_rover; +#endif + +#endif + +extern int netdev_finish_unregister(struct net_device *dev); + +#include + +#ifdef CONFIG_IPX_MODULE +extern struct datalink_proto *make_EII_client(void); +extern struct datalink_proto *make_8023_client(void); +extern void destroy_EII_client(struct datalink_proto *); +extern void destroy_8023_client(struct datalink_proto *); +#endif + +#ifdef CONFIG_ATALK_MODULE +#include +#endif + +#ifdef CONFIG_SYSCTL +extern int sysctl_max_syn_backlog; +#endif + +/* Skbuff symbols. */ +EXPORT_SYMBOL(skb_over_panic); +EXPORT_SYMBOL(skb_under_panic); + +/* Socket layer registration */ +EXPORT_SYMBOL(sock_register); +EXPORT_SYMBOL(sock_unregister); + +/* Socket locking */ +EXPORT_SYMBOL(__lock_sock); +EXPORT_SYMBOL(__release_sock); + +/* Socket layer support routines */ +EXPORT_SYMBOL(memcpy_fromiovec); +EXPORT_SYMBOL(memcpy_tokerneliovec); +EXPORT_SYMBOL(sock_create); +EXPORT_SYMBOL(sock_alloc); +EXPORT_SYMBOL(sock_release); +EXPORT_SYMBOL(sock_setsockopt); +EXPORT_SYMBOL(sock_getsockopt); +EXPORT_SYMBOL(sock_sendmsg); +EXPORT_SYMBOL(sock_recvmsg); +EXPORT_SYMBOL(sk_alloc); +EXPORT_SYMBOL(sk_free); +EXPORT_SYMBOL(sock_wake_async); +EXPORT_SYMBOL(sock_alloc_send_skb); +EXPORT_SYMBOL(sock_alloc_send_pskb); +EXPORT_SYMBOL(sock_init_data); +EXPORT_SYMBOL(sock_no_release); +EXPORT_SYMBOL(sock_no_bind); +EXPORT_SYMBOL(sock_no_connect); +EXPORT_SYMBOL(sock_no_socketpair); +EXPORT_SYMBOL(sock_no_accept); +EXPORT_SYMBOL(sock_no_getname); +EXPORT_SYMBOL(sock_no_poll); +EXPORT_SYMBOL(sock_no_ioctl); +EXPORT_SYMBOL(sock_no_listen); +EXPORT_SYMBOL(sock_no_shutdown); +EXPORT_SYMBOL(sock_no_getsockopt); +EXPORT_SYMBOL(sock_no_setsockopt); +EXPORT_SYMBOL(sock_no_sendmsg); +EXPORT_SYMBOL(sock_no_recvmsg); +EXPORT_SYMBOL(sock_no_mmap); +EXPORT_SYMBOL(sock_no_sendpage); +EXPORT_SYMBOL(sock_rfree); +EXPORT_SYMBOL(sock_wfree); +EXPORT_SYMBOL(sock_wmalloc); +EXPORT_SYMBOL(sock_rmalloc); +EXPORT_SYMBOL(skb_linearize); +EXPORT_SYMBOL(skb_checksum); +EXPORT_SYMBOL(skb_checksum_help); +EXPORT_SYMBOL(skb_recv_datagram); +EXPORT_SYMBOL(skb_free_datagram); +EXPORT_SYMBOL(skb_copy_datagram); +EXPORT_SYMBOL(skb_copy_datagram_iovec); +EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec); +EXPORT_SYMBOL(skb_copy_bits); +EXPORT_SYMBOL(skb_copy_and_csum_bits); +EXPORT_SYMBOL(skb_copy_and_csum_dev); +EXPORT_SYMBOL(skb_copy_expand); +EXPORT_SYMBOL(___pskb_trim); +EXPORT_SYMBOL(__pskb_pull_tail); +EXPORT_SYMBOL(pskb_expand_head); +EXPORT_SYMBOL(pskb_copy); +EXPORT_SYMBOL(skb_realloc_headroom); +EXPORT_SYMBOL(datagram_poll); +EXPORT_SYMBOL(put_cmsg); +EXPORT_SYMBOL(sock_kmalloc); +EXPORT_SYMBOL(sock_kfree_s); + +#ifdef CONFIG_FILTER +EXPORT_SYMBOL(sk_run_filter); +EXPORT_SYMBOL(sk_chk_filter); +#endif + +EXPORT_SYMBOL(neigh_table_init); +EXPORT_SYMBOL(neigh_table_clear); +EXPORT_SYMBOL(neigh_resolve_output); +EXPORT_SYMBOL(neigh_connected_output); +EXPORT_SYMBOL(neigh_update); +EXPORT_SYMBOL(neigh_create); +EXPORT_SYMBOL(neigh_lookup); +EXPORT_SYMBOL(__neigh_event_send); +EXPORT_SYMBOL(neigh_event_ns); +EXPORT_SYMBOL(neigh_ifdown); +#ifdef CONFIG_ARPD +EXPORT_SYMBOL(neigh_app_ns); +#endif +#ifdef CONFIG_SYSCTL +EXPORT_SYMBOL(neigh_sysctl_register); +#endif +EXPORT_SYMBOL(pneigh_lookup); +EXPORT_SYMBOL(pneigh_enqueue); +EXPORT_SYMBOL(neigh_destroy); +EXPORT_SYMBOL(neigh_parms_alloc); +EXPORT_SYMBOL(neigh_parms_release); +EXPORT_SYMBOL(neigh_rand_reach_time); +EXPORT_SYMBOL(neigh_compat_output); + +/* dst_entry */ +EXPORT_SYMBOL(dst_alloc); +EXPORT_SYMBOL(__dst_free); +EXPORT_SYMBOL(dst_destroy); + +/* misc. support routines */ +EXPORT_SYMBOL(net_ratelimit); +EXPORT_SYMBOL(net_random); +EXPORT_SYMBOL(net_srandom); + +/* Needed by smbfs.o */ +EXPORT_SYMBOL(__scm_destroy); +EXPORT_SYMBOL(__scm_send); + +/* Needed by unix.o */ +EXPORT_SYMBOL(scm_fp_dup); +EXPORT_SYMBOL(files_stat); +EXPORT_SYMBOL(memcpy_toiovec); + +#ifdef CONFIG_IPX_MODULE +EXPORT_SYMBOL(make_8023_client); +EXPORT_SYMBOL(destroy_8023_client); +EXPORT_SYMBOL(make_EII_client); +EXPORT_SYMBOL(destroy_EII_client); +#endif + +/* for 801q VLAN support */ +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +EXPORT_SYMBOL(dev_change_flags); +EXPORT_SYMBOL(vlan_ioctl_hook); +#endif + +EXPORT_SYMBOL(sklist_destroy_socket); +EXPORT_SYMBOL(sklist_insert_socket); + +EXPORT_SYMBOL(scm_detach_fds); + +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +EXPORT_SYMBOL(br_handle_frame_hook); +#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ + defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) +EXPORT_SYMBOL(broute_decision); +#endif +#ifdef CONFIG_INET +EXPORT_SYMBOL(br_ioctl_hook); +#endif +#endif + +#ifdef CONFIG_NET_DIVERT +EXPORT_SYMBOL(alloc_divert_blk); +EXPORT_SYMBOL(free_divert_blk); +EXPORT_SYMBOL(divert_ioctl); +#endif /* CONFIG_NET_DIVERT */ + +#ifdef CONFIG_INET +/* Internet layer registration */ +EXPORT_SYMBOL(inetdev_lock); +EXPORT_SYMBOL(inet_add_protocol); +EXPORT_SYMBOL(inet_del_protocol); +EXPORT_SYMBOL(inet_register_protosw); +EXPORT_SYMBOL(inet_unregister_protosw); +EXPORT_SYMBOL(ip_route_output_key); +EXPORT_SYMBOL(ip_route_input); +EXPORT_SYMBOL(icmp_send); +EXPORT_SYMBOL(ip_options_compile); +EXPORT_SYMBOL(ip_options_undo); +EXPORT_SYMBOL(arp_send); +EXPORT_SYMBOL(arp_broken_ops); +EXPORT_SYMBOL(__ip_select_ident); +EXPORT_SYMBOL(ip_send_check); +EXPORT_SYMBOL(ip_fragment); +EXPORT_SYMBOL(inet_family_ops); +EXPORT_SYMBOL(in_aton); +EXPORT_SYMBOL(ip_mc_inc_group); +EXPORT_SYMBOL(ip_mc_dec_group); +EXPORT_SYMBOL(ip_finish_output); +EXPORT_SYMBOL(inet_stream_ops); +EXPORT_SYMBOL(inet_dgram_ops); +EXPORT_SYMBOL(ip_cmsg_recv); +EXPORT_SYMBOL(inet_addr_type); +EXPORT_SYMBOL(inet_select_addr); +EXPORT_SYMBOL(ip_dev_find); +EXPORT_SYMBOL(inetdev_by_index); +EXPORT_SYMBOL(in_dev_finish_destroy); +EXPORT_SYMBOL(ip_defrag); + +/* Route manipulation */ +EXPORT_SYMBOL(ip_rt_ioctl); +EXPORT_SYMBOL(devinet_ioctl); +EXPORT_SYMBOL(register_inetaddr_notifier); +EXPORT_SYMBOL(unregister_inetaddr_notifier); + +/* needed for ip_gre -cw */ +EXPORT_SYMBOL(ip_statistics); + +#ifdef CONFIG_DLCI_MODULE +extern int (*dlci_ioctl_hook)(unsigned int, void *); +EXPORT_SYMBOL(dlci_ioctl_hook); +#endif + + +#ifdef CONFIG_IPV6 +EXPORT_SYMBOL(ipv6_addr_type); +EXPORT_SYMBOL(icmpv6_send); +EXPORT_SYMBOL(ndisc_mc_map); +EXPORT_SYMBOL(register_inet6addr_notifier); +EXPORT_SYMBOL(unregister_inet6addr_notifier); +#include +EXPORT_SYMBOL(ip6_route_output); +#endif +#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) +/* inet functions common to v4 and v6 */ +EXPORT_SYMBOL(inet_release); +EXPORT_SYMBOL(inet_stream_connect); +EXPORT_SYMBOL(inet_dgram_connect); +EXPORT_SYMBOL(inet_accept); +EXPORT_SYMBOL(inet_listen); +EXPORT_SYMBOL(inet_shutdown); +EXPORT_SYMBOL(inet_setsockopt); +EXPORT_SYMBOL(inet_getsockopt); +EXPORT_SYMBOL(inet_sendmsg); +EXPORT_SYMBOL(inet_recvmsg); +#ifdef INET_REFCNT_DEBUG +EXPORT_SYMBOL(inet_sock_nr); +#endif +EXPORT_SYMBOL(inet_sock_destruct); +EXPORT_SYMBOL(inet_sock_release); + +/* Socket demultiplexing. */ +EXPORT_SYMBOL(tcp_hashinfo); +EXPORT_SYMBOL(tcp_listen_wlock); +EXPORT_SYMBOL(udp_hash); +EXPORT_SYMBOL(udp_hash_lock); + +EXPORT_SYMBOL(tcp_destroy_sock); +EXPORT_SYMBOL(ip_queue_xmit); +EXPORT_SYMBOL(memcpy_fromiovecend); +EXPORT_SYMBOL(csum_partial_copy_fromiovecend); +EXPORT_SYMBOL(tcp_v4_lookup_listener); +/* UDP/TCP exported functions for TCPv6 */ +EXPORT_SYMBOL(udp_ioctl); +EXPORT_SYMBOL(udp_connect); +EXPORT_SYMBOL(udp_disconnect); +EXPORT_SYMBOL(udp_sendmsg); +EXPORT_SYMBOL(tcp_close); +EXPORT_SYMBOL(tcp_disconnect); +EXPORT_SYMBOL(tcp_accept); +EXPORT_SYMBOL(tcp_write_wakeup); +EXPORT_SYMBOL(tcp_write_space); +EXPORT_SYMBOL(tcp_poll); +EXPORT_SYMBOL(tcp_ioctl); +EXPORT_SYMBOL(tcp_shutdown); +EXPORT_SYMBOL(tcp_setsockopt); +EXPORT_SYMBOL(tcp_getsockopt); +EXPORT_SYMBOL(tcp_recvmsg); +EXPORT_SYMBOL(tcp_send_synack); +EXPORT_SYMBOL(tcp_check_req); +EXPORT_SYMBOL(tcp_child_process); +EXPORT_SYMBOL(tcp_parse_options); +EXPORT_SYMBOL(tcp_rcv_established); +EXPORT_SYMBOL(tcp_init_xmit_timers); +EXPORT_SYMBOL(tcp_clear_xmit_timers); +EXPORT_SYMBOL(tcp_statistics); +EXPORT_SYMBOL(tcp_rcv_state_process); +EXPORT_SYMBOL(tcp_timewait_state_process); +EXPORT_SYMBOL(tcp_timewait_cachep); +EXPORT_SYMBOL(tcp_timewait_kill); +EXPORT_SYMBOL(tcp_sendmsg); +EXPORT_SYMBOL(tcp_v4_rebuild_header); +EXPORT_SYMBOL(tcp_v4_send_check); +EXPORT_SYMBOL(tcp_v4_conn_request); +EXPORT_SYMBOL(tcp_create_openreq_child); +EXPORT_SYMBOL(tcp_bucket_create); +EXPORT_SYMBOL(__tcp_put_port); +EXPORT_SYMBOL(tcp_put_port); +EXPORT_SYMBOL(tcp_inherit_port); +EXPORT_SYMBOL(tcp_v4_syn_recv_sock); +EXPORT_SYMBOL(tcp_v4_do_rcv); +EXPORT_SYMBOL(tcp_v4_connect); +EXPORT_SYMBOL(tcp_v4_hash_connecting); +EXPORT_SYMBOL(tcp_unhash); +EXPORT_SYMBOL(udp_prot); +EXPORT_SYMBOL(tcp_prot); +EXPORT_SYMBOL(tcp_openreq_cachep); +EXPORT_SYMBOL(ipv4_specific); +EXPORT_SYMBOL(tcp_simple_retransmit); +EXPORT_SYMBOL(tcp_transmit_skb); +EXPORT_SYMBOL(tcp_connect); +EXPORT_SYMBOL(tcp_make_synack); +EXPORT_SYMBOL(tcp_tw_deschedule); +EXPORT_SYMBOL(tcp_delete_keepalive_timer); +EXPORT_SYMBOL(tcp_reset_keepalive_timer); +EXPORT_SYMBOL(sysctl_local_port_range); +EXPORT_SYMBOL(tcp_port_rover); +EXPORT_SYMBOL(udp_port_rover); +EXPORT_SYMBOL(tcp_sync_mss); +EXPORT_SYMBOL(net_statistics); +EXPORT_SYMBOL(__tcp_mem_reclaim); +EXPORT_SYMBOL(tcp_sockets_allocated); +EXPORT_SYMBOL(sysctl_tcp_reordering); +EXPORT_SYMBOL(sysctl_tcp_rmem); +EXPORT_SYMBOL(sysctl_tcp_wmem); +EXPORT_SYMBOL(sysctl_tcp_ecn); +EXPORT_SYMBOL(tcp_cwnd_application_limited); +EXPORT_SYMBOL(tcp_sendpage); + +EXPORT_SYMBOL(tcp_write_xmit); + +EXPORT_SYMBOL(tcp_v4_remember_stamp); + +extern int sysctl_tcp_tw_recycle; + +#ifdef CONFIG_SYSCTL +EXPORT_SYMBOL(sysctl_tcp_tw_recycle); +EXPORT_SYMBOL(sysctl_max_syn_backlog); +#endif + +#if defined (CONFIG_IPV6_MODULE) +EXPORT_SYMBOL(secure_tcpv6_sequence_number); +EXPORT_SYMBOL(secure_ipv6_id); +#endif + +#endif + +EXPORT_SYMBOL(netlink_set_err); +EXPORT_SYMBOL(netlink_broadcast); +EXPORT_SYMBOL(netlink_unicast); +EXPORT_SYMBOL(netlink_kernel_create); +EXPORT_SYMBOL(netlink_dump_start); +EXPORT_SYMBOL(netlink_ack); +#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE) +EXPORT_SYMBOL(netlink_attach); +EXPORT_SYMBOL(netlink_detach); +EXPORT_SYMBOL(netlink_post); +#endif + +EXPORT_SYMBOL(rtattr_parse); +EXPORT_SYMBOL(rtnetlink_links); +EXPORT_SYMBOL(__rta_fill); +EXPORT_SYMBOL(rtnetlink_dump_ifinfo); +EXPORT_SYMBOL(rtnetlink_put_metrics); +EXPORT_SYMBOL(rtnl); +EXPORT_SYMBOL(neigh_delete); +EXPORT_SYMBOL(neigh_add); +EXPORT_SYMBOL(neigh_dump_info); + +EXPORT_SYMBOL(dev_set_allmulti); +EXPORT_SYMBOL(dev_set_promiscuity); +EXPORT_SYMBOL(sklist_remove_socket); +EXPORT_SYMBOL(rtnl_sem); +EXPORT_SYMBOL(rtnl_lock); +EXPORT_SYMBOL(rtnl_unlock); + +/* ABI emulation layers need this */ +EXPORT_SYMBOL(move_addr_to_kernel); +EXPORT_SYMBOL(move_addr_to_user); + +/* Used by at least ipip.c. */ +EXPORT_SYMBOL(ipv4_config); +EXPORT_SYMBOL(dev_open); + +/* Used by other modules */ +EXPORT_SYMBOL(in_ntoa); +EXPORT_SYMBOL(xrlim_allow); + +EXPORT_SYMBOL(ip_rcv); +EXPORT_SYMBOL(arp_rcv); +EXPORT_SYMBOL(arp_tbl); +EXPORT_SYMBOL(arp_find); + +#endif /* CONFIG_INET */ + +#ifdef CONFIG_TR +EXPORT_SYMBOL(tr_type_trans); +#endif + +/* Device callback registration */ +EXPORT_SYMBOL(register_netdevice_notifier); +EXPORT_SYMBOL(unregister_netdevice_notifier); + +/* support for loadable net drivers */ +#ifdef CONFIG_NET +EXPORT_SYMBOL(loopback_dev); +EXPORT_SYMBOL(register_netdevice); +EXPORT_SYMBOL(unregister_netdevice); +EXPORT_SYMBOL(netdev_state_change); +EXPORT_SYMBOL(dev_new_index); +EXPORT_SYMBOL(dev_get_by_index); +EXPORT_SYMBOL(__dev_get_by_index); +EXPORT_SYMBOL(dev_get_by_name); +EXPORT_SYMBOL(__dev_get_by_name); +EXPORT_SYMBOL(netdev_finish_unregister); +EXPORT_SYMBOL(netdev_set_master); +EXPORT_SYMBOL(eth_type_trans); +#ifdef CONFIG_FDDI +EXPORT_SYMBOL(fddi_type_trans); +#endif /* CONFIG_FDDI */ +#if 0 +EXPORT_SYMBOL(eth_copy_and_sum); +#endif +EXPORT_SYMBOL(alloc_skb); +EXPORT_SYMBOL(__kfree_skb); +EXPORT_SYMBOL(skb_clone); +EXPORT_SYMBOL(skb_copy); +EXPORT_SYMBOL(netif_rx); +EXPORT_SYMBOL(dev_add_pack); +EXPORT_SYMBOL(dev_remove_pack); +EXPORT_SYMBOL(dev_get); +EXPORT_SYMBOL(dev_alloc); +EXPORT_SYMBOL(dev_alloc_name); +EXPORT_SYMBOL(__netdev_watchdog_up); +#ifdef CONFIG_KMOD +EXPORT_SYMBOL(dev_load); +#endif +EXPORT_SYMBOL(dev_ioctl); +EXPORT_SYMBOL(dev_queue_xmit); +#ifdef CONFIG_NET_HW_FLOWCONTROL +EXPORT_SYMBOL(netdev_dropping); +EXPORT_SYMBOL(netdev_register_fc); +EXPORT_SYMBOL(netdev_unregister_fc); +EXPORT_SYMBOL(netdev_fc_xoff); +#endif +EXPORT_SYMBOL(dev_base); +EXPORT_SYMBOL(dev_base_lock); +EXPORT_SYMBOL(dev_close); +EXPORT_SYMBOL(dev_mc_add); +EXPORT_SYMBOL(dev_mc_delete); +EXPORT_SYMBOL(dev_mc_upload); +EXPORT_SYMBOL(__kill_fasync); + +EXPORT_SYMBOL(if_port_text); + +#ifdef CONFIG_HIPPI +EXPORT_SYMBOL(hippi_type_trans); +#endif + +#ifdef CONFIG_NET_FASTROUTE +EXPORT_SYMBOL(netdev_fastroute); +#endif + +#ifdef CONFIG_SYSCTL +EXPORT_SYMBOL(sysctl_wmem_max); +EXPORT_SYMBOL(sysctl_rmem_max); +#ifdef CONFIG_INET +EXPORT_SYMBOL(sysctl_ip_default_ttl); +#endif +#endif + +/* Packet scheduler modules want these. */ +EXPORT_SYMBOL(qdisc_destroy); +EXPORT_SYMBOL(qdisc_reset); +EXPORT_SYMBOL(qdisc_restart); +EXPORT_SYMBOL(qdisc_create_dflt); +EXPORT_SYMBOL(noop_qdisc); +EXPORT_SYMBOL(qdisc_tree_lock); +#ifdef CONFIG_NET_SCHED +PSCHED_EXPORTLIST; +EXPORT_SYMBOL(pfifo_qdisc_ops); +EXPORT_SYMBOL(register_qdisc); +EXPORT_SYMBOL(unregister_qdisc); +EXPORT_SYMBOL(qdisc_get_rtab); +EXPORT_SYMBOL(qdisc_put_rtab); +EXPORT_SYMBOL(qdisc_copy_stats); +#ifdef CONFIG_NET_ESTIMATOR +EXPORT_SYMBOL(qdisc_new_estimator); +EXPORT_SYMBOL(qdisc_kill_estimator); +#endif +#ifdef CONFIG_NET_CLS_POLICE +EXPORT_SYMBOL(tcf_police); +EXPORT_SYMBOL(tcf_police_locate); +EXPORT_SYMBOL(tcf_police_destroy); +EXPORT_SYMBOL(tcf_police_dump); +#endif +#endif +#ifdef CONFIG_NET_CLS +EXPORT_SYMBOL(register_tcf_proto_ops); +EXPORT_SYMBOL(unregister_tcf_proto_ops); +#endif +#ifdef CONFIG_NETFILTER +#include +EXPORT_SYMBOL(nf_register_hook); +EXPORT_SYMBOL(nf_unregister_hook); +EXPORT_SYMBOL(nf_register_sockopt); +EXPORT_SYMBOL(nf_unregister_sockopt); +EXPORT_SYMBOL(nf_reinject); +EXPORT_SYMBOL(nf_register_queue_handler); +EXPORT_SYMBOL(nf_unregister_queue_handler); +EXPORT_SYMBOL(nf_hook_slow); +EXPORT_SYMBOL(nf_hooks); +EXPORT_SYMBOL(nf_setsockopt); +EXPORT_SYMBOL(nf_getsockopt); +EXPORT_SYMBOL(ip_ct_attach); +#ifdef CONFIG_INET +#include +EXPORT_SYMBOL(ip_route_me_harder); +#endif +#endif + +EXPORT_SYMBOL(register_gifconf); + +EXPORT_SYMBOL(net_call_rx_atomic); +EXPORT_SYMBOL(softnet_data); + +#endif /* CONFIG_NET */ diff --git a/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff new file mode 100644 index 0000000..bab3e11 --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff @@ -0,0 +1,2621 @@ +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre1/net/Makefile Wed Apr 3 19:57:30 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -23,6 +24,12 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++endif ++endif ++ ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter + endif + endif + +--- linux/net/Config.in Wed Apr 3 21:50:19 2002 ++++ ebt2.0pre1/net/Config.in Wed Apr 3 18:47:51 2002 +@@ -60,6 +60,7 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++ source net/bridge/netfilter/Config.in + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/Makefile Wed Apr 3 18:48:52 2002 +@@ -0,0 +1,23 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++ ++include $(TOPDIR)/Rules.make +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/Config.in Wed Apr 3 18:48:59 2002 +@@ -0,0 +1,12 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/br_db.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200} ++}; ++ ++static int __init init(void) ++{ ++ int i, j, ret; ++ ++ ret = ebt_register_table(&frame_filter); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/ebtable_nat.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,149 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT ++// needed because of the bridge-nf patch (that allows use of iptables on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING to be executed _after_ the iptables stuff ++// when it's bridged, it's the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, 300}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, 200 + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/ebt_arp.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,95 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw)+sizeof(struct arphdr)+((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/ebt_ip.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,74 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/ebt_log.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,103 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, ++ void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : "");// max length: 29 + 10 + 2 * 16 ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));// length: 14 ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));// max length: 46 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);// max length: 26 ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ntohs(arph->ar_op));// max length: 40 ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/ebt_nat.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,114 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++__u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ if (skb_cloned(*pskb)) { ++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); ++ ++ if (!nskb) ++ return EBT_DROP; ++ if ((*pskb)->sk) ++ skb_set_owner_w(nskb, (*pskb)->sk); ++ kfree_skb(*pskb); ++ *pskb = nskb; ++ } ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ETH_ALEN * sizeof(unsigned char)); ++ return EBT_ACCEPT; ++} ++ ++__u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ if (skb_cloned(*pskb)) { ++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); ++ ++ if (!nskb) ++ return EBT_DROP; ++ if ((*pskb)->sk) ++ skb_set_owner_w(nskb, (*pskb)->sk); ++ kfree_skb(*pskb); ++ *pskb = nskb; ++ } ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ETH_ALEN * sizeof(unsigned char)); ++ return EBT_ACCEPT; ++} ++ ++int ebt_target_snat_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ return 0; ++} ++ ++int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT) ++ return -EINVAL; ++ return 0; ++} ++ ++struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, NULL, THIS_MODULE ++}; ++ ++struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/net/bridge/netfilter/ebtables.c Wed Apr 3 18:48:47 2002 +@@ -0,0 +1,1088 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define ASSERT_READ_LOCK(x) ++#define ASSERT_WRITE_LOCK(x) ++#include /* list_named_find */ ++ ++#if 0 // use this for remote debugging ++#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...)*/ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, w->watcher_size - sizeof(struct ebt_entry_watcher), c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, m->match_size - sizeof(struct ebt_entry_match), c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + cpu_number_map(smp_processor_id()) * table->private->nentries + table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ){ ++ ++ if ( (point->bitmask & EBT_SOURCEMAC) && ++ FWINV(!!memcmp(point->sourcemac, ((**pskb).mac.ethernet)->h_source, ETH_ALEN), EBT_ISOURCE) ) ++ goto letscontinue; ++ ++ if ( (point->bitmask & EBT_DESTMAC) && ++ FWINV(!!memcmp(point->destmac, ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), EBT_IDEST) ) ++ goto letscontinue; ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *)(((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *)list_named_find(&ebt_matches, m->u.name))) { ++ BUGPRINT("match does not exist: %s\n", m->u.name); ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, m->match_size - sizeof(*m)) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *)list_named_find(&ebt_watchers, w->u.name))) { ++ BUGPRINT("watcher does not exist: %s\n", w->u.name); ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, w->watcher_size - sizeof(*w)) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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 - sizeof(*m)); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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 - sizeof(*w)); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *)list_named_find(&ebt_targets, t->u.name))) { ++ BUGPRINT("Target does not exist: %s\n", t->u.name); ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, t->target_size - sizeof(*t)) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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 - sizeof(*t)); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ i = 0; // used to know what we need to clean up if something goes wrong ++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called with under write_lock ++void get_counters(struct ebt_table_info *info, struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *)vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc(sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter allocation ++ // Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *)vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc(table->table->nentries * sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ if (table->check && table->check(newinfo, table->valid_hooks)) { ++ BUGPRINT("The table doesn't like its own initial data, lol\n"); ++ return -EINVAL; ++ } ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ table->private = newinfo; ++ table->lock = RW_LOCK_UNLOCKED; ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++/* userspace just supplied us with counters */ ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ BUGPRINT("Table not found for update_counters\n"); ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *)vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *) (((char *)(info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/include/linux/netfilter_bridge/ebtables.h Wed Apr 3 20:44:38 2002 +@@ -0,0 +1,304 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include /* IFNAMSIZ */ ++#include ++#include /* ETH_ALEN */ ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++/* [gs]etsockopt numbers */ ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses different ++// techniques for naming the policy and such. So, iptables doesn't need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ unsigned int match_size;// size of this struct + sizee of data ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ unsigned int watcher_size;// size of this struct + sizee of data ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ unsigned int target_size;// size of this struct + sizee of data ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target; ++ __u8 verdict; ++}; ++ ++/* one entry */ ++struct ebt_entry { ++ __u32 bitmask; // this needs to be the first field ++ __u32 invflags; ++ __u16 ethproto; ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ unsigned int nentries; // nr of rules in the table ++ unsigned int entries_size; // total size of the entries ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; // start of the chains ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it? ++ unsigned int num_counters; // nr of counters userspace expects back ++ struct ebt_counter *counters; // where the kernel will put the old counters ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // true == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, ++ void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // true == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, ++ void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // true == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, ++ void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ unsigned int entries_size; // total size of the entries ++ unsigned int nentries; ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it? ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain matches, targets, ... ++ int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks); ++ struct ebt_table_info *private;// the data used by the kernel ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++/* fn returns 0 to continue iteration */ ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_arp.h Wed Apr 3 18:50:31 2002 +@@ -0,0 +1,25 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_ip.h Wed Apr 3 18:50:31 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_log.h Wed Apr 3 18:50:31 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_nat.h Wed Apr 3 18:50:31 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre1/include/linux/br_db.h Wed Apr 3 20:45:01 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff new file mode 100644 index 0000000..23ecce4 --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff @@ -0,0 +1,2752 @@ +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre2/net/Makefile Wed Apr 3 21:50:43 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -23,6 +24,12 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++endif ++endif ++ ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter + endif + endif + +--- linux/net/Config.in Sun Apr 14 15:19:26 2002 ++++ ebt2.0pre2/net/Config.in Wed Apr 3 21:50:43 2002 +@@ -60,6 +60,7 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++ source net/bridge/netfilter/Config.in + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/Makefile Wed Apr 3 21:50:43 2002 +@@ -0,0 +1,23 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++ ++include $(TOPDIR)/Rules.make +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/Config.in Wed Apr 3 21:50:43 2002 +@@ -0,0 +1,12 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/br_db.c Wed Apr 3 21:50:43 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ ++ (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200} ++}; ++ ++static int __init init(void) ++{ ++ int i, j, ret; ++ ++ ret = ebt_register_table(&frame_filter); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/ebtable_nat.c Sat Apr 13 21:54:58 2002 +@@ -0,0 +1,153 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. ++// needed because of the bridge-nf patch (that allows use of iptables ++// on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING ++// to be executed _after_ the iptables stuff. when it's bridged, it's ++// the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,300}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,200 + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/ebt_arp.c Sat Apr 13 21:45:34 2002 +@@ -0,0 +1,107 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + ++ ((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), ++ EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), ++ EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/ebt_ip.c Sat Apr 13 21:47:18 2002 +@@ -0,0 +1,81 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && ++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && ++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != ++ infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != ++ infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/ebt_log.c Sat Apr 13 21:49:45 2002 +@@ -0,0 +1,111 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ // max length: 29 + 10 + 2 * 16 ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : ""); ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ // length: 14 ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == ++ htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ // max length: 46 ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", ++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ // max length: 26 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ // max length: 40 ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ++ ntohs(arph->ar_op)); ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/ebt_nat.c Sat Apr 13 21:51:32 2002 +@@ -0,0 +1,118 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++__u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ if (skb_cloned(*pskb)) { ++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); ++ ++ if (!nskb) ++ return EBT_DROP; ++ if ((*pskb)->sk) ++ skb_set_owner_w(nskb, (*pskb)->sk); ++ kfree_skb(*pskb); ++ *pskb = nskb; ++ } ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return EBT_ACCEPT; ++} ++ ++__u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ if (skb_cloned(*pskb)) { ++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); ++ ++ if (!nskb) ++ return EBT_DROP; ++ if ((*pskb)->sk) ++ skb_set_owner_w(nskb, (*pskb)->sk); ++ kfree_skb(*pskb); ++ *pskb = nskb; ++ } ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return EBT_ACCEPT; ++} ++ ++int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ return 0; ++} ++ ++int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT) ++ return -EINVAL; ++ return 0; ++} ++ ++struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, ++ NULL, THIS_MODULE ++}; ++ ++struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, ++ NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/net/bridge/netfilter/ebtables.c Sat Apr 13 21:36:18 2002 +@@ -0,0 +1,1168 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// list_named_find ++#define ASSERT_READ_LOCK(x) ++#define ASSERT_WRITE_LOCK(x) ++#include ++ ++#if 0 // use this for remote debugging ++#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...) ++ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, ++ w->watcher_size - sizeof(struct ebt_entry_watcher), c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, ++ m->match_size - sizeof(struct ebt_entry_match), c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') ++ return 0; ++ if (!device) ++ return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + ++ cpu_number_map(smp_processor_id()) * table->private->nentries + ++ table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, ++ EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && ++ (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ ) { ++ if ( (point->bitmask & EBT_SOURCEMAC) && ++ FWINV(!!memcmp(point->sourcemac, ++ ((**pskb).mac.ethernet)->h_source, ETH_ALEN), ++ EBT_ISOURCE) ) ++ goto letscontinue; ++ ++ if ( (point->bitmask & EBT_DESTMAC) && ++ FWINV(!!memcmp(point->destmac, ++ ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), ++ EBT_IDEST) ) ++ goto letscontinue; ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, ++ out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us ++ // what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, ++ out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while " ++ "firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *) ++ (((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *) ++ list_named_find(&ebt_matches, m->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, ++ m->match_size - sizeof(*m)) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *) ++ list_named_find(&ebt_watchers, w->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, ++ w->watcher_size - sizeof(*w)) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " ++ "bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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 - sizeof(*m)); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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 - sizeof(*w)); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *) ++ list_named_find(&ebt_targets, t->u.name))) { ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, ++ t->target_size - sizeof(*t)) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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 - sizeof(*t)); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, ++ 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ // 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); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called under write_lock ++static inline void get_counters(struct ebt_table_info *info, ++ struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, ++ sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += ++ info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc( ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter ++ // allocation. Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ++ ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *) ++ vmalloc(table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ 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; ++ table->lock = RW_LOCK_UNLOCKED; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, ++ table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++// userspace just supplied us with counters ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + ++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *) ++ vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, ++ info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, ++ NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, ++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *) ++ (info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ++ ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/include/linux/netfilter_bridge/ebtables.h Sat Apr 13 16:06:20 2002 +@@ -0,0 +1,318 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include // IFNAMSIZ ++#include ++#include // ETH_ALEN ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++/* [gs]etsockopt numbers */ ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ++// ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses ++// different techniques for naming the policy and such. So, iptables doesn't ++// need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ ++ | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ // size of this struct + size of data ++ unsigned int match_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ // size of this struct + size of data ++ unsigned int watcher_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ // size of this struct + size of data ++ unsigned int target_size; ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target target; ++ __u8 verdict; ++}; ++ ++/* one entry */ ++struct ebt_entry { ++ // this needs to be the first field ++ __u32 bitmask; ++ __u32 invflags; ++ __u16 ethproto; ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ // nr of rules in the table ++ unsigned int nentries; ++ // total size of the entries ++ unsigned int entries_size; ++ // start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of it? ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ // nr of counters userspace expects back ++ unsigned int num_counters; ++ // where the kernel will put the old counters ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, ++ void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ // total size of the entries ++ unsigned int entries_size; ++ unsigned int nentries; ++ // pointers to the start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of the counters bolonging to a chain ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain ++ // matches, targets, ... 0 == let it in ++ int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks); ++ // the data used by the kernel ++ struct ebt_table_info *private; ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++// fn returns 0 to continue iteration ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_arp.h Sat Apr 13 16:11:46 2002 +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ ++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_ip.h Wed Apr 3 21:50:43 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_log.h Wed Apr 3 21:50:43 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_nat.h Wed Apr 3 21:50:43 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre2/include/linux/br_db.h Sat Apr 13 22:43:11 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff new file mode 100644 index 0000000..72e80fe --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff @@ -0,0 +1,3108 @@ +ebtables-v2.0pre3 - 27 April + +*** modifications for brouter support *** + +--- linux/net/bridge/br_private.h Sat Apr 27 22:55:42 2002 ++++ ebt2.0pre3/net/bridge/br_private.h Sat Apr 27 21:52:48 2002 +@@ -170,7 +170,7 @@ + + /* br_input.c */ + extern int br_handle_frame_finish(struct sk_buff *skb); +-extern void br_handle_frame(struct sk_buff *skb); ++extern int br_handle_frame(struct sk_buff *skb); + + /* br_ioctl.c */ + extern void br_call_ioctl_atomic(void (*fn)(void)); +--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001 ++++ ebt2.0pre3/include/linux/if_bridge.h Sat Apr 27 21:39:14 2002 +@@ -102,8 +102,13 @@ + struct net_bridge_port; + + extern int (*br_ioctl_hook)(unsigned long arg); +-extern void (*br_handle_frame_hook)(struct sk_buff *skb); +- ++extern int (*br_handle_frame_hook)(struct sk_buff *skb); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++#endif + #endif + + #endif +--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre3/net/core/dev.c Sat Apr 27 21:05:16 2002 +@@ -1384,7 +1384,14 @@ + } + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) = NULL; ++#endif + #endif + + static __inline__ int handle_bridge(struct sk_buff *skb, +@@ -1394,14 +1401,14 @@ + + if (pt_prev) { + if (!pt_prev->data) +- ret = deliver_to_old_ones(pt_prev, skb, 0); ++ deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); +- ret = pt_prev->func(skb, skb->dev, pt_prev); ++ pt_prev->func(skb, skb->dev, pt_prev); + } + } + +- br_handle_frame_hook(skb); ++ ret = br_handle_frame_hook(skb); + return ret; + } + +@@ -1479,9 +1486,10 @@ + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && + br_handle_frame_hook != NULL) { +- handle_bridge(skb, pt_prev); +- dev_put(rx_dev); +- continue; ++ if (handle_bridge(skb, pt_prev) == 0) { ++ dev_put(rx_dev); ++ continue; ++ } + } + #endif + +--- linux/net/bridge/br_input.c Sat Apr 27 22:55:42 2002 ++++ ebt2.0pre3/net/bridge/br_input.c Sat Apr 27 21:05:16 2002 +@@ -19,7 +19,10 @@ + #include + #include + #include "br_private.h" +- ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++#include ++#endif + unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + + static int br_pass_frame_up_finish(struct sk_buff *skb) +@@ -112,7 +115,7 @@ + return 0; + } + +-void br_handle_frame(struct sk_buff *skb) ++int br_handle_frame(struct sk_buff *skb) + { + struct net_bridge *br; + unsigned char *dest; +@@ -146,23 +149,30 @@ + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, ++ skb->dev, NULL, NULL) == NF_DROP) ++ return -1; ++#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); +- return; ++ return 0; + } + + err: + read_unlock(&br->lock); + err_nolock: + kfree_skb(skb); +- return; ++ return 0; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); +- return; ++ return 0; + } + + kfree_skb(skb); ++ return 0; + } +--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre3/net/netsyms.c Sat Apr 27 21:05:16 2002 +@@ -228,6 +228,10 @@ + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + EXPORT_SYMBOL(br_handle_frame_hook); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++EXPORT_SYMBOL(broute_decision); ++#endif + #ifdef CONFIG_INET + EXPORT_SYMBOL(br_ioctl_hook); + #endif +--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001 ++++ ebt2.0pre3/include/linux/netfilter_bridge.h Sat Apr 27 21:53:07 2002 +@@ -18,7 +18,19 @@ + #define NF_BR_LOCAL_OUT 3 + /* Packets about to hit the wire. */ + #define NF_BR_POST_ROUTING 4 +-#define NF_BR_NUMHOOKS 5 ++/* Not really a hook, but used for the ebtables broute table */ ++#define NF_BR_BROUTING 5 ++#define NF_BR_NUMHOOKS 6 + ++enum nf_br_hook_priorities { ++ NF_BR_PRI_FIRST = INT_MIN, ++ NF_BR_PRI_FILTER_BRIDGED = -200, ++ NF_BR_PRI_FILTER_OTHER = 200, ++ NF_BR_PRI_NAT_DST_BRIDGED = -300, ++ NF_BR_PRI_NAT_DST_OTHER = 100, ++ NF_BR_PRI_NAT_SRC_BRIDGED = -100, ++ NF_BR_PRI_NAT_SRC_OTHER = 300, ++ NF_BR_PRI_LAST = INT_MAX, ++}; + + #endif + +*** modifications for ebtables compilation *** + +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre3/net/Makefile Sat Apr 27 21:05:16 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -23,6 +24,12 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++endif ++endif ++ ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter + endif + endif + +--- linux/net/Config.in Sat Apr 27 22:55:42 2002 ++++ ebt2.0pre3/net/Config.in Sat Apr 27 21:05:16 2002 +@@ -60,6 +60,7 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++ source net/bridge/netfilter/Config.in + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF + +*** new ebtables files *** + +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/Makefile Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,24 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o ++include $(TOPDIR)/Rules.make +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/Config.in Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,14 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/br_db.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ ++ (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebtable_nat.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,156 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. ++// needed because of the bridge-nf patch (that allows use of iptables ++// on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING ++// to be executed _after_ the iptables stuff. when it's bridged, it's ++// the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_NAT_DST_OTHER}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_BRIDGED}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_OTHER}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, ++ NF_BR_PRI_NAT_DST_BRIDGED}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_OTHER + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebtable_broute.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,80 @@ ++/* ++ * ebtable_broute ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++// EBT_ACCEPT means the frame will be bridged ++// EBT_DROP means the frame will be routed ++static struct ebt_entries initial_chain = ++ {0, EBT_ACCEPT, 0}; ++ ++static struct ebt_replace initial_table = ++{ ++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), ++ { [NF_BR_BROUTING]&initial_chain}, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++static unsigned int ++ebt_broute (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, &broute_table); ++} ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ ret = ebt_register_table(&broute_table); ++ if (ret < 0) ++ return ret; ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ // in br_input.c, br_handle_frame() wants to call broute_decision() ++ broute_decision = ebt_broute; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ broute_decision = NULL; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ebt_unregister_table(&broute_table); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebt_redirect.c Sat Apr 27 22:48:52 2002 +@@ -0,0 +1,65 @@ ++/* ++ * ebt_redirect ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../br_private.h" ++ ++static __u8 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 *infostuff = (struct ebt_redirect_info *) data; ++ ++ memcpy((**pskb).mac.ethernet->h_dest, ++ in->br_port->br->dev.dev_addr, ETH_ALEN); ++ (*pskb)->pkt_type = PACKET_HOST; ++ return infostuff->target; ++} ++ ++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_redirect_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target redirect_target = ++{ ++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, ++ ebt_target_redirect_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebt_arp.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,107 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + ++ ((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), ++ EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), ++ EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebt_ip.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,81 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && ++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && ++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != ++ infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != ++ infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebt_log.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,111 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ // max length: 29 + 10 + 2 * 16 ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : ""); ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ // length: 14 ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == ++ htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ // max length: 46 ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", ++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ // max length: 26 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ // max length: 40 ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ++ ntohs(arph->ar_op)); ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebt_nat.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,106 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || ++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, ++ NULL, THIS_MODULE ++}; ++ ++static struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, ++ NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/net/bridge/netfilter/ebtables.c Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,1180 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++// 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 ++ ++#if 0 // use this for remote debugging ++#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...) ++ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, ++ w->watcher_size, c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, ++ m->match_size, c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') ++ return 0; ++ if (!device) ++ return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + ++ cpu_number_map(smp_processor_id()) * table->private->nentries + ++ table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, ++ EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && ++ (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) ++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) ++ && ((!out || !out->br_port) ? 1 : ++ FWINV(!ebt_dev_check((char *) ++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) ++ ++ ) { ++ if ( (point->bitmask & EBT_SOURCEMAC) && ++ FWINV(!!memcmp(point->sourcemac, ++ ((**pskb).mac.ethernet)->h_source, ETH_ALEN), ++ EBT_ISOURCE) ) ++ goto letscontinue; ++ ++ if ( (point->bitmask & EBT_DESTMAC) && ++ FWINV(!!memcmp(point->destmac, ++ ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), ++ EBT_IDEST) ) ++ goto letscontinue; ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, ++ out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us ++ // what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, ++ out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while " ++ "firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *) ++ (((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *) ++ list_named_find(&ebt_matches, m->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, ++ m->match_size) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *) ++ list_named_find(&ebt_watchers, w->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, ++ w->watcher_size) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " ++ "bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ e->in[IFNAMSIZ - 1] = '\0'; ++ e->out[IFNAMSIZ - 1] = '\0'; ++ e->logical_in[IFNAMSIZ - 1] = '\0'; ++ e->logical_out[IFNAMSIZ - 1] = '\0'; ++ // 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *) ++ list_named_find(&ebt_targets, t->u.name))) { ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, ++ t->target_size) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, ++ 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ // 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); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called under write_lock ++static inline void get_counters(struct ebt_table_info *info, ++ struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, ++ sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += ++ info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc( ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter ++ // allocation. Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ++ ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *) ++ vmalloc(table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ 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; ++ table->lock = RW_LOCK_UNLOCKED; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, ++ table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++// userspace just supplied us with counters ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + ++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *) ++ vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, ++ info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, ++ NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, ++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *) ++ (info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ++ ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/include/linux/netfilter_bridge/ebtables.h Sat Apr 27 21:54:11 2002 +@@ -0,0 +1,330 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include // IFNAMSIZ ++#include ++#include // ETH_ALEN ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++// [gs]etsockopt numbers ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ++// ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses ++// different techniques for naming the policy and such. So, iptables doesn't ++// need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ ++ | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_ILOGICALIN 0x20 ++#define EBT_ILOGICALOUT 0x40 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ ++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ // size of data ++ unsigned int match_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ // size of data ++ unsigned int watcher_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ // size of data ++ unsigned int target_size; ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target target; ++ __u8 verdict; ++}; ++ ++// one entry ++struct ebt_entry { ++ // this needs to be the first field ++ __u32 bitmask; ++ __u32 invflags; ++ __u16 ethproto; ++ // the physical in-dev ++ __u8 in[IFNAMSIZ]; ++ // the logical in-dev ++ __u8 logical_in[IFNAMSIZ]; ++ // the physical out-dev ++ __u8 out[IFNAMSIZ]; ++ // the logical out-dev ++ __u8 logical_out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ // nr of rules in the table ++ unsigned int nentries; ++ // total size of the entries ++ unsigned int entries_size; ++ // start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of it? ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ // nr of counters userspace expects back ++ unsigned int num_counters; ++ // where the kernel will put the old counters ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ // total size of the entries ++ unsigned int entries_size; ++ unsigned int nentries; ++ // pointers to the start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of the counters bolonging to a chain ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain ++ // matches, targets, ... 0 == let it in ++ int (*check)(const struct ebt_table_info *info, ++ unsigned int valid_hooks); ++ // the data used by the kernel ++ struct ebt_table_info *private; ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++// fn returns 0 to continue iteration ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size + \ ++ sizeof(struct ebt_entry_match)) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size + \ ++ sizeof(struct ebt_entry_watcher)) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_arp.h Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ ++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_ip.h Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_log.h Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_nat.h Sat Apr 27 21:05:16 2002 +@@ -0,0 +1,13 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3/include/linux/br_db.h Sat Apr 27 21:54:40 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff new file mode 100644 index 0000000..e50fba4 --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff @@ -0,0 +1,3122 @@ +ebtables-v2.0pre4 - 29 April + +*** modifications for brouter support *** + +--- linux/net/bridge/br_private.h Mon Apr 29 19:58:13 2002 ++++ ebt2.0pre4/net/bridge/br_private.h Mon Apr 29 19:54:04 2002 +@@ -170,7 +170,7 @@ + + /* br_input.c */ + extern int br_handle_frame_finish(struct sk_buff *skb); +-extern void br_handle_frame(struct sk_buff *skb); ++extern int br_handle_frame(struct sk_buff *skb); + + /* br_ioctl.c */ + extern void br_call_ioctl_atomic(void (*fn)(void)); +--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001 ++++ ebt2.0pre4/include/linux/if_bridge.h Mon Apr 29 19:54:04 2002 +@@ -102,8 +102,13 @@ + struct net_bridge_port; + + extern int (*br_ioctl_hook)(unsigned long arg); +-extern void (*br_handle_frame_hook)(struct sk_buff *skb); +- ++extern int (*br_handle_frame_hook)(struct sk_buff *skb); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++#endif + #endif + + #endif +--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre4/net/core/dev.c Mon Apr 29 19:54:04 2002 +@@ -1384,7 +1384,14 @@ + } + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) = NULL; ++#endif + #endif + + static __inline__ int handle_bridge(struct sk_buff *skb, +@@ -1394,14 +1401,14 @@ + + if (pt_prev) { + if (!pt_prev->data) +- ret = deliver_to_old_ones(pt_prev, skb, 0); ++ deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); +- ret = pt_prev->func(skb, skb->dev, pt_prev); ++ pt_prev->func(skb, skb->dev, pt_prev); + } + } + +- br_handle_frame_hook(skb); ++ ret = br_handle_frame_hook(skb); + return ret; + } + +@@ -1479,9 +1486,10 @@ + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && + br_handle_frame_hook != NULL) { +- handle_bridge(skb, pt_prev); +- dev_put(rx_dev); +- continue; ++ if (handle_bridge(skb, pt_prev) == 0) { ++ dev_put(rx_dev); ++ continue; ++ } + } + #endif + +--- linux/net/bridge/br_input.c Mon Apr 29 19:58:13 2002 ++++ ebt2.0pre4/net/bridge/br_input.c Mon Apr 29 19:54:04 2002 +@@ -19,7 +19,10 @@ + #include + #include + #include "br_private.h" +- ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++#include ++#endif + unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + + static int br_pass_frame_up_finish(struct sk_buff *skb) +@@ -112,7 +115,7 @@ + return 0; + } + +-void br_handle_frame(struct sk_buff *skb) ++int br_handle_frame(struct sk_buff *skb) + { + struct net_bridge *br; + unsigned char *dest; +@@ -146,23 +149,30 @@ + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, ++ skb->dev, NULL, NULL) == NF_DROP) ++ return -1; ++#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); +- return; ++ return 0; + } + + err: + read_unlock(&br->lock); + err_nolock: + kfree_skb(skb); +- return; ++ return 0; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); +- return; ++ return 0; + } + + kfree_skb(skb); ++ return 0; + } +--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre4/net/netsyms.c Mon Apr 29 19:54:04 2002 +@@ -228,6 +228,10 @@ + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + EXPORT_SYMBOL(br_handle_frame_hook); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++EXPORT_SYMBOL(broute_decision); ++#endif + #ifdef CONFIG_INET + EXPORT_SYMBOL(br_ioctl_hook); + #endif +--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001 ++++ ebt2.0pre4/include/linux/netfilter_bridge.h Mon Apr 29 19:54:04 2002 +@@ -18,7 +18,19 @@ + #define NF_BR_LOCAL_OUT 3 + /* Packets about to hit the wire. */ + #define NF_BR_POST_ROUTING 4 +-#define NF_BR_NUMHOOKS 5 ++/* Not really a hook, but used for the ebtables broute table */ ++#define NF_BR_BROUTING 5 ++#define NF_BR_NUMHOOKS 6 + ++enum nf_br_hook_priorities { ++ NF_BR_PRI_FIRST = INT_MIN, ++ NF_BR_PRI_FILTER_BRIDGED = -200, ++ NF_BR_PRI_FILTER_OTHER = 200, ++ NF_BR_PRI_NAT_DST_BRIDGED = -300, ++ NF_BR_PRI_NAT_DST_OTHER = 100, ++ NF_BR_PRI_NAT_SRC_BRIDGED = -100, ++ NF_BR_PRI_NAT_SRC_OTHER = 300, ++ NF_BR_PRI_LAST = INT_MAX, ++}; + + #endif + +*** modifications for ebtables compilation *** + +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre4/net/Makefile Mon Apr 29 19:54:04 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -23,6 +24,12 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++endif ++endif ++ ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter + endif + endif + +--- linux/net/Config.in Mon Apr 29 19:58:13 2002 ++++ ebt2.0pre4/net/Config.in Mon Apr 29 19:54:04 2002 +@@ -60,6 +60,7 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++ source net/bridge/netfilter/Config.in + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF + +*** new ebtables files *** + +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/Makefile Mon Apr 29 19:54:04 2002 +@@ -0,0 +1,24 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o ++include $(TOPDIR)/Rules.make +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/Config.in Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,14 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/br_db.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ ++ (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebtable_nat.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,156 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. ++// needed because of the bridge-nf patch (that allows use of iptables ++// on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING ++// to be executed _after_ the iptables stuff. when it's bridged, it's ++// the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_NAT_DST_OTHER}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_BRIDGED}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_OTHER}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, ++ NF_BR_PRI_NAT_DST_BRIDGED}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_OTHER + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebtable_broute.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,80 @@ ++/* ++ * ebtable_broute ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++// EBT_ACCEPT means the frame will be bridged ++// EBT_DROP means the frame will be routed ++static struct ebt_entries initial_chain = ++ {0, EBT_ACCEPT, 0}; ++ ++static struct ebt_replace initial_table = ++{ ++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), ++ { [NF_BR_BROUTING]&initial_chain}, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++static unsigned int ++ebt_broute (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, &broute_table); ++} ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ ret = ebt_register_table(&broute_table); ++ if (ret < 0) ++ return ret; ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ // in br_input.c, br_handle_frame() wants to call broute_decision() ++ broute_decision = ebt_broute; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ broute_decision = NULL; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ebt_unregister_table(&broute_table); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebt_redirect.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,65 @@ ++/* ++ * ebt_redirect ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../br_private.h" ++ ++static __u8 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 *infostuff = (struct ebt_redirect_info *) data; ++ ++ memcpy((**pskb).mac.ethernet->h_dest, ++ in->br_port->br->dev.dev_addr, ETH_ALEN); ++ (*pskb)->pkt_type = PACKET_HOST; ++ return infostuff->target; ++} ++ ++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_redirect_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target redirect_target = ++{ ++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, ++ ebt_target_redirect_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebt_arp.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,107 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + ++ ((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), ++ EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), ++ EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebt_ip.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,81 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && ++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && ++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != ++ infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != ++ infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebt_log.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,111 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ // max length: 29 + 10 + 2 * 16 ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : ""); ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ // length: 14 ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == ++ htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ // max length: 46 ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", ++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ // max length: 26 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ // max length: 40 ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ++ ntohs(arph->ar_op)); ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebt_nat.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,106 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || ++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, ++ NULL, THIS_MODULE ++}; ++ ++static struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, ++ NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/net/bridge/netfilter/ebtables.c Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,1180 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++// 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 ++ ++#if 0 // use this for remote debugging ++#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...) ++ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, ++ w->watcher_size, c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, ++ m->match_size, c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') ++ return 0; ++ if (!device) ++ return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + ++ cpu_number_map(smp_processor_id()) * table->private->nentries + ++ table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, ++ EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && ++ (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) ++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) ++ && ((!out || !out->br_port) ? 1 : ++ FWINV(!ebt_dev_check((char *) ++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) ++ ++ ) { ++ if ( (point->bitmask & EBT_SOURCEMAC) && ++ FWINV(!!memcmp(point->sourcemac, ++ ((**pskb).mac.ethernet)->h_source, ETH_ALEN), ++ EBT_ISOURCE) ) ++ goto letscontinue; ++ ++ if ( (point->bitmask & EBT_DESTMAC) && ++ FWINV(!!memcmp(point->destmac, ++ ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), ++ EBT_IDEST) ) ++ goto letscontinue; ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, ++ out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us ++ // what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, ++ out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while " ++ "firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *) ++ (((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *) ++ list_named_find(&ebt_matches, m->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, ++ m->match_size) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *) ++ list_named_find(&ebt_watchers, w->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, ++ w->watcher_size) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " ++ "bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ e->in[IFNAMSIZ - 1] = '\0'; ++ e->out[IFNAMSIZ - 1] = '\0'; ++ e->logical_in[IFNAMSIZ - 1] = '\0'; ++ e->logical_out[IFNAMSIZ - 1] = '\0'; ++ // 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *) ++ list_named_find(&ebt_targets, t->u.name))) { ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, ++ t->target_size) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, ++ 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ // 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); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called under write_lock ++static inline void get_counters(struct ebt_table_info *info, ++ struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, ++ sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += ++ info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc( ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter ++ // allocation. Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ++ ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *) ++ vmalloc(table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ 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; ++ table->lock = RW_LOCK_UNLOCKED; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, ++ table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++// userspace just supplied us with counters ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + ++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *) ++ vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, ++ info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, ++ NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, ++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *) ++ (info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ++ ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebtables.h Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,330 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include // IFNAMSIZ ++#include ++#include // ETH_ALEN ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++// [gs]etsockopt numbers ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ++// ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses ++// different techniques for naming the policy and such. So, iptables doesn't ++// need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ ++ | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_ILOGICALIN 0x20 ++#define EBT_ILOGICALOUT 0x40 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ ++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ // size of data ++ unsigned int match_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ // size of data ++ unsigned int watcher_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ // size of data ++ unsigned int target_size; ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target target; ++ __u8 verdict; ++}; ++ ++// one entry ++struct ebt_entry { ++ // this needs to be the first field ++ __u32 bitmask; ++ __u32 invflags; ++ __u16 ethproto; ++ // the physical in-dev ++ __u8 in[IFNAMSIZ]; ++ // the logical in-dev ++ __u8 logical_in[IFNAMSIZ]; ++ // the physical out-dev ++ __u8 out[IFNAMSIZ]; ++ // the logical out-dev ++ __u8 logical_out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ // nr of rules in the table ++ unsigned int nentries; ++ // total size of the entries ++ unsigned int entries_size; ++ // start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of it? ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ // nr of counters userspace expects back ++ unsigned int num_counters; ++ // where the kernel will put the old counters ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ // total size of the entries ++ unsigned int entries_size; ++ unsigned int nentries; ++ // pointers to the start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of the counters bolonging to a chain ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain ++ // matches, targets, ... 0 == let it in ++ int (*check)(const struct ebt_table_info *info, ++ unsigned int valid_hooks); ++ // the data used by the kernel ++ struct ebt_table_info *private; ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++// fn returns 0 to continue iteration ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size + \ ++ sizeof(struct ebt_entry_match)) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size + \ ++ sizeof(struct ebt_entry_watcher)) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_arp.h Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ ++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_ip.h Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_log.h Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_nat.h Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,13 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_redirect.h Mon Apr 29 20:00:05 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H ++#define __LINUX_BRIDGE_EBT_REDIRECT_H ++ ++struct ebt_redirect_info ++{ ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_REDIRECT_TARGET "redirect" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/br_db.h Mon Apr 29 19:54:05 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff new file mode 100644 index 0000000..9537244 --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff @@ -0,0 +1,3133 @@ +ebtables-v2.0pre5 - 01 May + +*** modifications for brouter support *** + +--- linux/net/bridge/br_private.h Fri May 3 21:15:24 2002 ++++ ebt2.0pre5/net/bridge/br_private.h Wed May 1 15:58:13 2002 +@@ -170,7 +170,7 @@ + + /* br_input.c */ + extern int br_handle_frame_finish(struct sk_buff *skb); +-extern void br_handle_frame(struct sk_buff *skb); ++extern int br_handle_frame(struct sk_buff *skb); + + /* br_ioctl.c */ + extern void br_call_ioctl_atomic(void (*fn)(void)); +--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001 ++++ ebt2.0pre5/include/linux/if_bridge.h Wed May 1 15:42:31 2002 +@@ -102,8 +102,13 @@ + struct net_bridge_port; + + extern int (*br_ioctl_hook)(unsigned long arg); +-extern void (*br_handle_frame_hook)(struct sk_buff *skb); +- ++extern int (*br_handle_frame_hook)(struct sk_buff *skb); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++#endif + #endif + + #endif +--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre5/net/core/dev.c Wed May 1 14:51:46 2002 +@@ -1384,7 +1384,14 @@ + } + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) = NULL; ++#endif + #endif + + static __inline__ int handle_bridge(struct sk_buff *skb, +@@ -1394,14 +1401,14 @@ + + if (pt_prev) { + if (!pt_prev->data) +- ret = deliver_to_old_ones(pt_prev, skb, 0); ++ deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); +- ret = pt_prev->func(skb, skb->dev, pt_prev); ++ pt_prev->func(skb, skb->dev, pt_prev); + } + } + +- br_handle_frame_hook(skb); ++ ret = br_handle_frame_hook(skb); + return ret; + } + +@@ -1479,9 +1486,10 @@ + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && + br_handle_frame_hook != NULL) { +- handle_bridge(skb, pt_prev); +- dev_put(rx_dev); +- continue; ++ if (handle_bridge(skb, pt_prev) == 0) { ++ dev_put(rx_dev); ++ continue; ++ } + } + #endif + +--- linux/net/bridge/br_input.c Fri May 3 21:15:24 2002 ++++ ebt2.0pre5/net/bridge/br_input.c Wed May 1 14:51:46 2002 +@@ -19,7 +19,10 @@ + #include + #include + #include "br_private.h" +- ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++#include ++#endif + unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + + static int br_pass_frame_up_finish(struct sk_buff *skb) +@@ -112,7 +115,7 @@ + return 0; + } + +-void br_handle_frame(struct sk_buff *skb) ++int br_handle_frame(struct sk_buff *skb) + { + struct net_bridge *br; + unsigned char *dest; +@@ -146,23 +149,30 @@ + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, ++ skb->dev, NULL, NULL) == NF_DROP) ++ return -1; ++#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); +- return; ++ return 0; + } + + err: + read_unlock(&br->lock); + err_nolock: + kfree_skb(skb); +- return; ++ return 0; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); +- return; ++ return 0; + } + + kfree_skb(skb); ++ return 0; + } +--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre5/net/netsyms.c Wed May 1 14:51:46 2002 +@@ -228,6 +228,10 @@ + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + EXPORT_SYMBOL(br_handle_frame_hook); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++EXPORT_SYMBOL(broute_decision); ++#endif + #ifdef CONFIG_INET + EXPORT_SYMBOL(br_ioctl_hook); + #endif +--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001 ++++ ebt2.0pre5/include/linux/netfilter_bridge.h Wed May 1 15:58:31 2002 +@@ -18,7 +18,19 @@ + #define NF_BR_LOCAL_OUT 3 + /* Packets about to hit the wire. */ + #define NF_BR_POST_ROUTING 4 +-#define NF_BR_NUMHOOKS 5 ++/* Not really a hook, but used for the ebtables broute table */ ++#define NF_BR_BROUTING 5 ++#define NF_BR_NUMHOOKS 6 + ++enum nf_br_hook_priorities { ++ NF_BR_PRI_FIRST = INT_MIN, ++ NF_BR_PRI_FILTER_BRIDGED = -200, ++ NF_BR_PRI_FILTER_OTHER = 200, ++ NF_BR_PRI_NAT_DST_BRIDGED = -300, ++ NF_BR_PRI_NAT_DST_OTHER = 100, ++ NF_BR_PRI_NAT_SRC_BRIDGED = -100, ++ NF_BR_PRI_NAT_SRC_OTHER = 300, ++ NF_BR_PRI_LAST = INT_MAX, ++}; + + #endif + +*** modifications for ebtables compilation *** + +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre5/net/Makefile Wed May 1 14:51:46 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -23,6 +24,12 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++endif ++endif ++ ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter + endif + endif + +--- linux/net/Config.in Fri May 3 21:15:24 2002 ++++ ebt2.0pre5/net/Config.in Wed May 1 14:51:46 2002 +@@ -60,6 +60,7 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++ source net/bridge/netfilter/Config.in + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF + +*** new ebtables files *** + +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/Makefile Wed May 1 14:51:46 2002 +@@ -0,0 +1,24 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o ++include $(TOPDIR)/Rules.make +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/Config.in Wed May 1 14:51:46 2002 +@@ -0,0 +1,14 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/br_db.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ ++ (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebtable_nat.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,156 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. ++// needed because of the bridge-nf patch (that allows use of iptables ++// on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING ++// to be executed _after_ the iptables stuff. when it's bridged, it's ++// the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_NAT_DST_OTHER}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_BRIDGED}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_OTHER}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, ++ NF_BR_PRI_NAT_DST_BRIDGED}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_OTHER + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebtable_broute.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,80 @@ ++/* ++ * ebtable_broute ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++// EBT_ACCEPT means the frame will be bridged ++// EBT_DROP means the frame will be routed ++static struct ebt_entries initial_chain = ++ {0, EBT_ACCEPT, 0}; ++ ++static struct ebt_replace initial_table = ++{ ++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), ++ { [NF_BR_BROUTING]&initial_chain}, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++static unsigned int ++ebt_broute (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, &broute_table); ++} ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ ret = ebt_register_table(&broute_table); ++ if (ret < 0) ++ return ret; ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ // in br_input.c, br_handle_frame() wants to call broute_decision() ++ broute_decision = ebt_broute; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ broute_decision = NULL; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ebt_unregister_table(&broute_table); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebt_redirect.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,65 @@ ++/* ++ * ebt_redirect ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../br_private.h" ++ ++static __u8 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 *infostuff = (struct ebt_redirect_info *) data; ++ ++ memcpy((**pskb).mac.ethernet->h_dest, ++ in->br_port->br->dev.dev_addr, ETH_ALEN); ++ (*pskb)->pkt_type = PACKET_HOST; ++ return infostuff->target; ++} ++ ++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_redirect_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target redirect_target = ++{ ++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, ++ ebt_target_redirect_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebt_arp.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,107 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + ++ ((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), ++ EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), ++ EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebt_ip.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,81 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && ++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && ++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != ++ infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != ++ infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebt_log.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,111 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ // max length: 29 + 10 + 2 * 16 ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : ""); ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ // length: 14 ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == ++ htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ // max length: 46 ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", ++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ // max length: 26 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ // max length: 40 ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ++ ntohs(arph->ar_op)); ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebt_nat.c Wed May 1 14:51:46 2002 +@@ -0,0 +1,106 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || ++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, ++ NULL, THIS_MODULE ++}; ++ ++static struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, ++ NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/net/bridge/netfilter/ebtables.c Fri May 3 20:28:46 2002 +@@ -0,0 +1,1189 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++// 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 ++ ++#if 0 // use this for remote debugging ++#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...) ++ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, ++ w->watcher_size, c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, ++ m->match_size, c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') ++ return 0; ++ if (!device) ++ return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + ++ cpu_number_map(smp_processor_id()) * table->private->nentries + ++ table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, ++ EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && ++ (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) ++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) ++ && ((!out || !out->br_port) ? 1 : ++ FWINV(!ebt_dev_check((char *) ++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) ++ ++ ) { ++ char hlpmac[6]; ++ int j; ++ ++ if (point->bitmask & EBT_SOURCEMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_source[j] & point->sourcemsk[j]; ++ if (FWINV(!!memcmp(point->sourcemac, hlpmac, ++ ETH_ALEN), EBT_ISOURCE) ) ++ goto letscontinue; ++ } ++ ++ if (point->bitmask & EBT_DESTMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_dest[j] & point->destmsk[j]; ++ if (FWINV(!!memcmp(point->destmac, hlpmac, ++ ETH_ALEN), EBT_IDEST) ) ++ goto letscontinue; ++ } ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, ++ out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us ++ // what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, ++ out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while " ++ "firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *) ++ (((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *) ++ list_named_find(&ebt_matches, m->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, ++ m->match_size) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *) ++ list_named_find(&ebt_watchers, w->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, ++ w->watcher_size) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " ++ "bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ e->in[IFNAMSIZ - 1] = '\0'; ++ e->out[IFNAMSIZ - 1] = '\0'; ++ e->logical_in[IFNAMSIZ - 1] = '\0'; ++ e->logical_out[IFNAMSIZ - 1] = '\0'; ++ // 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *) ++ list_named_find(&ebt_targets, t->u.name))) { ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, ++ t->target_size) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, ++ 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ // 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); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called under write_lock ++static inline void get_counters(struct ebt_table_info *info, ++ struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, ++ sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += ++ info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc( ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter ++ // allocation. Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ++ ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *) ++ vmalloc(table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ 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; ++ table->lock = RW_LOCK_UNLOCKED; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, ++ table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++// userspace just supplied us with counters ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + ++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *) ++ vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, ++ info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, ++ NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, ++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *) ++ (info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ++ ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/netfilter_bridge/ebtables.h Thu May 2 19:01:09 2002 +@@ -0,0 +1,332 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include // IFNAMSIZ ++#include ++#include // ETH_ALEN ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++// [gs]etsockopt numbers ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ++// ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses ++// different techniques for naming the policy and such. So, iptables doesn't ++// need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ ++ | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_ILOGICALIN 0x20 ++#define EBT_ILOGICALOUT 0x40 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ ++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ // size of data ++ unsigned int match_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ // size of data ++ unsigned int watcher_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ // size of data ++ unsigned int target_size; ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target target; ++ __u8 verdict; ++}; ++ ++// one entry ++struct ebt_entry { ++ // this needs to be the first field ++ __u32 bitmask; ++ __u32 invflags; ++ __u16 ethproto; ++ // the physical in-dev ++ __u8 in[IFNAMSIZ]; ++ // the logical in-dev ++ __u8 logical_in[IFNAMSIZ]; ++ // the physical out-dev ++ __u8 out[IFNAMSIZ]; ++ // the logical out-dev ++ __u8 logical_out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 sourcemsk[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ __u8 destmsk[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ // nr of rules in the table ++ unsigned int nentries; ++ // total size of the entries ++ unsigned int entries_size; ++ // start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of it? ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ // nr of counters userspace expects back ++ unsigned int num_counters; ++ // where the kernel will put the old counters ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ // total size of the entries ++ unsigned int entries_size; ++ unsigned int nentries; ++ // pointers to the start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of the counters bolonging to a chain ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain ++ // matches, targets, ... 0 == let it in ++ int (*check)(const struct ebt_table_info *info, ++ unsigned int valid_hooks); ++ // the data used by the kernel ++ struct ebt_table_info *private; ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++// fn returns 0 to continue iteration ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size + \ ++ sizeof(struct ebt_entry_match)) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size + \ ++ sizeof(struct ebt_entry_watcher)) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_arp.h Wed May 1 14:51:46 2002 +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ ++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_ip.h Wed May 1 14:51:46 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_log.h Wed May 1 14:51:46 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_nat.h Wed May 1 14:51:46 2002 +@@ -0,0 +1,13 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_redirect.h Wed May 1 14:51:46 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H ++#define __LINUX_BRIDGE_EBT_REDIRECT_H ++ ++struct ebt_redirect_info ++{ ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_REDIRECT_TARGET "redirect" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre5/include/linux/br_db.h Wed May 1 16:00:00 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff new file mode 100644 index 0000000..7aa458c --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff @@ -0,0 +1,3135 @@ +ebtables-v2.0pre5 - 01 May + +*** modifications for brouter support *** + +--- linux/net/bridge/br_private.h Mon May 20 12:28:17 2002 ++++ ebt2.0pre6/net/bridge/br_private.h Mon May 20 11:57:27 2002 +@@ -170,7 +170,7 @@ + + /* br_input.c */ + extern int br_handle_frame_finish(struct sk_buff *skb); +-extern void br_handle_frame(struct sk_buff *skb); ++extern int br_handle_frame(struct sk_buff *skb); + + /* br_ioctl.c */ + extern void br_call_ioctl_atomic(void (*fn)(void)); +--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001 ++++ ebt2.0pre6/include/linux/if_bridge.h Mon May 20 11:57:27 2002 +@@ -102,8 +102,13 @@ + struct net_bridge_port; + + extern int (*br_ioctl_hook)(unsigned long arg); +-extern void (*br_handle_frame_hook)(struct sk_buff *skb); +- ++extern int (*br_handle_frame_hook)(struct sk_buff *skb); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++#endif + #endif + + #endif +--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre6/net/core/dev.c Mon May 20 11:57:27 2002 +@@ -1384,7 +1384,14 @@ + } + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) = NULL; ++#endif + #endif + + static __inline__ int handle_bridge(struct sk_buff *skb, +@@ -1394,14 +1401,14 @@ + + if (pt_prev) { + if (!pt_prev->data) +- ret = deliver_to_old_ones(pt_prev, skb, 0); ++ deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); +- ret = pt_prev->func(skb, skb->dev, pt_prev); ++ pt_prev->func(skb, skb->dev, pt_prev); + } + } + +- br_handle_frame_hook(skb); ++ ret = br_handle_frame_hook(skb); + return ret; + } + +@@ -1479,9 +1486,10 @@ + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && + br_handle_frame_hook != NULL) { +- handle_bridge(skb, pt_prev); +- dev_put(rx_dev); +- continue; ++ if (handle_bridge(skb, pt_prev) == 0) { ++ dev_put(rx_dev); ++ continue; ++ } + } + #endif + +--- linux/net/bridge/br_input.c Mon May 20 12:28:17 2002 ++++ ebt2.0pre6/net/bridge/br_input.c Mon May 20 11:57:27 2002 +@@ -19,7 +19,10 @@ + #include + #include + #include "br_private.h" +- ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++#include ++#endif + unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + + static int br_pass_frame_up_finish(struct sk_buff *skb) +@@ -112,7 +115,7 @@ + return 0; + } + +-void br_handle_frame(struct sk_buff *skb) ++int br_handle_frame(struct sk_buff *skb) + { + struct net_bridge *br; + unsigned char *dest; +@@ -146,23 +149,30 @@ + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, ++ skb->dev, NULL, NULL) == NF_DROP) ++ return -1; ++#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); +- return; ++ return 0; + } + + err: + read_unlock(&br->lock); + err_nolock: + kfree_skb(skb); +- return; ++ return 0; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); +- return; ++ return 0; + } + + kfree_skb(skb); ++ return 0; + } +--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre6/net/netsyms.c Mon May 20 11:57:27 2002 +@@ -228,6 +228,10 @@ + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + EXPORT_SYMBOL(br_handle_frame_hook); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++EXPORT_SYMBOL(broute_decision); ++#endif + #ifdef CONFIG_INET + EXPORT_SYMBOL(br_ioctl_hook); + #endif +--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001 ++++ ebt2.0pre6/include/linux/netfilter_bridge.h Mon May 20 11:57:27 2002 +@@ -18,7 +18,19 @@ + #define NF_BR_LOCAL_OUT 3 + /* Packets about to hit the wire. */ + #define NF_BR_POST_ROUTING 4 +-#define NF_BR_NUMHOOKS 5 ++/* Not really a hook, but used for the ebtables broute table */ ++#define NF_BR_BROUTING 5 ++#define NF_BR_NUMHOOKS 6 + ++enum nf_br_hook_priorities { ++ NF_BR_PRI_FIRST = INT_MIN, ++ NF_BR_PRI_FILTER_BRIDGED = -200, ++ NF_BR_PRI_FILTER_OTHER = 200, ++ NF_BR_PRI_NAT_DST_BRIDGED = -300, ++ NF_BR_PRI_NAT_DST_OTHER = 100, ++ NF_BR_PRI_NAT_SRC_BRIDGED = -100, ++ NF_BR_PRI_NAT_SRC_OTHER = 300, ++ NF_BR_PRI_LAST = INT_MAX, ++}; + + #endif + +*** modifications for ebtables compilation *** + +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre6/net/Makefile Mon May 20 11:57:27 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -26,6 +27,12 @@ + endif + endif + ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter ++endif ++endif ++ + subdir-$(CONFIG_KHTTPD) += khttpd + subdir-$(CONFIG_PACKET) += packet + subdir-$(CONFIG_NET_SCHED) += sched +--- linux/net/Config.in Mon May 20 12:28:17 2002 ++++ ebt2.0pre6/net/Config.in Mon May 20 11:58:15 2002 +@@ -60,6 +60,9 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then ++ source net/bridge/netfilter/Config.in ++fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF + +*** new ebtables files *** + +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/Makefile Mon May 20 11:57:27 2002 +@@ -0,0 +1,24 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o ++include $(TOPDIR)/Rules.make +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/Config.in Mon May 20 11:57:27 2002 +@@ -0,0 +1,14 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/br_db.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ ++ (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebtable_nat.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,156 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. ++// needed because of the bridge-nf patch (that allows use of iptables ++// on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING ++// to be executed _after_ the iptables stuff. when it's bridged, it's ++// the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_NAT_DST_OTHER}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_BRIDGED}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_OTHER}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, ++ NF_BR_PRI_NAT_DST_BRIDGED}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_OTHER + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebtable_broute.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,80 @@ ++/* ++ * ebtable_broute ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++// EBT_ACCEPT means the frame will be bridged ++// EBT_DROP means the frame will be routed ++static struct ebt_entries initial_chain = ++ {0, EBT_ACCEPT, 0}; ++ ++static struct ebt_replace initial_table = ++{ ++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), ++ { [NF_BR_BROUTING]&initial_chain}, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++static unsigned int ++ebt_broute (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, &broute_table); ++} ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ ret = ebt_register_table(&broute_table); ++ if (ret < 0) ++ return ret; ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ // in br_input.c, br_handle_frame() wants to call broute_decision() ++ broute_decision = ebt_broute; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ broute_decision = NULL; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ebt_unregister_table(&broute_table); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebt_redirect.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,65 @@ ++/* ++ * ebt_redirect ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../br_private.h" ++ ++static __u8 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 *infostuff = (struct ebt_redirect_info *) data; ++ ++ memcpy((**pskb).mac.ethernet->h_dest, ++ in->br_port->br->dev.dev_addr, ETH_ALEN); ++ (*pskb)->pkt_type = PACKET_HOST; ++ return infostuff->target; ++} ++ ++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_redirect_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target redirect_target = ++{ ++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, ++ ebt_target_redirect_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebt_arp.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,107 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + ++ ((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), ++ EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), ++ EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebt_ip.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,81 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && ++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && ++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != ++ infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != ++ infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebt_log.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,111 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ // max length: 29 + 10 + 2 * 16 ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : ""); ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ // length: 14 ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == ++ htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ // max length: 46 ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", ++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ // max length: 26 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ // max length: 40 ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ++ ntohs(arph->ar_op)); ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebt_nat.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,106 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || ++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, ++ NULL, THIS_MODULE ++}; ++ ++static struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, ++ NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/net/bridge/netfilter/ebtables.c Mon May 20 11:57:27 2002 +@@ -0,0 +1,1189 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++// 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 ++ ++#if 0 // use this for remote debugging ++#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...) ++ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, ++ w->watcher_size, c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, ++ m->match_size, c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') ++ return 0; ++ if (!device) ++ return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + ++ cpu_number_map(smp_processor_id()) * table->private->nentries + ++ table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, ++ EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && ++ (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) ++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) ++ && ((!out || !out->br_port) ? 1 : ++ FWINV(!ebt_dev_check((char *) ++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) ++ ++ ) { ++ char hlpmac[6]; ++ int j; ++ ++ if (point->bitmask & EBT_SOURCEMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_source[j] & point->sourcemsk[j]; ++ if (FWINV(!!memcmp(point->sourcemac, hlpmac, ++ ETH_ALEN), EBT_ISOURCE) ) ++ goto letscontinue; ++ } ++ ++ if (point->bitmask & EBT_DESTMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_dest[j] & point->destmsk[j]; ++ if (FWINV(!!memcmp(point->destmac, hlpmac, ++ ETH_ALEN), EBT_IDEST) ) ++ goto letscontinue; ++ } ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, ++ out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us ++ // what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, ++ out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while " ++ "firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *) ++ (((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *) ++ list_named_find(&ebt_matches, m->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, ++ m->match_size) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *) ++ list_named_find(&ebt_watchers, w->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, ++ w->watcher_size) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " ++ "bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ e->in[IFNAMSIZ - 1] = '\0'; ++ e->out[IFNAMSIZ - 1] = '\0'; ++ e->logical_in[IFNAMSIZ - 1] = '\0'; ++ e->logical_out[IFNAMSIZ - 1] = '\0'; ++ // 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *) ++ list_named_find(&ebt_targets, t->u.name))) { ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, ++ t->target_size) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, ++ 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ // 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); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called under write_lock ++static inline void get_counters(struct ebt_table_info *info, ++ struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, ++ sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += ++ info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc( ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter ++ // allocation. Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ++ ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *) ++ vmalloc(table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ 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; ++ table->lock = RW_LOCK_UNLOCKED; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, ++ table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++// userspace just supplied us with counters ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + ++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *) ++ vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, ++ info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, ++ NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, ++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *) ++ (info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ++ ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/netfilter_bridge/ebtables.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,332 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include // IFNAMSIZ ++#include ++#include // ETH_ALEN ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++// [gs]etsockopt numbers ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ++// ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses ++// different techniques for naming the policy and such. So, iptables doesn't ++// need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ ++ | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_ILOGICALIN 0x20 ++#define EBT_ILOGICALOUT 0x40 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ ++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ // size of data ++ unsigned int match_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ // size of data ++ unsigned int watcher_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ // size of data ++ unsigned int target_size; ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target target; ++ __u8 verdict; ++}; ++ ++// one entry ++struct ebt_entry { ++ // this needs to be the first field ++ __u32 bitmask; ++ __u32 invflags; ++ __u16 ethproto; ++ // the physical in-dev ++ __u8 in[IFNAMSIZ]; ++ // the logical in-dev ++ __u8 logical_in[IFNAMSIZ]; ++ // the physical out-dev ++ __u8 out[IFNAMSIZ]; ++ // the logical out-dev ++ __u8 logical_out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 sourcemsk[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ __u8 destmsk[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ // nr of rules in the table ++ unsigned int nentries; ++ // total size of the entries ++ unsigned int entries_size; ++ // start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of it? ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ // nr of counters userspace expects back ++ unsigned int num_counters; ++ // where the kernel will put the old counters ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ // total size of the entries ++ unsigned int entries_size; ++ unsigned int nentries; ++ // pointers to the start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of the counters bolonging to a chain ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain ++ // matches, targets, ... 0 == let it in ++ int (*check)(const struct ebt_table_info *info, ++ unsigned int valid_hooks); ++ // the data used by the kernel ++ struct ebt_table_info *private; ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++// fn returns 0 to continue iteration ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size + \ ++ sizeof(struct ebt_entry_match)) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size + \ ++ sizeof(struct ebt_entry_watcher)) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_arp.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ ++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_ip.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_log.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_nat.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,13 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_redirect.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H ++#define __LINUX_BRIDGE_EBT_REDIRECT_H ++ ++struct ebt_redirect_info ++{ ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_REDIRECT_TARGET "redirect" ++ ++#endif +--- /dev/null Sat May 18 12:04:21 2002 ++++ ebt2.0pre6/include/linux/br_db.h Mon May 20 11:57:27 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff new file mode 100644 index 0000000..963d6a9 --- /dev/null +++ b/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff @@ -0,0 +1,3285 @@ +ebtables-v2.0pre7 - 30 May + +*** modifications for brouter support *** + +--- linux/net/bridge/br_private.h Fri May 31 18:16:26 2002 ++++ ebt2.0pre7/net/bridge/br_private.h Thu May 30 19:59:39 2002 +@@ -170,7 +170,7 @@ + + /* br_input.c */ + extern int br_handle_frame_finish(struct sk_buff *skb); +-extern void br_handle_frame(struct sk_buff *skb); ++extern int br_handle_frame(struct sk_buff *skb); + + /* br_ioctl.c */ + extern void br_call_ioctl_atomic(void (*fn)(void)); +--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001 ++++ ebt2.0pre7/include/linux/if_bridge.h Thu May 30 19:45:33 2002 +@@ -102,8 +102,13 @@ + struct net_bridge_port; + + extern int (*br_ioctl_hook)(unsigned long arg); +-extern void (*br_handle_frame_hook)(struct sk_buff *skb); +- ++extern int (*br_handle_frame_hook)(struct sk_buff *skb); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++#endif + #endif + + #endif +--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre7/net/core/dev.c Thu May 30 19:03:46 2002 +@@ -1384,7 +1384,14 @@ + } + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) = NULL; ++#endif + #endif + + static __inline__ int handle_bridge(struct sk_buff *skb, +@@ -1394,14 +1401,14 @@ + + if (pt_prev) { + if (!pt_prev->data) +- ret = deliver_to_old_ones(pt_prev, skb, 0); ++ deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); +- ret = pt_prev->func(skb, skb->dev, pt_prev); ++ pt_prev->func(skb, skb->dev, pt_prev); + } + } + +- br_handle_frame_hook(skb); ++ ret = br_handle_frame_hook(skb); + return ret; + } + +@@ -1479,9 +1486,10 @@ + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && + br_handle_frame_hook != NULL) { +- handle_bridge(skb, pt_prev); +- dev_put(rx_dev); +- continue; ++ if (handle_bridge(skb, pt_prev) == 0) { ++ dev_put(rx_dev); ++ continue; ++ } + } + #endif + +--- linux/net/bridge/br_input.c Fri May 31 18:16:26 2002 ++++ ebt2.0pre7/net/bridge/br_input.c Thu May 30 19:03:46 2002 +@@ -19,7 +19,10 @@ + #include + #include + #include "br_private.h" +- ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++#include ++#endif + unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + + static int br_pass_frame_up_finish(struct sk_buff *skb) +@@ -112,7 +115,7 @@ + return 0; + } + +-void br_handle_frame(struct sk_buff *skb) ++int br_handle_frame(struct sk_buff *skb) + { + struct net_bridge *br; + unsigned char *dest; +@@ -146,23 +149,30 @@ + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, ++ skb->dev, NULL, NULL) == NF_DROP) ++ return -1; ++#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); +- return; ++ return 0; + } + + err: + read_unlock(&br->lock); + err_nolock: + kfree_skb(skb); +- return; ++ return 0; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); +- return; ++ return 0; + } + + kfree_skb(skb); ++ return 0; + } +--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre7/net/netsyms.c Thu May 30 19:03:46 2002 +@@ -228,6 +228,10 @@ + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + EXPORT_SYMBOL(br_handle_frame_hook); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++EXPORT_SYMBOL(broute_decision); ++#endif + #ifdef CONFIG_INET + EXPORT_SYMBOL(br_ioctl_hook); + #endif +--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001 ++++ ebt2.0pre7/include/linux/netfilter_bridge.h Thu May 30 19:59:54 2002 +@@ -18,7 +18,19 @@ + #define NF_BR_LOCAL_OUT 3 + /* Packets about to hit the wire. */ + #define NF_BR_POST_ROUTING 4 +-#define NF_BR_NUMHOOKS 5 ++/* Not really a hook, but used for the ebtables broute table */ ++#define NF_BR_BROUTING 5 ++#define NF_BR_NUMHOOKS 6 + ++enum nf_br_hook_priorities { ++ NF_BR_PRI_FIRST = INT_MIN, ++ NF_BR_PRI_FILTER_BRIDGED = -200, ++ NF_BR_PRI_FILTER_OTHER = 200, ++ NF_BR_PRI_NAT_DST_BRIDGED = -300, ++ NF_BR_PRI_NAT_DST_OTHER = 100, ++ NF_BR_PRI_NAT_SRC_BRIDGED = -100, ++ NF_BR_PRI_NAT_SRC_OTHER = 300, ++ NF_BR_PRI_LAST = INT_MAX, ++}; + + #endif + +*** modifications for ebtables compilation *** + +--- linux/net/Makefile Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre7/net/Makefile Thu May 30 19:03:46 2002 +@@ -7,7 +7,8 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched ++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \ ++ bluetooth atm netlink sched + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -23,6 +24,12 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++endif ++endif ++ ++ifneq ($(CONFIG_BRIDGE),n) ++ifneq ($CONFIG_BRIDGE),) ++subdir-$(CONFIG_BRIDGE) += bridge/netfilter + endif + endif + +--- linux/net/Config.in Fri May 31 18:16:26 2002 ++++ ebt2.0pre7/net/Config.in Thu May 30 19:03:46 2002 +@@ -60,6 +60,9 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then ++ source net/bridge/netfilter/Config.in ++fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF + +*** new ebtables files *** + +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/Makefile Thu May 30 19:03:57 2002 +@@ -0,0 +1,25 @@ ++# ++# Makefile for the netfilter modules on top of bridging. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now in the main makefile... ++ ++O_TARGET := netfilter.o ++ ++export-objs = ebtables.o ++ ++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o ++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o ++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o ++obj-$(CONFIG_BRIDGE_DB) += br_db.o ++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o ++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o ++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o ++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o ++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o ++include $(TOPDIR)/Rules.make +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/Config.in Thu May 30 19:03:57 2002 +@@ -0,0 +1,15 @@ ++# ++# Bridge netfilter configuration ++# ++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE ++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT ++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE ++ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/br_db.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,357 @@ ++/* ++ * bridge ethernet protocol database ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.c, April, 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* PF_BRIDGE */ ++#include /* rwlock_t */ ++#include ++#include /* copy_[to,from]_user */ ++#include /* multiprocessors */ ++ ++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args) ++/*#define BUGPRINT(format, args...)*/ ++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args) ++/*#define MEMPRINT(format, args...)*/ ++ ++/* database variables */ ++static __u16 allowdb = BRDB_NODB; ++static struct brdb_dbentry **flowdb = NULL; ++static unsigned int *dbsize; ++static unsigned int *dbnum; ++/* database lock */ ++static rwlock_t brdb_dblock; ++ ++static inline int brdb_dev_check(char *entry, const struct net_device *device){ ++ if (*entry == '\0') return 0; ++ if (!device) return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++static inline int brdb_proto_check(unsigned int a, unsigned int b){ ++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0; ++ return 1; ++} ++ ++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct brdb_dbentry *hlp; ++ int i, cpunr; ++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto; ++ ++ cpunr = cpu_number_map(smp_processor_id()); ++ ++ read_lock_bh(&brdb_dblock); ++ ++ if (allowdb == BRDB_NODB) {// must be after readlock ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp = flowdb[cpunr]; ++ /* search for existing entry */ ++ for (i = 0; i < dbnum[cpunr]; i++) { ++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) && ++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) { ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ hlp++; ++ } ++ /* add new entry to database */ ++ if (dbnum[cpunr] == dbsize[cpunr]) { ++ dbsize[cpunr] *= 2; ++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) { ++ dbsize[cpunr] /= 2; ++ MEMPRINT("maintaindb && nomemory\n"); ++ read_unlock_bh(&brdb_dblock); ++ return NF_ACCEPT; ++ } ++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry)); ++ vfree(flowdb[cpunr]); ++ flowdb[cpunr] = hlp; ++ } ++ ++ hlp = flowdb[cpunr] + dbnum[cpunr]; ++ hlp->hook = hook; ++ if (in) ++ strncpy(hlp->in, in->name, IFNAMSIZ); ++ else ++ hlp->in[0] = '\0'; ++ if (out) ++ strncpy(hlp->out, out->name, IFNAMSIZ); ++ else ++ hlp->out[0] = '\0'; ++ if (ntohs(ethproto) < 1536) ++ hlp->ethproto = IDENTIFY802_3; ++ else ++ hlp->ethproto = ethproto; ++ dbnum[cpunr]++; ++ ++ read_unlock_bh(&brdb_dblock); ++ ++ return NF_ACCEPT; ++} ++ ++static int copy_db(void *user, int *len) ++{ ++ int i, j, nentries = 0, ret; ++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2; ++ ++ write_lock_bh(&brdb_dblock); ++ for (i = 0; i < smp_num_cpus; i++) ++ nentries += dbnum[i]; ++ if (*len > nentries) ++ return -EINVAL; ++ ++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) ) ++ return -ENOMEM; ++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry)); ++ end1 = begin + dbnum[0]; ++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */ ++ point2 = flowdb[i]; ++ end2 = end1; ++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */ ++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */ ++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) && ++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto) ++ goto out;/* already exists in a database of another cpu */ ++ ++ memcpy(end1, point2, sizeof(struct brdb_dbentry)); ++ end1++; ++out: ++ point2++; ++ } ++ } ++ write_unlock_bh(&brdb_dblock); ++ i = (int)( (char *)end1 - (char *)begin); ++ *len = i < *len ? i : *len; ++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ vfree(begin); ++ return ret; ++} ++ ++static int switch_nodb(void){ ++ int i; ++ ++ if (!flowdb) ++ BUGPRINT("switch_nodb && !flowdb\n"); ++ for (i = 0; i < smp_num_cpus; i++) ++ vfree(flowdb[i]); ++ vfree(flowdb); ++ if (!dbsize) ++ BUGPRINT("switch_nodb && !dbsize\n"); ++ vfree(dbsize); ++ if (!dbnum) ++ BUGPRINT("switch_nodb && !dbnum\n"); ++ vfree(dbnum); ++ flowdb = NULL; ++ allowdb = BRDB_NODB; ++ return 0; ++} ++ ++static int switch_db(void) ++{ ++ int i, j; ++ ++ if (flowdb) BUGPRINT("switch_db && flowdb\n"); ++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) { ++ MEMPRINT("switch_db && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < smp_num_cpus; i++) ++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) ) ++ goto sw_free1; ++ else ++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry)); ++ ++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free2; ++ ++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) ) ++ goto sw_free3; ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ dbnum[i] = 0; ++ dbsize[i] = INITIAL_DBSIZE; ++ } ++ allowdb = BRDB_DB; ++ return 0; ++ ++sw_free3: ++ MEMPRINT("switch_db && nomemory2\n"); ++ vfree(dbnum); ++ dbnum = NULL; ++sw_free2: ++ MEMPRINT("switch_db && nomemory3\n"); ++sw_free1: ++ MEMPRINT("switch_db && nomemory4\n"); ++ for (j = 0; j ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ ++ (1 << NF_BR_LOCAL_OUT)) ++ ++static struct ebt_entries initial_chains[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1], ++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++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[] = { ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); 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 < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++) ++ nf_unregister_hook(&ebt_ops_filter[i]); ++ ebt_unregister_table(&frame_filter); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebtable_nat.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,156 @@ ++/* ++ * ebtable_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#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[] = ++{ ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0}, ++ {0, EBT_ACCEPT, 0} ++}; ++ ++static struct ebt_replace initial_table = ++{ ++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries), ++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1], ++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT. ++// needed because of the bridge-nf patch (that allows use of iptables ++// on bridged traffic) ++// if the packet is routed, we want the ebtables stuff on POSTROUTING ++// to be executed _after_ the iptables stuff. when it's bridged, it's ++// the way around ++static struct net_device __fake_net_device = { ++ hard_header_len: ETH_HLEN ++}; ++ ++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); ++} ++ ++// let snat know this frame is routed ++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = NULL; ++ return NF_ACCEPT; ++} ++ ++// let snat know this frame is bridged ++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ (*pskb)->physindev = &__fake_net_device; ++ return NF_ACCEPT; ++} ++ ++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 *)) ++{ ++ // this is a routed packet ++ if ((*pskb)->physindev == NULL) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev != &__fake_net_device) ++ printk("ebtables (br_nat_src): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ // this is a bridged packet ++ if ((*pskb)->physindev == &__fake_net_device) ++ return NF_ACCEPT; ++ if ((*pskb)->physindev) ++ printk("ebtables (br_nat_src_route): physindev hack " ++ "doesn't work - BUG\n"); ++ ++ return ebt_do_table(hook, pskb, in, out, &frame_nat); ++} ++ ++static struct nf_hook_ops ebt_ops_nat[] = { ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_NAT_DST_OTHER}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_BRIDGED}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_OTHER}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, ++ NF_BR_PRI_NAT_DST_BRIDGED}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_OTHER + 1} ++}; ++ ++static int __init init(void) ++{ ++ int i, ret, j; ++ ++ ret = ebt_register_table(&frame_nat); ++ if (ret < 0) ++ return ret; ++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); 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 < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++) ++ nf_unregister_hook(&ebt_ops_nat[i]); ++ ebt_unregister_table(&frame_nat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebtable_broute.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,80 @@ ++/* ++ * ebtable_broute ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++// EBT_ACCEPT means the frame will be bridged ++// EBT_DROP means the frame will be routed ++static struct ebt_entries initial_chain = ++ {0, EBT_ACCEPT, 0}; ++ ++static struct ebt_replace initial_table = ++{ ++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), ++ { [NF_BR_BROUTING]&initial_chain}, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++static unsigned int ++ebt_broute (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, &broute_table); ++} ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ ret = ebt_register_table(&broute_table); ++ if (ret < 0) ++ return ret; ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ // in br_input.c, br_handle_frame() wants to call broute_decision() ++ broute_decision = ebt_broute; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ broute_decision = NULL; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ebt_unregister_table(&broute_table); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebt_redirect.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,65 @@ ++/* ++ * ebt_redirect ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../br_private.h" ++ ++static __u8 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 *infostuff = (struct ebt_redirect_info *) data; ++ ++ memcpy((**pskb).mac.ethernet->h_dest, ++ in->br_port->br->dev.dev_addr, ETH_ALEN); ++ (*pskb)->pkt_type = PACKET_HOST; ++ return infostuff->target; ++} ++ ++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_redirect_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target redirect_target = ++{ ++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, ++ ebt_target_redirect_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebt_arp.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,107 @@ ++/* ++ * ebt_arp ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Tim Gardner ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data; ++ ++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE)) ++ return 1; ++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE)) ++ return 1; ++ ++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) ++ { ++ __u32 arp_len = sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (2*(((*skb).nh.arph)->ar_pln)); ++ __u32 dst; ++ __u32 src; ++ ++ // Make sure the packet is long enough. ++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail) ++ return 1; ++ // IPV4 addresses are always 4 bytes. ++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32)) ++ return 1; ++ ++ if (infostuff->bitmask & EBT_ARP_SRC_IP) { ++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) + ++ ((*skb).nh.arph)->ar_hln, sizeof(__u32)); ++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), ++ EBT_ARP_SRC_IP)) ++ return 1; ++ } ++ ++ if (infostuff->bitmask & EBT_ARP_DST_IP) { ++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + ++ (2*(((*skb).nh.arph)->ar_hln)) + ++ (((*skb).nh.arph)->ar_pln), sizeof(__u32)); ++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), ++ EBT_ARP_DST_IP)) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int ebt_arp_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data; ++ ++ if (datalen != sizeof(struct ebt_arp_info)) ++ return -EINVAL; ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ (e->ethproto != __constant_htons(ETH_P_ARP) && ++ e->ethproto != __constant_htons(ETH_P_RARP)) || ++ e->invflags & EBT_IPROTO) ++ return -EINVAL; ++ if (infostuff->bitmask & ~EBT_ARP_MASK) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_match filter_arp = ++{ ++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebt_ip.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,81 @@ ++/* ++ * ebt_ip ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg)) ++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, const struct ebt_counter *c) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (infostuff->bitmask & EBT_IP_TOS && ++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO)) ++ return 1; ++ if (infostuff->bitmask & EBT_IP_SOURCE && ++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != ++ infostuff->saddr, EBT_IP_SOURCE)) ++ return 1; ++ if ((infostuff->bitmask & EBT_IP_DEST) && ++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != ++ infostuff->daddr, EBT_IP_DEST)) ++ return 1; ++ return 0; ++} ++ ++static int ebt_ip_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data; ++ ++ if (datalen != sizeof(struct ebt_ip_info)) { ++ return -EINVAL; ++ } ++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || ++ e->ethproto != __constant_htons(ETH_P_IP) || ++ e->invflags & EBT_IPROTO) ++ { ++ return -EINVAL; ++ } ++ if (infostuff->bitmask & ~EBT_IP_MASK) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ebt_match filter_ip = ++{ ++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebt_vlan.c Thu May 30 19:03:57 2002 +@@ -0,0 +1,124 @@ ++/* ++ * ebt_vlan kernelspace ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Nick Fedchik ++ * ++ * May, 2002 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned char debug; ++MODULE_PARM (debug, "0-1b"); ++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages"); ++ ++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, ++ const struct ebt_counter *c) ++{ ++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; ++ struct vlan_ethhdr *vlanethhdr = ++ (struct vlan_ethhdr *) skb->mac.raw; ++ unsigned short v_id; ++ unsigned short v_prio; ++ ++ /* ++ * Calculate 802.1Q VLAN ID and Priority ++ * Reserved one bit (13) for CFI ++ */ ++ v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF; ++ v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13; ++ ++ /* ++ * Checking VLANs ++ */ ++ if (infostuff->bitmask & EBT_VLAN_ID) { /* Is VLAN ID parsed? */ ++ if (!((infostuff->id == v_id) ++ ^ !!(infostuff->invflags & EBT_VLAN_ID))) ++ return 1; ++ if (debug) ++ printk (KERN_DEBUG ++ "ebt_vlan: matched ID=%s%d (mask=%X)\n", ++ (infostuff->invflags & EBT_VLAN_ID) ? "!" : "", ++ infostuff->id, ++ (unsigned char) infostuff->bitmask); ++ } ++ /* ++ * Checking Priority ++ */ ++ if (infostuff->bitmask & EBT_VLAN_PRIO) { /* Is VLAN Prio parsed? */ ++ if (!( (infostuff->prio == v_prio) ++ ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) ++ return 1; /* missed */ ++ if (debug) ++ printk (KERN_DEBUG ++ "ebt_vlan: matched Prio=%s%d (mask=%X)\n", ++ (infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "", ++ infostuff->prio, ++ (unsigned char) infostuff->bitmask); ++ } ++ /* ++ * rule matched ++ */ ++ return 0; ++} ++ ++/* ++ * ebt_vlan_check() is called when userspace delivers the table to the kernel, ++ * * it is called to check that userspace doesn't give a bad table. ++ */ ++static int ebt_vlan_check (const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, ++ unsigned int datalen) ++{ ++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; ++ ++ if (datalen != sizeof (struct ebt_vlan_info)) ++ return -EINVAL; ++ ++ if (e->ethproto != __constant_htons (ETH_P_8021Q)) ++ return -EINVAL; ++ ++ if (infostuff->bitmask & ~EBT_VLAN_MASK) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct ebt_match filter_vlan = { ++ {NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check, ++ NULL, ++ THIS_MODULE ++}; ++ ++static int __init init (void) ++{ ++ printk (KERN_INFO ++ "ebt_vlan: 802.1Q VLAN matching module for EBTables\n"); ++ if (debug) ++ printk (KERN_DEBUG ++ "ebt_vlan: 802.1Q matching debug is on\n"); ++ return ebt_register_match (&filter_vlan); ++} ++ ++static void __exit fini (void) ++{ ++ ebt_unregister_match (&filter_vlan); ++} ++ ++module_init (init); ++module_exit (fini); ++EXPORT_NO_SYMBOLS; ++MODULE_AUTHOR ("Nick Fedchik "); ++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1"); ++MODULE_LICENSE ("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebt_log.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,111 @@ ++/* ++ * ebt_log ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED; ++ ++static int ebt_log_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ ++ if (datalen != sizeof(struct ebt_log_info)) ++ return -EINVAL; ++ if (loginfo->bitmask & ~EBT_LOG_MASK) ++ return -EINVAL; ++ if (loginfo->loglevel >= 8) ++ return -EINVAL; ++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; ++ return 0; ++} ++ ++static void ebt_log(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *data, unsigned int datalen, ++ const struct ebt_counter *c) ++{ ++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data; ++ char level_string[4] = "< >"; ++ level_string[1] = '0' + loginfo->loglevel; ++ ++ spin_lock_bh(&ebt_log_lock); ++ printk(level_string); ++ // max length: 29 + 10 + 2 * 16 ++ printk("%s IN=%s OUT=%s ", ++ loginfo->prefix, ++ in ? in->name : "", ++ out ? out->name : ""); ++ ++ if (skb->dev->hard_header_len) { ++ int i; ++ unsigned char *p = (skb->mac.ethernet)->h_source; ++ printk("MAC source = "); ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 31 ++ printk("MAC dest = "); ++ p = (skb->mac.ethernet)->h_dest; ++ for (i = 0; i < ETH_ALEN; i++,p++) ++ printk("%02x%c", *p, ++ i == ETH_ALEN - 1 ++ ? ' ':':');// length: 29 ++ } ++ // length: 14 ++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto)); ++ ++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == ++ htons(ETH_P_IP)){ ++ struct iphdr *iph = skb->nh.iph; ++ // max length: 46 ++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", ++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ // max length: 26 ++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol); ++ } ++ ++ if ((loginfo->bitmask & EBT_LOG_ARP) && ++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) || ++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) { ++ struct arphdr * arph = skb->nh.arph; ++ // max length: 40 ++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", ++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ++ ntohs(arph->ar_op)); ++ } ++ printk("\n"); ++ spin_unlock_bh(&ebt_log_lock); ++} ++ ++struct ebt_watcher log = ++{ ++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, ++ 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebt_nat.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,106 @@ ++/* ++ * ebt_nat ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; ++ ++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ++ ETH_ALEN * sizeof(unsigned char)); ++ return infostuff->target; ++} ++ ++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if (strcmp(tablename, "nat")) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (hooknr != NF_BR_POST_ROUTING) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || ++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_nat_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target snat = ++{ ++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, ++ NULL, THIS_MODULE ++}; ++ ++static struct ebt_target dnat = ++{ ++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, ++ NULL, THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ int ret; ++ ret = ebt_register_target(&snat); ++ if (ret != 0) ++ return ret; ++ ret = ebt_register_target(&dnat); ++ if (ret == 0) ++ return 0; ++ ebt_unregister_target(&snat); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ ebt_unregister_target(&snat); ++ ebt_unregister_target(&dnat); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/net/bridge/netfilter/ebtables.c Thu May 30 19:03:46 2002 +@@ -0,0 +1,1189 @@ ++/* ++ * ebtables ++ * ++ * Author: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++// 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 ++ ++#if 0 // use this for remote debugging ++#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...) ++ ++static void print_string(char *str); ++ ++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, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ebt_counter *c) ++{ ++ w->u.watcher->watcher(skb, in, out, w->data, ++ w->watcher_size, c); ++ // 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, ++ const struct ebt_counter *c) ++{ ++ return m->u.match->match(skb, in, out, m->data, ++ m->match_size, c); ++} ++ ++static inline int ebt_dev_check(char *entry, const struct net_device *device) ++{ ++ if (*entry == '\0') ++ return 0; ++ if (!device) ++ return 1; ++ return strncmp(entry, device->name, IFNAMSIZ); ++} ++ ++// 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; ++ struct ebt_entry_target *t; ++ __u8 verdict; ++ ++ read_lock_bh(&table->lock); ++ nentries = table->private->hook_entry[hook]->nentries; ++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data); ++ counter_base = table->private->counters + ++ cpu_number_map(smp_processor_id()) * table->private->nentries + ++ table->private->counter_entry[hook]; ++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg)) ++ for (i = 0; i < nentries; i++) { ++ if ( ( point->bitmask & EBT_NOPROTO || ++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, ++ EBT_IPROTO) ++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && ++ (point->bitmask & EBT_802_3), EBT_IPROTO) ) ++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) ++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) ++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) ++ && ((!out || !out->br_port) ? 1 : ++ FWINV(!ebt_dev_check((char *) ++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) ++ ++ ) { ++ char hlpmac[6]; ++ int j; ++ ++ if (point->bitmask & EBT_SOURCEMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_source[j] & point->sourcemsk[j]; ++ if (FWINV(!!memcmp(point->sourcemac, hlpmac, ++ ETH_ALEN), EBT_ISOURCE) ) ++ goto letscontinue; ++ } ++ ++ if (point->bitmask & EBT_DESTMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_dest[j] & point->destmsk[j]; ++ if (FWINV(!!memcmp(point->destmac, hlpmac, ++ ETH_ALEN), EBT_IDEST) ) ++ goto letscontinue; ++ } ++ ++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, ++ out, counter_base + i) != 0) ++ goto letscontinue; ++ ++ // increase counter ++ (*(counter_base + i)).pcnt++; ++ ++ // these should only watch: not modify, nor tell us ++ // what to do with the packet ++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, ++ out, counter_base + i); ++ ++ 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_CONTINUE) { ++ read_unlock_bh(&table->lock); ++ BUGPRINT("Illegal target while " ++ "firewalling!!\n"); ++ // Try not to get oopsen ++ return NF_DROP; ++ } ++ } ++letscontinue: ++ point = (struct ebt_entry *) ++ (((char *)point) + point->next_offset); ++ } ++ ++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) { ++ read_unlock_bh(&table->lock); ++ return NF_ACCEPT; ++ } ++ read_unlock_bh(&table->lock); ++ return NF_DROP; ++} ++ ++static inline int ++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_match *match; ++ int ret; ++ ++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ if (!(match = (struct ebt_match *) ++ list_named_find(&ebt_matches, m->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ m->u.match = match; ++ if (match->check && ++ match->check(name, hook, e, m->data, ++ m->match_size) != 0) { ++ BUGPRINT("match->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (match->me) ++ __MOD_INC_USE_COUNT(match->me); ++ up(&ebt_mutex); ++ (*cnt)++; ++ return 0; ++} ++ ++static inline int ++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, ++ const char *name, unsigned int hook, unsigned int *cnt) ++{ ++ struct ebt_watcher *watcher; ++ int ret; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return -EFAULT; ++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(watcher = (struct ebt_watcher *) ++ list_named_find(&ebt_watchers, w->u.name))) { ++ up(&ebt_mutex); ++ return -ENOENT; ++ } ++ w->u.watcher = watcher; ++ if (watcher->check && ++ watcher->check(name, hook, e, w->data, ++ w->watcher_size) != 0) { ++ BUGPRINT("watcher->check failed\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ if (watcher->me) ++ __MOD_INC_USE_COUNT(watcher->me); ++ up(&ebt_mutex); ++ (*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 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) { ++ 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) { ++ BUGPRINT("bad policy\n"); ++ return -EINVAL; ++ } ++ *n = ((struct ebt_entries *)e)->nentries; ++ *cnt = 0; ++ newinfo->hook_entry[i] = (struct ebt_entries *)e; ++ newinfo->counter_entry[i] = *totalcnt; ++ 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; ++ } ++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) { ++ BUGPRINT("entry offsets point too far\n"); ++ return -EINVAL; ++ } ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) { ++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in " ++ "bitmask for an entry\n"); ++ return -EINVAL; ++ } ++ (*cnt)++; ++ (*totalcnt)++; ++ 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); ++ if (m->u.match->me) ++ __MOD_DEC_USE_COUNT(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); ++ if (w->u.watcher->me) ++ __MOD_DEC_USE_COUNT(w->u.watcher->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_entry_target *t; ++ struct ebt_target *target; ++ unsigned int i, j, hook = 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; ++ } ++ e->in[IFNAMSIZ - 1] = '\0'; ++ e->out[IFNAMSIZ - 1] = '\0'; ++ e->logical_in[IFNAMSIZ - 1] = '\0'; ++ e->logical_out[IFNAMSIZ - 1] = '\0'; ++ // 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; ++ } ++ i = 0; ++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i); ++ if (ret != 0) ++ goto cleanup_matches; ++ j = 0; ++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto cleanup_watchers; ++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0'; ++ if (!(target = (struct ebt_target *) ++ list_named_find(&ebt_targets, t->u.name))) { ++ ret = -ENOENT; ++ up(&ebt_mutex); ++ goto cleanup_watchers; ++ } ++ if (target->me) ++ __MOD_INC_USE_COUNT(target->me); ++ 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 (t->u.target->check && ++ t->u.target->check(name, hook, e, t->data, ++ t->target_size) != 0) { ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(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; ++} ++ ++static inline int ++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) ++{ ++ struct ebt_entry_target *t; ++ ++ if (e->bitmask == 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); ++ if (t->u.target->me) ++ __MOD_DEC_USE_COUNT(t->u.target->me); ++ ++ 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; ++ int ret; ++ ++ 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->counter_entry[i] = 0; ++ } ++ ++ 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 ++ 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, ++ 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; ++ } ++ } ++ ++ // we just don't trust anything ++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ // 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); ++ if (ret != 0) { ++ BUGPRINT("ebt_check_entry gave fault back\n"); ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i); ++ } ++ return ret; ++} ++ ++// called under write_lock ++static inline void get_counters(struct ebt_table_info *info, ++ struct ebt_counter *counters) ++{ ++ int i, cpu, counter_base; ++ ++ // counters of cpu 0 ++ memcpy(counters, info->counters, ++ sizeof(struct ebt_counter) * info->nentries); ++ // add other counters to those of cpu 0 ++ for (cpu = 1; cpu < smp_num_cpus; cpu++) { ++ counter_base = cpu * info->nentries; ++ for (i = 0; i < info->nentries; i++) ++ counters[i].pcnt += ++ info->counters[counter_base + i].pcnt; ++ } ++} ++ ++// replace the table ++static int do_replace(void *user, unsigned int len) ++{ ++ int ret; ++ 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; ++ } ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ if (!newinfo) ++ return -ENOMEM; ++ ++ if (tmp.nentries) { ++ newinfo->counters = (struct ebt_counter *)vmalloc( ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ if (!newinfo->counters) { ++ ret = -ENOMEM; ++ goto free_newinfo; ++ } ++ memset(newinfo->counters, 0, ++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ newinfo->entries = (char *)vmalloc(tmp.entries_size); ++ if (!newinfo->entries) { ++ ret = -ENOMEM; ++ goto free_counters; ++ } ++ 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; ++ ++ ret = translate_table(&tmp, newinfo); ++ ++ if (ret != 0) ++ goto free_counterstmp; ++ ++ ret = down_interruptible(&ebt_mutex); ++ ++ if (ret != 0) ++ goto free_cleanup; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ ret = -ENOENT; ++ // give some help to the poor user ++ print_string("The table is not present, try insmod\n"); ++ goto free_unlock; ++ } ++ ++ // 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; ++ // we need an atomic snapshot of the counters ++ write_lock_bh(&t->lock); ++ if (tmp.num_counters) ++ get_counters(t->private, counterstmp); ++ ++ t->private = newinfo; ++ write_unlock_bh(&t->lock); ++ up(&ebt_mutex); ++ // So, a user can change the chains while having messed up his counter ++ // allocation. Only reason why I do this 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->counters) ++ vfree(table->counters); ++ vfree(table); ++ ++ if (counterstmp) ++ vfree(counterstmp); ++ return ret; ++ ++free_unlock: ++ up(&ebt_mutex); ++free_cleanup: ++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ++ ebt_cleanup_entry, NULL); ++free_counterstmp: ++ if (counterstmp) ++ vfree(counterstmp); ++free_entries: ++ if (newinfo->entries) ++ vfree(newinfo->entries); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_target(struct ebt_target *target) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_targets, target); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_match(struct ebt_match *match) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_matches, match); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++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); ++ MOD_INC_USE_COUNT; ++ ++ return 0; ++} ++ ++void ebt_unregister_watcher(struct ebt_watcher *watcher) ++{ ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_watchers, watcher); ++ up(&ebt_mutex); ++ MOD_DEC_USE_COUNT; ++} ++ ++int ebt_register_table(struct ebt_table *table) ++{ ++ struct ebt_table_info *newinfo; ++ int ret; ++ ++ 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; ++ } ++ ++ newinfo = (struct ebt_table_info *) ++ vmalloc(sizeof(struct ebt_table_info)); ++ 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 (table->table->nentries) { ++ newinfo->counters = (struct ebt_counter *) ++ vmalloc(table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ if (!newinfo->counters) ++ goto free_entries; ++ memset(newinfo->counters, 0, table->table->nentries * ++ sizeof(struct ebt_counter) * smp_num_cpus); ++ } ++ else ++ newinfo->counters = NULL; ++ ++ // fill in newinfo and parse the entries ++ ret = translate_table(table->table, newinfo); ++ if (ret != 0) { ++ BUGPRINT("Translate_table failed\n"); ++ goto free_counters; ++ } ++ ++ 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; ++ table->lock = RW_LOCK_UNLOCKED; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_counters; ++ ++ if (list_named_find(&ebt_tables, table->name)) { ++ ret = -EEXIST; ++ BUGPRINT("Table name already exists\n"); ++ goto free_unlock; ++ } ++ ++ list_prepend(&ebt_tables, table); ++ up(&ebt_mutex); ++ MOD_INC_USE_COUNT; ++ return 0; ++free_unlock: ++ up(&ebt_mutex); ++free_counters: ++ if (newinfo->counters) ++ vfree(newinfo->counters); ++free_entries: ++ vfree(newinfo->entries); ++free_newinfo: ++ vfree(newinfo); ++ return ret; ++} ++ ++void ebt_unregister_table(struct ebt_table *table) ++{ ++ if (!table) { ++ BUGPRINT("Request to unregister NULL table!!!\n"); ++ return; ++ } ++ down(&ebt_mutex); ++ LIST_DELETE(&ebt_tables, table); ++ up(&ebt_mutex); ++ EBT_ENTRY_ITERATE(table->private->entries, ++ table->private->entries_size, ebt_cleanup_entry, NULL); ++ if (table->private->counters) ++ vfree(table->private->counters); ++ if (table->private->entries) ++ vfree(table->private->entries); ++ vfree(table->private); ++ MOD_DEC_USE_COUNT; ++} ++ ++// userspace just supplied us with counters ++static int update_counters(void *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("Updata_counters && nomemory\n"); ++ return -ENOMEM; ++ } ++ ++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0'; ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ goto free_tmp; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) { ++ ret = -EINVAL; ++ goto unlock_mutex; ++ } ++ ++ 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; ++ ++ 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 = ubase - base + (char *)e + e->target_offset; ++ struct ebt_entry_target *t; ++ ++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) ++ return 0; ++ ++ 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, int *len) ++{ ++ struct ebt_replace tmp; ++ struct ebt_table_info *info = t->private; ++ struct ebt_counter *counterstmp; ++ int i; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) { ++ BUGPRINT("Cfu didn't work\n"); ++ return -EFAULT; ++ } ++ ++ if (*len != sizeof(struct ebt_replace) + info->entries_size + ++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.nentries != info->nentries) { ++ BUGPRINT("Nentries wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (tmp.entries_size != info->entries_size) { ++ BUGPRINT("Wrong size\n"); ++ return -EINVAL; ++ } ++ ++ // userspace might not need the counters ++ if (tmp.num_counters) { ++ if (tmp.num_counters != info->nentries) { ++ BUGPRINT("Num_counters wrong\n"); ++ return -EINVAL; ++ } ++ counterstmp = (struct ebt_counter *) ++ vmalloc(info->nentries * sizeof(struct ebt_counter)); ++ if (!counterstmp) { ++ BUGPRINT("Couldn't copy counters, out of memory\n"); ++ return -ENOMEM; ++ } ++ write_lock_bh(&t->lock); ++ get_counters(info, counterstmp); ++ write_unlock_bh(&t->lock); ++ ++ if (copy_to_user(tmp.counters, counterstmp, ++ info->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, info->entries, info->entries_size)) { ++ BUGPRINT("Couldn't copy entries to userspace\n"); ++ return -EFAULT; ++ } ++ // make userspace's life easier ++ memcpy(tmp.counter_entry, info->counter_entry, ++ NF_BR_NUMHOOKS * sizeof(int)); ++ memcpy(tmp.hook_entry, info->hook_entry, ++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *)); ++ for (i = 0; i < NF_BR_NUMHOOKS; i++) ++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *) ++ (info->hook_entry[i])) - info->entries + tmp.entries); ++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) { ++ BUGPRINT("Couldn't copy ebt_replace to userspace\n"); ++ return -EFAULT; ++ } ++ // set the match/watcher/target names right ++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ++ ebt_make_names, info->entries, tmp.entries); ++} ++ ++static int do_ebt_set_ctl(struct sock *sk, ++ int cmd, void *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, int *len) ++{ ++ int ret; ++ struct ebt_replace tmp; ++ struct ebt_table *t; ++ ++ if (copy_from_user(&tmp, user, sizeof(tmp))) ++ return -EFAULT; ++ ++ ret = down_interruptible(&ebt_mutex); ++ if (ret != 0) ++ return ret; ++ ++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) { ++ print_string("Table not found, try insmod\n"); ++ up(&ebt_mutex); ++ return -EINVAL; ++ } ++ ++ switch(cmd) { ++ case EBT_SO_GET_INFO: ++ if (*len != sizeof(struct ebt_replace)){ ++ ret = -EINVAL; ++ up(&ebt_mutex); ++ break; ++ } ++ tmp.nentries = t->private->nentries; ++ tmp.entries_size = t->private->entries_size; ++ // userspace needs this to check the chain names ++ tmp.valid_hooks = t->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: ++ ret = copy_everything_to_user(t, user, len); ++ 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 ++}; ++ ++// 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->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); ++ } ++} ++ ++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; ++ ++ print_string("Ebtables v2.0 registered"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ nf_unregister_sockopt(&ebt_sockopts); ++ print_string("Ebtables v2.0 unregistered"); ++} ++ ++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"); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebtables.h Thu May 30 20:00:49 2002 +@@ -0,0 +1,332 @@ ++/* ++ * ebtables ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * ebtables.c,v 2.0, April, 2002 ++ * ++ * This code is stongly inspired on the iptables code which is ++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling ++ */ ++ ++#ifndef __LINUX_BRIDGE_EFF_H ++#define __LINUX_BRIDGE_EFF_H ++#include // IFNAMSIZ ++#include ++#include // ETH_ALEN ++ ++#define EBT_TABLE_MAXNAMELEN 32 ++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN ++ ++// [gs]etsockopt numbers ++#define EBT_BASE_CTL 128 ++ ++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) ++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) ++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) ++ ++#define EBT_SO_GET_INFO (EBT_BASE_CTL) ++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) ++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1) ++ ++#define EBT_ACCEPT 0 ++#define EBT_DROP 1 ++#define EBT_CONTINUE 2 ++#define NUM_STANDARD_TARGETS 3 ++ ++struct ebt_entries { ++ // this field is always set to zero (including userspace). ++ // See EBT_ENTRY_OR_ENTRIES. ++ // Must be same size as ebt_entry.bitmask ++ __u32 distinguisher; ++ // one standard (accept or drop) per hook ++ __u8 policy; ++ // nr. of entries ++ __u32 nentries; ++ // entry list ++ __u8 data[0]; ++}; ++ ++// used for the bitmask of struct ebt_entry ++ ++// This is a hack to make a difference between an ebt_entry struct and an ++// ebt_entries struct when traversing the entries from start to end. ++// Using this simplifies the code alot, while still being able to use ++// ebt_entries. ++// Contrary, iptables doesn't use something like ebt_entries and therefore uses ++// different techniques for naming the policy and such. So, iptables doesn't ++// need a hack like this. ++#define EBT_ENTRY_OR_ENTRIES 0x01 ++// these are the normal masks ++#define EBT_NOPROTO 0x02 ++#define EBT_802_3 0x04 ++#define EBT_SOURCEMAC 0x08 ++#define EBT_DESTMAC 0x10 ++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ ++ | EBT_ENTRY_OR_ENTRIES) ++ ++#define EBT_IPROTO 0x01 ++#define EBT_IIN 0x02 ++#define EBT_IOUT 0x04 ++#define EBT_ISOURCE 0x8 ++#define EBT_IDEST 0x10 ++#define EBT_ILOGICALIN 0x20 ++#define EBT_ILOGICALOUT 0x40 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ ++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) ++ ++struct ebt_counter ++{ ++ __u64 pcnt; ++}; ++ ++struct ebt_entry_match ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_match *match; ++ } u; ++ // size of data ++ unsigned int match_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_watcher ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_watcher *watcher; ++ } u; ++ // size of data ++ unsigned int watcher_size; ++ unsigned char data[0]; ++}; ++ ++struct ebt_entry_target ++{ ++ union { ++ char name[EBT_FUNCTION_MAXNAMELEN]; ++ struct ebt_target *target; ++ } u; ++ // size of data ++ unsigned int target_size; ++ unsigned char data[0]; ++}; ++ ++#define EBT_STANDARD_TARGET "standard" ++struct ebt_standard_target ++{ ++ struct ebt_entry_target target; ++ __u8 verdict; ++}; ++ ++// one entry ++struct ebt_entry { ++ // this needs to be the first field ++ __u32 bitmask; ++ __u32 invflags; ++ __u16 ethproto; ++ // the physical in-dev ++ __u8 in[IFNAMSIZ]; ++ // the logical in-dev ++ __u8 logical_in[IFNAMSIZ]; ++ // the physical out-dev ++ __u8 out[IFNAMSIZ]; ++ // the logical out-dev ++ __u8 logical_out[IFNAMSIZ]; ++ __u8 sourcemac[ETH_ALEN]; ++ __u8 sourcemsk[ETH_ALEN]; ++ __u8 destmac[ETH_ALEN]; ++ __u8 destmsk[ETH_ALEN]; ++ // sizeof ebt_entry + matches ++ __u16 watchers_offset; ++ // sizeof ebt_entry + matches + watchers ++ __u16 target_offset; ++ // sizeof ebt_entry + matches + watchers + target ++ __u16 next_offset; ++ unsigned char elems[0]; ++}; ++ ++struct ebt_replace ++{ ++ char name[EBT_TABLE_MAXNAMELEN]; ++ unsigned int valid_hooks; ++ // nr of rules in the table ++ unsigned int nentries; ++ // total size of the entries ++ unsigned int entries_size; ++ // start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of it? ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ // nr of counters userspace expects back ++ unsigned int num_counters; ++ // where the kernel will put the old counters ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++#ifdef __KERNEL__ ++ ++struct ebt_match ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // 0 == it matches ++ int (*match)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *matchdata, unsigned int datalen); ++ void (*destroy)(void *matchdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_watcher ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *watcherdata, ++ unsigned int datalen, const struct ebt_counter *c); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen); ++ void (*destroy)(void *watcherdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_target ++{ ++ struct list_head list; ++ const char name[EBT_FUNCTION_MAXNAMELEN]; ++ // returns one of the standard verdicts ++ __u8 (*target)(struct sk_buff **pskb, ++ unsigned int hooknr, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targetdata, ++ unsigned int datalen); ++ // 0 == let it in ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *targetdata, unsigned int datalen); ++ void (*destroy)(void *targetdata, unsigned int datalen); ++ struct module *me; ++}; ++ ++struct ebt_table_info ++{ ++ // total size of the entries ++ unsigned int entries_size; ++ unsigned int nentries; ++ // pointers to the start of the chains ++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; ++ // how many counters in front of the counters bolonging to a chain ++ unsigned int counter_entry[NF_BR_NUMHOOKS]; ++ struct ebt_counter *counters; ++ char *entries; ++}; ++ ++struct ebt_table ++{ ++ struct list_head list; ++ char name[EBT_TABLE_MAXNAMELEN]; ++ struct ebt_replace *table; ++ unsigned int valid_hooks; ++ rwlock_t lock; ++ // e.g. could be the table explicitly only allows certain ++ // matches, targets, ... 0 == let it in ++ int (*check)(const struct ebt_table_info *info, ++ unsigned int valid_hooks); ++ // the data used by the kernel ++ struct ebt_table_info *private; ++}; ++ ++extern int ebt_register_table(struct ebt_table *table); ++extern void ebt_unregister_table(struct ebt_table *table); ++extern int ebt_register_match(struct ebt_match *match); ++extern void ebt_unregister_match(struct ebt_match *match); ++extern int ebt_register_watcher(struct ebt_watcher *watcher); ++extern void ebt_unregister_watcher(struct ebt_watcher *watcher); ++extern int ebt_register_target(struct ebt_target *target); ++extern void ebt_unregister_target(struct ebt_target *target); ++extern 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); ++ ++#endif /* __KERNEL__ */ ++ ++// blatently stolen from ip_tables.h ++// fn returns 0 to continue iteration ++#define EBT_MATCH_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_match *__match; \ ++ \ ++ for (__i = sizeof(struct ebt_entry); \ ++ __i < (e)->watchers_offset; \ ++ __i += __match->match_size + \ ++ sizeof(struct ebt_entry_match)) { \ ++ __match = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__match , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->watchers_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_WATCHER_ITERATE(e, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry_watcher *__watcher; \ ++ \ ++ for (__i = e->watchers_offset; \ ++ __i < (e)->target_offset; \ ++ __i += __watcher->watcher_size + \ ++ sizeof(struct ebt_entry_watcher)) { \ ++ __watcher = (void *)(e) + __i; \ ++ \ ++ __ret = fn(__watcher , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (e)->target_offset) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ ++({ \ ++ unsigned int __i; \ ++ int __ret = 0; \ ++ struct ebt_entry *__entry; \ ++ \ ++ for (__i = 0; __i < (size);) { \ ++ __entry = (void *)(entries) + __i; \ ++ __ret = fn(__entry , ## args); \ ++ if (__ret != 0) \ ++ break; \ ++ if (__entry->bitmask != 0) \ ++ __i += __entry->next_offset; \ ++ else \ ++ __i += sizeof(struct ebt_entries); \ ++ } \ ++ if (__ret == 0) { \ ++ if (__i != (size)) \ ++ __ret = -EINVAL; \ ++ } \ ++ __ret; \ ++}) ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_arp.h Thu May 30 19:03:46 2002 +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_BRIDGE_EBT_ARP_H ++#define __LINUX_BRIDGE_EBT_ARP_H ++ ++#define EBT_ARP_OPCODE 0x01 ++#define EBT_ARP_HTYPE 0x02 ++#define EBT_ARP_PTYPE 0x04 ++#define EBT_ARP_SRC_IP 0x08 ++#define EBT_ARP_DST_IP 0x10 ++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ ++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP) ++#define EBT_ARP_MATCH "arp" ++ ++struct ebt_arp_info ++{ ++ __u16 htype; ++ __u16 ptype; ++ __u16 opcode; ++ __u32 saddr; ++ __u32 smsk; ++ __u32 daddr; ++ __u32 dmsk; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_ip.h Thu May 30 19:03:46 2002 +@@ -0,0 +1,24 @@ ++#ifndef __LINUX_BRIDGE_EBT_IP_H ++#define __LINUX_BRIDGE_EBT_IP_H ++ ++#define EBT_IP_SOURCE 0x01 ++#define EBT_IP_DEST 0x02 ++#define EBT_IP_TOS 0x04 ++#define EBT_IP_PROTO 0x08 ++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO) ++#define EBT_IP_MATCH "ip" ++ ++// the same values are used for the invflags ++struct ebt_ip_info ++{ ++ __u32 saddr; ++ __u32 daddr; ++ __u32 smsk; ++ __u32 dmsk; ++ __u8 tos; ++ __u8 protocol; ++ __u8 bitmask; ++ __u8 invflags; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_vlan.h Thu May 30 19:03:57 2002 +@@ -0,0 +1,18 @@ ++#ifndef __LINUX_BRIDGE_EBT_VLAN_H ++#define __LINUX_BRIDGE_EBT_VLAN_H ++ ++#define EBT_VLAN_ID 0x01 ++#define EBT_VLAN_PRIO 0x02 ++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO) ++#define EBT_VLAN_MATCH "vlan" ++ ++struct ebt_vlan_info { ++ __u16 id; /* VLAN ID {1-4095} */ ++ __u16 prio; /* VLAN Priority {0-7} */ ++ __u8 bitmask; /* Args bitmask bit 1=1 - ID arg, ++ bit 2=1 - Pirority arg */ ++ __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, ++ bit 2=1 - inversed Pirority arg */ ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_log.h Thu May 30 19:03:46 2002 +@@ -0,0 +1,17 @@ ++#ifndef __LINUX_BRIDGE_EBT_LOG_H ++#define __LINUX_BRIDGE_EBT_LOG_H ++ ++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information ++#define EBT_LOG_ARP 0x02 ++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) ++#define EBT_LOG_PREFIX_SIZE 30 ++#define EBT_LOG_WATCHER "log" ++ ++struct ebt_log_info ++{ ++ __u8 loglevel; ++ __u8 prefix[EBT_LOG_PREFIX_SIZE]; ++ __u32 bitmask; ++}; ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_nat.h Thu May 30 19:03:46 2002 +@@ -0,0 +1,13 @@ ++#ifndef __LINUX_BRIDGE_EBT_NAT_H ++#define __LINUX_BRIDGE_EBT_NAT_H ++ ++struct ebt_nat_info ++{ ++ unsigned char mac[ETH_ALEN]; ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_SNAT_TARGET "snat" ++#define EBT_DNAT_TARGET "dnat" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_redirect.h Thu May 30 19:03:46 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H ++#define __LINUX_BRIDGE_EBT_REDIRECT_H ++ ++struct ebt_redirect_info ++{ ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_REDIRECT_TARGET "redirect" ++ ++#endif +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre7/include/linux/br_db.h Thu May 30 20:01:13 2002 +@@ -0,0 +1,53 @@ ++/* ++ * bridge ethernet protocol filter ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * br_db.h,v 1.1 2001/04/16 ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_BRIDGE_DB_H ++#define __LINUX_BRIDGE_DB_H ++#include /* IFNAMSIZ */ ++#ifdef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++#define BRDB_BASE_CTL 135 ++ ++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL) ++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1) ++ ++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL) ++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1) ++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1) ++ ++#define BRDB_NODB 0 ++#define BRDB_DB 1 ++ ++#define INITIAL_DBSIZE 10 ++#define IDENTIFY802_3 46 ++ ++struct brdb_dbinfo { ++ __u32 nentries; ++}; ++ ++struct brdb_dbentry { ++ __u8 in[IFNAMSIZ]; ++ __u8 out[IFNAMSIZ]; ++ __u16 ethproto; ++ __u32 hook; ++}; ++ ++#endif diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff new file mode 100644 index 0000000..ceeabe1 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff @@ -0,0 +1,11 @@ +--- linux/include/linux/netfilter_bridge/ebtables.h Wed Apr 10 19:44:56 2002 ++++ ebt2.0pre1/include/linux/netfilter_bridge/ebtables.h Wed Apr 10 19:30:22 2002 +@@ -108,7 +108,7 @@ + #define EBT_STANDARD_TARGET "standard" + struct ebt_standard_target + { +- struct ebt_entry_target; ++ struct ebt_entry_target target; + __u8 verdict; + }; + diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff new file mode 100644 index 0000000..db77c43 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff @@ -0,0 +1,166 @@ +Make size members of ebt_entry_* denote the size of the actual data. +This makes ebt_do_table() go faster. +19 April 2002 + +--- linux/net/bridge/netfilter/ebtables.c Fri Apr 19 20:47:12 2002 ++++ ebt2.0pre3.001/net/bridge/netfilter/ebtables.c Fri Apr 19 20:42:50 2002 +@@ -65,7 +65,7 @@ + const struct ebt_counter *c) + { + w->u.watcher->watcher(skb, in, out, w->data, +- w->watcher_size - sizeof(struct ebt_entry_watcher), c); ++ w->watcher_size, c); + // watchers don't give a verdict + return 0; + } +@@ -77,7 +77,7 @@ + const struct ebt_counter *c) + { + return m->u.match->match(skb, in, out, m->data, +- m->match_size - sizeof(struct ebt_entry_match), c); ++ m->match_size, c); + } + + static inline int ebt_dev_check(char *entry, const struct net_device *device) +@@ -197,7 +197,7 @@ + m->u.match = match; + if (match->check && + match->check(name, hook, e, m->data, +- m->match_size - sizeof(*m)) != 0) { ++ m->match_size) != 0) { + BUGPRINT("match->check failed\n"); + up(&ebt_mutex); + return -EINVAL; +@@ -228,7 +228,7 @@ + w->u.watcher = watcher; + if (watcher->check && + watcher->check(name, hook, e, w->data, +- w->watcher_size - sizeof(*w)) != 0) { ++ w->watcher_size) != 0) { + BUGPRINT("watcher->check failed\n"); + up(&ebt_mutex); + return -EINVAL; +@@ -318,7 +318,7 @@ + if (i && (*i)-- == 0) + return 1; + if (m->u.match->destroy) +- m->u.match->destroy(m->data, m->match_size - sizeof(*m)); ++ m->u.match->destroy(m->data, m->match_size); + if (m->u.match->me) + __MOD_DEC_USE_COUNT(m->u.match->me); + +@@ -331,7 +331,7 @@ + if (i && (*i)-- == 0) + return 1; + if (w->u.watcher->destroy) +- w->u.watcher->destroy(w->data, w->watcher_size - sizeof(*w)); ++ w->u.watcher->destroy(w->data, w->watcher_size); + if (w->u.watcher->me) + __MOD_DEC_USE_COUNT(w->u.watcher->me); + +@@ -411,7 +411,7 @@ + } + } else if (t->u.target->check && + t->u.target->check(name, hook, e, t->data, +- t->target_size - sizeof(*t)) != 0) { ++ t->target_size) != 0) { + if (t->u.target->me) + __MOD_DEC_USE_COUNT(t->u.target->me); + ret = -EFAULT; +@@ -440,7 +440,7 @@ + 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 - sizeof(*t)); ++ t->u.target->destroy(t->data, t->target_size); + if (t->u.target->me) + __MOD_DEC_USE_COUNT(t->u.target->me); + +--- linux/include/linux/netfilter_bridge/ebtables.h Fri Apr 19 20:47:12 2002 ++++ ebt2.0pre3.001/include/linux/netfilter_bridge/ebtables.h Fri Apr 19 20:50:24 2002 +@@ -19,7 +19,7 @@ + #define EBT_TABLE_MAXNAMELEN 32 + #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN + +-/* [gs]etsockopt numbers */ ++// [gs]etsockopt numbers + #define EBT_BASE_CTL 128 + + #define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) +@@ -84,7 +84,7 @@ + char name[EBT_FUNCTION_MAXNAMELEN]; + struct ebt_match *match; + } u; +- // size of this struct + size of data ++ // size of data + unsigned int match_size; + unsigned char data[0]; + }; +@@ -95,7 +95,7 @@ + char name[EBT_FUNCTION_MAXNAMELEN]; + struct ebt_watcher *watcher; + } u; +- // size of this struct + size of data ++ // size of data + unsigned int watcher_size; + unsigned char data[0]; + }; +@@ -106,7 +106,7 @@ + char name[EBT_FUNCTION_MAXNAMELEN]; + struct ebt_target *target; + } u; +- // size of this struct + size of data ++ // size of data + unsigned int target_size; + unsigned char data[0]; + }; +@@ -118,7 +118,7 @@ + __u8 verdict; + }; + +-/* one entry */ ++// one entry + struct ebt_entry { + // this needs to be the first field + __u32 bitmask; +@@ -199,8 +199,8 @@ + const void *targetdata, + unsigned int datalen); + // 0 == let it in +- int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, +- void *targetdata, unsigned int datalen); ++ int (*check)(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *targetdata, unsigned int datalen); + void (*destroy)(void *targetdata, unsigned int datalen); + struct module *me; + }; +@@ -227,7 +227,8 @@ + rwlock_t lock; + // e.g. could be the table explicitly only allows certain + // matches, targets, ... 0 == let it in +- int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks); ++ int (*check)(const struct ebt_table_info *info, ++ unsigned int valid_hooks); + // the data used by the kernel + struct ebt_table_info *private; + }; +@@ -256,7 +257,8 @@ + \ + for (__i = sizeof(struct ebt_entry); \ + __i < (e)->watchers_offset; \ +- __i += __match->match_size) { \ ++ __i += __match->match_size + \ ++ sizeof(struct ebt_entry_match)) { \ + __match = (void *)(e) + __i; \ + \ + __ret = fn(__match , ## args); \ +@@ -278,7 +280,8 @@ + \ + for (__i = e->watchers_offset; \ + __i < (e)->target_offset; \ +- __i += __watcher->watcher_size) { \ ++ __i += __watcher->watcher_size + \ ++ sizeof(struct ebt_entry_watcher)) { \ + __watcher = (void *)(e) + __i; \ + \ + __ret = fn(__watcher , ## args); \ diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff new file mode 100644 index 0000000..7fb0399 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff @@ -0,0 +1,66 @@ +* Add logical bridge in/out device filtering support +* Be more paranoid about the given userspace device names + +--- linux/net/bridge/netfilter/ebtables.c Fri Apr 19 21:48:59 2002 ++++ ebt2.0pre3.002/net/bridge/netfilter/ebtables.c Fri Apr 19 23:21:22 2002 +@@ -30,6 +30,8 @@ + #include + #include + #include ++// needed for logical [in,out]-dev filtering ++#include "../br_private.h" + + // list_named_find + #define ASSERT_READ_LOCK(x) +@@ -115,6 +117,11 @@ + (point->bitmask & EBT_802_3), EBT_IPROTO) ) + && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN) + && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *) ++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN)) ++ && ((!out || !out->br_port) ? 1 : ++ FWINV(!ebt_dev_check((char *) ++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) + ) { + if ( (point->bitmask & EBT_SOURCEMAC) && + FWINV(!!memcmp(point->sourcemac, +@@ -363,6 +370,10 @@ + BUGPRINT("NOPROTO & 802_3 not allowed\n"); + return -EINVAL; + } ++ e->in[IFNAMSIZ - 1] = '\0'; ++ e->out[IFNAMSIZ - 1] = '\0'; ++ e->logical_in[IFNAMSIZ - 1] = '\0'; ++ e->logical_out[IFNAMSIZ - 1] = '\0'; + // what hook do we belong to? + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + if ((valid_hooks & (1 << i)) == 0) +--- linux/include/linux/netfilter_bridge/ebtables.h Fri Apr 19 21:48:59 2002 ++++ ebt2.0pre3.002/include/linux/netfilter_bridge/ebtables.h Fri Apr 19 21:06:25 2002 +@@ -71,7 +71,10 @@ + #define EBT_IOUT 0x04 + #define EBT_ISOURCE 0x8 + #define EBT_IDEST 0x10 +-#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST) ++#define EBT_ILOGICALIN 0x20 ++#define EBT_ILOGICALOUT 0x40 ++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ ++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) + + struct ebt_counter + { +@@ -124,8 +127,14 @@ + __u32 bitmask; + __u32 invflags; + __u16 ethproto; ++ // the physical in-dev + __u8 in[IFNAMSIZ]; ++ // the logical in-dev ++ __u8 logical_in[IFNAMSIZ]; ++ // the physical out-dev + __u8 out[IFNAMSIZ]; ++ // the logical out-dev ++ __u8 logical_out[IFNAMSIZ]; + __u8 sourcemac[ETH_ALEN]; + __u8 destmac[ETH_ALEN]; + // sizeof ebt_entry + matches diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff new file mode 100644 index 0000000..375d347 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff @@ -0,0 +1,367 @@ +April 22 +* add brouter support +* add --[d,s]nat-target option kernel support for the nat target + +--- linux/net/bridge/br_private.h Mon Apr 22 23:03:27 2002 ++++ ebt2.0pre3.003/net/bridge/br_private.h Mon Apr 22 22:44:20 2002 +@@ -170,7 +170,7 @@ + + /* br_input.c */ + extern int br_handle_frame_finish(struct sk_buff *skb); +-extern void br_handle_frame(struct sk_buff *skb); ++extern int br_handle_frame(struct sk_buff *skb); + + /* br_ioctl.c */ + extern void br_call_ioctl_atomic(void (*fn)(void)); +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3.003/net/bridge/netfilter/ebtable_broute.c Mon Apr 22 19:11:31 2002 +@@ -0,0 +1,80 @@ ++/* ++ * ebtable_broute ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++// EBT_ACCEPT means the frame will be bridged ++// EBT_DROP means the frame will be routed ++static struct ebt_entries initial_chain = ++ {0, EBT_ACCEPT, 0}; ++ ++static struct ebt_replace initial_table = ++{ ++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries), ++ { [NF_BR_BROUTING]&initial_chain}, {}, ++ 0, NULL, (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 = ++{ ++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING, ++ RW_LOCK_UNLOCKED, check, NULL ++}; ++ ++static unsigned int ++ebt_broute (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, &broute_table); ++} ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ ret = ebt_register_table(&broute_table); ++ if (ret < 0) ++ return ret; ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ // in br_input.c, br_handle_frame() wants to call broute_decision() ++ broute_decision = ebt_broute; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ broute_decision = NULL; ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ebt_unregister_table(&broute_table); ++} ++ ++module_init(init); ++module_exit(fini); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001 ++++ ebt2.0pre3.003/include/linux/if_bridge.h Mon Apr 22 19:29:41 2002 +@@ -102,8 +102,13 @@ + struct net_bridge_port; + + extern int (*br_ioctl_hook)(unsigned long arg); +-extern void (*br_handle_frame_hook)(struct sk_buff *skb); +- ++extern int (*br_handle_frame_hook)(struct sk_buff *skb); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++#endif + #endif + + #endif +--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre3.003/net/core/dev.c Sun Apr 21 18:15:38 2002 +@@ -1384,7 +1384,14 @@ + } + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) = NULL; ++#endif + #endif + + static __inline__ int handle_bridge(struct sk_buff *skb, +@@ -1394,14 +1401,14 @@ + + if (pt_prev) { + if (!pt_prev->data) +- ret = deliver_to_old_ones(pt_prev, skb, 0); ++ deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); +- ret = pt_prev->func(skb, skb->dev, pt_prev); ++ pt_prev->func(skb, skb->dev, pt_prev); + } + } + +- br_handle_frame_hook(skb); ++ ret = br_handle_frame_hook(skb); + return ret; + } + +@@ -1479,9 +1486,10 @@ + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && + br_handle_frame_hook != NULL) { +- handle_bridge(skb, pt_prev); +- dev_put(rx_dev); +- continue; ++ if (handle_bridge(skb, pt_prev) == 0) { ++ dev_put(rx_dev); ++ continue; ++ } + } + #endif + +--- linux/net/bridge/br_input.c Mon Apr 22 23:03:27 2002 ++++ ebt2.0pre3.003/net/bridge/br_input.c Sun Apr 21 18:15:23 2002 +@@ -19,7 +19,10 @@ + #include + #include + #include "br_private.h" +- ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++#include ++#endif + unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + + static int br_pass_frame_up_finish(struct sk_buff *skb) +@@ -112,7 +115,7 @@ + return 0; + } + +-void br_handle_frame(struct sk_buff *skb) ++int br_handle_frame(struct sk_buff *skb) + { + struct net_bridge *br; + unsigned char *dest; +@@ -146,23 +149,30 @@ + goto handle_special_frame; + + if (p->state == BR_STATE_FORWARDING) { ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb, ++ skb->dev, NULL, NULL) == NF_DROP) ++ return -1; ++#endif + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish); + read_unlock(&br->lock); +- return; ++ return 0; + } + + err: + read_unlock(&br->lock); + err_nolock: + kfree_skb(skb); +- return; ++ return 0; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); +- return; ++ return 0; + } + + kfree_skb(skb); ++ return 0; + } +--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002 ++++ ebt2.0pre3.003/net/netsyms.c Sun Apr 21 18:15:56 2002 +@@ -228,6 +228,10 @@ + + #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + EXPORT_SYMBOL(br_handle_frame_hook); ++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \ ++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE) ++EXPORT_SYMBOL(broute_decision); ++#endif + #ifdef CONFIG_INET + EXPORT_SYMBOL(br_ioctl_hook); + #endif +--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001 ++++ ebt2.0pre3.003/include/linux/netfilter_bridge.h Sun Apr 21 19:02:02 2002 +@@ -18,7 +18,8 @@ + #define NF_BR_LOCAL_OUT 3 + /* Packets about to hit the wire. */ + #define NF_BR_POST_ROUTING 4 +-#define NF_BR_NUMHOOKS 5 +- ++/* Not really a hook, but used for the ebtables broute table */ ++#define NF_BR_BROUTING 5 ++#define NF_BR_NUMHOOKS 6 + + #endif +--- linux/net/bridge/netfilter/Makefile Mon Apr 22 23:03:37 2002 ++++ ebt2.0pre3.003/net/bridge/netfilter/Makefile Sun Apr 21 14:17:32 2002 +@@ -14,6 +14,7 @@ + obj-$(CONFIG_BRIDGE_EBT) += ebtables.o + obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o + obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o ++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o + obj-$(CONFIG_BRIDGE_DB) += br_db.o + obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o + obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o +--- linux/net/bridge/netfilter/Config.in Mon Apr 22 23:03:37 2002 ++++ ebt2.0pre3.003/net/bridge/netfilter/Config.in Sat Apr 20 18:08:53 2002 +@@ -4,6 +4,7 @@ + dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE + dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT +--- linux/net/bridge/netfilter/ebtable_nat.c Mon Apr 22 23:03:37 2002 ++++ ebt2.0pre3.003/net/bridge/netfilter/ebtable_nat.c Sat Apr 20 17:34:35 2002 +@@ -55,18 +55,16 @@ + + 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 *)) ++ 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); + } + + // let snat know this frame is routed + static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb, +- const struct net_device *in, +- const struct net_device *out, +- int (*okfn)(struct sk_buff *)) ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) + { + (*pskb)->physindev = NULL; + return NF_ACCEPT; +@@ -74,9 +72,8 @@ + + // let snat know this frame is bridged + static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb, +- const struct net_device *in, +- const struct net_device *out, +- int (*okfn)(struct sk_buff *)) ++ const struct net_device *in, const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) + { + (*pskb)->physindev = &__fake_net_device; + return NF_ACCEPT; +--- linux/net/bridge/netfilter/ebt_nat.c Mon Apr 22 23:03:37 2002 ++++ ebt2.0pre3.003/net/bridge/netfilter/ebt_nat.c Mon Apr 22 22:48:15 2002 +@@ -33,7 +33,7 @@ + } + memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, + ETH_ALEN * sizeof(unsigned char)); +- return EBT_ACCEPT; ++ return infostuff->target; + } + + __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr, +@@ -54,29 +54,37 @@ + } + memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, + ETH_ALEN * sizeof(unsigned char)); +- return EBT_ACCEPT; ++ return infostuff->target; + } + + int ebt_target_snat_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) + { ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ + if (strcmp(tablename, "nat")) + return -EINVAL; + if (datalen != sizeof(struct ebt_nat_info)) + return -EINVAL; + if (hooknr != NF_BR_POST_ROUTING) + return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; + return 0; + } + + int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) + { ++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; ++ + if (strcmp(tablename, "nat")) + return -EINVAL; + if (datalen != sizeof(struct ebt_nat_info)) + return -EINVAL; + if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) + return -EINVAL; + return 0; + } +--- linux/include/linux/netfilter_bridge/ebt_nat.h Mon Apr 22 23:03:37 2002 ++++ ebt2.0pre3.003/include/linux/netfilter_bridge/ebt_nat.h Mon Apr 22 20:43:40 2002 +@@ -4,6 +4,8 @@ + struct ebt_nat_info + { + unsigned char mac[ETH_ALEN]; ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; + }; + #define EBT_SNAT_TARGET "snat" + #define EBT_DNAT_TARGET "dnat" diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff new file mode 100644 index 0000000..6ff7f33 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff @@ -0,0 +1,252 @@ +* add redirect target +* remove some bugs from nat target +* use NF_BR_PRI_* + +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre3.004/net/bridge/netfilter/ebt_redirect.c Sat Apr 27 13:09:16 2002 +@@ -0,0 +1,63 @@ ++/* ++ * ebt_redirect ++ * ++ * Authors: ++ * Bart De Schuymer ++ * ++ * April, 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static __u8 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 *infostuff = (struct ebt_redirect_info *) data; ++ ++ memcpy((**pskb).mac.ethernet->h_dest, in->dev_addr, ETH_ALEN); ++ (*pskb)->pkt_type = PACKET_HOST; ++ return infostuff->target; ++} ++ ++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, unsigned int datalen) ++{ ++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; ++ ++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) ++ return -EINVAL; ++ if (datalen != sizeof(struct ebt_redirect_info)) ++ return -EINVAL; ++ if (infostuff->target >= NUM_STANDARD_TARGETS) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct ebt_target redirect_target = ++{ ++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect, ++ ebt_target_redirect_check, NULL, 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); ++EXPORT_NO_SYMBOLS; ++MODULE_LICENSE("GPL"); +--- linux/include/linux/netfilter_bridge.h Sun Apr 21 19:02:02 2002 ++++ ebt2.0pre3.004/include/linux/netfilter_bridge.h Sat Apr 27 17:40:09 2002 +@@ -22,4 +22,15 @@ + #define NF_BR_BROUTING 5 + #define NF_BR_NUMHOOKS 6 + ++enum nf_br_hook_priorities { ++ NF_BR_PRI_FIRST = INT_MIN, ++ NF_BR_PRI_FILTER_BRIDGED = -200, ++ NF_BR_PRI_FILTER_OTHER = 200, ++ NF_BR_PRI_NAT_DST_BRIDGED = -300, ++ NF_BR_PRI_NAT_DST_OTHER = 100, ++ NF_BR_PRI_NAT_SRC_BRIDGED = -100, ++ NF_BR_PRI_NAT_SRC_OTHER = 300, ++ NF_BR_PRI_LAST = INT_MAX, ++}; ++ + #endif +--- linux/net/bridge/netfilter/Makefile Sun Apr 21 14:17:32 2002 ++++ ebt2.0pre3.004/net/bridge/netfilter/Makefile Tue Apr 23 22:52:25 2002 +@@ -20,5 +20,5 @@ + obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o + obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o + obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o +- ++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o + include $(TOPDIR)/Rules.make +--- linux/net/bridge/netfilter/Config.in Sat Apr 20 18:08:53 2002 ++++ ebt2.0pre3.004/net/bridge/netfilter/Config.in Tue Apr 23 22:51:38 2002 +@@ -9,5 +9,6 @@ + dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT + dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE + +--- linux/net/bridge/netfilter/ebtable_filter.c Sat Apr 13 21:51:47 2002 ++++ ebt2.0pre3.004/net/bridge/netfilter/ebtable_filter.c Sat Apr 27 17:44:20 2002 +@@ -52,9 +52,12 @@ + } + + static struct nf_hook_ops ebt_ops_filter[] = { +- { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200}, +- { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200}, +- { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200} ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_BRIDGED}, ++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER} + }; + + static int __init init(void) +--- linux/net/bridge/netfilter/ebtable_nat.c Sat Apr 20 17:34:35 2002 ++++ ebt2.0pre3.004/net/bridge/netfilter/ebtable_nat.c Sat Apr 27 17:42:28 2002 +@@ -109,12 +109,18 @@ + } + + static struct nf_hook_ops ebt_ops_nat[] = { +- { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100}, +- { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100}, +- { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,300}, +- { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300}, +- { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,200 + 1}, +- { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1} ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_NAT_DST_OTHER}, ++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_BRIDGED}, ++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, ++ NF_BR_PRI_NAT_SRC_OTHER}, ++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, ++ NF_BR_PRI_NAT_DST_BRIDGED}, ++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, ++ NF_BR_PRI_FILTER_OTHER + 1}, ++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, ++ NF_BR_PRI_FILTER_OTHER + 1} + }; + + static int __init init(void) +--- linux/net/bridge/netfilter/ebt_nat.c Mon Apr 22 22:48:15 2002 ++++ ebt2.0pre3.004/net/bridge/netfilter/ebt_nat.c Thu Apr 25 18:49:14 2002 +@@ -15,49 +15,29 @@ + #include + #include + +-__u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr, ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; + +- if (skb_cloned(*pskb)) { +- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); +- +- if (!nskb) +- return EBT_DROP; +- if ((*pskb)->sk) +- skb_set_owner_w(nskb, (*pskb)->sk); +- kfree_skb(*pskb); +- *pskb = nskb; +- } + memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, + ETH_ALEN * sizeof(unsigned char)); + return infostuff->target; + } + +-__u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr, ++static __u8 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 *infostuff = (struct ebt_nat_info *) data; + +- if (skb_cloned(*pskb)) { +- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); +- +- if (!nskb) +- return EBT_DROP; +- if ((*pskb)->sk) +- skb_set_owner_w(nskb, (*pskb)->sk); +- kfree_skb(*pskb); +- *pskb = nskb; +- } + memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, + ETH_ALEN * sizeof(unsigned char)); + return infostuff->target; + } + +-int ebt_target_snat_check(const char *tablename, unsigned int hooknr, ++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) + { + struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; +@@ -73,29 +53,29 @@ + return 0; + } + +-int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, ++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, + const struct ebt_entry *e, void *data, unsigned int datalen) + { + struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data; + +- if (strcmp(tablename, "nat")) ++ if ( (strcmp(tablename, "nat") || ++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) && ++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) ) + return -EINVAL; + if (datalen != sizeof(struct ebt_nat_info)) + return -EINVAL; +- if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT) +- return -EINVAL; + if (infostuff->target >= NUM_STANDARD_TARGETS) + return -EINVAL; + return 0; + } + +-struct ebt_target snat = ++static struct ebt_target snat = + { + {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, + NULL, THIS_MODULE + }; + +-struct ebt_target dnat = ++static struct ebt_target dnat = + { + {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, + NULL, THIS_MODULE +--- linux/net/bridge/netfilter/ebtables.c Sat Apr 20 14:05:07 2002 ++++ ebt2.0pre3.004/net/bridge/netfilter/ebtables.c Sat Apr 27 18:10:53 2002 +@@ -122,6 +122,7 @@ + && ((!out || !out->br_port) ? 1 : + FWINV(!ebt_dev_check((char *) + (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) ++ + ) { + if ( (point->bitmask & EBT_SOURCEMAC) && + FWINV(!!memcmp(point->sourcemac, diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff new file mode 100644 index 0000000..425bc53 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff @@ -0,0 +1,22 @@ +make redirect work for bridged traffic too + +--- ebt2.0pre3.004/net/bridge/netfilter/ebt_redirect.c Sat Apr 27 13:09:16 2002 ++++ ebt2.0pre3.005/net/bridge/netfilter/ebt_redirect.c Sat Apr 27 22:48:52 2002 +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include "../br_private.h" + + static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr, + const struct net_device *in, const struct net_device *out, +@@ -21,7 +22,8 @@ + { + struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data; + +- memcpy((**pskb).mac.ethernet->h_dest, in->dev_addr, ETH_ALEN); ++ memcpy((**pskb).mac.ethernet->h_dest, ++ in->br_port->br->dev.dev_addr, ETH_ALEN); + (*pskb)->pkt_type = PACKET_HOST; + return infostuff->target; + } diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff new file mode 100644 index 0000000..3699bdf --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff @@ -0,0 +1,14 @@ +--- /dev/null Thu Aug 24 11:00:32 2000 ++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_redirect.h Mon Apr 29 20:00:05 2002 +@@ -0,0 +1,11 @@ ++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H ++#define __LINUX_BRIDGE_EBT_REDIRECT_H ++ ++struct ebt_redirect_info ++{ ++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE ++ __u8 target; ++}; ++#define EBT_REDIRECT_TARGET "redirect" ++ ++#endif diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff new file mode 100644 index 0000000..ba86d00 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff @@ -0,0 +1,51 @@ +--- linux/net/bridge/netfilter/ebtables.c Fri May 3 20:37:08 2002 ++++ ebt2.0pre5.001/net/bridge/netfilter/ebtables.c Fri May 3 20:28:46 2002 +@@ -124,17 +124,26 @@ + (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT)) + + ) { +- if ( (point->bitmask & EBT_SOURCEMAC) && +- FWINV(!!memcmp(point->sourcemac, +- ((**pskb).mac.ethernet)->h_source, ETH_ALEN), +- EBT_ISOURCE) ) +- goto letscontinue; ++ char hlpmac[6]; ++ int j; + +- if ( (point->bitmask & EBT_DESTMAC) && +- FWINV(!!memcmp(point->destmac, +- ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), +- EBT_IDEST) ) +- goto letscontinue; ++ if (point->bitmask & EBT_SOURCEMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_source[j] & point->sourcemsk[j]; ++ if (FWINV(!!memcmp(point->sourcemac, hlpmac, ++ ETH_ALEN), EBT_ISOURCE) ) ++ goto letscontinue; ++ } ++ ++ if (point->bitmask & EBT_DESTMAC) { ++ for (j = 0; j < 6; j++) ++ hlpmac[j] = ((**pskb).mac.ethernet)-> ++ h_dest[j] & point->destmsk[j]; ++ if (FWINV(!!memcmp(point->destmac, hlpmac, ++ ETH_ALEN), EBT_IDEST) ) ++ goto letscontinue; ++ } + + if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, + out, counter_base + i) != 0) +--- linux/include/linux/netfilter_bridge/ebtables.h Fri May 3 20:37:08 2002 ++++ ebt2.0pre5.001/include/linux/netfilter_bridge/ebtables.h Thu May 2 19:01:09 2002 +@@ -136,7 +136,9 @@ + // the logical out-dev + __u8 logical_out[IFNAMSIZ]; + __u8 sourcemac[ETH_ALEN]; ++ __u8 sourcemsk[ETH_ALEN]; + __u8 destmac[ETH_ALEN]; ++ __u8 destmsk[ETH_ALEN]; + // sizeof ebt_entry + matches + __u16 watchers_offset; + // sizeof ebt_entry + matches + watchers diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff new file mode 100644 index 0000000..4de7890 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff @@ -0,0 +1,12 @@ +--- linux/net/Config.in Mon May 20 12:15:38 2002 ++++ ebt2.0pre6/net/Config.in Mon May 20 11:58:15 2002 +@@ -60,7 +60,9 @@ + source net/decnet/Config.in + fi + dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + source net/bridge/netfilter/Config.in ++fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then + bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff new file mode 100644 index 0000000..edfe983 --- /dev/null +++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff @@ -0,0 +1,172 @@ +diff -Naur linux-2.4.18-br-nf-ebt/include/linux/netfilter_bridge/ebt_vlan.h linux/include/linux/netfilter_bridge/ebt_vlan.h +--- linux-2.4.18-br-nf-ebt/include/linux/netfilter_bridge/ebt_vlan.h Thu Jan 1 03:00:00 1970 ++++ linux/include/linux/netfilter_bridge/ebt_vlan.h Tue May 21 21:04:15 2002 +@@ -0,0 +1,18 @@ ++#ifndef __LINUX_BRIDGE_EBT_VLAN_H ++#define __LINUX_BRIDGE_EBT_VLAN_H ++ ++#define EBT_VLAN_ID 0x01 ++#define EBT_VLAN_PRIO 0x02 ++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO) ++#define EBT_VLAN_MATCH "vlan" ++ ++struct ebt_vlan_info { ++ __u16 id; /* VLAN ID {1-4095} */ ++ __u16 prio; /* VLAN Priority {0-7} */ ++ __u8 bitmask; /* Args bitmask bit 1=1 - ID arg, ++ bit 2=1 - Pirority arg */ ++ __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, ++ bit 2=1 - inversed Pirority arg */ ++}; ++ ++#endif +diff -Naur linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Config.in linux/net/bridge/netfilter/Config.in +--- linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Config.in Tue May 28 19:20:41 2002 ++++ linux/net/bridge/netfilter/Config.in Tue May 28 19:22:22 2002 +@@ -8,6 +8,7 @@ + dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT ++dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT + dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT + dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE +diff -Naur linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Makefile linux/net/bridge/netfilter/Makefile +--- linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Makefile Tue May 28 19:20:41 2002 ++++ linux/net/bridge/netfilter/Makefile Thu May 30 11:21:10 2002 +@@ -18,6 +18,7 @@ + obj-$(CONFIG_BRIDGE_DB) += br_db.o + obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o + obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o ++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o + obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o + obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o + obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o +diff -Naur linux-2.4.18-br-nf-ebt/net/bridge/netfilter/ebt_vlan.c linux/net/bridge/netfilter/ebt_vlan.c +--- linux-2.4.18-br-nf-ebt/net/bridge/netfilter/ebt_vlan.c Thu Jan 1 03:00:00 1970 ++++ linux/net/bridge/netfilter/ebt_vlan.c Wed May 29 11:48:38 2002 +@@ -0,0 +1,124 @@ ++/* ++ * ebt_vlan kernelspace ++ * ++ * Authors: ++ * Bart De Schuymer ++ * Nick Fedchik ++ * ++ * May, 2002 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned char debug; ++MODULE_PARM (debug, "0-1b"); ++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages"); ++ ++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, ++ const struct ebt_counter *c) ++{ ++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; ++ struct vlan_ethhdr *vlanethhdr = ++ (struct vlan_ethhdr *) skb->mac.raw; ++ unsigned short v_id; ++ unsigned short v_prio; ++ ++ /* ++ * Calculate 802.1Q VLAN ID and Priority ++ * Reserved one bit (13) for CFI ++ */ ++ v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF; ++ v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13; ++ ++ /* ++ * Checking VLANs ++ */ ++ if (infostuff->bitmask & EBT_VLAN_ID) { /* Is VLAN ID parsed? */ ++ if (!((infostuff->id == v_id) ++ ^ !!(infostuff->invflags & EBT_VLAN_ID))) ++ return 1; ++ if (debug) ++ printk (KERN_DEBUG ++ "ebt_vlan: matched ID=%s%d (mask=%X)\n", ++ (infostuff->invflags & EBT_VLAN_ID) ? "!" : "", ++ infostuff->id, ++ (unsigned char) infostuff->bitmask); ++ } ++ /* ++ * Checking Priority ++ */ ++ if (infostuff->bitmask & EBT_VLAN_PRIO) { /* Is VLAN Prio parsed? */ ++ if (!( (infostuff->prio == v_prio) ++ ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) ++ return 1; /* missed */ ++ if (debug) ++ printk (KERN_DEBUG ++ "ebt_vlan: matched Prio=%s%d (mask=%X)\n", ++ (infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "", ++ infostuff->prio, ++ (unsigned char) infostuff->bitmask); ++ } ++ /* ++ * rule matched ++ */ ++ return 0; ++} ++ ++/* ++ * ebt_vlan_check() is called when userspace delivers the table to the kernel, ++ * * it is called to check that userspace doesn't give a bad table. ++ */ ++static int ebt_vlan_check (const char *tablename, unsigned int hooknr, ++ const struct ebt_entry *e, void *data, ++ unsigned int datalen) ++{ ++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; ++ ++ if (datalen != sizeof (struct ebt_vlan_info)) ++ return -EINVAL; ++ ++ if (e->ethproto != __constant_htons (ETH_P_8021Q)) ++ return -EINVAL; ++ ++ if (infostuff->bitmask & ~EBT_VLAN_MASK) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct ebt_match filter_vlan = { ++ {NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check, ++ NULL, ++ THIS_MODULE ++}; ++ ++static int __init init (void) ++{ ++ printk (KERN_INFO ++ "ebt_vlan: 802.1Q VLAN matching module for EBTables\n"); ++ if (debug) ++ printk (KERN_DEBUG ++ "ebt_vlan: 802.1Q matching debug is on\n"); ++ return ebt_register_match (&filter_vlan); ++} ++ ++static void __exit fini (void) ++{ ++ ebt_unregister_match (&filter_vlan); ++} ++ ++module_init (init); ++module_exit (fini); ++EXPORT_NO_SYMBOLS; ++MODULE_AUTHOR ("Nick Fedchik "); ++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1"); ++MODULE_LICENSE ("GPL"); -- cgit v1.2.3