Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
32
net/bridge/Kconfig
Normal file
32
net/bridge/Kconfig
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# 802.1d Ethernet Bridging
|
||||
#
|
||||
|
||||
config BRIDGE
|
||||
tristate "802.1d Ethernet Bridging"
|
||||
select LLC
|
||||
---help---
|
||||
If you say Y here, then your Linux box will be able to act as an
|
||||
Ethernet bridge, which means that the different Ethernet segments it
|
||||
is connected to will appear as one Ethernet to the participants.
|
||||
Several such bridges can work together to create even larger
|
||||
networks of Ethernets using the IEEE 802.1 spanning tree algorithm.
|
||||
As this is a standard, Linux bridges will cooperate properly with
|
||||
other third party bridge products.
|
||||
|
||||
In order to use the Ethernet bridge, you'll need the bridge
|
||||
configuration tools; see <file:Documentation/networking/bridge.txt>
|
||||
for location. Please read the Bridge mini-HOWTO for more
|
||||
information.
|
||||
|
||||
If you enable iptables support along with the bridge support then you
|
||||
turn your bridge into a bridging IP firewall.
|
||||
iptables will then see the IP packets being bridged, so you need to
|
||||
take this into account when setting up your firewall rules.
|
||||
Enabling arptables support when bridging will let arptables see
|
||||
bridged ARP traffic in the arptables FORWARD chain.
|
||||
|
||||
To compile this code as a module, choose M here: the module
|
||||
will be called bridge.
|
||||
|
||||
If unsure, say N.
|
||||
15
net/bridge/Makefile
Normal file
15
net/bridge/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Makefile for the IEEE 802.1d ethernet bridging layer.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_BRIDGE) += bridge.o
|
||||
|
||||
bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
|
||||
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
|
||||
br_stp_if.o br_stp_timer.o br_netlink.o
|
||||
|
||||
bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
|
||||
|
||||
bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
|
||||
|
||||
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
|
||||
92
net/bridge/br.c
Normal file
92
net/bridge/br.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Generic parts
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/llc.h>
|
||||
#include <net/llc.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
|
||||
|
||||
static struct llc_sap *br_stp_sap;
|
||||
|
||||
static int __init br_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv);
|
||||
if (!br_stp_sap) {
|
||||
printk(KERN_ERR "bridge: can't register sap for STP\n");
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
|
||||
br_fdb_init();
|
||||
|
||||
err = br_netfilter_init();
|
||||
if (err)
|
||||
goto err_out1;
|
||||
|
||||
err = register_netdevice_notifier(&br_device_notifier);
|
||||
if (err)
|
||||
goto err_out2;
|
||||
|
||||
br_netlink_init();
|
||||
brioctl_set(br_ioctl_deviceless_stub);
|
||||
br_handle_frame_hook = br_handle_frame;
|
||||
|
||||
br_fdb_get_hook = br_fdb_get;
|
||||
br_fdb_put_hook = br_fdb_put;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out2:
|
||||
br_netfilter_fini();
|
||||
err_out1:
|
||||
llc_sap_put(br_stp_sap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit br_deinit(void)
|
||||
{
|
||||
rcu_assign_pointer(br_stp_sap->rcv_func, NULL);
|
||||
|
||||
br_netlink_fini();
|
||||
br_netfilter_fini();
|
||||
unregister_netdevice_notifier(&br_device_notifier);
|
||||
brioctl_set(NULL);
|
||||
|
||||
br_cleanup_bridges();
|
||||
|
||||
synchronize_net();
|
||||
|
||||
llc_sap_put(br_stp_sap);
|
||||
br_fdb_get_hook = NULL;
|
||||
br_fdb_put_hook = NULL;
|
||||
|
||||
br_handle_frame_hook = NULL;
|
||||
br_fdb_fini();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(br_should_route_hook);
|
||||
|
||||
module_init(br_init)
|
||||
module_exit(br_deinit)
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(BR_VERSION);
|
||||
189
net/bridge/br_device.c
Normal file
189
net/bridge/br_device.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Device handling code
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_device.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include "br_private.h"
|
||||
|
||||
static struct net_device_stats *br_dev_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
return &br->statistics;
|
||||
}
|
||||
|
||||
/* net device transmit always called with no BH (preempt_disabled) */
|
||||
int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
const unsigned char *dest = skb->data;
|
||||
struct net_bridge_fdb_entry *dst;
|
||||
|
||||
br->statistics.tx_packets++;
|
||||
br->statistics.tx_bytes += skb->len;
|
||||
|
||||
skb->mac.raw = skb->data;
|
||||
skb_pull(skb, ETH_HLEN);
|
||||
|
||||
if (dest[0] & 1)
|
||||
br_flood_deliver(br, skb, 0);
|
||||
else if ((dst = __br_fdb_get(br, dest)) != NULL)
|
||||
br_deliver(dst->dst, skb);
|
||||
else
|
||||
br_flood_deliver(br, skb, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_dev_open(struct net_device *dev)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
|
||||
br_features_recompute(br);
|
||||
netif_start_queue(dev);
|
||||
br_stp_enable_bridge(br);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void br_dev_set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int br_dev_stop(struct net_device *dev)
|
||||
{
|
||||
br_stp_disable_bridge(netdev_priv(dev));
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
if (new_mtu < 68 || new_mtu > br_min_mtu(netdev_priv(dev)))
|
||||
return -EINVAL;
|
||||
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow setting mac address of pseudo-bridge to be same as
|
||||
* any of the bound interfaces
|
||||
*/
|
||||
static int br_set_mac_address(struct net_device *dev, void *p)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
struct sockaddr *addr = p;
|
||||
struct net_bridge_port *port;
|
||||
int err = -EADDRNOTAVAIL;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
list_for_each_entry(port, &br->port_list, list) {
|
||||
if (!compare_ether_addr(port->dev->dev_addr, addr->sa_data)) {
|
||||
br_stp_change_bridge_id(br, addr->sa_data);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
strcpy(info->driver, "bridge");
|
||||
strcpy(info->version, BR_VERSION);
|
||||
strcpy(info->fw_version, "N/A");
|
||||
strcpy(info->bus_info, "N/A");
|
||||
}
|
||||
|
||||
static int br_set_sg(struct net_device *dev, u32 data)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
|
||||
if (data)
|
||||
br->feature_mask |= NETIF_F_SG;
|
||||
else
|
||||
br->feature_mask &= ~NETIF_F_SG;
|
||||
|
||||
br_features_recompute(br);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_set_tso(struct net_device *dev, u32 data)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
|
||||
if (data)
|
||||
br->feature_mask |= NETIF_F_TSO;
|
||||
else
|
||||
br->feature_mask &= ~NETIF_F_TSO;
|
||||
|
||||
br_features_recompute(br);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_set_tx_csum(struct net_device *dev, u32 data)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
|
||||
if (data)
|
||||
br->feature_mask |= NETIF_F_NO_CSUM;
|
||||
else
|
||||
br->feature_mask &= ~NETIF_F_ALL_CSUM;
|
||||
|
||||
br_features_recompute(br);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ethtool_ops br_ethtool_ops = {
|
||||
.get_drvinfo = br_getinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_sg = ethtool_op_get_sg,
|
||||
.set_sg = br_set_sg,
|
||||
.get_tx_csum = ethtool_op_get_tx_csum,
|
||||
.set_tx_csum = br_set_tx_csum,
|
||||
.get_tso = ethtool_op_get_tso,
|
||||
.set_tso = br_set_tso,
|
||||
};
|
||||
|
||||
void br_dev_setup(struct net_device *dev)
|
||||
{
|
||||
memset(dev->dev_addr, 0, ETH_ALEN);
|
||||
|
||||
ether_setup(dev);
|
||||
|
||||
dev->do_ioctl = br_dev_ioctl;
|
||||
dev->get_stats = br_dev_get_stats;
|
||||
dev->hard_start_xmit = br_dev_xmit;
|
||||
dev->open = br_dev_open;
|
||||
dev->set_multicast_list = br_dev_set_multicast_list;
|
||||
dev->change_mtu = br_change_mtu;
|
||||
dev->destructor = free_netdev;
|
||||
SET_MODULE_OWNER(dev);
|
||||
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
|
||||
dev->stop = br_dev_stop;
|
||||
dev->tx_queue_len = 0;
|
||||
dev->set_mac_address = br_set_mac_address;
|
||||
dev->priv_flags = IFF_EBRIDGE;
|
||||
|
||||
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
|
||||
NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST;
|
||||
}
|
||||
371
net/bridge/br_fdb.c
Normal file
371
net/bridge/br_fdb.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Forwarding database
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_fdb.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/times.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <asm/atomic.h>
|
||||
#include "br_private.h"
|
||||
|
||||
static struct kmem_cache *br_fdb_cache __read_mostly;
|
||||
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr);
|
||||
|
||||
void __init br_fdb_init(void)
|
||||
{
|
||||
br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
|
||||
sizeof(struct net_bridge_fdb_entry),
|
||||
0,
|
||||
SLAB_HWCACHE_ALIGN, NULL, NULL);
|
||||
}
|
||||
|
||||
void __exit br_fdb_fini(void)
|
||||
{
|
||||
kmem_cache_destroy(br_fdb_cache);
|
||||
}
|
||||
|
||||
|
||||
/* if topology_changing then use forward_delay (default 15 sec)
|
||||
* otherwise keep longer (default 5 minutes)
|
||||
*/
|
||||
static __inline__ unsigned long hold_time(const struct net_bridge *br)
|
||||
{
|
||||
return br->topology_change ? br->forward_delay : br->ageing_time;
|
||||
}
|
||||
|
||||
static __inline__ int has_expired(const struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb)
|
||||
{
|
||||
return !fdb->is_static
|
||||
&& time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
|
||||
}
|
||||
|
||||
static __inline__ int br_mac_hash(const unsigned char *mac)
|
||||
{
|
||||
return jhash(mac, ETH_ALEN, 0) & (BR_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
|
||||
{
|
||||
hlist_del_rcu(&f->hlist);
|
||||
br_fdb_put(f);
|
||||
}
|
||||
|
||||
void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
|
||||
{
|
||||
struct net_bridge *br = p->br;
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
|
||||
/* Search all chains since old address/hash is unknown */
|
||||
for (i = 0; i < BR_HASH_SIZE; i++) {
|
||||
struct hlist_node *h;
|
||||
hlist_for_each(h, &br->hash[i]) {
|
||||
struct net_bridge_fdb_entry *f;
|
||||
|
||||
f = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
|
||||
if (f->dst == p && f->is_local) {
|
||||
/* maybe another port has same hw addr? */
|
||||
struct net_bridge_port *op;
|
||||
list_for_each_entry(op, &br->port_list, list) {
|
||||
if (op != p &&
|
||||
!compare_ether_addr(op->dev->dev_addr,
|
||||
f->addr.addr)) {
|
||||
f->dst = op;
|
||||
goto insert;
|
||||
}
|
||||
}
|
||||
|
||||
/* delete old one */
|
||||
fdb_delete(f);
|
||||
goto insert;
|
||||
}
|
||||
}
|
||||
}
|
||||
insert:
|
||||
/* insert new address, may fail if invalid address or dup. */
|
||||
fdb_insert(br, p, newaddr);
|
||||
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
}
|
||||
|
||||
void br_fdb_cleanup(unsigned long _data)
|
||||
{
|
||||
struct net_bridge *br = (struct net_bridge *)_data;
|
||||
unsigned long delay = hold_time(br);
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
for (i = 0; i < BR_HASH_SIZE; i++) {
|
||||
struct net_bridge_fdb_entry *f;
|
||||
struct hlist_node *h, *n;
|
||||
|
||||
hlist_for_each_entry_safe(f, h, n, &br->hash[i], hlist) {
|
||||
if (!f->is_static &&
|
||||
time_before_eq(f->ageing_timer + delay, jiffies))
|
||||
fdb_delete(f);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
|
||||
mod_timer(&br->gc_timer, jiffies + HZ/10);
|
||||
}
|
||||
|
||||
|
||||
void br_fdb_delete_by_port(struct net_bridge *br,
|
||||
const struct net_bridge_port *p,
|
||||
int do_all)
|
||||
{
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
for (i = 0; i < BR_HASH_SIZE; i++) {
|
||||
struct hlist_node *h, *g;
|
||||
|
||||
hlist_for_each_safe(h, g, &br->hash[i]) {
|
||||
struct net_bridge_fdb_entry *f
|
||||
= hlist_entry(h, struct net_bridge_fdb_entry, hlist);
|
||||
if (f->dst != p)
|
||||
continue;
|
||||
|
||||
if (f->is_static && !do_all)
|
||||
continue;
|
||||
/*
|
||||
* if multiple ports all have the same device address
|
||||
* then when one port is deleted, assign
|
||||
* the local entry to other port
|
||||
*/
|
||||
if (f->is_local) {
|
||||
struct net_bridge_port *op;
|
||||
list_for_each_entry(op, &br->port_list, list) {
|
||||
if (op != p &&
|
||||
!compare_ether_addr(op->dev->dev_addr,
|
||||
f->addr.addr)) {
|
||||
f->dst = op;
|
||||
goto skip_delete;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fdb_delete(f);
|
||||
skip_delete: ;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
}
|
||||
|
||||
/* No locking or refcounting, assumes caller has no preempt (rcu_read_lock) */
|
||||
struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
|
||||
const unsigned char *addr)
|
||||
{
|
||||
struct hlist_node *h;
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) {
|
||||
if (!compare_ether_addr(fdb->addr.addr, addr)) {
|
||||
if (unlikely(has_expired(br, fdb)))
|
||||
break;
|
||||
return fdb;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Interface used by ATM hook that keeps a ref count */
|
||||
struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
|
||||
unsigned char *addr)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
rcu_read_lock();
|
||||
fdb = __br_fdb_get(br, addr);
|
||||
if (fdb && !atomic_inc_not_zero(&fdb->use_count))
|
||||
fdb = NULL;
|
||||
rcu_read_unlock();
|
||||
return fdb;
|
||||
}
|
||||
|
||||
static void fdb_rcu_free(struct rcu_head *head)
|
||||
{
|
||||
struct net_bridge_fdb_entry *ent
|
||||
= container_of(head, struct net_bridge_fdb_entry, rcu);
|
||||
kmem_cache_free(br_fdb_cache, ent);
|
||||
}
|
||||
|
||||
/* Set entry up for deletion with RCU */
|
||||
void br_fdb_put(struct net_bridge_fdb_entry *ent)
|
||||
{
|
||||
if (atomic_dec_and_test(&ent->use_count))
|
||||
call_rcu(&ent->rcu, fdb_rcu_free);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill buffer with forwarding table records in
|
||||
* the API format.
|
||||
*/
|
||||
int br_fdb_fillbuf(struct net_bridge *br, void *buf,
|
||||
unsigned long maxnum, unsigned long skip)
|
||||
{
|
||||
struct __fdb_entry *fe = buf;
|
||||
int i, num = 0;
|
||||
struct hlist_node *h;
|
||||
struct net_bridge_fdb_entry *f;
|
||||
|
||||
memset(buf, 0, maxnum*sizeof(struct __fdb_entry));
|
||||
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < BR_HASH_SIZE; i++) {
|
||||
hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
|
||||
if (num >= maxnum)
|
||||
goto out;
|
||||
|
||||
if (has_expired(br, f))
|
||||
continue;
|
||||
|
||||
if (skip) {
|
||||
--skip;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* convert from internal format to API */
|
||||
memcpy(fe->mac_addr, f->addr.addr, ETH_ALEN);
|
||||
fe->port_no = f->dst->port_no;
|
||||
fe->is_local = f->is_local;
|
||||
if (!f->is_static)
|
||||
fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer);
|
||||
++fe;
|
||||
++num;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
|
||||
const unsigned char *addr)
|
||||
{
|
||||
struct hlist_node *h;
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
hlist_for_each_entry_rcu(fdb, h, head, hlist) {
|
||||
if (!compare_ether_addr(fdb->addr.addr, addr))
|
||||
return fdb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
|
||||
struct net_bridge_port *source,
|
||||
const unsigned char *addr,
|
||||
int is_local)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
|
||||
if (fdb) {
|
||||
memcpy(fdb->addr.addr, addr, ETH_ALEN);
|
||||
atomic_set(&fdb->use_count, 1);
|
||||
hlist_add_head_rcu(&fdb->hlist, head);
|
||||
|
||||
fdb->dst = source;
|
||||
fdb->is_local = is_local;
|
||||
fdb->is_static = is_local;
|
||||
fdb->ageing_timer = jiffies;
|
||||
}
|
||||
return fdb;
|
||||
}
|
||||
|
||||
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr)
|
||||
{
|
||||
struct hlist_head *head = &br->hash[br_mac_hash(addr)];
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
if (!is_valid_ether_addr(addr))
|
||||
return -EINVAL;
|
||||
|
||||
fdb = fdb_find(head, addr);
|
||||
if (fdb) {
|
||||
/* it is okay to have multiple ports with same
|
||||
* address, just use the first one.
|
||||
*/
|
||||
if (fdb->is_local)
|
||||
return 0;
|
||||
|
||||
printk(KERN_WARNING "%s adding interface with same address "
|
||||
"as a received packet\n",
|
||||
source->dev->name);
|
||||
fdb_delete(fdb);
|
||||
}
|
||||
|
||||
if (!fdb_create(head, source, addr, 1))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
ret = fdb_insert(br, source, addr);
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr)
|
||||
{
|
||||
struct hlist_head *head = &br->hash[br_mac_hash(addr)];
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
/* some users want to always flood. */
|
||||
if (hold_time(br) == 0)
|
||||
return;
|
||||
|
||||
fdb = fdb_find(head, addr);
|
||||
if (likely(fdb)) {
|
||||
/* attempt to update an entry for a local interface */
|
||||
if (unlikely(fdb->is_local)) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_WARNING "%s: received packet with "
|
||||
" own address as source address\n",
|
||||
source->dev->name);
|
||||
} else {
|
||||
/* fastpath: update of existing entry */
|
||||
fdb->dst = source;
|
||||
fdb->ageing_timer = jiffies;
|
||||
}
|
||||
} else {
|
||||
spin_lock(&br->hash_lock);
|
||||
if (!fdb_find(head, addr))
|
||||
fdb_create(head, source, addr, 0);
|
||||
/* else we lose race and someone else inserts
|
||||
* it first, don't bother updating
|
||||
*/
|
||||
spin_unlock(&br->hash_lock);
|
||||
}
|
||||
}
|
||||
160
net/bridge/br_forward.c
Normal file
160
net/bridge/br_forward.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Forwarding decision
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_forward.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include "br_private.h"
|
||||
|
||||
/* Don't forward packets to originating port or forwarding diasabled */
|
||||
static inline int should_deliver(const struct net_bridge_port *p,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING);
|
||||
}
|
||||
|
||||
static inline unsigned packet_length(const struct sk_buff *skb)
|
||||
{
|
||||
return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0);
|
||||
}
|
||||
|
||||
int br_dev_queue_push_xmit(struct sk_buff *skb)
|
||||
{
|
||||
/* drop mtu oversized packets except gso */
|
||||
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
|
||||
kfree_skb(skb);
|
||||
else {
|
||||
/* ip_refrag calls ip_fragment, doesn't copy the MAC header. */
|
||||
if (nf_bridge_maybe_copy_header(skb))
|
||||
kfree_skb(skb);
|
||||
else {
|
||||
skb_push(skb, ETH_HLEN);
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int br_forward_finish(struct sk_buff *skb)
|
||||
{
|
||||
return NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
|
||||
br_dev_queue_push_xmit);
|
||||
|
||||
}
|
||||
|
||||
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
|
||||
{
|
||||
skb->dev = to->dev;
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
|
||||
br_forward_finish);
|
||||
}
|
||||
|
||||
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *indev;
|
||||
|
||||
indev = skb->dev;
|
||||
skb->dev = to->dev;
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
|
||||
br_forward_finish);
|
||||
}
|
||||
|
||||
/* called with rcu_read_lock */
|
||||
void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
|
||||
{
|
||||
if (should_deliver(to, skb)) {
|
||||
__br_deliver(to, skb);
|
||||
return;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* called with rcu_read_lock */
|
||||
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
|
||||
{
|
||||
if (should_deliver(to, skb)) {
|
||||
__br_forward(to, skb);
|
||||
return;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
|
||||
void (*__packet_hook)(const struct net_bridge_port *p,
|
||||
struct sk_buff *skb))
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
struct net_bridge_port *prev;
|
||||
|
||||
if (clone) {
|
||||
struct sk_buff *skb2;
|
||||
|
||||
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
|
||||
br->statistics.tx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
skb = skb2;
|
||||
}
|
||||
|
||||
prev = NULL;
|
||||
|
||||
list_for_each_entry_rcu(p, &br->port_list, list) {
|
||||
if (should_deliver(p, skb)) {
|
||||
if (prev != NULL) {
|
||||
struct sk_buff *skb2;
|
||||
|
||||
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
|
||||
br->statistics.tx_dropped++;
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
__packet_hook(prev, skb2);
|
||||
}
|
||||
|
||||
prev = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev != NULL) {
|
||||
__packet_hook(prev, skb);
|
||||
return;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
|
||||
/* called with rcu_read_lock */
|
||||
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
|
||||
{
|
||||
br_flood(br, skb, clone, __br_deliver);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
|
||||
{
|
||||
br_flood(br, skb, clone, __br_forward);
|
||||
}
|
||||
481
net/bridge/br_if.c
Normal file
481
net/bridge/br_if.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Userspace interface
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_if.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
/*
|
||||
* Determine initial path cost based on speed.
|
||||
* using recommendations from 802.1d standard
|
||||
*
|
||||
* Need to simulate user ioctl because not all device's that support
|
||||
* ethtool, use ethtool_ops. Also, since driver might sleep need to
|
||||
* not be holding any locks.
|
||||
*/
|
||||
static int port_cost(struct net_device *dev)
|
||||
{
|
||||
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
|
||||
struct ifreq ifr;
|
||||
mm_segment_t old_fs;
|
||||
int err;
|
||||
|
||||
strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
|
||||
ifr.ifr_data = (void __user *) &ecmd;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
err = dev_ethtool(&ifr);
|
||||
set_fs(old_fs);
|
||||
|
||||
if (!err) {
|
||||
switch(ecmd.speed) {
|
||||
case SPEED_100:
|
||||
return 19;
|
||||
case SPEED_1000:
|
||||
return 4;
|
||||
case SPEED_10000:
|
||||
return 2;
|
||||
case SPEED_10:
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
/* Old silly heuristics based on name */
|
||||
if (!strncmp(dev->name, "lec", 3))
|
||||
return 7;
|
||||
|
||||
if (!strncmp(dev->name, "plip", 4))
|
||||
return 2500;
|
||||
|
||||
return 100; /* assume old 10Mbps */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check for port carrier transistions.
|
||||
* Called from work queue to allow for calling functions that
|
||||
* might sleep (such as speed check), and to debounce.
|
||||
*/
|
||||
void br_port_carrier_check(struct net_bridge_port *p)
|
||||
{
|
||||
struct net_device *dev = p->dev;
|
||||
struct net_bridge *br = p->br;
|
||||
|
||||
if (netif_carrier_ok(dev))
|
||||
p->path_cost = port_cost(dev);
|
||||
|
||||
if (netif_running(br->dev)) {
|
||||
spin_lock_bh(&br->lock);
|
||||
if (netif_carrier_ok(dev)) {
|
||||
if (p->state == BR_STATE_DISABLED)
|
||||
br_stp_enable_port(p);
|
||||
} else {
|
||||
if (p->state != BR_STATE_DISABLED)
|
||||
br_stp_disable_port(p);
|
||||
}
|
||||
spin_unlock_bh(&br->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void release_nbp(struct kobject *kobj)
|
||||
{
|
||||
struct net_bridge_port *p
|
||||
= container_of(kobj, struct net_bridge_port, kobj);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static struct kobj_type brport_ktype = {
|
||||
#ifdef CONFIG_SYSFS
|
||||
.sysfs_ops = &brport_sysfs_ops,
|
||||
#endif
|
||||
.release = release_nbp,
|
||||
};
|
||||
|
||||
static void destroy_nbp(struct net_bridge_port *p)
|
||||
{
|
||||
struct net_device *dev = p->dev;
|
||||
|
||||
p->br = NULL;
|
||||
p->dev = NULL;
|
||||
dev_put(dev);
|
||||
|
||||
kobject_put(&p->kobj);
|
||||
}
|
||||
|
||||
static void destroy_nbp_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct net_bridge_port *p =
|
||||
container_of(head, struct net_bridge_port, rcu);
|
||||
destroy_nbp(p);
|
||||
}
|
||||
|
||||
/* Delete port(interface) from bridge is done in two steps.
|
||||
* via RCU. First step, marks device as down. That deletes
|
||||
* all the timers and stops new packets from flowing through.
|
||||
*
|
||||
* Final cleanup doesn't occur until after all CPU's finished
|
||||
* processing packets.
|
||||
*
|
||||
* Protected from multiple admin operations by RTNL mutex
|
||||
*/
|
||||
static void del_nbp(struct net_bridge_port *p)
|
||||
{
|
||||
struct net_bridge *br = p->br;
|
||||
struct net_device *dev = p->dev;
|
||||
|
||||
sysfs_remove_link(&br->ifobj, dev->name);
|
||||
|
||||
dev_set_promiscuity(dev, -1);
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br_stp_disable_port(p);
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
br_fdb_delete_by_port(br, p, 1);
|
||||
|
||||
list_del_rcu(&p->list);
|
||||
|
||||
rcu_assign_pointer(dev->br_port, NULL);
|
||||
|
||||
kobject_uevent(&p->kobj, KOBJ_REMOVE);
|
||||
kobject_del(&p->kobj);
|
||||
|
||||
call_rcu(&p->rcu, destroy_nbp_rcu);
|
||||
}
|
||||
|
||||
/* called with RTNL */
|
||||
static void del_br(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p, *n;
|
||||
|
||||
list_for_each_entry_safe(p, n, &br->port_list, list) {
|
||||
del_nbp(p);
|
||||
}
|
||||
|
||||
del_timer_sync(&br->gc_timer);
|
||||
|
||||
br_sysfs_delbr(br->dev);
|
||||
unregister_netdevice(br->dev);
|
||||
}
|
||||
|
||||
static struct net_device *new_bridge_dev(const char *name)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
struct net_device *dev;
|
||||
|
||||
dev = alloc_netdev(sizeof(struct net_bridge), name,
|
||||
br_dev_setup);
|
||||
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
br = netdev_priv(dev);
|
||||
br->dev = dev;
|
||||
|
||||
spin_lock_init(&br->lock);
|
||||
INIT_LIST_HEAD(&br->port_list);
|
||||
spin_lock_init(&br->hash_lock);
|
||||
|
||||
br->bridge_id.prio[0] = 0x80;
|
||||
br->bridge_id.prio[1] = 0x00;
|
||||
|
||||
memcpy(br->group_addr, br_group_address, ETH_ALEN);
|
||||
|
||||
br->feature_mask = dev->features;
|
||||
br->stp_enabled = 0;
|
||||
br->designated_root = br->bridge_id;
|
||||
br->root_path_cost = 0;
|
||||
br->root_port = 0;
|
||||
br->bridge_max_age = br->max_age = 20 * HZ;
|
||||
br->bridge_hello_time = br->hello_time = 2 * HZ;
|
||||
br->bridge_forward_delay = br->forward_delay = 15 * HZ;
|
||||
br->topology_change = 0;
|
||||
br->topology_change_detected = 0;
|
||||
br->ageing_time = 300 * HZ;
|
||||
INIT_LIST_HEAD(&br->age_list);
|
||||
|
||||
br_stp_timer_init(br);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* find an available port number */
|
||||
static int find_portno(struct net_bridge *br)
|
||||
{
|
||||
int index;
|
||||
struct net_bridge_port *p;
|
||||
unsigned long *inuse;
|
||||
|
||||
inuse = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
if (!inuse)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(0, inuse); /* zero is reserved */
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
set_bit(p->port_no, inuse);
|
||||
}
|
||||
index = find_first_zero_bit(inuse, BR_MAX_PORTS);
|
||||
kfree(inuse);
|
||||
|
||||
return (index >= BR_MAX_PORTS) ? -EXFULL : index;
|
||||
}
|
||||
|
||||
/* called with RTNL but without bridge lock */
|
||||
static struct net_bridge_port *new_nbp(struct net_bridge *br,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int index;
|
||||
struct net_bridge_port *p;
|
||||
|
||||
index = find_portno(br);
|
||||
if (index < 0)
|
||||
return ERR_PTR(index);
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (p == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
p->br = br;
|
||||
dev_hold(dev);
|
||||
p->dev = dev;
|
||||
p->path_cost = port_cost(dev);
|
||||
p->priority = 0x8000 >> BR_PORT_BITS;
|
||||
p->port_no = index;
|
||||
br_init_port(p);
|
||||
p->state = BR_STATE_DISABLED;
|
||||
br_stp_port_timer_init(p);
|
||||
|
||||
kobject_init(&p->kobj);
|
||||
kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
|
||||
p->kobj.ktype = &brport_ktype;
|
||||
p->kobj.parent = &(dev->dev.kobj);
|
||||
p->kobj.kset = NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int br_add_bridge(const char *name)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = new_bridge_dev(name);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
rtnl_lock();
|
||||
if (strchr(dev->name, '%')) {
|
||||
ret = dev_alloc_name(dev, dev->name);
|
||||
if (ret < 0) {
|
||||
free_netdev(dev);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = register_netdevice(dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = br_sysfs_addbr(dev);
|
||||
if (ret)
|
||||
unregister_netdevice(dev);
|
||||
out:
|
||||
rtnl_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int br_del_bridge(const char *name)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int ret = 0;
|
||||
|
||||
rtnl_lock();
|
||||
dev = __dev_get_by_name(name);
|
||||
if (dev == NULL)
|
||||
ret = -ENXIO; /* Could not find device */
|
||||
|
||||
else if (!(dev->priv_flags & IFF_EBRIDGE)) {
|
||||
/* Attempt to delete non bridge device! */
|
||||
ret = -EPERM;
|
||||
}
|
||||
|
||||
else if (dev->flags & IFF_UP) {
|
||||
/* Not shutdown yet. */
|
||||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
else
|
||||
del_br(netdev_priv(dev));
|
||||
|
||||
rtnl_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
|
||||
int br_min_mtu(const struct net_bridge *br)
|
||||
{
|
||||
const struct net_bridge_port *p;
|
||||
int mtu = 0;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (list_empty(&br->port_list))
|
||||
mtu = ETH_DATA_LEN;
|
||||
else {
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (!mtu || p->dev->mtu < mtu)
|
||||
mtu = p->dev->mtu;
|
||||
}
|
||||
}
|
||||
return mtu;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recomputes features using slave's features
|
||||
*/
|
||||
void br_features_recompute(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
unsigned long features, checksum;
|
||||
|
||||
checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0;
|
||||
features = br->feature_mask & ~NETIF_F_ALL_CSUM;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
unsigned long feature = p->dev->features;
|
||||
|
||||
if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM))
|
||||
checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
|
||||
if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM))
|
||||
checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM;
|
||||
if (!(feature & NETIF_F_IP_CSUM))
|
||||
checksum = 0;
|
||||
|
||||
if (feature & NETIF_F_GSO)
|
||||
feature |= NETIF_F_GSO_SOFTWARE;
|
||||
feature |= NETIF_F_GSO;
|
||||
|
||||
features &= feature;
|
||||
}
|
||||
|
||||
if (!(checksum & NETIF_F_ALL_CSUM))
|
||||
features &= ~NETIF_F_SG;
|
||||
if (!(features & NETIF_F_SG))
|
||||
features &= ~NETIF_F_GSO_MASK;
|
||||
|
||||
br->dev->features = features | checksum | NETIF_F_LLTX |
|
||||
NETIF_F_GSO_ROBUST;
|
||||
}
|
||||
|
||||
/* called with RTNL */
|
||||
int br_add_if(struct net_bridge *br, struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
int err = 0;
|
||||
|
||||
if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->hard_start_xmit == br_dev_xmit)
|
||||
return -ELOOP;
|
||||
|
||||
if (dev->br_port != NULL)
|
||||
return -EBUSY;
|
||||
|
||||
p = new_nbp(br, dev);
|
||||
if (IS_ERR(p))
|
||||
return PTR_ERR(p);
|
||||
|
||||
err = kobject_add(&p->kobj);
|
||||
if (err)
|
||||
goto err0;
|
||||
|
||||
err = br_fdb_insert(br, p, dev->dev_addr);
|
||||
if (err)
|
||||
goto err1;
|
||||
|
||||
err = br_sysfs_addif(p);
|
||||
if (err)
|
||||
goto err2;
|
||||
|
||||
rcu_assign_pointer(dev->br_port, p);
|
||||
dev_set_promiscuity(dev, 1);
|
||||
|
||||
list_add_rcu(&p->list, &br->port_list);
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br_stp_recalculate_bridge_id(br);
|
||||
br_features_recompute(br);
|
||||
|
||||
if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
|
||||
(br->dev->flags & IFF_UP))
|
||||
br_stp_enable_port(p);
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
dev_set_mtu(br->dev, br_min_mtu(br));
|
||||
|
||||
kobject_uevent(&p->kobj, KOBJ_ADD);
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
br_fdb_delete_by_port(br, p, 1);
|
||||
err1:
|
||||
kobject_del(&p->kobj);
|
||||
err0:
|
||||
kobject_put(&p->kobj);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* called with RTNL */
|
||||
int br_del_if(struct net_bridge *br, struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *p = dev->br_port;
|
||||
|
||||
if (!p || p->br != br)
|
||||
return -EINVAL;
|
||||
|
||||
del_nbp(p);
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br_stp_recalculate_bridge_id(br);
|
||||
br_features_recompute(br);
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit br_cleanup_bridges(void)
|
||||
{
|
||||
struct net_device *dev, *nxt;
|
||||
|
||||
rtnl_lock();
|
||||
for (dev = dev_base; dev; dev = nxt) {
|
||||
nxt = dev->next;
|
||||
if (dev->priv_flags & IFF_EBRIDGE)
|
||||
del_br(dev->priv);
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
}
|
||||
157
net/bridge/br_input.c
Normal file
157
net/bridge/br_input.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Handle incoming frames
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_input.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include "br_private.h"
|
||||
|
||||
/* Bridge group multicast address 802.1d (pg 51). */
|
||||
const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
|
||||
|
||||
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;
|
||||
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
|
||||
netif_receive_skb);
|
||||
}
|
||||
|
||||
/* note: already called with rcu_read_lock (preempt_disabled) */
|
||||
int br_handle_frame_finish(struct sk_buff *skb)
|
||||
{
|
||||
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
||||
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
|
||||
struct net_bridge *br;
|
||||
struct net_bridge_fdb_entry *dst;
|
||||
int passedup = 0;
|
||||
|
||||
if (!p || p->state == BR_STATE_DISABLED)
|
||||
goto drop;
|
||||
|
||||
/* insert into forwarding database after filtering to avoid spoofing */
|
||||
br = p->br;
|
||||
br_fdb_update(br, p, eth_hdr(skb)->h_source);
|
||||
|
||||
if (p->state == BR_STATE_LEARNING)
|
||||
goto drop;
|
||||
|
||||
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 (is_multicast_ether_addr(dest)) {
|
||||
br->statistics.multicast++;
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dst != NULL) {
|
||||
br_forward(dst->dst, skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
br_flood_forward(br, skb, 0);
|
||||
|
||||
out:
|
||||
return 0;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* note: already called with rcu_read_lock (preempt_disabled) */
|
||||
static int br_handle_local_finish(struct sk_buff *skb)
|
||||
{
|
||||
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
|
||||
|
||||
if (p && p->state != BR_STATE_DISABLED)
|
||||
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
|
||||
|
||||
return 0; /* process further */
|
||||
}
|
||||
|
||||
/* Does address match the link local multicast address.
|
||||
* 01:80:c2:00:00:0X
|
||||
*/
|
||||
static inline int is_link_local(const unsigned char *dest)
|
||||
{
|
||||
return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called via br_handle_frame_hook.
|
||||
* Return 0 if *pskb should be processed furthur
|
||||
* 1 if *pskb is handled
|
||||
* note: already called with rcu_read_lock (preempt_disabled)
|
||||
*/
|
||||
int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
||||
|
||||
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
|
||||
goto err;
|
||||
|
||||
if (unlikely(is_link_local(dest))) {
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
|
||||
NULL, br_handle_local_finish) != 0;
|
||||
}
|
||||
|
||||
if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
|
||||
if (br_should_route_hook) {
|
||||
if (br_should_route_hook(pskb))
|
||||
return 0;
|
||||
skb = *pskb;
|
||||
dest = eth_hdr(skb)->h_dest;
|
||||
}
|
||||
|
||||
if (!compare_ether_addr(p->br->dev->dev_addr, dest))
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
|
||||
br_handle_frame_finish);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
408
net/bridge/br_ioctl.c
Normal file
408
net/bridge/br_ioctl.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Ioctl handler
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_ioctl.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/capability.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/times.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "br_private.h"
|
||||
|
||||
/* called with RTNL */
|
||||
static int get_bridge_ifindices(int *indices, int num)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int i = 0;
|
||||
|
||||
for (dev = dev_base; dev && i < num; dev = dev->next) {
|
||||
if (dev->priv_flags & IFF_EBRIDGE)
|
||||
indices[i++] = dev->ifindex;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* called with RTNL */
|
||||
static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->port_no < num)
|
||||
ifindices[p->port_no] = p->dev->ifindex;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Format up to a page worth of forwarding table entries
|
||||
* userbuf -- where to copy result
|
||||
* maxnum -- maximum number of entries desired
|
||||
* (limited to a page for sanity)
|
||||
* offset -- number of records to skip
|
||||
*/
|
||||
static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
|
||||
unsigned long maxnum, unsigned long offset)
|
||||
{
|
||||
int num;
|
||||
void *buf;
|
||||
size_t size;
|
||||
|
||||
/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
|
||||
if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
|
||||
maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
|
||||
|
||||
size = maxnum * sizeof(struct __fdb_entry);
|
||||
|
||||
buf = kmalloc(size, GFP_USER);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
num = br_fdb_fillbuf(br, buf, maxnum, offset);
|
||||
if (num > 0) {
|
||||
if (copy_to_user(userbuf, buf, num*sizeof(struct __fdb_entry)))
|
||||
num = -EFAULT;
|
||||
}
|
||||
kfree(buf);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
dev = dev_get_by_index(ifindex);
|
||||
if (dev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (isadd)
|
||||
ret = br_add_if(br, dev);
|
||||
else
|
||||
ret = br_del_if(br, dev);
|
||||
|
||||
dev_put(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Legacy ioctl's through SIOCDEVPRIVATE
|
||||
* This interface is deprecated because it was too difficult to
|
||||
* to do the translation for 32/64bit ioctl compatability.
|
||||
*/
|
||||
static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
unsigned long args[4];
|
||||
|
||||
if (copy_from_user(args, rq->ifr_data, sizeof(args)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (args[0]) {
|
||||
case BRCTL_ADD_IF:
|
||||
case BRCTL_DEL_IF:
|
||||
return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);
|
||||
|
||||
case BRCTL_GET_BRIDGE_INFO:
|
||||
{
|
||||
struct __bridge_info b;
|
||||
|
||||
memset(&b, 0, sizeof(struct __bridge_info));
|
||||
rcu_read_lock();
|
||||
memcpy(&b.designated_root, &br->designated_root, 8);
|
||||
memcpy(&b.bridge_id, &br->bridge_id, 8);
|
||||
b.root_path_cost = br->root_path_cost;
|
||||
b.max_age = jiffies_to_clock_t(br->max_age);
|
||||
b.hello_time = jiffies_to_clock_t(br->hello_time);
|
||||
b.forward_delay = br->forward_delay;
|
||||
b.bridge_max_age = br->bridge_max_age;
|
||||
b.bridge_hello_time = br->bridge_hello_time;
|
||||
b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay);
|
||||
b.topology_change = br->topology_change;
|
||||
b.topology_change_detected = br->topology_change_detected;
|
||||
b.root_port = br->root_port;
|
||||
b.stp_enabled = br->stp_enabled;
|
||||
b.ageing_time = jiffies_to_clock_t(br->ageing_time);
|
||||
b.hello_timer_value = br_timer_value(&br->hello_timer);
|
||||
b.tcn_timer_value = br_timer_value(&br->tcn_timer);
|
||||
b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
|
||||
b.gc_timer_value = br_timer_value(&br->gc_timer);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BRCTL_GET_PORT_LIST:
|
||||
{
|
||||
int num, *indices;
|
||||
|
||||
num = args[2];
|
||||
if (num < 0)
|
||||
return -EINVAL;
|
||||
if (num == 0)
|
||||
num = 256;
|
||||
if (num > BR_MAX_PORTS)
|
||||
num = BR_MAX_PORTS;
|
||||
|
||||
indices = kcalloc(num, sizeof(int), GFP_KERNEL);
|
||||
if (indices == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
get_port_ifindices(br, indices, num);
|
||||
if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
|
||||
num = -EFAULT;
|
||||
kfree(indices);
|
||||
return num;
|
||||
}
|
||||
|
||||
case BRCTL_SET_BRIDGE_FORWARD_DELAY:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br->bridge_forward_delay = clock_t_to_jiffies(args[1]);
|
||||
if (br_is_root_bridge(br))
|
||||
br->forward_delay = br->bridge_forward_delay;
|
||||
spin_unlock_bh(&br->lock);
|
||||
return 0;
|
||||
|
||||
case BRCTL_SET_BRIDGE_HELLO_TIME:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br->bridge_hello_time = clock_t_to_jiffies(args[1]);
|
||||
if (br_is_root_bridge(br))
|
||||
br->hello_time = br->bridge_hello_time;
|
||||
spin_unlock_bh(&br->lock);
|
||||
return 0;
|
||||
|
||||
case BRCTL_SET_BRIDGE_MAX_AGE:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br->bridge_max_age = clock_t_to_jiffies(args[1]);
|
||||
if (br_is_root_bridge(br))
|
||||
br->max_age = br->bridge_max_age;
|
||||
spin_unlock_bh(&br->lock);
|
||||
return 0;
|
||||
|
||||
case BRCTL_SET_AGEING_TIME:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
br->ageing_time = clock_t_to_jiffies(args[1]);
|
||||
return 0;
|
||||
|
||||
case BRCTL_GET_PORT_INFO:
|
||||
{
|
||||
struct __port_info p;
|
||||
struct net_bridge_port *pt;
|
||||
|
||||
rcu_read_lock();
|
||||
if ((pt = br_get_port(br, args[2])) == NULL) {
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&p, 0, sizeof(struct __port_info));
|
||||
memcpy(&p.designated_root, &pt->designated_root, 8);
|
||||
memcpy(&p.designated_bridge, &pt->designated_bridge, 8);
|
||||
p.port_id = pt->port_id;
|
||||
p.designated_port = pt->designated_port;
|
||||
p.path_cost = pt->path_cost;
|
||||
p.designated_cost = pt->designated_cost;
|
||||
p.state = pt->state;
|
||||
p.top_change_ack = pt->topology_change_ack;
|
||||
p.config_pending = pt->config_pending;
|
||||
p.message_age_timer_value = br_timer_value(&pt->message_age_timer);
|
||||
p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer);
|
||||
p.hold_timer_value = br_timer_value(&pt->hold_timer);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (copy_to_user((void __user *)args[1], &p, sizeof(p)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BRCTL_SET_BRIDGE_STP_STATE:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
br->stp_enabled = args[1]?1:0;
|
||||
return 0;
|
||||
|
||||
case BRCTL_SET_BRIDGE_PRIORITY:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br_stp_set_bridge_priority(br, args[1]);
|
||||
spin_unlock_bh(&br->lock);
|
||||
return 0;
|
||||
|
||||
case BRCTL_SET_PORT_PRIORITY:
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
int ret = 0;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (args[2] >= (1<<(16-BR_PORT_BITS)))
|
||||
return -ERANGE;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
if ((p = br_get_port(br, args[1])) == NULL)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
br_stp_set_port_priority(p, args[2]);
|
||||
spin_unlock_bh(&br->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
case BRCTL_SET_PATH_COST:
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
int ret = 0;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if ((p = br_get_port(br, args[1])) == NULL)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
br_stp_set_path_cost(p, args[2]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
case BRCTL_GET_FDB_ENTRIES:
|
||||
return get_fdb_entries(br, (void __user *)args[1],
|
||||
args[2], args[3]);
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int old_deviceless(void __user *uarg)
|
||||
{
|
||||
unsigned long args[3];
|
||||
|
||||
if (copy_from_user(args, uarg, sizeof(args)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (args[0]) {
|
||||
case BRCTL_GET_VERSION:
|
||||
return BRCTL_VERSION;
|
||||
|
||||
case BRCTL_GET_BRIDGES:
|
||||
{
|
||||
int *indices;
|
||||
int ret = 0;
|
||||
|
||||
if (args[2] >= 2048)
|
||||
return -ENOMEM;
|
||||
indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
|
||||
if (indices == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
args[2] = get_bridge_ifindices(indices, args[2]);
|
||||
|
||||
ret = copy_to_user((void __user *)args[1], indices, args[2]*sizeof(int))
|
||||
? -EFAULT : args[2];
|
||||
|
||||
kfree(indices);
|
||||
return ret;
|
||||
}
|
||||
|
||||
case BRCTL_ADD_BRIDGE:
|
||||
case BRCTL_DEL_BRIDGE:
|
||||
{
|
||||
char buf[IFNAMSIZ];
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
|
||||
return -EFAULT;
|
||||
|
||||
buf[IFNAMSIZ-1] = 0;
|
||||
|
||||
if (args[0] == BRCTL_ADD_BRIDGE)
|
||||
return br_add_bridge(buf);
|
||||
|
||||
return br_del_bridge(buf);
|
||||
}
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int br_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case SIOCGIFBR:
|
||||
case SIOCSIFBR:
|
||||
return old_deviceless(uarg);
|
||||
|
||||
case SIOCBRADDBR:
|
||||
case SIOCBRDELBR:
|
||||
{
|
||||
char buf[IFNAMSIZ];
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(buf, uarg, IFNAMSIZ))
|
||||
return -EFAULT;
|
||||
|
||||
buf[IFNAMSIZ-1] = 0;
|
||||
if (cmd == SIOCBRADDBR)
|
||||
return br_add_bridge(buf);
|
||||
|
||||
return br_del_bridge(buf);
|
||||
}
|
||||
}
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
|
||||
switch(cmd) {
|
||||
case SIOCDEVPRIVATE:
|
||||
return old_dev_ioctl(dev, rq, cmd);
|
||||
|
||||
case SIOCBRADDIF:
|
||||
case SIOCBRDELIF:
|
||||
return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
|
||||
|
||||
}
|
||||
|
||||
pr_debug("Bridge does not support ioctl 0x%x\n", cmd);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
978
net/bridge/br_netfilter.c
Normal file
978
net/bridge/br_netfilter.c
Normal file
@@ -0,0 +1,978 @@
|
||||
/*
|
||||
* Handle firewalling
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
* Bart De Schuymer (maintainer) <bdschuym@pandora.be>
|
||||
*
|
||||
* Changes:
|
||||
* Apr 29 2003: physdev module support (bdschuym)
|
||||
* Jun 19 2003: let arptables see bridged ARP traffic (bdschuym)
|
||||
* Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
|
||||
* (bdschuym)
|
||||
* Sep 01 2004: add IPv6 filtering (bdschuym)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Lennert dedicates this file to Kerstin Wurdinger.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/netfilter_arp.h>
|
||||
#include <linux/in_route.h>
|
||||
#include <linux/inetdevice.h>
|
||||
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include "br_private.h"
|
||||
#ifdef CONFIG_SYSCTL
|
||||
#include <linux/sysctl.h>
|
||||
#endif
|
||||
|
||||
#define skb_origaddr(skb) (((struct bridge_skb_cb *) \
|
||||
(skb->nf_bridge->data))->daddr.ipv4)
|
||||
#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
|
||||
#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static struct ctl_table_header *brnf_sysctl_header;
|
||||
static int brnf_call_iptables __read_mostly = 1;
|
||||
static int brnf_call_ip6tables __read_mostly = 1;
|
||||
static int brnf_call_arptables __read_mostly = 1;
|
||||
static int brnf_filter_vlan_tagged __read_mostly = 1;
|
||||
#else
|
||||
#define brnf_filter_vlan_tagged 1
|
||||
#endif
|
||||
|
||||
static inline __be16 vlan_proto(const struct sk_buff *skb)
|
||||
{
|
||||
return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
|
||||
}
|
||||
|
||||
#define IS_VLAN_IP(skb) \
|
||||
(skb->protocol == htons(ETH_P_8021Q) && \
|
||||
vlan_proto(skb) == htons(ETH_P_IP) && \
|
||||
brnf_filter_vlan_tagged)
|
||||
|
||||
#define IS_VLAN_IPV6(skb) \
|
||||
(skb->protocol == htons(ETH_P_8021Q) && \
|
||||
vlan_proto(skb) == htons(ETH_P_IPV6) &&\
|
||||
brnf_filter_vlan_tagged)
|
||||
|
||||
#define IS_VLAN_ARP(skb) \
|
||||
(skb->protocol == htons(ETH_P_8021Q) && \
|
||||
vlan_proto(skb) == htons(ETH_P_ARP) && \
|
||||
brnf_filter_vlan_tagged)
|
||||
|
||||
/* We need these fake structures to make netfilter happy --
|
||||
* lots of places assume that skb->dst != NULL, which isn't
|
||||
* all that unreasonable.
|
||||
*
|
||||
* Currently, we fill in the PMTU entry because netfilter
|
||||
* refragmentation needs it, and the rt_flags entry because
|
||||
* ipt_REJECT needs it. Future netfilter modules might
|
||||
* require us to fill additional fields. */
|
||||
static struct net_device __fake_net_device = {
|
||||
.hard_header_len = ETH_HLEN
|
||||
};
|
||||
|
||||
static struct rtable __fake_rtable = {
|
||||
.u = {
|
||||
.dst = {
|
||||
.__refcnt = ATOMIC_INIT(1),
|
||||
.dev = &__fake_net_device,
|
||||
.path = &__fake_rtable.u.dst,
|
||||
.metrics = {[RTAX_MTU - 1] = 1500},
|
||||
.flags = DST_NOXFRM,
|
||||
}
|
||||
},
|
||||
.rt_flags = 0,
|
||||
};
|
||||
|
||||
static inline struct net_device *bridge_parent(const struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *port = rcu_dereference(dev->br_port);
|
||||
|
||||
return port ? port->br->dev : NULL;
|
||||
}
|
||||
|
||||
static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
|
||||
{
|
||||
skb->nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC);
|
||||
if (likely(skb->nf_bridge))
|
||||
atomic_set(&(skb->nf_bridge->use), 1);
|
||||
|
||||
return skb->nf_bridge;
|
||||
}
|
||||
|
||||
static inline void nf_bridge_save_header(struct sk_buff *skb)
|
||||
{
|
||||
int header_size = ETH_HLEN;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q))
|
||||
header_size += VLAN_HLEN;
|
||||
|
||||
memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* When forwarding bridge frames, we save a copy of the original
|
||||
* header before processing.
|
||||
*/
|
||||
int nf_bridge_copy_header(struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
int header_size = ETH_HLEN;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q))
|
||||
header_size += VLAN_HLEN;
|
||||
|
||||
err = skb_cow(skb, header_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(skb->data - header_size, skb->nf_bridge->data, header_size);
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q))
|
||||
__skb_push(skb, VLAN_HLEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/PRE_ROUTING *********************************************/
|
||||
/* Undo the changes made for ip6tables PREROUTING and continue the
|
||||
* bridge PRE_ROUTING hook. */
|
||||
static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
|
||||
|
||||
if (nf_bridge->mask & BRNF_PKT_TYPE) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->mask ^= BRNF_PKT_TYPE;
|
||||
}
|
||||
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
|
||||
|
||||
skb->dst = (struct dst_entry *)&__fake_rtable;
|
||||
dst_hold(skb->dst);
|
||||
|
||||
skb->dev = nf_bridge->physindev;
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_push(skb, VLAN_HLEN);
|
||||
skb->nh.raw -= VLAN_HLEN;
|
||||
}
|
||||
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
|
||||
br_handle_frame_finish, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __br_dnat_complain(void)
|
||||
{
|
||||
static unsigned long last_complaint;
|
||||
|
||||
if (jiffies - last_complaint >= 5 * HZ) {
|
||||
printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
|
||||
"forwarding to be enabled\n");
|
||||
last_complaint = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
/* This requires some explaining. If DNAT has taken place,
|
||||
* we will need to fix up the destination Ethernet address,
|
||||
* and this is a tricky process.
|
||||
*
|
||||
* There are two cases to consider:
|
||||
* 1. The packet was DNAT'ed to a device in the same bridge
|
||||
* port group as it was received on. We can still bridge
|
||||
* the packet.
|
||||
* 2. The packet was DNAT'ed to a different device, either
|
||||
* a non-bridged device or another bridge port group.
|
||||
* The packet will need to be routed.
|
||||
*
|
||||
* The correct way of distinguishing between these two cases is to
|
||||
* call ip_route_input() and to look at skb->dst->dev, which is
|
||||
* changed to the destination device if ip_route_input() succeeds.
|
||||
*
|
||||
* Let us first consider the case that ip_route_input() succeeds:
|
||||
*
|
||||
* If skb->dst->dev equals the logical bridge device the packet
|
||||
* came in on, we can consider this bridging. We then call
|
||||
* skb->dst->output() which will make the packet enter br_nf_local_out()
|
||||
* not much later. In that function it is assured that the iptables
|
||||
* FORWARD chain is traversed for the packet.
|
||||
*
|
||||
* Otherwise, the packet is considered to be routed and we just
|
||||
* change the destination MAC address so that the packet will
|
||||
* later be passed up to the IP stack to be routed. For a redirected
|
||||
* packet, ip_route_input() will give back the localhost as output device,
|
||||
* which differs from the bridge device.
|
||||
*
|
||||
* Let us now consider the case that ip_route_input() fails:
|
||||
*
|
||||
* This can be because the destination address is martian, in which case
|
||||
* the packet will be dropped.
|
||||
* After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
|
||||
* will fail, while __ip_route_output_key() will return success. The source
|
||||
* address for __ip_route_output_key() is set to zero, so __ip_route_output_key
|
||||
* thinks we're handling a locally generated packet and won't care
|
||||
* if IP forwarding is allowed. We send a warning message to the users's
|
||||
* log telling her to put IP forwarding on.
|
||||
*
|
||||
* ip_route_input() will also fail if there is no route available.
|
||||
* In that case we just drop the packet.
|
||||
*
|
||||
* --Lennert, 20020411
|
||||
* --Bart, 20020416 (updated)
|
||||
* --Bart, 20021007 (updated)
|
||||
* --Bart, 20062711 (updated) */
|
||||
static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
|
||||
{
|
||||
if (skb->pkt_type == PACKET_OTHERHOST) {
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
skb->nf_bridge->mask |= BRNF_PKT_TYPE;
|
||||
}
|
||||
skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
|
||||
|
||||
skb->dev = bridge_parent(skb->dev);
|
||||
if (!skb->dev)
|
||||
kfree_skb(skb);
|
||||
else {
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_pull(skb, VLAN_HLEN);
|
||||
skb->nh.raw += VLAN_HLEN;
|
||||
}
|
||||
skb->dst->output(skb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_nf_pre_routing_finish(struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *dev = skb->dev;
|
||||
struct iphdr *iph = skb->nh.iph;
|
||||
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
|
||||
int err;
|
||||
|
||||
if (nf_bridge->mask & BRNF_PKT_TYPE) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->mask ^= BRNF_PKT_TYPE;
|
||||
}
|
||||
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
|
||||
if (dnat_took_place(skb)) {
|
||||
if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) {
|
||||
struct rtable *rt;
|
||||
struct flowi fl = {
|
||||
.nl_u = {
|
||||
.ip4_u = {
|
||||
.daddr = iph->daddr,
|
||||
.saddr = 0,
|
||||
.tos = RT_TOS(iph->tos) },
|
||||
},
|
||||
.proto = 0,
|
||||
};
|
||||
struct in_device *in_dev = in_dev_get(dev);
|
||||
|
||||
/* If err equals -EHOSTUNREACH the error is due to a
|
||||
* martian destination or due to the fact that
|
||||
* forwarding is disabled. For most martian packets,
|
||||
* ip_route_output_key() will fail. It won't fail for 2 types of
|
||||
* martian destinations: loopback destinations and destination
|
||||
* 0.0.0.0. In both cases the packet will be dropped because the
|
||||
* destination is the loopback device and not the bridge. */
|
||||
if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
|
||||
goto free_skb;
|
||||
|
||||
if (!ip_route_output_key(&rt, &fl)) {
|
||||
/* - Bridged-and-DNAT'ed traffic doesn't
|
||||
* require ip_forwarding. */
|
||||
if (((struct dst_entry *)rt)->dev == dev) {
|
||||
skb->dst = (struct dst_entry *)rt;
|
||||
goto bridged_dnat;
|
||||
}
|
||||
/* we are sure that forwarding is disabled, so printing
|
||||
* this message is no problem. Note that the packet could
|
||||
* still have a martian destination address, in which case
|
||||
* the packet could be dropped even if forwarding were enabled */
|
||||
__br_dnat_complain();
|
||||
dst_release((struct dst_entry *)rt);
|
||||
}
|
||||
free_skb:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
} else {
|
||||
if (skb->dst->dev == dev) {
|
||||
bridged_dnat:
|
||||
/* Tell br_nf_local_out this is a
|
||||
* bridged frame */
|
||||
nf_bridge->mask |= BRNF_BRIDGED_DNAT;
|
||||
skb->dev = nf_bridge->physindev;
|
||||
if (skb->protocol ==
|
||||
htons(ETH_P_8021Q)) {
|
||||
skb_push(skb, VLAN_HLEN);
|
||||
skb->nh.raw -= VLAN_HLEN;
|
||||
}
|
||||
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
|
||||
skb, skb->dev, NULL,
|
||||
br_nf_pre_routing_finish_bridge,
|
||||
1);
|
||||
return 0;
|
||||
}
|
||||
memcpy(eth_hdr(skb)->h_dest, dev->dev_addr, ETH_ALEN);
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
}
|
||||
} else {
|
||||
skb->dst = (struct dst_entry *)&__fake_rtable;
|
||||
dst_hold(skb->dst);
|
||||
}
|
||||
|
||||
skb->dev = nf_bridge->physindev;
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_push(skb, VLAN_HLEN);
|
||||
skb->nh.raw -= VLAN_HLEN;
|
||||
}
|
||||
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
|
||||
br_handle_frame_finish, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Some common code for IPv4/IPv6 */
|
||||
static struct net_device *setup_pre_routing(struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
|
||||
|
||||
if (skb->pkt_type == PACKET_OTHERHOST) {
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
nf_bridge->mask |= BRNF_PKT_TYPE;
|
||||
}
|
||||
|
||||
nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
|
||||
nf_bridge->physindev = skb->dev;
|
||||
skb->dev = bridge_parent(skb->dev);
|
||||
|
||||
return skb->dev;
|
||||
}
|
||||
|
||||
/* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
|
||||
static int check_hbh_len(struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *raw = (u8 *) (skb->nh.ipv6h + 1);
|
||||
u32 pkt_len;
|
||||
int off = raw - skb->nh.raw;
|
||||
int len = (raw[1] + 1) << 3;
|
||||
|
||||
if ((raw + len) - skb->data > skb_headlen(skb))
|
||||
goto bad;
|
||||
|
||||
off += 2;
|
||||
len -= 2;
|
||||
|
||||
while (len > 0) {
|
||||
int optlen = skb->nh.raw[off + 1] + 2;
|
||||
|
||||
switch (skb->nh.raw[off]) {
|
||||
case IPV6_TLV_PAD0:
|
||||
optlen = 1;
|
||||
break;
|
||||
|
||||
case IPV6_TLV_PADN:
|
||||
break;
|
||||
|
||||
case IPV6_TLV_JUMBO:
|
||||
if (skb->nh.raw[off + 1] != 4 || (off & 3) != 2)
|
||||
goto bad;
|
||||
pkt_len = ntohl(*(__be32 *) (skb->nh.raw + off + 2));
|
||||
if (pkt_len <= IPV6_MAXPLEN ||
|
||||
skb->nh.ipv6h->payload_len)
|
||||
goto bad;
|
||||
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
|
||||
goto bad;
|
||||
if (pskb_trim_rcsum(skb,
|
||||
pkt_len + sizeof(struct ipv6hdr)))
|
||||
goto bad;
|
||||
break;
|
||||
default:
|
||||
if (optlen > len)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
off += optlen;
|
||||
len -= optlen;
|
||||
}
|
||||
if (len == 0)
|
||||
return 0;
|
||||
bad:
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* Replicate the checks that IPv6 does on packet reception and pass the packet
|
||||
* to ip6tables, which doesn't support NAT, so things are fairly simple. */
|
||||
static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct ipv6hdr *hdr;
|
||||
u32 pkt_len;
|
||||
|
||||
if (skb->len < sizeof(struct ipv6hdr))
|
||||
goto inhdr_error;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
goto inhdr_error;
|
||||
|
||||
hdr = skb->nh.ipv6h;
|
||||
|
||||
if (hdr->version != 6)
|
||||
goto inhdr_error;
|
||||
|
||||
pkt_len = ntohs(hdr->payload_len);
|
||||
|
||||
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
|
||||
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
|
||||
goto inhdr_error;
|
||||
if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
|
||||
goto inhdr_error;
|
||||
}
|
||||
if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb))
|
||||
goto inhdr_error;
|
||||
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
if (!nf_bridge_alloc(skb))
|
||||
return NF_DROP;
|
||||
if (!setup_pre_routing(skb))
|
||||
return NF_DROP;
|
||||
|
||||
NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
|
||||
br_nf_pre_routing_finish_ipv6);
|
||||
|
||||
return NF_STOLEN;
|
||||
|
||||
inhdr_error:
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
/* Direct IPv6 traffic to br_nf_pre_routing_ipv6.
|
||||
* Replicate the checks that IPv4 does on packet reception.
|
||||
* Set skb->dev to the bridge device (i.e. parent of the
|
||||
* receiving device) to make netfilter happy, the REDIRECT
|
||||
* target in particular. Save the original destination IP
|
||||
* address to be able to detect DNAT afterwards. */
|
||||
static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct iphdr *iph;
|
||||
__u32 len;
|
||||
struct sk_buff *skb = *pskb;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) {
|
||||
#ifdef CONFIG_SYSCTL
|
||||
if (!brnf_call_ip6tables)
|
||||
return NF_ACCEPT;
|
||||
#endif
|
||||
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
|
||||
goto out;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_pull_rcsum(skb, VLAN_HLEN);
|
||||
skb->nh.raw += VLAN_HLEN;
|
||||
}
|
||||
return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
|
||||
}
|
||||
#ifdef CONFIG_SYSCTL
|
||||
if (!brnf_call_iptables)
|
||||
return NF_ACCEPT;
|
||||
#endif
|
||||
|
||||
if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb))
|
||||
return NF_ACCEPT;
|
||||
|
||||
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
|
||||
goto out;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_pull_rcsum(skb, VLAN_HLEN);
|
||||
skb->nh.raw += VLAN_HLEN;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
||||
goto inhdr_error;
|
||||
|
||||
iph = skb->nh.iph;
|
||||
if (iph->ihl < 5 || iph->version != 4)
|
||||
goto inhdr_error;
|
||||
|
||||
if (!pskb_may_pull(skb, 4 * iph->ihl))
|
||||
goto inhdr_error;
|
||||
|
||||
iph = skb->nh.iph;
|
||||
if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0)
|
||||
goto inhdr_error;
|
||||
|
||||
len = ntohs(iph->tot_len);
|
||||
if (skb->len < len || len < 4 * iph->ihl)
|
||||
goto inhdr_error;
|
||||
|
||||
pskb_trim_rcsum(skb, len);
|
||||
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
if (!nf_bridge_alloc(skb))
|
||||
return NF_DROP;
|
||||
if (!setup_pre_routing(skb))
|
||||
return NF_DROP;
|
||||
store_orig_dstaddr(skb);
|
||||
|
||||
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
|
||||
br_nf_pre_routing_finish);
|
||||
|
||||
return NF_STOLEN;
|
||||
|
||||
inhdr_error:
|
||||
// IP_INC_STATS_BH(IpInHdrErrors);
|
||||
out:
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
|
||||
/* PF_BRIDGE/LOCAL_IN ************************************************/
|
||||
/* The packet is locally destined, which requires a real
|
||||
* dst_entry, so detach the fake one. On the way up, the
|
||||
* packet would pass through PRE_ROUTING again (which already
|
||||
* took place when the packet entered the bridge), but we
|
||||
* register an IPv4 PRE_ROUTING 'sabotage' hook that will
|
||||
* prevent this from happening. */
|
||||
static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
|
||||
if (skb->dst == (struct dst_entry *)&__fake_rtable) {
|
||||
dst_release(skb->dst);
|
||||
skb->dst = NULL;
|
||||
}
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/FORWARD *************************************************/
|
||||
static int br_nf_forward_finish(struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
|
||||
struct net_device *in;
|
||||
|
||||
if (skb->protocol != htons(ETH_P_ARP) && !IS_VLAN_ARP(skb)) {
|
||||
in = nf_bridge->physindev;
|
||||
if (nf_bridge->mask & BRNF_PKT_TYPE) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->mask ^= BRNF_PKT_TYPE;
|
||||
}
|
||||
} else {
|
||||
in = *((struct net_device **)(skb->cb));
|
||||
}
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_push(skb, VLAN_HLEN);
|
||||
skb->nh.raw -= VLAN_HLEN;
|
||||
}
|
||||
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
|
||||
skb->dev, br_forward_finish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the 'purely bridged' case. For IP, we pass the packet to
|
||||
* netfilter with indev and outdev set to the bridge device,
|
||||
* but we are still able to filter on the 'real' indev/outdev
|
||||
* because of the physdev module. For ARP, indev and outdev are the
|
||||
* bridge ports. */
|
||||
static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct nf_bridge_info *nf_bridge;
|
||||
struct net_device *parent;
|
||||
int pf;
|
||||
|
||||
if (!skb->nf_bridge)
|
||||
return NF_ACCEPT;
|
||||
|
||||
parent = bridge_parent(out);
|
||||
if (!parent)
|
||||
return NF_DROP;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
|
||||
pf = PF_INET;
|
||||
else
|
||||
pf = PF_INET6;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_pull(*pskb, VLAN_HLEN);
|
||||
(*pskb)->nh.raw += VLAN_HLEN;
|
||||
}
|
||||
|
||||
nf_bridge = skb->nf_bridge;
|
||||
if (skb->pkt_type == PACKET_OTHERHOST) {
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
nf_bridge->mask |= BRNF_PKT_TYPE;
|
||||
}
|
||||
|
||||
/* The physdev module checks on this */
|
||||
nf_bridge->mask |= BRNF_BRIDGED;
|
||||
nf_bridge->physoutdev = skb->dev;
|
||||
|
||||
NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), parent,
|
||||
br_nf_forward_finish);
|
||||
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct net_device **d = (struct net_device **)(skb->cb);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
if (!brnf_call_arptables)
|
||||
return NF_ACCEPT;
|
||||
#endif
|
||||
|
||||
if (skb->protocol != htons(ETH_P_ARP)) {
|
||||
if (!IS_VLAN_ARP(skb))
|
||||
return NF_ACCEPT;
|
||||
skb_pull(*pskb, VLAN_HLEN);
|
||||
(*pskb)->nh.raw += VLAN_HLEN;
|
||||
}
|
||||
|
||||
if (skb->nh.arph->ar_pln != 4) {
|
||||
if (IS_VLAN_ARP(skb)) {
|
||||
skb_push(*pskb, VLAN_HLEN);
|
||||
(*pskb)->nh.raw -= VLAN_HLEN;
|
||||
}
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
*d = (struct net_device *)in;
|
||||
NF_HOOK(NF_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in,
|
||||
(struct net_device *)out, br_nf_forward_finish);
|
||||
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/LOCAL_OUT ***********************************************
|
||||
*
|
||||
* This function sees both locally originated IP packets and forwarded
|
||||
* IP packets (in both cases the destination device is a bridge
|
||||
* device). It also sees bridged-and-DNAT'ed packets.
|
||||
*
|
||||
* If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
|
||||
* and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
|
||||
* will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
|
||||
* NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
|
||||
* will be executed.
|
||||
*/
|
||||
static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct net_device *realindev;
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct nf_bridge_info *nf_bridge;
|
||||
|
||||
if (!skb->nf_bridge)
|
||||
return NF_ACCEPT;
|
||||
|
||||
nf_bridge = skb->nf_bridge;
|
||||
if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* Bridged, take PF_BRIDGE/FORWARD.
|
||||
* (see big note in front of br_nf_pre_routing_finish) */
|
||||
nf_bridge->physoutdev = skb->dev;
|
||||
realindev = nf_bridge->physindev;
|
||||
|
||||
if (nf_bridge->mask & BRNF_PKT_TYPE) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->mask ^= BRNF_PKT_TYPE;
|
||||
}
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_push(skb, VLAN_HLEN);
|
||||
skb->nh.raw -= VLAN_HLEN;
|
||||
}
|
||||
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev,
|
||||
br_forward_finish);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
static int br_nf_dev_queue_xmit(struct sk_buff *skb)
|
||||
{
|
||||
if (skb->protocol == htons(ETH_P_IP) &&
|
||||
skb->len > skb->dev->mtu &&
|
||||
!skb_is_gso(skb))
|
||||
return ip_fragment(skb, br_dev_queue_push_xmit);
|
||||
else
|
||||
return br_dev_queue_push_xmit(skb);
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/POST_ROUTING ********************************************/
|
||||
static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
|
||||
struct net_device *realoutdev = bridge_parent(skb->dev);
|
||||
int pf;
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
/* Be very paranoid. This probably won't happen anymore, but let's
|
||||
* keep the check just to be sure... */
|
||||
if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
|
||||
printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
|
||||
"bad mac.raw pointer.\n");
|
||||
goto print_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!nf_bridge)
|
||||
return NF_ACCEPT;
|
||||
|
||||
if (!realoutdev)
|
||||
return NF_DROP;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
|
||||
pf = PF_INET;
|
||||
else
|
||||
pf = PF_INET6;
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
if (skb->dst == NULL) {
|
||||
printk(KERN_INFO "br_netfilter post_routing: skb->dst == NULL\n");
|
||||
goto print_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
|
||||
* about the value of skb->pkt_type. */
|
||||
if (skb->pkt_type == PACKET_OTHERHOST) {
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
nf_bridge->mask |= BRNF_PKT_TYPE;
|
||||
}
|
||||
|
||||
if (skb->protocol == htons(ETH_P_8021Q)) {
|
||||
skb_pull(skb, VLAN_HLEN);
|
||||
skb->nh.raw += VLAN_HLEN;
|
||||
}
|
||||
|
||||
nf_bridge_save_header(skb);
|
||||
|
||||
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
|
||||
if (nf_bridge->netoutdev)
|
||||
realoutdev = nf_bridge->netoutdev;
|
||||
#endif
|
||||
NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev,
|
||||
br_nf_dev_queue_xmit);
|
||||
|
||||
return NF_STOLEN;
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
print_error:
|
||||
if (skb->dev != NULL) {
|
||||
printk("[%s]", skb->dev->name);
|
||||
if (realoutdev)
|
||||
printk("[%s]", realoutdev->name);
|
||||
}
|
||||
printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw,
|
||||
skb->data);
|
||||
dump_stack();
|
||||
return NF_ACCEPT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* IP/SABOTAGE *****************************************************/
|
||||
/* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
|
||||
* for the second time. */
|
||||
static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
if ((*pskb)->nf_bridge &&
|
||||
!((*pskb)->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) {
|
||||
return NF_STOP;
|
||||
}
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
|
||||
* PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
|
||||
* For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
|
||||
* ip_refrag() can return NF_STOLEN. */
|
||||
static struct nf_hook_ops br_nf_ops[] = {
|
||||
{ .hook = br_nf_pre_routing,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_PRE_ROUTING,
|
||||
.priority = NF_BR_PRI_BRNF, },
|
||||
{ .hook = br_nf_local_in,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_LOCAL_IN,
|
||||
.priority = NF_BR_PRI_BRNF, },
|
||||
{ .hook = br_nf_forward_ip,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_FORWARD,
|
||||
.priority = NF_BR_PRI_BRNF - 1, },
|
||||
{ .hook = br_nf_forward_arp,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_FORWARD,
|
||||
.priority = NF_BR_PRI_BRNF, },
|
||||
{ .hook = br_nf_local_out,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_LOCAL_OUT,
|
||||
.priority = NF_BR_PRI_FIRST, },
|
||||
{ .hook = br_nf_post_routing,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_POST_ROUTING,
|
||||
.priority = NF_BR_PRI_LAST, },
|
||||
{ .hook = ip_sabotage_in,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_INET,
|
||||
.hooknum = NF_IP_PRE_ROUTING,
|
||||
.priority = NF_IP_PRI_FIRST, },
|
||||
{ .hook = ip_sabotage_in,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_INET6,
|
||||
.hooknum = NF_IP6_PRE_ROUTING,
|
||||
.priority = NF_IP6_PRI_FIRST, },
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static
|
||||
int brnf_sysctl_call_tables(ctl_table * ctl, int write, struct file *filp,
|
||||
void __user * buffer, size_t * lenp, loff_t * ppos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
|
||||
|
||||
if (write && *(int *)(ctl->data))
|
||||
*(int *)(ctl->data) = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ctl_table brnf_table[] = {
|
||||
{
|
||||
.ctl_name = NET_BRIDGE_NF_CALL_ARPTABLES,
|
||||
.procname = "bridge-nf-call-arptables",
|
||||
.data = &brnf_call_arptables,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &brnf_sysctl_call_tables,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_BRIDGE_NF_CALL_IPTABLES,
|
||||
.procname = "bridge-nf-call-iptables",
|
||||
.data = &brnf_call_iptables,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &brnf_sysctl_call_tables,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_BRIDGE_NF_CALL_IP6TABLES,
|
||||
.procname = "bridge-nf-call-ip6tables",
|
||||
.data = &brnf_call_ip6tables,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &brnf_sysctl_call_tables,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_BRIDGE_NF_FILTER_VLAN_TAGGED,
|
||||
.procname = "bridge-nf-filter-vlan-tagged",
|
||||
.data = &brnf_filter_vlan_tagged,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &brnf_sysctl_call_tables,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table brnf_bridge_table[] = {
|
||||
{
|
||||
.ctl_name = NET_BRIDGE,
|
||||
.procname = "bridge",
|
||||
.mode = 0555,
|
||||
.child = brnf_table,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table brnf_net_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = brnf_bridge_table,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
#endif
|
||||
|
||||
int __init br_netfilter_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_SYSCTL
|
||||
brnf_sysctl_header = register_sysctl_table(brnf_net_table);
|
||||
if (brnf_sysctl_header == NULL) {
|
||||
printk(KERN_WARNING
|
||||
"br_netfilter: can't register to sysctl.\n");
|
||||
nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
|
||||
return -ENOMEM;
|
||||
}
|
||||
#endif
|
||||
printk(KERN_NOTICE "Bridge firewalling registered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void br_netfilter_fini(void)
|
||||
{
|
||||
nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
|
||||
#ifdef CONFIG_SYSCTL
|
||||
unregister_sysctl_table(brnf_sysctl_header);
|
||||
#endif
|
||||
}
|
||||
196
net/bridge/br_netlink.c
Normal file
196
net/bridge/br_netlink.c
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Bridge netlink control interface
|
||||
*
|
||||
* Authors:
|
||||
* Stephen Hemminger <shemminger@osdl.org>
|
||||
*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/netlink.h>
|
||||
#include "br_private.h"
|
||||
|
||||
static inline size_t br_nlmsg_size(void)
|
||||
{
|
||||
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
|
||||
+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
|
||||
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
|
||||
+ nla_total_size(4) /* IFLA_MASTER */
|
||||
+ nla_total_size(4) /* IFLA_MTU */
|
||||
+ nla_total_size(4) /* IFLA_LINK */
|
||||
+ nla_total_size(1) /* IFLA_OPERSTATE */
|
||||
+ nla_total_size(1); /* IFLA_PROTINFO */
|
||||
}
|
||||
|
||||
/*
|
||||
* Create one netlink message for one interface
|
||||
* Contains port and master info as well as carrier and bridge state.
|
||||
*/
|
||||
static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *port,
|
||||
u32 pid, u32 seq, int event, unsigned int flags)
|
||||
{
|
||||
const struct net_bridge *br = port->br;
|
||||
const struct net_device *dev = port->dev;
|
||||
struct ifinfomsg *hdr;
|
||||
struct nlmsghdr *nlh;
|
||||
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
|
||||
|
||||
pr_debug("br_fill_info event %d port %s master %s\n",
|
||||
event, dev->name, br->dev->name);
|
||||
|
||||
nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags);
|
||||
if (nlh == NULL)
|
||||
return -EMSGSIZE;
|
||||
|
||||
hdr = nlmsg_data(nlh);
|
||||
hdr->ifi_family = AF_BRIDGE;
|
||||
hdr->__ifi_pad = 0;
|
||||
hdr->ifi_type = dev->type;
|
||||
hdr->ifi_index = dev->ifindex;
|
||||
hdr->ifi_flags = dev_get_flags(dev);
|
||||
hdr->ifi_change = 0;
|
||||
|
||||
NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
|
||||
NLA_PUT_U32(skb, IFLA_MASTER, br->dev->ifindex);
|
||||
NLA_PUT_U32(skb, IFLA_MTU, dev->mtu);
|
||||
NLA_PUT_U8(skb, IFLA_OPERSTATE, operstate);
|
||||
|
||||
if (dev->addr_len)
|
||||
NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
|
||||
|
||||
if (dev->ifindex != dev->iflink)
|
||||
NLA_PUT_U32(skb, IFLA_LINK, dev->iflink);
|
||||
|
||||
if (event == RTM_NEWLINK)
|
||||
NLA_PUT_U8(skb, IFLA_PROTINFO, port->state);
|
||||
|
||||
return nlmsg_end(skb, nlh);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify listeners of a change in port information
|
||||
*/
|
||||
void br_ifinfo_notify(int event, struct net_bridge_port *port)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOBUFS;
|
||||
|
||||
pr_debug("bridge notify event=%d\n", event);
|
||||
skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
goto errout;
|
||||
|
||||
err = br_fill_ifinfo(skb, port, 0, 0, event, 0);
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in br_nlmsg_size() */
|
||||
WARN_ON(err == -EMSGSIZE);
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
err = rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
|
||||
errout:
|
||||
if (err < 0)
|
||||
rtnl_set_sk_err(RTNLGRP_LINK, err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump information about all ports, in response to GETLINK
|
||||
*/
|
||||
static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int idx;
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
for (dev = dev_base, idx = 0; dev; dev = dev->next) {
|
||||
/* not a bridge port */
|
||||
if (dev->br_port == NULL || idx < cb->args[0])
|
||||
goto skip;
|
||||
|
||||
if (br_fill_ifinfo(skb, dev->br_port, NETLINK_CB(cb->skb).pid,
|
||||
cb->nlh->nlmsg_seq, RTM_NEWLINK,
|
||||
NLM_F_MULTI) < 0)
|
||||
break;
|
||||
skip:
|
||||
++idx;
|
||||
}
|
||||
read_unlock(&dev_base_lock);
|
||||
|
||||
cb->args[0] = idx;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change state of port (ie from forwarding to blocking etc)
|
||||
* Used by spanning tree in user space.
|
||||
*/
|
||||
static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
|
||||
{
|
||||
struct ifinfomsg *ifm;
|
||||
struct nlattr *protinfo;
|
||||
struct net_device *dev;
|
||||
struct net_bridge_port *p;
|
||||
u8 new_state;
|
||||
|
||||
if (nlmsg_len(nlh) < sizeof(*ifm))
|
||||
return -EINVAL;
|
||||
|
||||
ifm = nlmsg_data(nlh);
|
||||
if (ifm->ifi_family != AF_BRIDGE)
|
||||
return -EPFNOSUPPORT;
|
||||
|
||||
protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
|
||||
if (!protinfo || nla_len(protinfo) < sizeof(u8))
|
||||
return -EINVAL;
|
||||
|
||||
new_state = nla_get_u8(protinfo);
|
||||
if (new_state > BR_STATE_BLOCKING)
|
||||
return -EINVAL;
|
||||
|
||||
dev = __dev_get_by_index(ifm->ifi_index);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
p = dev->br_port;
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
/* if kernel STP is running, don't allow changes */
|
||||
if (p->br->stp_enabled)
|
||||
return -EBUSY;
|
||||
|
||||
if (!netif_running(dev) ||
|
||||
(!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
|
||||
return -ENETDOWN;
|
||||
|
||||
p->state = new_state;
|
||||
br_log_state(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct rtnetlink_link bridge_rtnetlink_table[RTM_NR_MSGTYPES] = {
|
||||
[RTM_GETLINK - RTM_BASE] = { .dumpit = br_dump_ifinfo, },
|
||||
[RTM_SETLINK - RTM_BASE] = { .doit = br_rtm_setlink, },
|
||||
};
|
||||
|
||||
void __init br_netlink_init(void)
|
||||
{
|
||||
rtnetlink_links[PF_BRIDGE] = bridge_rtnetlink_table;
|
||||
}
|
||||
|
||||
void __exit br_netlink_fini(void)
|
||||
{
|
||||
rtnetlink_links[PF_BRIDGE] = NULL;
|
||||
}
|
||||
|
||||
89
net/bridge/br_notify.c
Normal file
89
net/bridge/br_notify.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Device event handling
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_notify.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr);
|
||||
|
||||
struct notifier_block br_device_notifier = {
|
||||
.notifier_call = br_device_event
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle changes in state of network devices enslaved to a bridge.
|
||||
*
|
||||
* Note: don't care about up/down if bridge itself is down, because
|
||||
* port state is checked when bridge is brought up.
|
||||
*/
|
||||
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = ptr;
|
||||
struct net_bridge_port *p = dev->br_port;
|
||||
struct net_bridge *br;
|
||||
|
||||
/* not a port of a bridge */
|
||||
if (p == NULL)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
br = p->br;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_CHANGEMTU:
|
||||
dev_set_mtu(br->dev, br_min_mtu(br));
|
||||
break;
|
||||
|
||||
case NETDEV_CHANGEADDR:
|
||||
spin_lock_bh(&br->lock);
|
||||
br_fdb_changeaddr(p, dev->dev_addr);
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
br_stp_recalculate_bridge_id(br);
|
||||
spin_unlock_bh(&br->lock);
|
||||
break;
|
||||
|
||||
case NETDEV_CHANGE:
|
||||
br_port_carrier_check(p);
|
||||
break;
|
||||
|
||||
case NETDEV_FEAT_CHANGE:
|
||||
spin_lock_bh(&br->lock);
|
||||
if (netif_running(br->dev))
|
||||
br_features_recompute(br);
|
||||
spin_unlock_bh(&br->lock);
|
||||
break;
|
||||
|
||||
case NETDEV_DOWN:
|
||||
spin_lock_bh(&br->lock);
|
||||
if (br->dev->flags & IFF_UP)
|
||||
br_stp_disable_port(p);
|
||||
spin_unlock_bh(&br->lock);
|
||||
break;
|
||||
|
||||
case NETDEV_UP:
|
||||
spin_lock_bh(&br->lock);
|
||||
if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP))
|
||||
br_stp_enable_port(p);
|
||||
spin_unlock_bh(&br->lock);
|
||||
break;
|
||||
|
||||
case NETDEV_UNREGISTER:
|
||||
br_del_if(br, dev);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
258
net/bridge/br_private.h
Normal file
258
net/bridge/br_private.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_private.h,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/netdevice.h>
|
||||
#include <linux/if_bridge.h>
|
||||
|
||||
#define BR_HASH_BITS 8
|
||||
#define BR_HASH_SIZE (1 << BR_HASH_BITS)
|
||||
|
||||
#define BR_HOLD_TIME (1*HZ)
|
||||
|
||||
#define BR_PORT_BITS 10
|
||||
#define BR_MAX_PORTS (1<<BR_PORT_BITS)
|
||||
|
||||
#define BR_VERSION "2.2"
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
struct net_bridge_fdb_entry
|
||||
{
|
||||
struct hlist_node hlist;
|
||||
struct net_bridge_port *dst;
|
||||
|
||||
struct rcu_head rcu;
|
||||
atomic_t use_count;
|
||||
unsigned long ageing_timer;
|
||||
mac_addr addr;
|
||||
unsigned char is_local;
|
||||
unsigned char is_static;
|
||||
};
|
||||
|
||||
struct net_bridge_port
|
||||
{
|
||||
struct net_bridge *br;
|
||||
struct net_device *dev;
|
||||
struct list_head list;
|
||||
|
||||
/* STP */
|
||||
u8 priority;
|
||||
u8 state;
|
||||
u16 port_no;
|
||||
unsigned char topology_change_ack;
|
||||
unsigned char config_pending;
|
||||
port_id port_id;
|
||||
port_id designated_port;
|
||||
bridge_id designated_root;
|
||||
bridge_id designated_bridge;
|
||||
u32 path_cost;
|
||||
u32 designated_cost;
|
||||
|
||||
struct timer_list forward_delay_timer;
|
||||
struct timer_list hold_timer;
|
||||
struct timer_list message_age_timer;
|
||||
struct kobject kobj;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
struct net_bridge
|
||||
{
|
||||
spinlock_t lock;
|
||||
struct list_head port_list;
|
||||
struct net_device *dev;
|
||||
struct net_device_stats statistics;
|
||||
spinlock_t hash_lock;
|
||||
struct hlist_head hash[BR_HASH_SIZE];
|
||||
struct list_head age_list;
|
||||
unsigned long feature_mask;
|
||||
|
||||
/* STP */
|
||||
bridge_id designated_root;
|
||||
bridge_id bridge_id;
|
||||
u32 root_path_cost;
|
||||
unsigned long max_age;
|
||||
unsigned long hello_time;
|
||||
unsigned long forward_delay;
|
||||
unsigned long bridge_max_age;
|
||||
unsigned long ageing_time;
|
||||
unsigned long bridge_hello_time;
|
||||
unsigned long bridge_forward_delay;
|
||||
|
||||
u8 group_addr[ETH_ALEN];
|
||||
u16 root_port;
|
||||
unsigned char stp_enabled;
|
||||
unsigned char topology_change;
|
||||
unsigned char topology_change_detected;
|
||||
|
||||
struct timer_list hello_timer;
|
||||
struct timer_list tcn_timer;
|
||||
struct timer_list topology_change_timer;
|
||||
struct timer_list gc_timer;
|
||||
struct kobject ifobj;
|
||||
};
|
||||
|
||||
extern struct notifier_block br_device_notifier;
|
||||
extern const u8 br_group_address[ETH_ALEN];
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline int br_is_root_bridge(const struct net_bridge *br)
|
||||
{
|
||||
return !memcmp(&br->bridge_id, &br->designated_root, 8);
|
||||
}
|
||||
|
||||
|
||||
/* br_device.c */
|
||||
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_init(void);
|
||||
extern void br_fdb_fini(void);
|
||||
extern void br_fdb_changeaddr(struct net_bridge_port *p,
|
||||
const unsigned char *newaddr);
|
||||
extern void br_fdb_cleanup(unsigned long arg);
|
||||
extern void br_fdb_delete_by_port(struct net_bridge *br,
|
||||
const struct net_bridge_port *p, int do_all);
|
||||
extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
|
||||
const unsigned char *addr);
|
||||
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_fillbuf(struct net_bridge *br, void *buf,
|
||||
unsigned long count, unsigned long off);
|
||||
extern int br_fdb_insert(struct net_bridge *br,
|
||||
struct net_bridge_port *source,
|
||||
const unsigned char *addr);
|
||||
extern void br_fdb_update(struct net_bridge *br,
|
||||
struct net_bridge_port *source,
|
||||
const unsigned char *addr);
|
||||
|
||||
/* br_forward.c */
|
||||
extern void br_deliver(const struct net_bridge_port *to,
|
||||
struct sk_buff *skb);
|
||||
extern int br_dev_queue_push_xmit(struct sk_buff *skb);
|
||||
extern void br_forward(const 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 void br_port_carrier_check(struct net_bridge_port *p);
|
||||
extern int br_add_bridge(const char *name);
|
||||
extern int br_del_bridge(const char *name);
|
||||
extern void br_cleanup_bridges(void);
|
||||
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_min_mtu(const struct net_bridge *br);
|
||||
extern void br_features_recompute(struct net_bridge *br);
|
||||
|
||||
/* br_input.c */
|
||||
extern int br_handle_frame_finish(struct sk_buff *skb);
|
||||
extern int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb);
|
||||
|
||||
/* br_ioctl.c */
|
||||
extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
|
||||
extern int br_ioctl_deviceless_stub(unsigned int cmd, void __user *arg);
|
||||
|
||||
/* br_netfilter.c */
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
extern int br_netfilter_init(void);
|
||||
extern void br_netfilter_fini(void);
|
||||
#else
|
||||
#define br_netfilter_init() (0)
|
||||
#define br_netfilter_fini() do { } while(0)
|
||||
#endif
|
||||
|
||||
/* br_stp.c */
|
||||
extern void br_log_state(const struct net_bridge_port *p);
|
||||
extern struct net_bridge_port *br_get_port(struct net_bridge *br,
|
||||
u16 port_no);
|
||||
extern void br_init_port(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_change_bridge_id(struct net_bridge *br, const unsigned char *a);
|
||||
extern void br_stp_set_bridge_priority(struct net_bridge *br,
|
||||
u16 newprio);
|
||||
extern void br_stp_set_port_priority(struct net_bridge_port *p,
|
||||
u8 newprio);
|
||||
extern void br_stp_set_path_cost(struct net_bridge_port *p,
|
||||
u32 path_cost);
|
||||
extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
|
||||
|
||||
/* br_stp_bpdu.c */
|
||||
extern int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev);
|
||||
|
||||
/* br_stp_timer.c */
|
||||
extern void br_stp_timer_init(struct net_bridge *br);
|
||||
extern void br_stp_port_timer_init(struct net_bridge_port *p);
|
||||
extern unsigned long br_timer_value(const struct timer_list *timer);
|
||||
|
||||
/* br.c */
|
||||
extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
|
||||
unsigned char *addr);
|
||||
extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
|
||||
|
||||
|
||||
/* br_netlink.c */
|
||||
extern void br_netlink_init(void);
|
||||
extern void br_netlink_fini(void);
|
||||
extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
/* br_sysfs_if.c */
|
||||
extern struct sysfs_ops brport_sysfs_ops;
|
||||
extern int br_sysfs_addif(struct net_bridge_port *p);
|
||||
|
||||
/* br_sysfs_br.c */
|
||||
extern int br_sysfs_addbr(struct net_device *dev);
|
||||
extern void br_sysfs_delbr(struct net_device *dev);
|
||||
|
||||
#else
|
||||
|
||||
#define br_sysfs_addif(p) (0)
|
||||
#define br_sysfs_addbr(dev) (0)
|
||||
#define br_sysfs_delbr(dev) do { } while(0)
|
||||
#endif /* CONFIG_SYSFS */
|
||||
|
||||
#endif
|
||||
58
net/bridge/br_private_stp.h
Normal file
58
net/bridge/br_private_stp.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_private_stp.h,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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_STP_H
|
||||
#define _BR_PRIVATE_STP_H
|
||||
|
||||
#define BPDU_TYPE_CONFIG 0
|
||||
#define BPDU_TYPE_TCN 0x80
|
||||
|
||||
struct br_config_bpdu
|
||||
{
|
||||
unsigned topology_change:1;
|
||||
unsigned topology_change_ack:1;
|
||||
bridge_id root;
|
||||
int root_path_cost;
|
||||
bridge_id bridge_id;
|
||||
port_id port_id;
|
||||
int message_age;
|
||||
int max_age;
|
||||
int hello_time;
|
||||
int forward_delay;
|
||||
};
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline int br_is_designated_port(const struct net_bridge_port *p)
|
||||
{
|
||||
return !memcmp(&p->designated_bridge, &p->br->bridge_id, 8) &&
|
||||
(p->designated_port == p->port_id);
|
||||
}
|
||||
|
||||
|
||||
/* br_stp.c */
|
||||
extern void br_become_root_bridge(struct net_bridge *br);
|
||||
extern void br_config_bpdu_generation(struct net_bridge *);
|
||||
extern void br_configuration_update(struct net_bridge *);
|
||||
extern void br_port_state_selection(struct net_bridge *);
|
||||
extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu);
|
||||
extern void br_received_tcn_bpdu(struct net_bridge_port *p);
|
||||
extern void br_transmit_config(struct net_bridge_port *p);
|
||||
extern void br_transmit_tcn(struct net_bridge *br);
|
||||
extern void br_topology_change_detection(struct net_bridge *br);
|
||||
|
||||
/* br_stp_bpdu.c */
|
||||
extern void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *);
|
||||
extern void br_send_tcn_bpdu(struct net_bridge_port *);
|
||||
|
||||
#endif
|
||||
459
net/bridge/br_stp.c
Normal file
459
net/bridge/br_stp.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Spanning tree protocol; generic parts
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_stp.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/smp_lock.h>
|
||||
|
||||
#include "br_private.h"
|
||||
#include "br_private_stp.h"
|
||||
|
||||
/* since time values in bpdu are in jiffies and then scaled (1/256)
|
||||
* before sending, make sure that is at least one.
|
||||
*/
|
||||
#define MESSAGE_AGE_INCR ((HZ < 256) ? 1 : (HZ/256))
|
||||
|
||||
static const char *br_port_state_names[] = {
|
||||
[BR_STATE_DISABLED] = "disabled",
|
||||
[BR_STATE_LISTENING] = "listening",
|
||||
[BR_STATE_LEARNING] = "learning",
|
||||
[BR_STATE_FORWARDING] = "forwarding",
|
||||
[BR_STATE_BLOCKING] = "blocking",
|
||||
};
|
||||
|
||||
void br_log_state(const struct net_bridge_port *p)
|
||||
{
|
||||
pr_info("%s: port %d(%s) entering %s state\n",
|
||||
p->br->dev->name, p->port_no, p->dev->name,
|
||||
br_port_state_names[p->state]);
|
||||
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry_rcu(p, &br->port_list, list) {
|
||||
if (p->port_no == port_no)
|
||||
return p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static int br_should_become_root_port(const struct net_bridge_port *p,
|
||||
u16 root_port)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
struct net_bridge_port *rp;
|
||||
int t;
|
||||
|
||||
br = p->br;
|
||||
if (p->state == BR_STATE_DISABLED ||
|
||||
br_is_designated_port(p))
|
||||
return 0;
|
||||
|
||||
if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0)
|
||||
return 0;
|
||||
|
||||
if (!root_port)
|
||||
return 1;
|
||||
|
||||
rp = br_get_port(br, root_port);
|
||||
|
||||
t = memcmp(&p->designated_root, &rp->designated_root, 8);
|
||||
if (t < 0)
|
||||
return 1;
|
||||
else if (t > 0)
|
||||
return 0;
|
||||
|
||||
if (p->designated_cost + p->path_cost <
|
||||
rp->designated_cost + rp->path_cost)
|
||||
return 1;
|
||||
else if (p->designated_cost + p->path_cost >
|
||||
rp->designated_cost + rp->path_cost)
|
||||
return 0;
|
||||
|
||||
t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8);
|
||||
if (t < 0)
|
||||
return 1;
|
||||
else if (t > 0)
|
||||
return 0;
|
||||
|
||||
if (p->designated_port < rp->designated_port)
|
||||
return 1;
|
||||
else if (p->designated_port > rp->designated_port)
|
||||
return 0;
|
||||
|
||||
if (p->port_id < rp->port_id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static void br_root_selection(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
u16 root_port = 0;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (br_should_become_root_port(p, root_port))
|
||||
root_port = p->port_no;
|
||||
|
||||
}
|
||||
|
||||
br->root_port = root_port;
|
||||
|
||||
if (!root_port) {
|
||||
br->designated_root = br->bridge_id;
|
||||
br->root_path_cost = 0;
|
||||
} else {
|
||||
p = br_get_port(br, root_port);
|
||||
br->designated_root = p->designated_root;
|
||||
br->root_path_cost = p->designated_cost + p->path_cost;
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_become_root_bridge(struct net_bridge *br)
|
||||
{
|
||||
br->max_age = br->bridge_max_age;
|
||||
br->hello_time = br->bridge_hello_time;
|
||||
br->forward_delay = br->bridge_forward_delay;
|
||||
br_topology_change_detection(br);
|
||||
del_timer(&br->tcn_timer);
|
||||
|
||||
if (br->dev->flags & IFF_UP) {
|
||||
br_config_bpdu_generation(br);
|
||||
mod_timer(&br->hello_timer, jiffies + br->hello_time);
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_transmit_config(struct net_bridge_port *p)
|
||||
{
|
||||
struct br_config_bpdu bpdu;
|
||||
struct net_bridge *br;
|
||||
|
||||
|
||||
if (timer_pending(&p->hold_timer)) {
|
||||
p->config_pending = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
br = p->br;
|
||||
|
||||
bpdu.topology_change = br->topology_change;
|
||||
bpdu.topology_change_ack = p->topology_change_ack;
|
||||
bpdu.root = br->designated_root;
|
||||
bpdu.root_path_cost = br->root_path_cost;
|
||||
bpdu.bridge_id = br->bridge_id;
|
||||
bpdu.port_id = p->port_id;
|
||||
if (br_is_root_bridge(br))
|
||||
bpdu.message_age = 0;
|
||||
else {
|
||||
struct net_bridge_port *root
|
||||
= br_get_port(br, br->root_port);
|
||||
bpdu.message_age = br->max_age
|
||||
- (root->message_age_timer.expires - jiffies)
|
||||
+ MESSAGE_AGE_INCR;
|
||||
}
|
||||
bpdu.max_age = br->max_age;
|
||||
bpdu.hello_time = br->hello_time;
|
||||
bpdu.forward_delay = br->forward_delay;
|
||||
|
||||
if (bpdu.message_age < br->max_age) {
|
||||
br_send_config_bpdu(p, &bpdu);
|
||||
p->topology_change_ack = 0;
|
||||
p->config_pending = 0;
|
||||
mod_timer(&p->hold_timer, jiffies + BR_HOLD_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline void br_record_config_information(struct net_bridge_port *p,
|
||||
const struct br_config_bpdu *bpdu)
|
||||
{
|
||||
p->designated_root = bpdu->root;
|
||||
p->designated_cost = bpdu->root_path_cost;
|
||||
p->designated_bridge = bpdu->bridge_id;
|
||||
p->designated_port = bpdu->port_id;
|
||||
|
||||
mod_timer(&p->message_age_timer, jiffies
|
||||
+ (p->br->max_age - bpdu->message_age));
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline void br_record_config_timeout_values(struct net_bridge *br,
|
||||
const struct br_config_bpdu *bpdu)
|
||||
{
|
||||
br->max_age = bpdu->max_age;
|
||||
br->hello_time = bpdu->hello_time;
|
||||
br->forward_delay = bpdu->forward_delay;
|
||||
br->topology_change = bpdu->topology_change;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_transmit_tcn(struct net_bridge *br)
|
||||
{
|
||||
br_send_tcn_bpdu(br_get_port(br, br->root_port));
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static int br_should_become_designated_port(const struct net_bridge_port *p)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
int t;
|
||||
|
||||
br = p->br;
|
||||
if (br_is_designated_port(p))
|
||||
return 1;
|
||||
|
||||
if (memcmp(&p->designated_root, &br->designated_root, 8))
|
||||
return 1;
|
||||
|
||||
if (br->root_path_cost < p->designated_cost)
|
||||
return 1;
|
||||
else if (br->root_path_cost > p->designated_cost)
|
||||
return 0;
|
||||
|
||||
t = memcmp(&br->bridge_id, &p->designated_bridge, 8);
|
||||
if (t < 0)
|
||||
return 1;
|
||||
else if (t > 0)
|
||||
return 0;
|
||||
|
||||
if (p->port_id < p->designated_port)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static void br_designated_port_selection(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->state != BR_STATE_DISABLED &&
|
||||
br_should_become_designated_port(p))
|
||||
br_become_designated_port(p);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
|
||||
{
|
||||
int t;
|
||||
|
||||
t = memcmp(&bpdu->root, &p->designated_root, 8);
|
||||
if (t < 0)
|
||||
return 1;
|
||||
else if (t > 0)
|
||||
return 0;
|
||||
|
||||
if (bpdu->root_path_cost < p->designated_cost)
|
||||
return 1;
|
||||
else if (bpdu->root_path_cost > p->designated_cost)
|
||||
return 0;
|
||||
|
||||
t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8);
|
||||
if (t < 0)
|
||||
return 1;
|
||||
else if (t > 0)
|
||||
return 0;
|
||||
|
||||
if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8))
|
||||
return 1;
|
||||
|
||||
if (bpdu->port_id <= p->designated_port)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline void br_topology_change_acknowledged(struct net_bridge *br)
|
||||
{
|
||||
br->topology_change_detected = 0;
|
||||
del_timer(&br->tcn_timer);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_topology_change_detection(struct net_bridge *br)
|
||||
{
|
||||
int isroot = br_is_root_bridge(br);
|
||||
|
||||
pr_info("%s: topology change detected, %s\n", br->dev->name,
|
||||
isroot ? "propagating" : "sending tcn bpdu");
|
||||
|
||||
if (isroot) {
|
||||
br->topology_change = 1;
|
||||
mod_timer(&br->topology_change_timer, jiffies
|
||||
+ br->bridge_forward_delay + br->bridge_max_age);
|
||||
} else if (!br->topology_change_detected) {
|
||||
br_transmit_tcn(br);
|
||||
mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
|
||||
}
|
||||
|
||||
br->topology_change_detected = 1;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_config_bpdu_generation(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->state != BR_STATE_DISABLED &&
|
||||
br_is_designated_port(p))
|
||||
br_transmit_config(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline void br_reply(struct net_bridge_port *p)
|
||||
{
|
||||
br_transmit_config(p);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_configuration_update(struct net_bridge *br)
|
||||
{
|
||||
br_root_selection(br);
|
||||
br_designated_port_selection(br);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_become_designated_port(struct net_bridge_port *p)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
|
||||
br = p->br;
|
||||
p->designated_root = br->designated_root;
|
||||
p->designated_cost = br->root_path_cost;
|
||||
p->designated_bridge = br->bridge_id;
|
||||
p->designated_port = p->port_id;
|
||||
}
|
||||
|
||||
|
||||
/* called under bridge lock */
|
||||
static void br_make_blocking(struct net_bridge_port *p)
|
||||
{
|
||||
if (p->state != BR_STATE_DISABLED &&
|
||||
p->state != BR_STATE_BLOCKING) {
|
||||
if (p->state == BR_STATE_FORWARDING ||
|
||||
p->state == BR_STATE_LEARNING)
|
||||
br_topology_change_detection(p->br);
|
||||
|
||||
p->state = BR_STATE_BLOCKING;
|
||||
br_log_state(p);
|
||||
del_timer(&p->forward_delay_timer);
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static void br_make_forwarding(struct net_bridge_port *p)
|
||||
{
|
||||
if (p->state == BR_STATE_BLOCKING) {
|
||||
if (p->br->stp_enabled) {
|
||||
p->state = BR_STATE_LISTENING;
|
||||
} else {
|
||||
p->state = BR_STATE_LEARNING;
|
||||
}
|
||||
br_log_state(p);
|
||||
mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); }
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_port_state_selection(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->state != BR_STATE_DISABLED) {
|
||||
if (p->port_no == br->root_port) {
|
||||
p->config_pending = 0;
|
||||
p->topology_change_ack = 0;
|
||||
br_make_forwarding(p);
|
||||
} else if (br_is_designated_port(p)) {
|
||||
del_timer(&p->message_age_timer);
|
||||
br_make_forwarding(p);
|
||||
} else {
|
||||
p->config_pending = 0;
|
||||
p->topology_change_ack = 0;
|
||||
br_make_blocking(p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
static inline void br_topology_change_acknowledge(struct net_bridge_port *p)
|
||||
{
|
||||
p->topology_change_ack = 1;
|
||||
br_transmit_config(p);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
int was_root;
|
||||
|
||||
br = p->br;
|
||||
was_root = br_is_root_bridge(br);
|
||||
|
||||
if (br_supersedes_port_info(p, bpdu)) {
|
||||
br_record_config_information(p, bpdu);
|
||||
br_configuration_update(br);
|
||||
br_port_state_selection(br);
|
||||
|
||||
if (!br_is_root_bridge(br) && was_root) {
|
||||
del_timer(&br->hello_timer);
|
||||
if (br->topology_change_detected) {
|
||||
del_timer(&br->topology_change_timer);
|
||||
br_transmit_tcn(br);
|
||||
|
||||
mod_timer(&br->tcn_timer,
|
||||
jiffies + br->bridge_hello_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->port_no == br->root_port) {
|
||||
br_record_config_timeout_values(br, bpdu);
|
||||
br_config_bpdu_generation(br);
|
||||
if (bpdu->topology_change_ack)
|
||||
br_topology_change_acknowledged(br);
|
||||
}
|
||||
} else if (br_is_designated_port(p)) {
|
||||
br_reply(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_received_tcn_bpdu(struct net_bridge_port *p)
|
||||
{
|
||||
if (br_is_designated_port(p)) {
|
||||
pr_info("%s: received tcn bpdu on port %i(%s)\n",
|
||||
p->br->dev->name, p->port_no, p->dev->name);
|
||||
|
||||
br_topology_change_detection(p->br);
|
||||
br_topology_change_acknowledge(p);
|
||||
}
|
||||
}
|
||||
219
net/bridge/br_stp_bpdu.c
Normal file
219
net/bridge/br_stp_bpdu.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Spanning tree protocol; BPDU handling
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_stp_bpdu.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/llc.h>
|
||||
#include <net/llc.h>
|
||||
#include <net/llc_pdu.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "br_private.h"
|
||||
#include "br_private_stp.h"
|
||||
|
||||
#define STP_HZ 256
|
||||
|
||||
#define LLC_RESERVE sizeof(struct llc_pdu_un)
|
||||
|
||||
static void br_send_bpdu(struct net_bridge_port *p,
|
||||
const unsigned char *data, int length)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!p->br->stp_enabled)
|
||||
return;
|
||||
|
||||
skb = dev_alloc_skb(length+LLC_RESERVE);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb->dev = p->dev;
|
||||
skb->protocol = htons(ETH_P_802_2);
|
||||
|
||||
skb_reserve(skb, LLC_RESERVE);
|
||||
memcpy(__skb_put(skb, length), data, length);
|
||||
|
||||
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
|
||||
LLC_SAP_BSPAN, LLC_PDU_CMD);
|
||||
llc_pdu_init_as_ui_cmd(skb);
|
||||
|
||||
llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr);
|
||||
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
|
||||
dev_queue_xmit);
|
||||
}
|
||||
|
||||
static inline void br_set_ticks(unsigned char *dest, int j)
|
||||
{
|
||||
unsigned long ticks = (STP_HZ * j)/ HZ;
|
||||
|
||||
put_unaligned(htons(ticks), (__be16 *)dest);
|
||||
}
|
||||
|
||||
static inline int br_get_ticks(const unsigned char *src)
|
||||
{
|
||||
unsigned long ticks = ntohs(get_unaligned((__be16 *)src));
|
||||
|
||||
return (ticks * HZ + STP_HZ - 1) / STP_HZ;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
|
||||
{
|
||||
unsigned char buf[35];
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = 0;
|
||||
buf[3] = BPDU_TYPE_CONFIG;
|
||||
buf[4] = (bpdu->topology_change ? 0x01 : 0) |
|
||||
(bpdu->topology_change_ack ? 0x80 : 0);
|
||||
buf[5] = bpdu->root.prio[0];
|
||||
buf[6] = bpdu->root.prio[1];
|
||||
buf[7] = bpdu->root.addr[0];
|
||||
buf[8] = bpdu->root.addr[1];
|
||||
buf[9] = bpdu->root.addr[2];
|
||||
buf[10] = bpdu->root.addr[3];
|
||||
buf[11] = bpdu->root.addr[4];
|
||||
buf[12] = bpdu->root.addr[5];
|
||||
buf[13] = (bpdu->root_path_cost >> 24) & 0xFF;
|
||||
buf[14] = (bpdu->root_path_cost >> 16) & 0xFF;
|
||||
buf[15] = (bpdu->root_path_cost >> 8) & 0xFF;
|
||||
buf[16] = bpdu->root_path_cost & 0xFF;
|
||||
buf[17] = bpdu->bridge_id.prio[0];
|
||||
buf[18] = bpdu->bridge_id.prio[1];
|
||||
buf[19] = bpdu->bridge_id.addr[0];
|
||||
buf[20] = bpdu->bridge_id.addr[1];
|
||||
buf[21] = bpdu->bridge_id.addr[2];
|
||||
buf[22] = bpdu->bridge_id.addr[3];
|
||||
buf[23] = bpdu->bridge_id.addr[4];
|
||||
buf[24] = bpdu->bridge_id.addr[5];
|
||||
buf[25] = (bpdu->port_id >> 8) & 0xFF;
|
||||
buf[26] = bpdu->port_id & 0xFF;
|
||||
|
||||
br_set_ticks(buf+27, bpdu->message_age);
|
||||
br_set_ticks(buf+29, bpdu->max_age);
|
||||
br_set_ticks(buf+31, bpdu->hello_time);
|
||||
br_set_ticks(buf+33, bpdu->forward_delay);
|
||||
|
||||
br_send_bpdu(p, buf, 35);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_send_tcn_bpdu(struct net_bridge_port *p)
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = 0;
|
||||
buf[3] = BPDU_TYPE_TCN;
|
||||
br_send_bpdu(p, buf, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from llc.
|
||||
*
|
||||
* NO locks, but rcu_read_lock (preempt_disabled)
|
||||
*/
|
||||
int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
|
||||
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
||||
struct net_bridge_port *p = rcu_dereference(dev->br_port);
|
||||
struct net_bridge *br;
|
||||
const unsigned char *buf;
|
||||
|
||||
if (!p)
|
||||
goto err;
|
||||
|
||||
if (pdu->ssap != LLC_SAP_BSPAN
|
||||
|| pdu->dsap != LLC_SAP_BSPAN
|
||||
|| pdu->ctrl_1 != LLC_PDU_TYPE_U)
|
||||
goto err;
|
||||
|
||||
if (!pskb_may_pull(skb, 4))
|
||||
goto err;
|
||||
|
||||
/* compare of protocol id and version */
|
||||
buf = skb->data;
|
||||
if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
|
||||
goto err;
|
||||
|
||||
br = p->br;
|
||||
spin_lock(&br->lock);
|
||||
|
||||
if (p->state == BR_STATE_DISABLED
|
||||
|| !br->stp_enabled
|
||||
|| !(br->dev->flags & IFF_UP))
|
||||
goto out;
|
||||
|
||||
if (compare_ether_addr(dest, br->group_addr) != 0)
|
||||
goto out;
|
||||
|
||||
buf = skb_pull(skb, 3);
|
||||
|
||||
if (buf[0] == BPDU_TYPE_CONFIG) {
|
||||
struct br_config_bpdu bpdu;
|
||||
|
||||
if (!pskb_may_pull(skb, 32))
|
||||
goto out;
|
||||
|
||||
buf = skb->data;
|
||||
bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;
|
||||
bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;
|
||||
|
||||
bpdu.root.prio[0] = buf[2];
|
||||
bpdu.root.prio[1] = buf[3];
|
||||
bpdu.root.addr[0] = buf[4];
|
||||
bpdu.root.addr[1] = buf[5];
|
||||
bpdu.root.addr[2] = buf[6];
|
||||
bpdu.root.addr[3] = buf[7];
|
||||
bpdu.root.addr[4] = buf[8];
|
||||
bpdu.root.addr[5] = buf[9];
|
||||
bpdu.root_path_cost =
|
||||
(buf[10] << 24) |
|
||||
(buf[11] << 16) |
|
||||
(buf[12] << 8) |
|
||||
buf[13];
|
||||
bpdu.bridge_id.prio[0] = buf[14];
|
||||
bpdu.bridge_id.prio[1] = buf[15];
|
||||
bpdu.bridge_id.addr[0] = buf[16];
|
||||
bpdu.bridge_id.addr[1] = buf[17];
|
||||
bpdu.bridge_id.addr[2] = buf[18];
|
||||
bpdu.bridge_id.addr[3] = buf[19];
|
||||
bpdu.bridge_id.addr[4] = buf[20];
|
||||
bpdu.bridge_id.addr[5] = buf[21];
|
||||
bpdu.port_id = (buf[22] << 8) | buf[23];
|
||||
|
||||
bpdu.message_age = br_get_ticks(buf+24);
|
||||
bpdu.max_age = br_get_ticks(buf+26);
|
||||
bpdu.hello_time = br_get_ticks(buf+28);
|
||||
bpdu.forward_delay = br_get_ticks(buf+30);
|
||||
|
||||
br_received_config_bpdu(p, &bpdu);
|
||||
}
|
||||
|
||||
else if (buf[0] == BPDU_TYPE_TCN) {
|
||||
br_received_tcn_bpdu(p);
|
||||
}
|
||||
out:
|
||||
spin_unlock(&br->lock);
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
234
net/bridge/br_stp_if.c
Normal file
234
net/bridge/br_stp_if.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Spanning tree protocol; interface code
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_stp_if.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include "br_private.h"
|
||||
#include "br_private_stp.h"
|
||||
|
||||
|
||||
/* Port id is composed of priority and port number.
|
||||
* NB: least significant bits of priority are dropped to
|
||||
* make room for more ports.
|
||||
*/
|
||||
static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
|
||||
{
|
||||
return ((u16)priority << BR_PORT_BITS)
|
||||
| (port_no & ((1<<BR_PORT_BITS)-1));
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_init_port(struct net_bridge_port *p)
|
||||
{
|
||||
p->port_id = br_make_port_id(p->priority, p->port_no);
|
||||
br_become_designated_port(p);
|
||||
p->state = BR_STATE_BLOCKING;
|
||||
p->topology_change_ack = 0;
|
||||
p->config_pending = 0;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_enable_bridge(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
mod_timer(&br->hello_timer, jiffies + br->hello_time);
|
||||
mod_timer(&br->gc_timer, jiffies + HZ/10);
|
||||
|
||||
br_config_bpdu_generation(br);
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev))
|
||||
br_stp_enable_port(p);
|
||||
|
||||
}
|
||||
spin_unlock_bh(&br->lock);
|
||||
}
|
||||
|
||||
/* NO locks held */
|
||||
void br_stp_disable_bridge(struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->state != BR_STATE_DISABLED)
|
||||
br_stp_disable_port(p);
|
||||
|
||||
}
|
||||
|
||||
br->topology_change = 0;
|
||||
br->topology_change_detected = 0;
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
del_timer_sync(&br->hello_timer);
|
||||
del_timer_sync(&br->topology_change_timer);
|
||||
del_timer_sync(&br->tcn_timer);
|
||||
del_timer_sync(&br->gc_timer);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_enable_port(struct net_bridge_port *p)
|
||||
{
|
||||
br_init_port(p);
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
br_port_state_selection(p->br);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_disable_port(struct net_bridge_port *p)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
int wasroot;
|
||||
|
||||
br = p->br;
|
||||
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
|
||||
br->dev->name, p->port_no, p->dev->name, "disabled");
|
||||
|
||||
br_ifinfo_notify(RTM_DELLINK, p);
|
||||
|
||||
wasroot = br_is_root_bridge(br);
|
||||
br_become_designated_port(p);
|
||||
p->state = BR_STATE_DISABLED;
|
||||
p->topology_change_ack = 0;
|
||||
p->config_pending = 0;
|
||||
|
||||
del_timer(&p->message_age_timer);
|
||||
del_timer(&p->forward_delay_timer);
|
||||
del_timer(&p->hold_timer);
|
||||
|
||||
br_fdb_delete_by_port(br, p, 0);
|
||||
|
||||
br_configuration_update(br);
|
||||
|
||||
br_port_state_selection(br);
|
||||
|
||||
if (br_is_root_bridge(br) && !wasroot)
|
||||
br_become_root_bridge(br);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
|
||||
{
|
||||
/* should be aligned on 2 bytes for compare_ether_addr() */
|
||||
unsigned short oldaddr_aligned[ETH_ALEN >> 1];
|
||||
unsigned char *oldaddr = (unsigned char *)oldaddr_aligned;
|
||||
struct net_bridge_port *p;
|
||||
int wasroot;
|
||||
|
||||
wasroot = br_is_root_bridge(br);
|
||||
|
||||
memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
|
||||
memcpy(br->bridge_id.addr, addr, ETH_ALEN);
|
||||
memcpy(br->dev->dev_addr, addr, ETH_ALEN);
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (!compare_ether_addr(p->designated_bridge.addr, oldaddr))
|
||||
memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
|
||||
|
||||
if (!compare_ether_addr(p->designated_root.addr, oldaddr))
|
||||
memcpy(p->designated_root.addr, addr, ETH_ALEN);
|
||||
|
||||
}
|
||||
|
||||
br_configuration_update(br);
|
||||
br_port_state_selection(br);
|
||||
if (br_is_root_bridge(br) && !wasroot)
|
||||
br_become_root_bridge(br);
|
||||
}
|
||||
|
||||
/* should be aligned on 2 bytes for compare_ether_addr() */
|
||||
static const unsigned short br_mac_zero_aligned[ETH_ALEN >> 1];
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_recalculate_bridge_id(struct net_bridge *br)
|
||||
{
|
||||
const unsigned char *br_mac_zero =
|
||||
(const unsigned char *)br_mac_zero_aligned;
|
||||
const unsigned char *addr = br_mac_zero;
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (addr == br_mac_zero ||
|
||||
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
|
||||
addr = p->dev->dev_addr;
|
||||
|
||||
}
|
||||
|
||||
if (compare_ether_addr(br->bridge_id.addr, addr))
|
||||
br_stp_change_bridge_id(br, addr);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
int wasroot;
|
||||
|
||||
wasroot = br_is_root_bridge(br);
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->state != BR_STATE_DISABLED &&
|
||||
br_is_designated_port(p)) {
|
||||
p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
|
||||
p->designated_bridge.prio[1] = newprio & 0xFF;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
|
||||
br->bridge_id.prio[1] = newprio & 0xFF;
|
||||
br_configuration_update(br);
|
||||
br_port_state_selection(br);
|
||||
if (br_is_root_bridge(br) && !wasroot)
|
||||
br_become_root_bridge(br);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
|
||||
{
|
||||
port_id new_port_id = br_make_port_id(newprio, p->port_no);
|
||||
|
||||
if (br_is_designated_port(p))
|
||||
p->designated_port = new_port_id;
|
||||
|
||||
p->port_id = new_port_id;
|
||||
p->priority = newprio;
|
||||
if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
|
||||
p->port_id < p->designated_port) {
|
||||
br_become_designated_port(p);
|
||||
br_port_state_selection(p->br);
|
||||
}
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
|
||||
{
|
||||
p->path_cost = path_cost;
|
||||
br_configuration_update(p->br);
|
||||
br_port_state_selection(p->br);
|
||||
}
|
||||
|
||||
ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
|
||||
{
|
||||
return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
|
||||
id->prio[0], id->prio[1],
|
||||
id->addr[0], id->addr[1], id->addr[2],
|
||||
id->addr[3], id->addr[4], id->addr[5]);
|
||||
}
|
||||
179
net/bridge/br_stp_timer.c
Normal file
179
net/bridge/br_stp_timer.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Spanning tree protocol; timer-related code
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
*
|
||||
* $Id: br_stp_timer.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu 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 <linux/kernel.h>
|
||||
#include <linux/times.h>
|
||||
#include <linux/smp_lock.h>
|
||||
|
||||
#include "br_private.h"
|
||||
#include "br_private_stp.h"
|
||||
|
||||
/* called under bridge lock */
|
||||
static int br_is_designated_for_some_port(const struct net_bridge *br)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (p->state != BR_STATE_DISABLED &&
|
||||
!memcmp(&p->designated_bridge, &br->bridge_id, 8))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void br_hello_timer_expired(unsigned long arg)
|
||||
{
|
||||
struct net_bridge *br = (struct net_bridge *)arg;
|
||||
|
||||
pr_debug("%s: hello timer expired\n", br->dev->name);
|
||||
spin_lock(&br->lock);
|
||||
if (br->dev->flags & IFF_UP) {
|
||||
br_config_bpdu_generation(br);
|
||||
|
||||
mod_timer(&br->hello_timer, jiffies + br->hello_time);
|
||||
}
|
||||
spin_unlock(&br->lock);
|
||||
}
|
||||
|
||||
static void br_message_age_timer_expired(unsigned long arg)
|
||||
{
|
||||
struct net_bridge_port *p = (struct net_bridge_port *) arg;
|
||||
struct net_bridge *br = p->br;
|
||||
const bridge_id *id = &p->designated_bridge;
|
||||
int was_root;
|
||||
|
||||
if (p->state == BR_STATE_DISABLED)
|
||||
return;
|
||||
|
||||
|
||||
pr_info("%s: neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)\n",
|
||||
br->dev->name,
|
||||
id->prio[0], id->prio[1],
|
||||
id->addr[0], id->addr[1], id->addr[2],
|
||||
id->addr[3], id->addr[4], id->addr[5],
|
||||
p->port_no, p->dev->name);
|
||||
|
||||
/*
|
||||
* According to the spec, the message age timer cannot be
|
||||
* running when we are the root bridge. So.. this was_root
|
||||
* check is redundant. I'm leaving it in for now, though.
|
||||
*/
|
||||
spin_lock(&br->lock);
|
||||
if (p->state == BR_STATE_DISABLED)
|
||||
goto unlock;
|
||||
was_root = br_is_root_bridge(br);
|
||||
|
||||
br_become_designated_port(p);
|
||||
br_configuration_update(br);
|
||||
br_port_state_selection(br);
|
||||
if (br_is_root_bridge(br) && !was_root)
|
||||
br_become_root_bridge(br);
|
||||
unlock:
|
||||
spin_unlock(&br->lock);
|
||||
}
|
||||
|
||||
static void br_forward_delay_timer_expired(unsigned long arg)
|
||||
{
|
||||
struct net_bridge_port *p = (struct net_bridge_port *) arg;
|
||||
struct net_bridge *br = p->br;
|
||||
|
||||
pr_debug("%s: %d(%s) forward delay timer\n",
|
||||
br->dev->name, p->port_no, p->dev->name);
|
||||
spin_lock(&br->lock);
|
||||
if (p->state == BR_STATE_LISTENING) {
|
||||
p->state = BR_STATE_LEARNING;
|
||||
mod_timer(&p->forward_delay_timer,
|
||||
jiffies + br->forward_delay);
|
||||
} else if (p->state == BR_STATE_LEARNING) {
|
||||
p->state = BR_STATE_FORWARDING;
|
||||
if (br_is_designated_for_some_port(br))
|
||||
br_topology_change_detection(br);
|
||||
}
|
||||
br_log_state(p);
|
||||
spin_unlock(&br->lock);
|
||||
}
|
||||
|
||||
static void br_tcn_timer_expired(unsigned long arg)
|
||||
{
|
||||
struct net_bridge *br = (struct net_bridge *) arg;
|
||||
|
||||
pr_debug("%s: tcn timer expired\n", br->dev->name);
|
||||
spin_lock(&br->lock);
|
||||
if (br->dev->flags & IFF_UP) {
|
||||
br_transmit_tcn(br);
|
||||
|
||||
mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time);
|
||||
}
|
||||
spin_unlock(&br->lock);
|
||||
}
|
||||
|
||||
static void br_topology_change_timer_expired(unsigned long arg)
|
||||
{
|
||||
struct net_bridge *br = (struct net_bridge *) arg;
|
||||
|
||||
pr_debug("%s: topo change timer expired\n", br->dev->name);
|
||||
spin_lock(&br->lock);
|
||||
br->topology_change_detected = 0;
|
||||
br->topology_change = 0;
|
||||
spin_unlock(&br->lock);
|
||||
}
|
||||
|
||||
static void br_hold_timer_expired(unsigned long arg)
|
||||
{
|
||||
struct net_bridge_port *p = (struct net_bridge_port *) arg;
|
||||
|
||||
pr_debug("%s: %d(%s) hold timer expired\n",
|
||||
p->br->dev->name, p->port_no, p->dev->name);
|
||||
|
||||
spin_lock(&p->br->lock);
|
||||
if (p->config_pending)
|
||||
br_transmit_config(p);
|
||||
spin_unlock(&p->br->lock);
|
||||
}
|
||||
|
||||
void br_stp_timer_init(struct net_bridge *br)
|
||||
{
|
||||
setup_timer(&br->hello_timer, br_hello_timer_expired,
|
||||
(unsigned long) br);
|
||||
|
||||
setup_timer(&br->tcn_timer, br_tcn_timer_expired,
|
||||
(unsigned long) br);
|
||||
|
||||
setup_timer(&br->topology_change_timer,
|
||||
br_topology_change_timer_expired,
|
||||
(unsigned long) br);
|
||||
|
||||
setup_timer(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);
|
||||
}
|
||||
|
||||
void br_stp_port_timer_init(struct net_bridge_port *p)
|
||||
{
|
||||
setup_timer(&p->message_age_timer, br_message_age_timer_expired,
|
||||
(unsigned long) p);
|
||||
|
||||
setup_timer(&p->forward_delay_timer, br_forward_delay_timer_expired,
|
||||
(unsigned long) p);
|
||||
|
||||
setup_timer(&p->hold_timer, br_hold_timer_expired,
|
||||
(unsigned long) p);
|
||||
}
|
||||
|
||||
/* Report ticks left (in USER_HZ) used for API */
|
||||
unsigned long br_timer_value(const struct timer_list *timer)
|
||||
{
|
||||
return timer_pending(timer)
|
||||
? jiffies_to_clock_t(timer->expires - jiffies) : 0;
|
||||
}
|
||||
434
net/bridge/br_sysfs_br.c
Normal file
434
net/bridge/br_sysfs_br.c
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* Sysfs attributes of bridge ports
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Stephen Hemminger <shemminger@osdl.org>
|
||||
*
|
||||
* 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 <linux/capability.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/times.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
#define to_dev(obj) container_of(obj, struct device, kobj)
|
||||
#define to_bridge(cd) ((struct net_bridge *)(to_net_dev(cd)->priv))
|
||||
|
||||
/*
|
||||
* Common code for storing bridge parameters.
|
||||
*/
|
||||
static ssize_t store_bridge_parm(struct device *d,
|
||||
const char *buf, size_t len,
|
||||
void (*set)(struct net_bridge *, unsigned long))
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
char *endp;
|
||||
unsigned long val;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
(*set)(br, val);
|
||||
spin_unlock_bh(&br->lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t show_forward_delay(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
|
||||
}
|
||||
|
||||
static void set_forward_delay(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
unsigned long delay = clock_t_to_jiffies(val);
|
||||
br->forward_delay = delay;
|
||||
if (br_is_root_bridge(br))
|
||||
br->bridge_forward_delay = delay;
|
||||
}
|
||||
|
||||
static ssize_t store_forward_delay(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
return store_bridge_parm(d, buf, len, set_forward_delay);
|
||||
}
|
||||
static DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
|
||||
show_forward_delay, store_forward_delay);
|
||||
|
||||
static ssize_t show_hello_time(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%lu\n",
|
||||
jiffies_to_clock_t(to_bridge(d)->hello_time));
|
||||
}
|
||||
|
||||
static void set_hello_time(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
unsigned long t = clock_t_to_jiffies(val);
|
||||
br->hello_time = t;
|
||||
if (br_is_root_bridge(br))
|
||||
br->bridge_hello_time = t;
|
||||
}
|
||||
|
||||
static ssize_t store_hello_time(struct device *d,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
return store_bridge_parm(d, buf, len, set_hello_time);
|
||||
}
|
||||
static DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
|
||||
store_hello_time);
|
||||
|
||||
static ssize_t show_max_age(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%lu\n",
|
||||
jiffies_to_clock_t(to_bridge(d)->max_age));
|
||||
}
|
||||
|
||||
static void set_max_age(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
unsigned long t = clock_t_to_jiffies(val);
|
||||
br->max_age = t;
|
||||
if (br_is_root_bridge(br))
|
||||
br->bridge_max_age = t;
|
||||
}
|
||||
|
||||
static ssize_t store_max_age(struct device *d, struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
return store_bridge_parm(d, buf, len, set_max_age);
|
||||
}
|
||||
static DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
|
||||
|
||||
static ssize_t show_ageing_time(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
|
||||
}
|
||||
|
||||
static void set_ageing_time(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
br->ageing_time = clock_t_to_jiffies(val);
|
||||
}
|
||||
|
||||
static ssize_t store_ageing_time(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
return store_bridge_parm(d, buf, len, set_ageing_time);
|
||||
}
|
||||
static DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
|
||||
store_ageing_time);
|
||||
|
||||
static ssize_t show_stp_state(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%d\n", br->stp_enabled);
|
||||
}
|
||||
|
||||
static void set_stp_state(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
br->stp_enabled = val;
|
||||
}
|
||||
|
||||
static ssize_t store_stp_state(struct device *d,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
return store_bridge_parm(d, buf, len, set_stp_state);
|
||||
}
|
||||
static DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
|
||||
store_stp_state);
|
||||
|
||||
static ssize_t show_priority(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%d\n",
|
||||
(br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
|
||||
}
|
||||
|
||||
static void set_priority(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
br_stp_set_bridge_priority(br, (u16) val);
|
||||
}
|
||||
|
||||
static ssize_t store_priority(struct device *d, struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
return store_bridge_parm(d, buf, len, set_priority);
|
||||
}
|
||||
static DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
|
||||
|
||||
static ssize_t show_root_id(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
|
||||
}
|
||||
static DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
|
||||
|
||||
static ssize_t show_bridge_id(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return br_show_bridge_id(buf, &to_bridge(d)->bridge_id);
|
||||
}
|
||||
static DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
|
||||
|
||||
static ssize_t show_root_port(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", to_bridge(d)->root_port);
|
||||
}
|
||||
static DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
|
||||
|
||||
static ssize_t show_root_path_cost(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
|
||||
}
|
||||
static DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
|
||||
|
||||
static ssize_t show_topology_change(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
|
||||
}
|
||||
static DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
|
||||
|
||||
static ssize_t show_topology_change_detected(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%d\n", br->topology_change_detected);
|
||||
}
|
||||
static DEVICE_ATTR(topology_change_detected, S_IRUGO,
|
||||
show_topology_change_detected, NULL);
|
||||
|
||||
static ssize_t show_hello_timer(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
|
||||
}
|
||||
static DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
|
||||
|
||||
static ssize_t show_tcn_timer(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
|
||||
}
|
||||
static DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
|
||||
|
||||
static ssize_t show_topology_change_timer(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
|
||||
}
|
||||
static DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
|
||||
NULL);
|
||||
|
||||
static ssize_t show_gc_timer(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
|
||||
}
|
||||
static DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
|
||||
|
||||
static ssize_t show_group_addr(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
|
||||
br->group_addr[0], br->group_addr[1],
|
||||
br->group_addr[2], br->group_addr[3],
|
||||
br->group_addr[4], br->group_addr[5]);
|
||||
}
|
||||
|
||||
static ssize_t store_group_addr(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
unsigned new_addr[6];
|
||||
int i;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
|
||||
&new_addr[0], &new_addr[1], &new_addr[2],
|
||||
&new_addr[3], &new_addr[4], &new_addr[5]) != 6)
|
||||
return -EINVAL;
|
||||
|
||||
/* Must be 01:80:c2:00:00:0X */
|
||||
for (i = 0; i < 5; i++)
|
||||
if (new_addr[i] != br_group_address[i])
|
||||
return -EINVAL;
|
||||
|
||||
if (new_addr[5] & ~0xf)
|
||||
return -EINVAL;
|
||||
|
||||
if (new_addr[5] == 1 /* 802.3x Pause address */
|
||||
|| new_addr[5] == 2 /* 802.3ad Slow protocols */
|
||||
|| new_addr[5] == 3) /* 802.1X PAE address */
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
for (i = 0; i < 6; i++)
|
||||
br->group_addr[i] = new_addr[i];
|
||||
spin_unlock_bh(&br->lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
|
||||
show_group_addr, store_group_addr);
|
||||
|
||||
|
||||
static struct attribute *bridge_attrs[] = {
|
||||
&dev_attr_forward_delay.attr,
|
||||
&dev_attr_hello_time.attr,
|
||||
&dev_attr_max_age.attr,
|
||||
&dev_attr_ageing_time.attr,
|
||||
&dev_attr_stp_state.attr,
|
||||
&dev_attr_priority.attr,
|
||||
&dev_attr_bridge_id.attr,
|
||||
&dev_attr_root_id.attr,
|
||||
&dev_attr_root_path_cost.attr,
|
||||
&dev_attr_root_port.attr,
|
||||
&dev_attr_topology_change.attr,
|
||||
&dev_attr_topology_change_detected.attr,
|
||||
&dev_attr_hello_timer.attr,
|
||||
&dev_attr_tcn_timer.attr,
|
||||
&dev_attr_topology_change_timer.attr,
|
||||
&dev_attr_gc_timer.attr,
|
||||
&dev_attr_group_addr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group bridge_group = {
|
||||
.name = SYSFS_BRIDGE_ATTR,
|
||||
.attrs = bridge_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Export the forwarding information table as a binary file
|
||||
* The records are struct __fdb_entry.
|
||||
*
|
||||
* Returns the number of bytes read.
|
||||
*/
|
||||
static ssize_t brforward_read(struct kobject *kobj, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = to_dev(kobj);
|
||||
struct net_bridge *br = to_bridge(dev);
|
||||
int n;
|
||||
|
||||
/* must read whole records */
|
||||
if (off % sizeof(struct __fdb_entry) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
n = br_fdb_fillbuf(br, buf,
|
||||
count / sizeof(struct __fdb_entry),
|
||||
off / sizeof(struct __fdb_entry));
|
||||
|
||||
if (n > 0)
|
||||
n *= sizeof(struct __fdb_entry);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct bin_attribute bridge_forward = {
|
||||
.attr = { .name = SYSFS_BRIDGE_FDB,
|
||||
.mode = S_IRUGO,
|
||||
.owner = THIS_MODULE, },
|
||||
.read = brforward_read,
|
||||
};
|
||||
|
||||
/*
|
||||
* Add entries in sysfs onto the existing network class device
|
||||
* for the bridge.
|
||||
* Adds a attribute group "bridge" containing tuning parameters.
|
||||
* Binary attribute containing the forward table
|
||||
* Sub directory to hold links to interfaces.
|
||||
*
|
||||
* Note: the ifobj exists only to be a subdirectory
|
||||
* to hold links. The ifobj exists in same data structure
|
||||
* as it's parent the bridge so reference counting works.
|
||||
*/
|
||||
int br_sysfs_addbr(struct net_device *dev)
|
||||
{
|
||||
struct kobject *brobj = &dev->dev.kobj;
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
err = sysfs_create_group(brobj, &bridge_group);
|
||||
if (err) {
|
||||
pr_info("%s: can't create group %s/%s\n",
|
||||
__FUNCTION__, dev->name, bridge_group.name);
|
||||
goto out1;
|
||||
}
|
||||
|
||||
err = sysfs_create_bin_file(brobj, &bridge_forward);
|
||||
if (err) {
|
||||
pr_info("%s: can't create attribute file %s/%s\n",
|
||||
__FUNCTION__, dev->name, bridge_forward.attr.name);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
|
||||
kobject_set_name(&br->ifobj, SYSFS_BRIDGE_PORT_SUBDIR);
|
||||
br->ifobj.ktype = NULL;
|
||||
br->ifobj.kset = NULL;
|
||||
br->ifobj.parent = brobj;
|
||||
|
||||
err = kobject_register(&br->ifobj);
|
||||
if (err) {
|
||||
pr_info("%s: can't add kobject (directory) %s/%s\n",
|
||||
__FUNCTION__, dev->name, br->ifobj.name);
|
||||
goto out3;
|
||||
}
|
||||
return 0;
|
||||
out3:
|
||||
sysfs_remove_bin_file(&dev->dev.kobj, &bridge_forward);
|
||||
out2:
|
||||
sysfs_remove_group(&dev->dev.kobj, &bridge_group);
|
||||
out1:
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
void br_sysfs_delbr(struct net_device *dev)
|
||||
{
|
||||
struct kobject *kobj = &dev->dev.kobj;
|
||||
struct net_bridge *br = netdev_priv(dev);
|
||||
|
||||
kobject_unregister(&br->ifobj);
|
||||
sysfs_remove_bin_file(kobj, &bridge_forward);
|
||||
sysfs_remove_group(kobj, &bridge_group);
|
||||
}
|
||||
228
net/bridge/br_sysfs_if.c
Normal file
228
net/bridge/br_sysfs_if.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Sysfs attributes of bridge ports
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Stephen Hemminger <shemminger@osdl.org>
|
||||
*
|
||||
* 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 <linux/capability.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
struct brport_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct net_bridge_port *, char *);
|
||||
ssize_t (*store)(struct net_bridge_port *, unsigned long);
|
||||
};
|
||||
|
||||
#define BRPORT_ATTR(_name,_mode,_show,_store) \
|
||||
struct brport_attribute brport_attr_##_name = { \
|
||||
.attr = {.name = __stringify(_name), \
|
||||
.mode = _mode, \
|
||||
.owner = THIS_MODULE, }, \
|
||||
.show = _show, \
|
||||
.store = _store, \
|
||||
};
|
||||
|
||||
static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->path_cost);
|
||||
}
|
||||
static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
|
||||
{
|
||||
br_stp_set_path_cost(p, v);
|
||||
return 0;
|
||||
}
|
||||
static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
|
||||
show_path_cost, store_path_cost);
|
||||
|
||||
static ssize_t show_priority(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->priority);
|
||||
}
|
||||
static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
|
||||
{
|
||||
if (v >= (1<<(16-BR_PORT_BITS)))
|
||||
return -ERANGE;
|
||||
br_stp_set_port_priority(p, v);
|
||||
return 0;
|
||||
}
|
||||
static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
|
||||
show_priority, store_priority);
|
||||
|
||||
static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return br_show_bridge_id(buf, &p->designated_root);
|
||||
}
|
||||
static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
|
||||
|
||||
static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return br_show_bridge_id(buf, &p->designated_bridge);
|
||||
}
|
||||
static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
|
||||
|
||||
static ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->designated_port);
|
||||
}
|
||||
static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
|
||||
|
||||
static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->designated_cost);
|
||||
}
|
||||
static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
|
||||
|
||||
static ssize_t show_port_id(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "0x%x\n", p->port_id);
|
||||
}
|
||||
static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
|
||||
|
||||
static ssize_t show_port_no(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "0x%x\n", p->port_no);
|
||||
}
|
||||
|
||||
static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL);
|
||||
|
||||
static ssize_t show_change_ack(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->topology_change_ack);
|
||||
}
|
||||
static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
|
||||
|
||||
static ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->config_pending);
|
||||
}
|
||||
static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
|
||||
|
||||
static ssize_t show_port_state(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", p->state);
|
||||
}
|
||||
static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
|
||||
|
||||
static ssize_t show_message_age_timer(struct net_bridge_port *p,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
|
||||
}
|
||||
static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
|
||||
|
||||
static ssize_t show_forward_delay_timer(struct net_bridge_port *p,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
|
||||
}
|
||||
static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
|
||||
|
||||
static ssize_t show_hold_timer(struct net_bridge_port *p,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
|
||||
}
|
||||
static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
|
||||
|
||||
static struct brport_attribute *brport_attrs[] = {
|
||||
&brport_attr_path_cost,
|
||||
&brport_attr_priority,
|
||||
&brport_attr_port_id,
|
||||
&brport_attr_port_no,
|
||||
&brport_attr_designated_root,
|
||||
&brport_attr_designated_bridge,
|
||||
&brport_attr_designated_port,
|
||||
&brport_attr_designated_cost,
|
||||
&brport_attr_state,
|
||||
&brport_attr_change_ack,
|
||||
&brport_attr_config_pending,
|
||||
&brport_attr_message_age_timer,
|
||||
&brport_attr_forward_delay_timer,
|
||||
&brport_attr_hold_timer,
|
||||
NULL
|
||||
};
|
||||
|
||||
#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
|
||||
#define to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
|
||||
|
||||
static ssize_t brport_show(struct kobject * kobj,
|
||||
struct attribute * attr, char * buf)
|
||||
{
|
||||
struct brport_attribute * brport_attr = to_brport_attr(attr);
|
||||
struct net_bridge_port * p = to_brport(kobj);
|
||||
|
||||
return brport_attr->show(p, buf);
|
||||
}
|
||||
|
||||
static ssize_t brport_store(struct kobject * kobj,
|
||||
struct attribute * attr,
|
||||
const char * buf, size_t count)
|
||||
{
|
||||
struct brport_attribute * brport_attr = to_brport_attr(attr);
|
||||
struct net_bridge_port * p = to_brport(kobj);
|
||||
ssize_t ret = -EINVAL;
|
||||
char *endp;
|
||||
unsigned long val;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp != buf) {
|
||||
rtnl_lock();
|
||||
if (p->dev && p->br && brport_attr->store) {
|
||||
spin_lock_bh(&p->br->lock);
|
||||
ret = brport_attr->store(p, val);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
if (ret == 0)
|
||||
ret = count;
|
||||
}
|
||||
rtnl_unlock();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sysfs_ops brport_sysfs_ops = {
|
||||
.show = brport_show,
|
||||
.store = brport_store,
|
||||
};
|
||||
|
||||
/*
|
||||
* Add sysfs entries to ethernet device added to a bridge.
|
||||
* Creates a brport subdirectory with bridge attributes.
|
||||
* Puts symlink in bridge's brport subdirectory
|
||||
*/
|
||||
int br_sysfs_addif(struct net_bridge_port *p)
|
||||
{
|
||||
struct net_bridge *br = p->br;
|
||||
struct brport_attribute **a;
|
||||
int err;
|
||||
|
||||
err = sysfs_create_link(&p->kobj, &br->dev->dev.kobj,
|
||||
SYSFS_BRIDGE_PORT_LINK);
|
||||
if (err)
|
||||
goto out2;
|
||||
|
||||
for (a = brport_attrs; *a; ++a) {
|
||||
err = sysfs_create_file(&p->kobj, &((*a)->attr));
|
||||
if (err)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
err= sysfs_create_link(&br->ifobj, &p->kobj, p->dev->name);
|
||||
out2:
|
||||
return err;
|
||||
}
|
||||
215
net/bridge/netfilter/Kconfig
Normal file
215
net/bridge/netfilter/Kconfig
Normal file
@@ -0,0 +1,215 @@
|
||||
#
|
||||
# Bridge netfilter configuration
|
||||
#
|
||||
|
||||
menu "Bridge: Netfilter Configuration"
|
||||
depends on BRIDGE && NETFILTER
|
||||
|
||||
config BRIDGE_NF_EBTABLES
|
||||
tristate "Ethernet Bridge tables (ebtables) support"
|
||||
help
|
||||
ebtables is a general, extensible frame/packet identification
|
||||
framework. Say 'Y' or 'M' here if you want to do Ethernet
|
||||
filtering/NAT/brouting on the Ethernet bridge.
|
||||
#
|
||||
# tables
|
||||
#
|
||||
config BRIDGE_EBT_BROUTE
|
||||
tristate "ebt: broute table support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
The ebtables broute table is used to define rules that decide between
|
||||
bridging and routing frames, giving Linux the functionality of a
|
||||
brouter. See the man page for ebtables(8) and examples on the ebtables
|
||||
website.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_T_FILTER
|
||||
tristate "ebt: filter table support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
The ebtables filter table is used to define frame filtering rules at
|
||||
local input, forwarding and local output. See the man page for
|
||||
ebtables(8).
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_T_NAT
|
||||
tristate "ebt: nat table support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
The ebtables nat table is used to define rules that alter the MAC
|
||||
source address (MAC SNAT) or the MAC destination address (MAC DNAT).
|
||||
See the man page for ebtables(8).
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
#
|
||||
# matches
|
||||
#
|
||||
config BRIDGE_EBT_802_3
|
||||
tristate "ebt: 802.3 filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds matching support for 802.3 Ethernet frames.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_AMONG
|
||||
tristate "ebt: among filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the among match, which allows matching the MAC source
|
||||
and/or destination address on a list of addresses. Optionally,
|
||||
MAC/IP address pairs can be matched, f.e. for anti-spoofing rules.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_ARP
|
||||
tristate "ebt: ARP filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the ARP match, which allows ARP and RARP header field
|
||||
filtering.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_IP
|
||||
tristate "ebt: IP filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the IP match, which allows basic IP header field
|
||||
filtering.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_LIMIT
|
||||
tristate "ebt: limit match support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the limit match, which allows you to control
|
||||
the rate at which a rule can be matched. This match is the
|
||||
equivalent of the iptables limit match.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config BRIDGE_EBT_MARK
|
||||
tristate "ebt: mark filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the mark match, which allows matching frames based on
|
||||
the 'nfmark' value in the frame. This can be set by the mark target.
|
||||
This value is the same as the one used in the iptables mark match and
|
||||
target.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_PKTTYPE
|
||||
tristate "ebt: packet type filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the packet type match, which allows matching on the
|
||||
type of packet based on its Ethernet "class" (as determined by
|
||||
the generic networking code): broadcast, multicast,
|
||||
for this host alone or for another host.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_STP
|
||||
tristate "ebt: STP filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the Spanning Tree Protocol match, which
|
||||
allows STP header field filtering.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_VLAN
|
||||
tristate "ebt: 802.1Q VLAN filter support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the 802.1Q vlan match, which allows the filtering of
|
||||
802.1Q vlan fields.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
#
|
||||
# targets
|
||||
#
|
||||
config BRIDGE_EBT_ARPREPLY
|
||||
tristate "ebt: arp reply target support"
|
||||
depends on BRIDGE_NF_EBTABLES && INET
|
||||
help
|
||||
This option adds the arp reply target, which allows
|
||||
automatically sending arp replies to arp requests.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_DNAT
|
||||
tristate "ebt: dnat target support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the MAC DNAT target, which allows altering the MAC
|
||||
destination address of frames.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_MARK_T
|
||||
tristate "ebt: mark target support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the mark target, which allows marking frames by
|
||||
setting the 'nfmark' value in the frame.
|
||||
This value is the same as the one used in the iptables mark match and
|
||||
target.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_REDIRECT
|
||||
tristate "ebt: redirect target support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the MAC redirect target, which allows altering the MAC
|
||||
destination address of a frame to that of the device it arrived on.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_SNAT
|
||||
tristate "ebt: snat target support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the MAC SNAT target, which allows altering the MAC
|
||||
source address of frames.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
#
|
||||
# watchers
|
||||
#
|
||||
config BRIDGE_EBT_LOG
|
||||
tristate "ebt: log support"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option adds the log watcher, that you can use in any rule
|
||||
in any ebtables table. It records info about the frame header
|
||||
to the syslog.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config BRIDGE_EBT_ULOG
|
||||
tristate "ebt: ulog support (OBSOLETE)"
|
||||
depends on BRIDGE_NF_EBTABLES
|
||||
help
|
||||
This option enables the old bridge-specific "ebt_ulog" implementation
|
||||
which has been obsoleted by the new "nfnetlink_log" code (see
|
||||
CONFIG_NETFILTER_NETLINK_LOG).
|
||||
|
||||
This option adds the ulog watcher, that you can use in any rule
|
||||
in any ebtables table. The packet is passed to a userspace
|
||||
logging daemon using netlink multicast sockets. This differs
|
||||
from the log watcher in the sense that the complete packet is
|
||||
sent to userspace instead of a descriptive text and that
|
||||
netlink multicast sockets are used instead of the syslog.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
endmenu
|
||||
32
net/bridge/netfilter/Makefile
Normal file
32
net/bridge/netfilter/Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Makefile for the netfilter modules for Link Layer filtering on a bridge.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
|
||||
|
||||
# tables
|
||||
obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
|
||||
|
||||
#matches
|
||||
obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_VLAN) += ebt_vlan.o
|
||||
|
||||
# targets
|
||||
obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
|
||||
|
||||
# watchers
|
||||
obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
|
||||
obj-$(CONFIG_BRIDGE_EBT_ULOG) += ebt_ulog.o
|
||||
73
net/bridge/netfilter/ebt_802_3.c
Normal file
73
net/bridge/netfilter/ebt_802_3.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 802_3
|
||||
*
|
||||
* Author:
|
||||
* Chris Vitale csv@bluetail.com
|
||||
*
|
||||
* May 2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_802_3.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
|
||||
struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb);
|
||||
__be16 type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
|
||||
|
||||
if (info->bitmask & EBT_802_3_SAP) {
|
||||
if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP))
|
||||
return EBT_NOMATCH;
|
||||
if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
|
||||
if (info->bitmask & EBT_802_3_TYPE) {
|
||||
if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
|
||||
return EBT_NOMATCH;
|
||||
if (FWINV(info->type != type, EBT_802_3_TYPE))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_802_3;
|
||||
static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
|
||||
|
||||
if (datalen < sizeof(struct ebt_802_3_info))
|
||||
return -EINVAL;
|
||||
if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_802_3 =
|
||||
{
|
||||
.name = EBT_802_3_MATCH,
|
||||
.match = ebt_filter_802_3,
|
||||
.check = ebt_802_3_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_802_3_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_802_3);
|
||||
}
|
||||
|
||||
static void __exit ebt_802_3_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_802_3);
|
||||
}
|
||||
|
||||
module_init(ebt_802_3_init);
|
||||
module_exit(ebt_802_3_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
228
net/bridge/netfilter/ebt_among.c
Normal file
228
net/bridge/netfilter/ebt_among.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* ebt_among
|
||||
*
|
||||
* Authors:
|
||||
* Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
|
||||
*
|
||||
* August, 2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_among.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
|
||||
const char *mac, __be32 ip)
|
||||
{
|
||||
/* You may be puzzled as to how this code works.
|
||||
* Some tricks were used, refer to
|
||||
* include/linux/netfilter_bridge/ebt_among.h
|
||||
* as there you can find a solution of this mystery.
|
||||
*/
|
||||
const struct ebt_mac_wormhash_tuple *p;
|
||||
int start, limit, i;
|
||||
uint32_t cmp[2] = { 0, 0 };
|
||||
int key = (const unsigned char) mac[5];
|
||||
|
||||
memcpy(((char *) cmp) + 2, mac, 6);
|
||||
start = wh->table[key];
|
||||
limit = wh->table[key + 1];
|
||||
if (ip) {
|
||||
for (i = start; i < limit; i++) {
|
||||
p = &wh->pool[i];
|
||||
if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
|
||||
if (p->ip == 0 || p->ip == ip) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = start; i < limit; i++) {
|
||||
p = &wh->pool[i];
|
||||
if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
|
||||
if (p->ip == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
|
||||
*wh)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (wh->table[i] > wh->table[i + 1])
|
||||
return -0x100 - i;
|
||||
if (wh->table[i] < 0)
|
||||
return -0x200 - i;
|
||||
if (wh->table[i] > wh->poolsize)
|
||||
return -0x300 - i;
|
||||
}
|
||||
if (wh->table[256] > wh->poolsize)
|
||||
return -0xc00;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ip_dst(const struct sk_buff *skb, __be32 *addr)
|
||||
{
|
||||
if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
|
||||
struct iphdr _iph, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
|
||||
if (ih == NULL)
|
||||
return -1;
|
||||
*addr = ih->daddr;
|
||||
} else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
|
||||
struct arphdr _arph, *ah;
|
||||
__be32 buf, *bp;
|
||||
|
||||
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
|
||||
if (ah == NULL ||
|
||||
ah->ar_pln != sizeof(__be32) ||
|
||||
ah->ar_hln != ETH_ALEN)
|
||||
return -1;
|
||||
bp = skb_header_pointer(skb, sizeof(struct arphdr) +
|
||||
2 * ETH_ALEN + sizeof(__be32),
|
||||
sizeof(__be32), &buf);
|
||||
if (bp == NULL)
|
||||
return -1;
|
||||
*addr = *bp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ip_src(const struct sk_buff *skb, __be32 *addr)
|
||||
{
|
||||
if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
|
||||
struct iphdr _iph, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
|
||||
if (ih == NULL)
|
||||
return -1;
|
||||
*addr = ih->saddr;
|
||||
} else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
|
||||
struct arphdr _arph, *ah;
|
||||
__be32 buf, *bp;
|
||||
|
||||
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
|
||||
if (ah == NULL ||
|
||||
ah->ar_pln != sizeof(__be32) ||
|
||||
ah->ar_hln != ETH_ALEN)
|
||||
return -1;
|
||||
bp = skb_header_pointer(skb, sizeof(struct arphdr) +
|
||||
ETH_ALEN, sizeof(__be32), &buf);
|
||||
if (bp == NULL)
|
||||
return -1;
|
||||
*addr = *bp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ebt_filter_among(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out, const void *data,
|
||||
unsigned int datalen)
|
||||
{
|
||||
struct ebt_among_info *info = (struct ebt_among_info *) data;
|
||||
const char *dmac, *smac;
|
||||
const struct ebt_mac_wormhash *wh_dst, *wh_src;
|
||||
__be32 dip = 0, sip = 0;
|
||||
|
||||
wh_dst = ebt_among_wh_dst(info);
|
||||
wh_src = ebt_among_wh_src(info);
|
||||
|
||||
if (wh_src) {
|
||||
smac = eth_hdr(skb)->h_source;
|
||||
if (get_ip_src(skb, &sip))
|
||||
return EBT_NOMATCH;
|
||||
if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
|
||||
/* we match only if it contains */
|
||||
if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
|
||||
return EBT_NOMATCH;
|
||||
} else {
|
||||
/* we match only if it DOES NOT contain */
|
||||
if (ebt_mac_wormhash_contains(wh_src, smac, sip))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (wh_dst) {
|
||||
dmac = eth_hdr(skb)->h_dest;
|
||||
if (get_ip_dst(skb, &dip))
|
||||
return EBT_NOMATCH;
|
||||
if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
|
||||
/* we match only if it contains */
|
||||
if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
|
||||
return EBT_NOMATCH;
|
||||
} else {
|
||||
/* we match only if it DOES NOT contain */
|
||||
if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static int ebt_among_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data,
|
||||
unsigned int datalen)
|
||||
{
|
||||
struct ebt_among_info *info = (struct ebt_among_info *) data;
|
||||
int expected_length = sizeof(struct ebt_among_info);
|
||||
const struct ebt_mac_wormhash *wh_dst, *wh_src;
|
||||
int err;
|
||||
|
||||
wh_dst = ebt_among_wh_dst(info);
|
||||
wh_src = ebt_among_wh_src(info);
|
||||
expected_length += ebt_mac_wormhash_size(wh_dst);
|
||||
expected_length += ebt_mac_wormhash_size(wh_src);
|
||||
|
||||
if (datalen != EBT_ALIGN(expected_length)) {
|
||||
printk(KERN_WARNING
|
||||
"ebtables: among: wrong size: %d"
|
||||
"against expected %d, rounded to %Zd\n",
|
||||
datalen, expected_length,
|
||||
EBT_ALIGN(expected_length));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
|
||||
printk(KERN_WARNING
|
||||
"ebtables: among: dst integrity fail: %x\n", -err);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
|
||||
printk(KERN_WARNING
|
||||
"ebtables: among: src integrity fail: %x\n", -err);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_among = {
|
||||
.name = EBT_AMONG_MATCH,
|
||||
.match = ebt_filter_among,
|
||||
.check = ebt_among_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_among_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_among);
|
||||
}
|
||||
|
||||
static void __exit ebt_among_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_among);
|
||||
}
|
||||
|
||||
module_init(ebt_among_init);
|
||||
module_exit(ebt_among_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
140
net/bridge/netfilter/ebt_arp.c
Normal file
140
net/bridge/netfilter/ebt_arp.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* ebt_arp
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
* Tim Gardner <timg@tpi.com>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_arp.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_arp_info *info = (struct ebt_arp_info *)data;
|
||||
struct arphdr _arph, *ah;
|
||||
|
||||
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
|
||||
if (ah == NULL)
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
|
||||
ah->ar_op, EBT_ARP_OPCODE))
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
|
||||
ah->ar_hrd, EBT_ARP_HTYPE))
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
|
||||
ah->ar_pro, EBT_ARP_PTYPE))
|
||||
return EBT_NOMATCH;
|
||||
|
||||
if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) {
|
||||
__be32 _addr, *ap;
|
||||
|
||||
/* IPv4 addresses are always 4 bytes */
|
||||
if (ah->ar_pln != sizeof(__be32))
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_ARP_SRC_IP) {
|
||||
ap = skb_header_pointer(skb, sizeof(struct arphdr) +
|
||||
ah->ar_hln, sizeof(_addr),
|
||||
&_addr);
|
||||
if (ap == NULL)
|
||||
return EBT_NOMATCH;
|
||||
if (FWINV(info->saddr != (*ap & info->smsk),
|
||||
EBT_ARP_SRC_IP))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
|
||||
if (info->bitmask & EBT_ARP_DST_IP) {
|
||||
ap = skb_header_pointer(skb, sizeof(struct arphdr) +
|
||||
2*ah->ar_hln+sizeof(__be32),
|
||||
sizeof(_addr), &_addr);
|
||||
if (ap == NULL)
|
||||
return EBT_NOMATCH;
|
||||
if (FWINV(info->daddr != (*ap & info->dmsk),
|
||||
EBT_ARP_DST_IP))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) {
|
||||
unsigned char _mac[ETH_ALEN], *mp;
|
||||
uint8_t verdict, i;
|
||||
|
||||
/* MAC addresses are 6 bytes */
|
||||
if (ah->ar_hln != ETH_ALEN)
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_ARP_SRC_MAC) {
|
||||
mp = skb_header_pointer(skb, sizeof(struct arphdr),
|
||||
sizeof(_mac), &_mac);
|
||||
if (mp == NULL)
|
||||
return EBT_NOMATCH;
|
||||
verdict = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
verdict |= (mp[i] ^ info->smaddr[i]) &
|
||||
info->smmsk[i];
|
||||
if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
|
||||
if (info->bitmask & EBT_ARP_DST_MAC) {
|
||||
mp = skb_header_pointer(skb, sizeof(struct arphdr) +
|
||||
ah->ar_hln + ah->ar_pln,
|
||||
sizeof(_mac), &_mac);
|
||||
if (mp == NULL)
|
||||
return EBT_NOMATCH;
|
||||
verdict = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
verdict |= (mp[i] ^ info->dmaddr[i]) &
|
||||
info->dmmsk[i];
|
||||
if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static int ebt_arp_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_arp_info *info = (struct ebt_arp_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
|
||||
return -EINVAL;
|
||||
if ((e->ethproto != htons(ETH_P_ARP) &&
|
||||
e->ethproto != htons(ETH_P_RARP)) ||
|
||||
e->invflags & EBT_IPROTO)
|
||||
return -EINVAL;
|
||||
if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_arp =
|
||||
{
|
||||
.name = EBT_ARP_MATCH,
|
||||
.match = ebt_filter_arp,
|
||||
.check = ebt_arp_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_arp_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_arp);
|
||||
}
|
||||
|
||||
static void __exit ebt_arp_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_arp);
|
||||
}
|
||||
|
||||
module_init(ebt_arp_init);
|
||||
module_exit(ebt_arp_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
97
net/bridge/netfilter/ebt_arpreply.c
Normal file
97
net/bridge/netfilter/ebt_arpreply.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* ebt_arpreply
|
||||
*
|
||||
* Authors:
|
||||
* Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* August, 2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_arpreply.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/arp.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
|
||||
__be32 _sip, *siptr, _dip, *diptr;
|
||||
struct arphdr _ah, *ap;
|
||||
unsigned char _sha[ETH_ALEN], *shp;
|
||||
struct sk_buff *skb = *pskb;
|
||||
|
||||
ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
|
||||
if (ap == NULL)
|
||||
return EBT_DROP;
|
||||
|
||||
if (ap->ar_op != htons(ARPOP_REQUEST) ||
|
||||
ap->ar_hln != ETH_ALEN ||
|
||||
ap->ar_pro != htons(ETH_P_IP) ||
|
||||
ap->ar_pln != 4)
|
||||
return EBT_CONTINUE;
|
||||
|
||||
shp = skb_header_pointer(skb, sizeof(_ah), ETH_ALEN, &_sha);
|
||||
if (shp == NULL)
|
||||
return EBT_DROP;
|
||||
|
||||
siptr = skb_header_pointer(skb, sizeof(_ah) + ETH_ALEN,
|
||||
sizeof(_sip), &_sip);
|
||||
if (siptr == NULL)
|
||||
return EBT_DROP;
|
||||
|
||||
diptr = skb_header_pointer(skb,
|
||||
sizeof(_ah) + 2 * ETH_ALEN + sizeof(_sip),
|
||||
sizeof(_dip), &_dip);
|
||||
if (diptr == NULL)
|
||||
return EBT_DROP;
|
||||
|
||||
arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)in,
|
||||
*diptr, shp, info->mac, shp);
|
||||
|
||||
return info->target;
|
||||
}
|
||||
|
||||
static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
|
||||
return -EINVAL;
|
||||
if (BASE_CHAIN && info->target == EBT_RETURN)
|
||||
return -EINVAL;
|
||||
if (e->ethproto != htons(ETH_P_ARP) ||
|
||||
e->invflags & EBT_IPROTO)
|
||||
return -EINVAL;
|
||||
CLEAR_BASE_CHAIN_BIT;
|
||||
if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_target reply_target =
|
||||
{
|
||||
.name = EBT_ARPREPLY_TARGET,
|
||||
.target = ebt_target_reply,
|
||||
.check = ebt_target_reply_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_arpreply_init(void)
|
||||
{
|
||||
return ebt_register_target(&reply_target);
|
||||
}
|
||||
|
||||
static void __exit ebt_arpreply_fini(void)
|
||||
{
|
||||
ebt_unregister_target(&reply_target);
|
||||
}
|
||||
|
||||
module_init(ebt_arpreply_init);
|
||||
module_exit(ebt_arpreply_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
76
net/bridge/netfilter/ebt_dnat.c
Normal file
76
net/bridge/netfilter/ebt_dnat.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* ebt_dnat
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* June, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_nat.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_nat_info *info = (struct ebt_nat_info *)data;
|
||||
|
||||
if (skb_shared(*pskb) || skb_cloned(*pskb)) {
|
||||
struct sk_buff *nskb;
|
||||
|
||||
nskb = skb_copy(*pskb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return NF_DROP;
|
||||
if ((*pskb)->sk)
|
||||
skb_set_owner_w(nskb, (*pskb)->sk);
|
||||
kfree_skb(*pskb);
|
||||
*pskb = nskb;
|
||||
}
|
||||
memcpy(eth_hdr(*pskb)->h_dest, info->mac, ETH_ALEN);
|
||||
return info->target;
|
||||
}
|
||||
|
||||
static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_nat_info *info = (struct ebt_nat_info *)data;
|
||||
|
||||
if (BASE_CHAIN && info->target == EBT_RETURN)
|
||||
return -EINVAL;
|
||||
CLEAR_BASE_CHAIN_BIT;
|
||||
if ( (strcmp(tablename, "nat") ||
|
||||
(hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
|
||||
(strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
|
||||
return -EINVAL;
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
|
||||
return -EINVAL;
|
||||
if (INVALID_TARGET)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_target dnat =
|
||||
{
|
||||
.name = EBT_DNAT_TARGET,
|
||||
.target = ebt_target_dnat,
|
||||
.check = ebt_target_dnat_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_dnat_init(void)
|
||||
{
|
||||
return ebt_register_target(&dnat);
|
||||
}
|
||||
|
||||
static void __exit ebt_dnat_fini(void)
|
||||
{
|
||||
ebt_unregister_target(&dnat);
|
||||
}
|
||||
|
||||
module_init(ebt_dnat_init);
|
||||
module_exit(ebt_dnat_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
128
net/bridge/netfilter/ebt_ip.c
Normal file
128
net/bridge/netfilter/ebt_ip.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* ebt_ip
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
* Changes:
|
||||
* added ip-sport and ip-dport
|
||||
* Innominate Security Technologies AG <mhopf@innominate.com>
|
||||
* September, 2002
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_ip.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
struct tcpudphdr {
|
||||
__be16 src;
|
||||
__be16 dst;
|
||||
};
|
||||
|
||||
static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const void *data,
|
||||
unsigned int datalen)
|
||||
{
|
||||
struct ebt_ip_info *info = (struct ebt_ip_info *)data;
|
||||
struct iphdr _iph, *ih;
|
||||
struct tcpudphdr _ports, *pptr;
|
||||
|
||||
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
|
||||
if (ih == NULL)
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_IP_TOS &&
|
||||
FWINV(info->tos != ih->tos, EBT_IP_TOS))
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_IP_SOURCE &&
|
||||
FWINV((ih->saddr & info->smsk) !=
|
||||
info->saddr, EBT_IP_SOURCE))
|
||||
return EBT_NOMATCH;
|
||||
if ((info->bitmask & EBT_IP_DEST) &&
|
||||
FWINV((ih->daddr & info->dmsk) !=
|
||||
info->daddr, EBT_IP_DEST))
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_IP_PROTO) {
|
||||
if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO))
|
||||
return EBT_NOMATCH;
|
||||
if (!(info->bitmask & EBT_IP_DPORT) &&
|
||||
!(info->bitmask & EBT_IP_SPORT))
|
||||
return EBT_MATCH;
|
||||
if (ntohs(ih->frag_off) & IP_OFFSET)
|
||||
return EBT_NOMATCH;
|
||||
pptr = skb_header_pointer(skb, ih->ihl*4,
|
||||
sizeof(_ports), &_ports);
|
||||
if (pptr == NULL)
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_IP_DPORT) {
|
||||
u32 dst = ntohs(pptr->dst);
|
||||
if (FWINV(dst < info->dport[0] ||
|
||||
dst > info->dport[1],
|
||||
EBT_IP_DPORT))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_IP_SPORT) {
|
||||
u32 src = ntohs(pptr->src);
|
||||
if (FWINV(src < info->sport[0] ||
|
||||
src > info->sport[1],
|
||||
EBT_IP_SPORT))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
}
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static int ebt_ip_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_ip_info *info = (struct ebt_ip_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
|
||||
return -EINVAL;
|
||||
if (e->ethproto != htons(ETH_P_IP) ||
|
||||
e->invflags & EBT_IPROTO)
|
||||
return -EINVAL;
|
||||
if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
|
||||
return -EINVAL;
|
||||
if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
|
||||
if (info->invflags & EBT_IP_PROTO)
|
||||
return -EINVAL;
|
||||
if (info->protocol != IPPROTO_TCP &&
|
||||
info->protocol != IPPROTO_UDP &&
|
||||
info->protocol != IPPROTO_UDPLITE &&
|
||||
info->protocol != IPPROTO_SCTP &&
|
||||
info->protocol != IPPROTO_DCCP)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
|
||||
return -EINVAL;
|
||||
if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_ip =
|
||||
{
|
||||
.name = EBT_IP_MATCH,
|
||||
.match = ebt_filter_ip,
|
||||
.check = ebt_ip_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_ip_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_ip);
|
||||
}
|
||||
|
||||
static void __exit ebt_ip_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_ip);
|
||||
}
|
||||
|
||||
module_init(ebt_ip_init);
|
||||
module_exit(ebt_ip_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
113
net/bridge/netfilter/ebt_limit.c
Normal file
113
net/bridge/netfilter/ebt_limit.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* ebt_limit
|
||||
*
|
||||
* Authors:
|
||||
* Tom Marshall <tommy@home.tig-grr.com>
|
||||
*
|
||||
* Mostly copied from netfilter's ipt_limit.c, see that file for
|
||||
* more explanation
|
||||
*
|
||||
* September, 2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_limit.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static DEFINE_SPINLOCK(limit_lock);
|
||||
|
||||
#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24))
|
||||
|
||||
#define _POW2_BELOW2(x) ((x)|((x)>>1))
|
||||
#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2))
|
||||
#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4))
|
||||
#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8))
|
||||
#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16))
|
||||
#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1)
|
||||
|
||||
#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
|
||||
|
||||
static int ebt_limit_match(const struct sk_buff *skb,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_limit_info *info = (struct ebt_limit_info *)data;
|
||||
unsigned long now = jiffies;
|
||||
|
||||
spin_lock_bh(&limit_lock);
|
||||
info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
|
||||
if (info->credit > info->credit_cap)
|
||||
info->credit = info->credit_cap;
|
||||
|
||||
if (info->credit >= info->cost) {
|
||||
/* We're not limited. */
|
||||
info->credit -= info->cost;
|
||||
spin_unlock_bh(&limit_lock);
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&limit_lock);
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
|
||||
/* Precision saver. */
|
||||
static u_int32_t
|
||||
user2credits(u_int32_t user)
|
||||
{
|
||||
/* If multiplying would overflow... */
|
||||
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
||||
/* Divide first. */
|
||||
return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
|
||||
|
||||
return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
|
||||
}
|
||||
|
||||
static int ebt_limit_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_limit_info *info = (struct ebt_limit_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Check for overflow. */
|
||||
if (info->burst == 0 ||
|
||||
user2credits(info->avg * info->burst) < user2credits(info->avg)) {
|
||||
printk("Overflow in ebt_limit, try lower: %u/%u\n",
|
||||
info->avg, info->burst);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
|
||||
info->prev = jiffies;
|
||||
info->credit = user2credits(info->avg * info->burst);
|
||||
info->credit_cap = user2credits(info->avg * info->burst);
|
||||
info->cost = user2credits(info->avg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match ebt_limit_reg =
|
||||
{
|
||||
.name = EBT_LIMIT_MATCH,
|
||||
.match = ebt_limit_match,
|
||||
.check = ebt_limit_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_limit_init(void)
|
||||
{
|
||||
return ebt_register_match(&ebt_limit_reg);
|
||||
}
|
||||
|
||||
static void __exit ebt_limit_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&ebt_limit_reg);
|
||||
}
|
||||
|
||||
module_init(ebt_limit_init);
|
||||
module_exit(ebt_limit_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
217
net/bridge/netfilter/ebt_log.c
Normal file
217
net/bridge/netfilter/ebt_log.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* ebt_log
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
* Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_log.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static DEFINE_SPINLOCK(ebt_log_lock);
|
||||
|
||||
static int ebt_log_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_log_info *info = (struct ebt_log_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
|
||||
return -EINVAL;
|
||||
if (info->bitmask & ~EBT_LOG_MASK)
|
||||
return -EINVAL;
|
||||
if (info->loglevel >= 8)
|
||||
return -EINVAL;
|
||||
info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tcpudphdr
|
||||
{
|
||||
__be16 src;
|
||||
__be16 dst;
|
||||
};
|
||||
|
||||
struct arppayload
|
||||
{
|
||||
unsigned char mac_src[ETH_ALEN];
|
||||
unsigned char ip_src[4];
|
||||
unsigned char mac_dst[ETH_ALEN];
|
||||
unsigned char ip_dst[4];
|
||||
};
|
||||
|
||||
static void print_MAC(unsigned char *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ETH_ALEN; i++, p++)
|
||||
printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
|
||||
}
|
||||
|
||||
#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
|
||||
static void
|
||||
ebt_log_packet(unsigned int pf, unsigned int hooknum,
|
||||
const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const struct nf_loginfo *loginfo,
|
||||
const char *prefix)
|
||||
{
|
||||
unsigned int bitmask;
|
||||
|
||||
spin_lock_bh(&ebt_log_lock);
|
||||
printk("<%c>%s IN=%s OUT=%s MAC source = ", '0' + loginfo->u.log.level,
|
||||
prefix, in ? in->name : "", out ? out->name : "");
|
||||
|
||||
print_MAC(eth_hdr(skb)->h_source);
|
||||
printk("MAC dest = ");
|
||||
print_MAC(eth_hdr(skb)->h_dest);
|
||||
|
||||
printk("proto = 0x%04x", ntohs(eth_hdr(skb)->h_proto));
|
||||
|
||||
if (loginfo->type == NF_LOG_TYPE_LOG)
|
||||
bitmask = loginfo->u.log.logflags;
|
||||
else
|
||||
bitmask = NF_LOG_MASK;
|
||||
|
||||
if ((bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto ==
|
||||
htons(ETH_P_IP)){
|
||||
struct iphdr _iph, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
|
||||
if (ih == NULL) {
|
||||
printk(" INCOMPLETE IP header");
|
||||
goto out;
|
||||
}
|
||||
printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u, IP "
|
||||
"tos=0x%02X, IP proto=%d", NIPQUAD(ih->saddr),
|
||||
NIPQUAD(ih->daddr), ih->tos, ih->protocol);
|
||||
if (ih->protocol == IPPROTO_TCP ||
|
||||
ih->protocol == IPPROTO_UDP ||
|
||||
ih->protocol == IPPROTO_UDPLITE ||
|
||||
ih->protocol == IPPROTO_SCTP ||
|
||||
ih->protocol == IPPROTO_DCCP) {
|
||||
struct tcpudphdr _ports, *pptr;
|
||||
|
||||
pptr = skb_header_pointer(skb, ih->ihl*4,
|
||||
sizeof(_ports), &_ports);
|
||||
if (pptr == NULL) {
|
||||
printk(" INCOMPLETE TCP/UDP header");
|
||||
goto out;
|
||||
}
|
||||
printk(" SPT=%u DPT=%u", ntohs(pptr->src),
|
||||
ntohs(pptr->dst));
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((bitmask & EBT_LOG_ARP) &&
|
||||
((eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) ||
|
||||
(eth_hdr(skb)->h_proto == htons(ETH_P_RARP)))) {
|
||||
struct arphdr _arph, *ah;
|
||||
|
||||
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
|
||||
if (ah == NULL) {
|
||||
printk(" INCOMPLETE ARP header");
|
||||
goto out;
|
||||
}
|
||||
printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
|
||||
ntohs(ah->ar_hrd), ntohs(ah->ar_pro),
|
||||
ntohs(ah->ar_op));
|
||||
|
||||
/* If it's for Ethernet and the lengths are OK,
|
||||
* then log the ARP payload */
|
||||
if (ah->ar_hrd == htons(1) &&
|
||||
ah->ar_hln == ETH_ALEN &&
|
||||
ah->ar_pln == sizeof(__be32)) {
|
||||
struct arppayload _arpp, *ap;
|
||||
|
||||
ap = skb_header_pointer(skb, sizeof(_arph),
|
||||
sizeof(_arpp), &_arpp);
|
||||
if (ap == NULL) {
|
||||
printk(" INCOMPLETE ARP payload");
|
||||
goto out;
|
||||
}
|
||||
printk(" ARP MAC SRC=");
|
||||
print_MAC(ap->mac_src);
|
||||
printk(" ARP IP SRC=%u.%u.%u.%u",
|
||||
myNIPQUAD(ap->ip_src));
|
||||
printk(" ARP MAC DST=");
|
||||
print_MAC(ap->mac_dst);
|
||||
printk(" ARP IP DST=%u.%u.%u.%u",
|
||||
myNIPQUAD(ap->ip_dst));
|
||||
}
|
||||
}
|
||||
out:
|
||||
printk("\n");
|
||||
spin_unlock_bh(&ebt_log_lock);
|
||||
|
||||
}
|
||||
|
||||
static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_log_info *info = (struct ebt_log_info *)data;
|
||||
struct nf_loginfo li;
|
||||
|
||||
li.type = NF_LOG_TYPE_LOG;
|
||||
li.u.log.level = info->loglevel;
|
||||
li.u.log.logflags = info->bitmask;
|
||||
|
||||
if (info->bitmask & EBT_LOG_NFLOG)
|
||||
nf_log_packet(PF_BRIDGE, hooknr, skb, in, out, &li,
|
||||
"%s", info->prefix);
|
||||
else
|
||||
ebt_log_packet(PF_BRIDGE, hooknr, skb, in, out, &li,
|
||||
info->prefix);
|
||||
}
|
||||
|
||||
static struct ebt_watcher log =
|
||||
{
|
||||
.name = EBT_LOG_WATCHER,
|
||||
.watcher = ebt_log,
|
||||
.check = ebt_log_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct nf_logger ebt_log_logger = {
|
||||
.name = "ebt_log",
|
||||
.logfn = &ebt_log_packet,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_log_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ebt_register_watcher(&log);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (nf_log_register(PF_BRIDGE, &ebt_log_logger) < 0) {
|
||||
printk(KERN_WARNING "ebt_log: not logging via system console "
|
||||
"since somebody else already registered for PF_INET\n");
|
||||
/* we cannot make module load fail here, since otherwise
|
||||
* ebtables userspace would abort */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ebt_log_fini(void)
|
||||
{
|
||||
nf_log_unregister(&ebt_log_logger);
|
||||
ebt_unregister_watcher(&log);
|
||||
}
|
||||
|
||||
module_init(ebt_log_init);
|
||||
module_exit(ebt_log_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
80
net/bridge/netfilter/ebt_mark.c
Normal file
80
net/bridge/netfilter/ebt_mark.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* ebt_mark
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* July, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
/* The mark target can be used in any chain,
|
||||
* I believe adding a mangle table just for marking is total overkill.
|
||||
* Marking a frame doesn't really change anything in the frame anyway.
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_mark_t.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
|
||||
int action = info->target & -16;
|
||||
|
||||
if (action == MARK_SET_VALUE)
|
||||
(*pskb)->mark = info->mark;
|
||||
else if (action == MARK_OR_VALUE)
|
||||
(*pskb)->mark |= info->mark;
|
||||
else if (action == MARK_AND_VALUE)
|
||||
(*pskb)->mark &= info->mark;
|
||||
else
|
||||
(*pskb)->mark ^= info->mark;
|
||||
|
||||
return info->target | ~EBT_VERDICT_BITS;
|
||||
}
|
||||
|
||||
static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
|
||||
int tmp;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
|
||||
return -EINVAL;
|
||||
tmp = info->target | ~EBT_VERDICT_BITS;
|
||||
if (BASE_CHAIN && tmp == EBT_RETURN)
|
||||
return -EINVAL;
|
||||
CLEAR_BASE_CHAIN_BIT;
|
||||
if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0)
|
||||
return -EINVAL;
|
||||
tmp = info->target & ~EBT_VERDICT_BITS;
|
||||
if (tmp != MARK_SET_VALUE && tmp != MARK_OR_VALUE &&
|
||||
tmp != MARK_AND_VALUE && tmp != MARK_XOR_VALUE)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_target mark_target =
|
||||
{
|
||||
.name = EBT_MARK_TARGET,
|
||||
.target = ebt_target_mark,
|
||||
.check = ebt_target_mark_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_mark_init(void)
|
||||
{
|
||||
return ebt_register_target(&mark_target);
|
||||
}
|
||||
|
||||
static void __exit ebt_mark_fini(void)
|
||||
{
|
||||
ebt_unregister_target(&mark_target);
|
||||
}
|
||||
|
||||
module_init(ebt_mark_init);
|
||||
module_exit(ebt_mark_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
62
net/bridge/netfilter/ebt_mark_m.c
Normal file
62
net/bridge/netfilter/ebt_mark_m.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* ebt_mark_m
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* July, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_mark_m.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_filter_mark(const struct sk_buff *skb,
|
||||
const struct net_device *in, const struct net_device *out, const void *data,
|
||||
unsigned int datalen)
|
||||
{
|
||||
struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
|
||||
|
||||
if (info->bitmask & EBT_MARK_OR)
|
||||
return !(!!(skb->mark & info->mask) ^ info->invert);
|
||||
return !(((skb->mark & info->mask) == info->mark) ^ info->invert);
|
||||
}
|
||||
|
||||
static int ebt_mark_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
|
||||
return -EINVAL;
|
||||
if (info->bitmask & ~EBT_MARK_MASK)
|
||||
return -EINVAL;
|
||||
if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
|
||||
return -EINVAL;
|
||||
if (!info->bitmask)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_mark =
|
||||
{
|
||||
.name = EBT_MARK_MATCH,
|
||||
.match = ebt_filter_mark,
|
||||
.check = ebt_mark_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_mark_m_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_mark);
|
||||
}
|
||||
|
||||
static void __exit ebt_mark_m_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_mark);
|
||||
}
|
||||
|
||||
module_init(ebt_mark_m_init);
|
||||
module_exit(ebt_mark_m_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
59
net/bridge/netfilter/ebt_pkttype.c
Normal file
59
net/bridge/netfilter/ebt_pkttype.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* ebt_pkttype
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* April, 2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_pkttype.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int ebt_filter_pkttype(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const void *data,
|
||||
unsigned int datalen)
|
||||
{
|
||||
struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
|
||||
|
||||
return (skb->pkt_type != info->pkt_type) ^ info->invert;
|
||||
}
|
||||
|
||||
static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
|
||||
return -EINVAL;
|
||||
if (info->invert != 0 && info->invert != 1)
|
||||
return -EINVAL;
|
||||
/* Allow any pkt_type value */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_pkttype =
|
||||
{
|
||||
.name = EBT_PKTTYPE_MATCH,
|
||||
.match = ebt_filter_pkttype,
|
||||
.check = ebt_pkttype_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_pkttype_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_pkttype);
|
||||
}
|
||||
|
||||
static void __exit ebt_pkttype_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_pkttype);
|
||||
}
|
||||
|
||||
module_init(ebt_pkttype_init);
|
||||
module_exit(ebt_pkttype_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
81
net/bridge/netfilter/ebt_redirect.c
Normal file
81
net/bridge/netfilter/ebt_redirect.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* ebt_redirect
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_redirect.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/sock.h>
|
||||
#include "../br_private.h"
|
||||
|
||||
static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
|
||||
|
||||
if (skb_shared(*pskb) || skb_cloned(*pskb)) {
|
||||
struct sk_buff *nskb;
|
||||
|
||||
nskb = skb_copy(*pskb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return NF_DROP;
|
||||
if ((*pskb)->sk)
|
||||
skb_set_owner_w(nskb, (*pskb)->sk);
|
||||
kfree_skb(*pskb);
|
||||
*pskb = nskb;
|
||||
}
|
||||
if (hooknr != NF_BR_BROUTING)
|
||||
memcpy(eth_hdr(*pskb)->h_dest,
|
||||
in->br_port->br->dev->dev_addr, ETH_ALEN);
|
||||
else
|
||||
memcpy(eth_hdr(*pskb)->h_dest, in->dev_addr, ETH_ALEN);
|
||||
(*pskb)->pkt_type = PACKET_HOST;
|
||||
return info->target;
|
||||
}
|
||||
|
||||
static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
|
||||
return -EINVAL;
|
||||
if (BASE_CHAIN && info->target == EBT_RETURN)
|
||||
return -EINVAL;
|
||||
CLEAR_BASE_CHAIN_BIT;
|
||||
if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
|
||||
(strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
|
||||
return -EINVAL;
|
||||
if (INVALID_TARGET)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_target redirect_target =
|
||||
{
|
||||
.name = EBT_REDIRECT_TARGET,
|
||||
.target = ebt_target_redirect,
|
||||
.check = ebt_target_redirect_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_redirect_init(void)
|
||||
{
|
||||
return ebt_register_target(&redirect_target);
|
||||
}
|
||||
|
||||
static void __exit ebt_redirect_fini(void)
|
||||
{
|
||||
ebt_unregister_target(&redirect_target);
|
||||
}
|
||||
|
||||
module_init(ebt_redirect_init);
|
||||
module_exit(ebt_redirect_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
97
net/bridge/netfilter/ebt_snat.c
Normal file
97
net/bridge/netfilter/ebt_snat.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* ebt_snat
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* June, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_nat.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_nat_info *info = (struct ebt_nat_info *) data;
|
||||
|
||||
if (skb_shared(*pskb) || skb_cloned(*pskb)) {
|
||||
struct sk_buff *nskb;
|
||||
|
||||
nskb = skb_copy(*pskb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return NF_DROP;
|
||||
if ((*pskb)->sk)
|
||||
skb_set_owner_w(nskb, (*pskb)->sk);
|
||||
kfree_skb(*pskb);
|
||||
*pskb = nskb;
|
||||
}
|
||||
memcpy(eth_hdr(*pskb)->h_source, info->mac, ETH_ALEN);
|
||||
if (!(info->target & NAT_ARP_BIT) &&
|
||||
eth_hdr(*pskb)->h_proto == htons(ETH_P_ARP)) {
|
||||
struct arphdr _ah, *ap;
|
||||
|
||||
ap = skb_header_pointer(*pskb, 0, sizeof(_ah), &_ah);
|
||||
if (ap == NULL)
|
||||
return EBT_DROP;
|
||||
if (ap->ar_hln != ETH_ALEN)
|
||||
goto out;
|
||||
if (skb_store_bits(*pskb, sizeof(_ah), info->mac,ETH_ALEN))
|
||||
return EBT_DROP;
|
||||
}
|
||||
out:
|
||||
return info->target | ~EBT_VERDICT_BITS;
|
||||
}
|
||||
|
||||
static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_nat_info *info = (struct ebt_nat_info *) data;
|
||||
int tmp;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
|
||||
return -EINVAL;
|
||||
tmp = info->target | ~EBT_VERDICT_BITS;
|
||||
if (BASE_CHAIN && tmp == EBT_RETURN)
|
||||
return -EINVAL;
|
||||
CLEAR_BASE_CHAIN_BIT;
|
||||
if (strcmp(tablename, "nat"))
|
||||
return -EINVAL;
|
||||
if (hookmask & ~(1 << NF_BR_POST_ROUTING))
|
||||
return -EINVAL;
|
||||
|
||||
if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0)
|
||||
return -EINVAL;
|
||||
tmp = info->target | EBT_VERDICT_BITS;
|
||||
if ((tmp & ~NAT_ARP_BIT) != ~NAT_ARP_BIT)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_target snat =
|
||||
{
|
||||
.name = EBT_SNAT_TARGET,
|
||||
.target = ebt_target_snat,
|
||||
.check = ebt_target_snat_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_snat_init(void)
|
||||
{
|
||||
return ebt_register_target(&snat);
|
||||
}
|
||||
|
||||
static void __exit ebt_snat_fini(void)
|
||||
{
|
||||
ebt_unregister_target(&snat);
|
||||
}
|
||||
|
||||
module_init(ebt_snat_init);
|
||||
module_exit(ebt_snat_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
195
net/bridge/netfilter/ebt_stp.c
Normal file
195
net/bridge/netfilter/ebt_stp.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* ebt_stp
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
* Stephen Hemminger <shemminger@osdl.org>
|
||||
*
|
||||
* July, 2003
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_stp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define BPDU_TYPE_CONFIG 0
|
||||
#define BPDU_TYPE_TCN 0x80
|
||||
|
||||
struct stp_header {
|
||||
uint8_t dsap;
|
||||
uint8_t ssap;
|
||||
uint8_t ctrl;
|
||||
uint8_t pid;
|
||||
uint8_t vers;
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
struct stp_config_pdu {
|
||||
uint8_t flags;
|
||||
uint8_t root[8];
|
||||
uint8_t root_cost[4];
|
||||
uint8_t sender[8];
|
||||
uint8_t port[2];
|
||||
uint8_t msg_age[2];
|
||||
uint8_t max_age[2];
|
||||
uint8_t hello_time[2];
|
||||
uint8_t forward_delay[2];
|
||||
};
|
||||
|
||||
#define NR16(p) (p[0] << 8 | p[1])
|
||||
#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
|
||||
|
||||
static int ebt_filter_config(struct ebt_stp_info *info,
|
||||
struct stp_config_pdu *stpc)
|
||||
{
|
||||
struct ebt_stp_config_info *c;
|
||||
uint16_t v16;
|
||||
uint32_t v32;
|
||||
int verdict, i;
|
||||
|
||||
c = &info->config;
|
||||
if ((info->bitmask & EBT_STP_FLAGS) &&
|
||||
FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
|
||||
return EBT_NOMATCH;
|
||||
if (info->bitmask & EBT_STP_ROOTPRIO) {
|
||||
v16 = NR16(stpc->root);
|
||||
if (FWINV(v16 < c->root_priol ||
|
||||
v16 > c->root_priou, EBT_STP_ROOTPRIO))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_ROOTADDR) {
|
||||
verdict = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
|
||||
c->root_addrmsk[i];
|
||||
if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_ROOTCOST) {
|
||||
v32 = NR32(stpc->root_cost);
|
||||
if (FWINV(v32 < c->root_costl ||
|
||||
v32 > c->root_costu, EBT_STP_ROOTCOST))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_SENDERPRIO) {
|
||||
v16 = NR16(stpc->sender);
|
||||
if (FWINV(v16 < c->sender_priol ||
|
||||
v16 > c->sender_priou, EBT_STP_SENDERPRIO))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_SENDERADDR) {
|
||||
verdict = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
|
||||
c->sender_addrmsk[i];
|
||||
if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_PORT) {
|
||||
v16 = NR16(stpc->port);
|
||||
if (FWINV(v16 < c->portl ||
|
||||
v16 > c->portu, EBT_STP_PORT))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_MSGAGE) {
|
||||
v16 = NR16(stpc->msg_age);
|
||||
if (FWINV(v16 < c->msg_agel ||
|
||||
v16 > c->msg_ageu, EBT_STP_MSGAGE))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_MAXAGE) {
|
||||
v16 = NR16(stpc->max_age);
|
||||
if (FWINV(v16 < c->max_agel ||
|
||||
v16 > c->max_ageu, EBT_STP_MAXAGE))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_HELLOTIME) {
|
||||
v16 = NR16(stpc->hello_time);
|
||||
if (FWINV(v16 < c->hello_timel ||
|
||||
v16 > c->hello_timeu, EBT_STP_HELLOTIME))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
if (info->bitmask & EBT_STP_FWDD) {
|
||||
v16 = NR16(stpc->forward_delay);
|
||||
if (FWINV(v16 < c->forward_delayl ||
|
||||
v16 > c->forward_delayu, EBT_STP_FWDD))
|
||||
return EBT_NOMATCH;
|
||||
}
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_stp_info *info = (struct ebt_stp_info *)data;
|
||||
struct stp_header _stph, *sp;
|
||||
uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
|
||||
|
||||
sp = skb_header_pointer(skb, 0, sizeof(_stph), &_stph);
|
||||
if (sp == NULL)
|
||||
return EBT_NOMATCH;
|
||||
|
||||
/* The stp code only considers these */
|
||||
if (memcmp(sp, header, sizeof(header)))
|
||||
return EBT_NOMATCH;
|
||||
|
||||
if (info->bitmask & EBT_STP_TYPE
|
||||
&& FWINV(info->type != sp->type, EBT_STP_TYPE))
|
||||
return EBT_NOMATCH;
|
||||
|
||||
if (sp->type == BPDU_TYPE_CONFIG &&
|
||||
info->bitmask & EBT_STP_CONFIG_MASK) {
|
||||
struct stp_config_pdu _stpc, *st;
|
||||
|
||||
st = skb_header_pointer(skb, sizeof(_stph),
|
||||
sizeof(_stpc), &_stpc);
|
||||
if (st == NULL)
|
||||
return EBT_NOMATCH;
|
||||
return ebt_filter_config(info, st);
|
||||
}
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static int ebt_stp_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_stp_info *info = (struct ebt_stp_info *)data;
|
||||
int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
|
||||
uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
|
||||
uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
|
||||
!(info->bitmask & EBT_STP_MASK))
|
||||
return -EINVAL;
|
||||
if (datalen != len)
|
||||
return -EINVAL;
|
||||
/* Make sure the match only receives stp frames */
|
||||
if (compare_ether_addr(e->destmac, bridge_ula) ||
|
||||
compare_ether_addr(e->destmsk, msk) || !(e->bitmask & EBT_DESTMAC))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_stp =
|
||||
{
|
||||
.name = EBT_STP_MATCH,
|
||||
.match = ebt_filter_stp,
|
||||
.check = ebt_stp_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_stp_init(void)
|
||||
{
|
||||
return ebt_register_match(&filter_stp);
|
||||
}
|
||||
|
||||
static void __exit ebt_stp_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_stp);
|
||||
}
|
||||
|
||||
module_init(ebt_stp_init);
|
||||
module_exit(ebt_stp_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
347
net/bridge/netfilter/ebt_ulog.c
Normal file
347
net/bridge/netfilter/ebt_ulog.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* netfilter module for userspace bridged Ethernet frames logging daemons
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
* Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* November, 2004
|
||||
*
|
||||
* Based on ipt_ULOG.c, which is
|
||||
* (C) 2000-2002 by Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* This module accepts two parameters:
|
||||
*
|
||||
* nlbufsiz:
|
||||
* The parameter specifies how big the buffer for each netlink multicast
|
||||
* group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will
|
||||
* get accumulated in the kernel until they are sent to userspace. It is
|
||||
* NOT possible to allocate more than 128kB, and it is strongly discouraged,
|
||||
* because atomically allocating 128kB inside the network rx softirq is not
|
||||
* reliable. Please also keep in mind that this buffer size is allocated for
|
||||
* each nlgroup you are using, so the total kernel memory usage increases
|
||||
* by that factor.
|
||||
*
|
||||
* flushtimeout:
|
||||
* Specify, after how many hundredths of a second the queue should be
|
||||
* flushed even if it is not full yet.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_ulog.h>
|
||||
#include <net/sock.h>
|
||||
#include "../br_private.h"
|
||||
|
||||
#define PRINTR(format, args...) do { if (net_ratelimit()) \
|
||||
printk(format , ## args); } while (0)
|
||||
|
||||
static unsigned int nlbufsiz = NLMSG_GOODSIZE;
|
||||
module_param(nlbufsiz, uint, 0600);
|
||||
MODULE_PARM_DESC(nlbufsiz, "netlink buffer size (number of bytes) "
|
||||
"(defaults to 4096)");
|
||||
|
||||
static unsigned int flushtimeout = 10;
|
||||
module_param(flushtimeout, uint, 0600);
|
||||
MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths ofa second) "
|
||||
"(defaults to 10)");
|
||||
|
||||
typedef struct {
|
||||
unsigned int qlen; /* number of nlmsgs' in the skb */
|
||||
struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */
|
||||
struct sk_buff *skb; /* the pre-allocated skb */
|
||||
struct timer_list timer; /* the timer function */
|
||||
spinlock_t lock; /* the per-queue lock */
|
||||
} ebt_ulog_buff_t;
|
||||
|
||||
static ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS];
|
||||
static struct sock *ebtulognl;
|
||||
|
||||
/* send one ulog_buff_t to userspace */
|
||||
static void ulog_send(unsigned int nlgroup)
|
||||
{
|
||||
ebt_ulog_buff_t *ub = &ulog_buffers[nlgroup];
|
||||
|
||||
if (timer_pending(&ub->timer))
|
||||
del_timer(&ub->timer);
|
||||
|
||||
if (!ub->skb)
|
||||
return;
|
||||
|
||||
/* last nlmsg needs NLMSG_DONE */
|
||||
if (ub->qlen > 1)
|
||||
ub->lastnlh->nlmsg_type = NLMSG_DONE;
|
||||
|
||||
NETLINK_CB(ub->skb).dst_group = nlgroup + 1;
|
||||
netlink_broadcast(ebtulognl, ub->skb, 0, nlgroup + 1, GFP_ATOMIC);
|
||||
|
||||
ub->qlen = 0;
|
||||
ub->skb = NULL;
|
||||
}
|
||||
|
||||
/* timer function to flush queue in flushtimeout time */
|
||||
static void ulog_timer(unsigned long data)
|
||||
{
|
||||
spin_lock_bh(&ulog_buffers[data].lock);
|
||||
if (ulog_buffers[data].skb)
|
||||
ulog_send(data);
|
||||
spin_unlock_bh(&ulog_buffers[data].lock);
|
||||
}
|
||||
|
||||
static struct sk_buff *ulog_alloc_skb(unsigned int size)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned int n;
|
||||
|
||||
n = max(size, nlbufsiz);
|
||||
skb = alloc_skb(n, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
PRINTR(KERN_ERR "ebt_ulog: can't alloc whole buffer "
|
||||
"of size %ub!\n", n);
|
||||
if (n > size) {
|
||||
/* try to allocate only as much as we need for
|
||||
* current packet */
|
||||
skb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
PRINTR(KERN_ERR "ebt_ulog: can't even allocate "
|
||||
"buffer of size %ub\n", size);
|
||||
}
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const struct ebt_ulog_info *uloginfo, const char *prefix)
|
||||
{
|
||||
ebt_ulog_packet_msg_t *pm;
|
||||
size_t size, copy_len;
|
||||
struct nlmsghdr *nlh;
|
||||
unsigned int group = uloginfo->nlgroup;
|
||||
ebt_ulog_buff_t *ub = &ulog_buffers[group];
|
||||
spinlock_t *lock = &ub->lock;
|
||||
|
||||
if ((uloginfo->cprange == 0) ||
|
||||
(uloginfo->cprange > skb->len + ETH_HLEN))
|
||||
copy_len = skb->len + ETH_HLEN;
|
||||
else
|
||||
copy_len = uloginfo->cprange;
|
||||
|
||||
size = NLMSG_SPACE(sizeof(*pm) + copy_len);
|
||||
if (size > nlbufsiz) {
|
||||
PRINTR("ebt_ulog: Size %Zd needed, but nlbufsiz=%d\n",
|
||||
size, nlbufsiz);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_bh(lock);
|
||||
|
||||
if (!ub->skb) {
|
||||
if (!(ub->skb = ulog_alloc_skb(size)))
|
||||
goto alloc_failure;
|
||||
} else if (size > skb_tailroom(ub->skb)) {
|
||||
ulog_send(group);
|
||||
|
||||
if (!(ub->skb = ulog_alloc_skb(size)))
|
||||
goto alloc_failure;
|
||||
}
|
||||
|
||||
nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, 0,
|
||||
size - NLMSG_ALIGN(sizeof(*nlh)));
|
||||
ub->qlen++;
|
||||
|
||||
pm = NLMSG_DATA(nlh);
|
||||
|
||||
/* Fill in the ulog data */
|
||||
pm->version = EBT_ULOG_VERSION;
|
||||
do_gettimeofday(&pm->stamp);
|
||||
if (ub->qlen == 1)
|
||||
skb_set_timestamp(ub->skb, &pm->stamp);
|
||||
pm->data_len = copy_len;
|
||||
pm->mark = skb->mark;
|
||||
pm->hook = hooknr;
|
||||
if (uloginfo->prefix != NULL)
|
||||
strcpy(pm->prefix, uloginfo->prefix);
|
||||
else
|
||||
*(pm->prefix) = '\0';
|
||||
|
||||
if (in) {
|
||||
strcpy(pm->physindev, in->name);
|
||||
/* If in isn't a bridge, then physindev==indev */
|
||||
if (in->br_port)
|
||||
strcpy(pm->indev, in->br_port->br->dev->name);
|
||||
else
|
||||
strcpy(pm->indev, in->name);
|
||||
} else
|
||||
pm->indev[0] = pm->physindev[0] = '\0';
|
||||
|
||||
if (out) {
|
||||
/* If out exists, then out is a bridge port */
|
||||
strcpy(pm->physoutdev, out->name);
|
||||
strcpy(pm->outdev, out->br_port->br->dev->name);
|
||||
} else
|
||||
pm->outdev[0] = pm->physoutdev[0] = '\0';
|
||||
|
||||
if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
|
||||
BUG();
|
||||
|
||||
if (ub->qlen > 1)
|
||||
ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;
|
||||
|
||||
ub->lastnlh = nlh;
|
||||
|
||||
if (ub->qlen >= uloginfo->qthreshold)
|
||||
ulog_send(group);
|
||||
else if (!timer_pending(&ub->timer)) {
|
||||
ub->timer.expires = jiffies + flushtimeout * HZ / 100;
|
||||
add_timer(&ub->timer);
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(lock);
|
||||
|
||||
return;
|
||||
|
||||
nlmsg_failure:
|
||||
printk(KERN_CRIT "ebt_ulog: error during NLMSG_PUT. This should "
|
||||
"not happen, please report to author.\n");
|
||||
goto unlock;
|
||||
alloc_failure:
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* this function is registered with the netfilter core */
|
||||
static void ebt_log_packet(unsigned int pf, unsigned int hooknum,
|
||||
const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const struct nf_loginfo *li,
|
||||
const char *prefix)
|
||||
{
|
||||
struct ebt_ulog_info loginfo;
|
||||
|
||||
if (!li || li->type != NF_LOG_TYPE_ULOG) {
|
||||
loginfo.nlgroup = EBT_ULOG_DEFAULT_NLGROUP;
|
||||
loginfo.cprange = 0;
|
||||
loginfo.qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD;
|
||||
loginfo.prefix[0] = '\0';
|
||||
} else {
|
||||
loginfo.nlgroup = li->u.ulog.group;
|
||||
loginfo.cprange = li->u.ulog.copy_len;
|
||||
loginfo.qthreshold = li->u.ulog.qthreshold;
|
||||
strlcpy(loginfo.prefix, prefix, sizeof(loginfo.prefix));
|
||||
}
|
||||
|
||||
ebt_ulog_packet(hooknum, skb, in, out, &loginfo, prefix);
|
||||
}
|
||||
|
||||
static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
|
||||
|
||||
ebt_ulog_packet(hooknr, skb, in, out, uloginfo, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int ebt_ulog_check(const char *tablename, unsigned int hookmask,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
|
||||
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_ulog_info)) ||
|
||||
uloginfo->nlgroup > 31)
|
||||
return -EINVAL;
|
||||
|
||||
uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0';
|
||||
|
||||
if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN)
|
||||
uloginfo->qthreshold = EBT_ULOG_MAX_QLEN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_watcher ulog = {
|
||||
.name = EBT_ULOG_WATCHER,
|
||||
.watcher = ebt_ulog,
|
||||
.check = ebt_ulog_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct nf_logger ebt_ulog_logger = {
|
||||
.name = EBT_ULOG_WATCHER,
|
||||
.logfn = &ebt_log_packet,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_ulog_init(void)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
if (nlbufsiz >= 128*1024) {
|
||||
printk(KERN_NOTICE "ebt_ulog: Netlink buffer has to be <= 128kB,"
|
||||
" please try a smaller nlbufsiz parameter.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* initialize ulog_buffers */
|
||||
for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
|
||||
init_timer(&ulog_buffers[i].timer);
|
||||
ulog_buffers[i].timer.function = ulog_timer;
|
||||
ulog_buffers[i].timer.data = i;
|
||||
spin_lock_init(&ulog_buffers[i].lock);
|
||||
}
|
||||
|
||||
ebtulognl = netlink_kernel_create(NETLINK_NFLOG, EBT_ULOG_MAXNLGROUPS,
|
||||
NULL, THIS_MODULE);
|
||||
if (!ebtulognl)
|
||||
ret = -ENOMEM;
|
||||
else if ((ret = ebt_register_watcher(&ulog)))
|
||||
sock_release(ebtulognl->sk_socket);
|
||||
|
||||
if (nf_log_register(PF_BRIDGE, &ebt_ulog_logger) < 0) {
|
||||
printk(KERN_WARNING "ebt_ulog: not logging via ulog "
|
||||
"since somebody else already registered for PF_BRIDGE\n");
|
||||
/* we cannot make module load fail here, since otherwise
|
||||
* ebtables userspace would abort */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ebt_ulog_fini(void)
|
||||
{
|
||||
ebt_ulog_buff_t *ub;
|
||||
int i;
|
||||
|
||||
nf_log_unregister(&ebt_ulog_logger);
|
||||
ebt_unregister_watcher(&ulog);
|
||||
for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
|
||||
ub = &ulog_buffers[i];
|
||||
if (timer_pending(&ub->timer))
|
||||
del_timer(&ub->timer);
|
||||
spin_lock_bh(&ub->lock);
|
||||
if (ub->skb) {
|
||||
kfree_skb(ub->skb);
|
||||
ub->skb = NULL;
|
||||
}
|
||||
spin_unlock_bh(&ub->lock);
|
||||
}
|
||||
sock_release(ebtulognl->sk_socket);
|
||||
}
|
||||
|
||||
module_init(ebt_ulog_init);
|
||||
module_exit(ebt_ulog_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>");
|
||||
MODULE_DESCRIPTION("ebtables userspace logging module for bridged Ethernet"
|
||||
" frames");
|
||||
195
net/bridge/netfilter/ebt_vlan.c
Normal file
195
net/bridge/netfilter/ebt_vlan.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Description: EBTables 802.1Q match extension kernelspace module.
|
||||
* Authors: Nick Fedchik <nick@fedchik.org.ua>
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/netfilter_bridge/ebt_vlan.h>
|
||||
|
||||
static int debug;
|
||||
#define MODULE_VERS "0.6"
|
||||
|
||||
module_param(debug, int, 0);
|
||||
MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
|
||||
MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
|
||||
MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
|
||||
MODULE_VERS);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
|
||||
#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
|
||||
#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
|
||||
#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
|
||||
#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return EBT_NOMATCH;}
|
||||
|
||||
static int
|
||||
ebt_filter_vlan(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
|
||||
struct vlan_hdr _frame, *fp;
|
||||
|
||||
unsigned short TCI; /* Whole TCI, given from parsed frame */
|
||||
unsigned short id; /* VLAN ID, given from frame TCI */
|
||||
unsigned char prio; /* user_priority, given from frame TCI */
|
||||
/* VLAN encapsulated Type/Length field, given from orig frame */
|
||||
__be16 encap;
|
||||
|
||||
fp = skb_header_pointer(skb, 0, sizeof(_frame), &_frame);
|
||||
if (fp == NULL)
|
||||
return EBT_NOMATCH;
|
||||
|
||||
/* Tag Control Information (TCI) consists of the following elements:
|
||||
* - User_priority. The user_priority field is three bits in length,
|
||||
* interpreted as a binary number.
|
||||
* - Canonical Format Indicator (CFI). The Canonical Format Indicator
|
||||
* (CFI) is a single bit flag value. Currently ignored.
|
||||
* - VLAN Identifier (VID). The VID is encoded as
|
||||
* an unsigned binary number. */
|
||||
TCI = ntohs(fp->h_vlan_TCI);
|
||||
id = TCI & VLAN_VID_MASK;
|
||||
prio = (TCI >> 13) & 0x7;
|
||||
encap = fp->h_vlan_encapsulated_proto;
|
||||
|
||||
/* Checking VLAN Identifier (VID) */
|
||||
if (GET_BITMASK(EBT_VLAN_ID))
|
||||
EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
|
||||
|
||||
/* Checking user_priority */
|
||||
if (GET_BITMASK(EBT_VLAN_PRIO))
|
||||
EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
|
||||
|
||||
/* Checking Encapsulated Proto (Length/Type) field */
|
||||
if (GET_BITMASK(EBT_VLAN_ENCAP))
|
||||
EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
|
||||
|
||||
return EBT_MATCH;
|
||||
}
|
||||
|
||||
static int
|
||||
ebt_check_vlan(const char *tablename,
|
||||
unsigned int hooknr,
|
||||
const struct ebt_entry *e, void *data, unsigned int datalen)
|
||||
{
|
||||
struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
|
||||
|
||||
/* Parameters buffer overflow check */
|
||||
if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
|
||||
DEBUG_MSG
|
||||
("passed size %d is not eq to ebt_vlan_info (%Zd)\n",
|
||||
datalen, sizeof(struct ebt_vlan_info));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Is it 802.1Q frame checked? */
|
||||
if (e->ethproto != htons(ETH_P_8021Q)) {
|
||||
DEBUG_MSG
|
||||
("passed entry proto %2.4X is not 802.1Q (8100)\n",
|
||||
(unsigned short) ntohs(e->ethproto));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check for bitmask range
|
||||
* True if even one bit is out of mask */
|
||||
if (info->bitmask & ~EBT_VLAN_MASK) {
|
||||
DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
|
||||
info->bitmask, EBT_VLAN_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check for inversion flags range */
|
||||
if (info->invflags & ~EBT_VLAN_MASK) {
|
||||
DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
|
||||
info->invflags, EBT_VLAN_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Reserved VLAN ID (VID) values
|
||||
* -----------------------------
|
||||
* 0 - The null VLAN ID.
|
||||
* 1 - The default Port VID (PVID)
|
||||
* 0x0FFF - Reserved for implementation use.
|
||||
* if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096. */
|
||||
if (GET_BITMASK(EBT_VLAN_ID)) {
|
||||
if (!!info->id) { /* if id!=0 => check vid range */
|
||||
if (info->id > VLAN_GROUP_ARRAY_LEN) {
|
||||
DEBUG_MSG
|
||||
("id %d is out of range (1-4096)\n",
|
||||
info->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Note: This is valid VLAN-tagged frame point.
|
||||
* Any value of user_priority are acceptable,
|
||||
* but should be ignored according to 802.1Q Std.
|
||||
* So we just drop the prio flag. */
|
||||
info->bitmask &= ~EBT_VLAN_PRIO;
|
||||
}
|
||||
/* Else, id=0 (null VLAN ID) => user_priority range (any?) */
|
||||
}
|
||||
|
||||
if (GET_BITMASK(EBT_VLAN_PRIO)) {
|
||||
if ((unsigned char) info->prio > 7) {
|
||||
DEBUG_MSG("prio %d is out of range (0-7)\n",
|
||||
info->prio);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
/* Check for encapsulated proto range - it is possible to be
|
||||
* any value for u_short range.
|
||||
* if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS */
|
||||
if (GET_BITMASK(EBT_VLAN_ENCAP)) {
|
||||
if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
|
||||
DEBUG_MSG
|
||||
("encap frame length %d is less than minimal\n",
|
||||
ntohs(info->encap));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_match filter_vlan = {
|
||||
.name = EBT_VLAN_MATCH,
|
||||
.match = ebt_filter_vlan,
|
||||
.check = ebt_check_vlan,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ebt_vlan_init(void)
|
||||
{
|
||||
DEBUG_MSG("ebtables 802.1Q extension module v"
|
||||
MODULE_VERS "\n");
|
||||
DEBUG_MSG("module debug=%d\n", !!debug);
|
||||
return ebt_register_match(&filter_vlan);
|
||||
}
|
||||
|
||||
static void __exit ebt_vlan_fini(void)
|
||||
{
|
||||
ebt_unregister_match(&filter_vlan);
|
||||
}
|
||||
|
||||
module_init(ebt_vlan_init);
|
||||
module_exit(ebt_vlan_fini);
|
||||
86
net/bridge/netfilter/ebtable_broute.c
Normal file
86
net/bridge/netfilter/ebtable_broute.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* ebtable_broute
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
* This table lets you choose between routing and bridging for frames
|
||||
* entering on a bridge enslaved nic. This table is traversed before any
|
||||
* other ebtables table. See net/bridge/br_input.c.
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/if_bridge.h>
|
||||
|
||||
/* EBT_ACCEPT means the frame will be bridged
|
||||
* EBT_DROP means the frame will be routed
|
||||
*/
|
||||
static struct ebt_entries initial_chain = {
|
||||
.name = "BROUTING",
|
||||
.policy = EBT_ACCEPT,
|
||||
};
|
||||
|
||||
static struct ebt_replace_kernel initial_table =
|
||||
{
|
||||
.name = "broute",
|
||||
.valid_hooks = 1 << NF_BR_BROUTING,
|
||||
.entries_size = sizeof(struct ebt_entries),
|
||||
.hook_entry = {
|
||||
[NF_BR_BROUTING] = &initial_chain,
|
||||
},
|
||||
.entries = (char *)&initial_chain,
|
||||
};
|
||||
|
||||
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
|
||||
{
|
||||
if (valid_hooks & ~(1 << NF_BR_BROUTING))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_table broute_table =
|
||||
{
|
||||
.name = "broute",
|
||||
.table = &initial_table,
|
||||
.valid_hooks = 1 << NF_BR_BROUTING,
|
||||
.lock = RW_LOCK_UNLOCKED,
|
||||
.check = check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ebt_broute(struct sk_buff **pskb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
|
||||
&broute_table);
|
||||
if (ret == NF_DROP)
|
||||
return 1; /* route it */
|
||||
return 0; /* bridge it */
|
||||
}
|
||||
|
||||
static int __init ebtable_broute_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ebt_register_table(&broute_table);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* see br_input.c */
|
||||
br_should_route_hook = ebt_broute;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ebtable_broute_fini(void)
|
||||
{
|
||||
br_should_route_hook = NULL;
|
||||
synchronize_net();
|
||||
ebt_unregister_table(&broute_table);
|
||||
}
|
||||
|
||||
module_init(ebtable_broute_init);
|
||||
module_exit(ebtable_broute_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
123
net/bridge/netfilter/ebtable_filter.c
Normal file
123
net/bridge/netfilter/ebtable_filter.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* ebtable_filter
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
|
||||
(1 << NF_BR_LOCAL_OUT))
|
||||
|
||||
static struct ebt_entries initial_chains[] =
|
||||
{
|
||||
{
|
||||
.name = "INPUT",
|
||||
.policy = EBT_ACCEPT,
|
||||
},
|
||||
{
|
||||
.name = "FORWARD",
|
||||
.policy = EBT_ACCEPT,
|
||||
},
|
||||
{
|
||||
.name = "OUTPUT",
|
||||
.policy = EBT_ACCEPT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct ebt_replace_kernel initial_table =
|
||||
{
|
||||
.name = "filter",
|
||||
.valid_hooks = FILTER_VALID_HOOKS,
|
||||
.entries_size = 3 * sizeof(struct ebt_entries),
|
||||
.hook_entry = {
|
||||
[NF_BR_LOCAL_IN] = &initial_chains[0],
|
||||
[NF_BR_FORWARD] = &initial_chains[1],
|
||||
[NF_BR_LOCAL_OUT] = &initial_chains[2],
|
||||
},
|
||||
.entries = (char *)initial_chains,
|
||||
};
|
||||
|
||||
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
|
||||
{
|
||||
if (valid_hooks & ~FILTER_VALID_HOOKS)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_table frame_filter =
|
||||
{
|
||||
.name = "filter",
|
||||
.table = &initial_table,
|
||||
.valid_hooks = FILTER_VALID_HOOKS,
|
||||
.lock = RW_LOCK_UNLOCKED,
|
||||
.check = check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
|
||||
const struct net_device *out, int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
return ebt_do_table(hook, pskb, in, out, &frame_filter);
|
||||
}
|
||||
|
||||
static struct nf_hook_ops ebt_ops_filter[] = {
|
||||
{
|
||||
.hook = ebt_hook,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_LOCAL_IN,
|
||||
.priority = NF_BR_PRI_FILTER_BRIDGED,
|
||||
},
|
||||
{
|
||||
.hook = ebt_hook,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_FORWARD,
|
||||
.priority = NF_BR_PRI_FILTER_BRIDGED,
|
||||
},
|
||||
{
|
||||
.hook = ebt_hook,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_LOCAL_OUT,
|
||||
.priority = NF_BR_PRI_FILTER_OTHER,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ebtable_filter_init(void)
|
||||
{
|
||||
int i, j, ret;
|
||||
|
||||
ret = ebt_register_table(&frame_filter);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
|
||||
if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
|
||||
goto cleanup;
|
||||
return ret;
|
||||
cleanup:
|
||||
for (j = 0; j < i; j++)
|
||||
nf_unregister_hook(&ebt_ops_filter[j]);
|
||||
ebt_unregister_table(&frame_filter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ebtable_filter_fini(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
|
||||
nf_unregister_hook(&ebt_ops_filter[i]);
|
||||
ebt_unregister_table(&frame_filter);
|
||||
}
|
||||
|
||||
module_init(ebtable_filter_init);
|
||||
module_exit(ebtable_filter_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
130
net/bridge/netfilter/ebtable_nat.c
Normal file
130
net/bridge/netfilter/ebtable_nat.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* ebtable_nat
|
||||
*
|
||||
* Authors:
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* April, 2002
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netfilter_bridge/ebtables.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
|
||||
(1 << NF_BR_POST_ROUTING))
|
||||
|
||||
static struct ebt_entries initial_chains[] =
|
||||
{
|
||||
{
|
||||
.name = "PREROUTING",
|
||||
.policy = EBT_ACCEPT,
|
||||
},
|
||||
{
|
||||
.name = "OUTPUT",
|
||||
.policy = EBT_ACCEPT,
|
||||
},
|
||||
{
|
||||
.name = "POSTROUTING",
|
||||
.policy = EBT_ACCEPT,
|
||||
}
|
||||
};
|
||||
|
||||
static struct ebt_replace_kernel initial_table =
|
||||
{
|
||||
.name = "nat",
|
||||
.valid_hooks = NAT_VALID_HOOKS,
|
||||
.entries_size = 3 * sizeof(struct ebt_entries),
|
||||
.hook_entry = {
|
||||
[NF_BR_PRE_ROUTING] = &initial_chains[0],
|
||||
[NF_BR_LOCAL_OUT] = &initial_chains[1],
|
||||
[NF_BR_POST_ROUTING] = &initial_chains[2],
|
||||
},
|
||||
.entries = (char *)initial_chains,
|
||||
};
|
||||
|
||||
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
|
||||
{
|
||||
if (valid_hooks & ~NAT_VALID_HOOKS)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ebt_table frame_nat =
|
||||
{
|
||||
.name = "nat",
|
||||
.table = &initial_table,
|
||||
.valid_hooks = NAT_VALID_HOOKS,
|
||||
.lock = RW_LOCK_UNLOCKED,
|
||||
.check = check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
|
||||
, const struct net_device *out, int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
return ebt_do_table(hook, pskb, in, out, &frame_nat);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
|
||||
, const struct net_device *out, int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
return ebt_do_table(hook, pskb, in, out, &frame_nat);
|
||||
}
|
||||
|
||||
static struct nf_hook_ops ebt_ops_nat[] = {
|
||||
{
|
||||
.hook = ebt_nat_dst,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_LOCAL_OUT,
|
||||
.priority = NF_BR_PRI_NAT_DST_OTHER,
|
||||
},
|
||||
{
|
||||
.hook = ebt_nat_src,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_POST_ROUTING,
|
||||
.priority = NF_BR_PRI_NAT_SRC,
|
||||
},
|
||||
{
|
||||
.hook = ebt_nat_dst,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = PF_BRIDGE,
|
||||
.hooknum = NF_BR_PRE_ROUTING,
|
||||
.priority = NF_BR_PRI_NAT_DST_BRIDGED,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ebtable_nat_init(void)
|
||||
{
|
||||
int i, ret, j;
|
||||
|
||||
ret = ebt_register_table(&frame_nat);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
|
||||
if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
|
||||
goto cleanup;
|
||||
return ret;
|
||||
cleanup:
|
||||
for (j = 0; j < i; j++)
|
||||
nf_unregister_hook(&ebt_ops_nat[j]);
|
||||
ebt_unregister_table(&frame_nat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ebtable_nat_fini(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
|
||||
nf_unregister_hook(&ebt_ops_nat[i]);
|
||||
ebt_unregister_table(&frame_nat);
|
||||
}
|
||||
|
||||
module_init(ebtable_nat_init);
|
||||
module_exit(ebtable_nat_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
1549
net/bridge/netfilter/ebtables.c
Normal file
1549
net/bridge/netfilter/ebtables.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user