2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Device handling code
|
|
|
|
* Linux ethernet bridge
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Lennert Buytenhek <buytenh@gnu.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/netdevice.h>
|
2010-05-06 11:48:24 +04:00
|
|
|
#include <linux/netpoll.h>
|
2005-12-22 05:51:49 +03:00
|
|
|
#include <linux/etherdevice.h>
|
2005-12-22 06:00:58 +03:00
|
|
|
#include <linux/ethtool.h>
|
2010-05-06 11:48:24 +04:00
|
|
|
#include <linux/list.h>
|
2010-04-15 14:14:51 +04:00
|
|
|
#include <linux/netfilter_bridge.h>
|
2005-12-22 05:51:49 +03:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include "br_private.h"
|
|
|
|
|
2013-05-22 11:49:34 +04:00
|
|
|
#define COMMON_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | \
|
|
|
|
NETIF_F_GSO_MASK | NETIF_F_HW_CSUM)
|
|
|
|
|
2015-03-10 12:27:18 +03:00
|
|
|
const struct nf_br_ops __rcu *nf_br_ops __read_mostly;
|
|
|
|
EXPORT_SYMBOL_GPL(nf_br_ops);
|
|
|
|
|
2010-07-27 12:26:30 +04:00
|
|
|
/* net device transmit always called with BH disabled */
|
2009-08-31 23:50:41 +04:00
|
|
|
netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
const unsigned char *dest = skb->data;
|
|
|
|
struct net_bridge_fdb_entry *dst;
|
2010-02-27 22:41:48 +03:00
|
|
|
struct net_bridge_mdb_entry *mdst;
|
2014-01-04 09:57:59 +04:00
|
|
|
struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
|
2015-03-10 12:27:18 +03:00
|
|
|
const struct nf_br_ops *nf_ops;
|
2013-02-13 16:00:14 +04:00
|
|
|
u16 vid = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-08-14 19:19:33 +04:00
|
|
|
rcu_read_lock();
|
2015-03-10 12:27:18 +03:00
|
|
|
nf_ops = rcu_dereference(nf_br_ops);
|
|
|
|
if (nf_ops && nf_ops->br_dev_xmit_hook(skb)) {
|
2012-08-14 19:19:33 +04:00
|
|
|
rcu_read_unlock();
|
2010-04-15 14:14:51 +04:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2010-06-24 00:00:48 +04:00
|
|
|
u64_stats_update_begin(&brstats->syncp);
|
2010-03-02 16:32:09 +03:00
|
|
|
brstats->tx_packets++;
|
|
|
|
brstats->tx_bytes += skb->len;
|
2010-06-24 00:00:48 +04:00
|
|
|
u64_stats_update_end(&brstats->syncp);
|
2010-02-27 22:41:42 +03:00
|
|
|
|
2010-03-02 16:32:09 +03:00
|
|
|
BR_INPUT_SKB_CB(skb)->brdev = dev;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-03-20 01:30:44 +03:00
|
|
|
skb_reset_mac_header(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
skb_pull(skb, ETH_HLEN);
|
|
|
|
|
bridge: vlan: add per-vlan struct and move to rhashtables
This patch changes the bridge vlan implementation to use rhashtables
instead of bitmaps. The main motivation behind this change is that we
need extensible per-vlan structures (both per-port and global) so more
advanced features can be introduced and the vlan support can be
extended. I've tried to break this up but the moment net_port_vlans is
changed and the whole API goes away, thus this is a larger patch.
A few short goals of this patch are:
- Extensible per-vlan structs stored in rhashtables and a sorted list
- Keep user-visible behaviour (compressed vlans etc)
- Keep fastpath ingress/egress logic the same (optimizations to come
later)
Here's a brief list of some of the new features we'd like to introduce:
- per-vlan counters
- vlan ingress/egress mapping
- per-vlan igmp configuration
- vlan priorities
- avoid fdb entries replication (e.g. local fdb scaling issues)
The structure is kept single for both global and per-port entries so to
avoid code duplication where possible and also because we'll soon introduce
"port0 / aka bridge as port" which should simplify things further
(thanks to Vlad for the suggestion!).
Now we have per-vlan global rhashtable (bridge-wide) and per-vlan port
rhashtable, if an entry is added to a port it'll get a pointer to its
global context so it can be quickly accessed later. There's also a
sorted vlan list which is used for stable walks and some user-visible
behaviour such as the vlan ranges, also for error paths.
VLANs are stored in a "vlan group" which currently contains the
rhashtable, sorted vlan list and the number of "real" vlan entries.
A good side-effect of this change is that it resembles how hw keeps
per-vlan data.
One important note after this change is that if a VLAN is being looked up
in the bridge's rhashtable for filtering purposes (or to check if it's an
existing usable entry, not just a global context) then the new helper
br_vlan_should_use() needs to be used if the vlan is found. In case the
lookup is done only with a port's vlan group, then this check can be
skipped.
Things tested so far:
- basic vlan ingress/egress
- pvids
- untagged vlans
- undef CONFIG_BRIDGE_VLAN_FILTERING
- adding/deleting vlans in different scenarios (with/without global ctx,
while transmitting traffic, in ranges etc)
- loading/removing the module while having/adding/deleting vlans
- extracting bridge vlan information (user ABI), compressed requests
- adding/deleting fdbs on vlans
- bridge mac change, promisc mode
- default pvid change
- kmemleak ON during the whole time
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-09-25 20:00:11 +03:00
|
|
|
if (!br_allowed_ingress(br, skb, &vid))
|
2014-03-27 16:46:55 +04:00
|
|
|
goto out;
|
|
|
|
|
2011-07-05 17:58:33 +04:00
|
|
|
if (is_broadcast_ether_addr(dest))
|
2013-06-05 18:08:01 +04:00
|
|
|
br_flood_deliver(br, skb, false);
|
2011-07-05 17:58:33 +04:00
|
|
|
else if (is_multicast_ether_addr(dest)) {
|
2010-06-10 20:12:50 +04:00
|
|
|
if (unlikely(netpoll_tx_running(dev))) {
|
2013-06-05 18:08:01 +04:00
|
|
|
br_flood_deliver(br, skb, false);
|
2010-06-10 20:12:50 +04:00
|
|
|
goto out;
|
|
|
|
}
|
2013-10-28 23:45:07 +04:00
|
|
|
if (br_multicast_rcv(br, NULL, skb, vid)) {
|
2010-07-29 05:12:31 +04:00
|
|
|
kfree_skb(skb);
|
2010-02-27 22:41:48 +03:00
|
|
|
goto out;
|
2010-07-29 05:12:31 +04:00
|
|
|
}
|
2010-02-27 22:41:48 +03:00
|
|
|
|
2013-03-07 07:05:33 +04:00
|
|
|
mdst = br_mdb_get(br, skb, vid);
|
2013-08-01 03:06:20 +04:00
|
|
|
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
|
2013-08-30 19:28:17 +04:00
|
|
|
br_multicast_querier_exists(br, eth_hdr(skb)))
|
2010-02-27 22:41:48 +03:00
|
|
|
br_multicast_deliver(mdst, skb);
|
|
|
|
else
|
2013-06-05 18:08:01 +04:00
|
|
|
br_flood_deliver(br, skb, false);
|
2013-02-13 16:00:16 +04:00
|
|
|
} else if ((dst = __br_fdb_get(br, dest, vid)) != NULL)
|
2005-04-17 02:20:36 +04:00
|
|
|
br_deliver(dst->dst, skb);
|
|
|
|
else
|
2013-06-05 18:08:01 +04:00
|
|
|
br_flood_deliver(br, skb, true);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2010-02-27 22:41:48 +03:00
|
|
|
out:
|
2010-07-27 12:26:30 +04:00
|
|
|
rcu_read_unlock();
|
2009-06-23 10:03:08 +04:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2011-04-04 18:03:32 +04:00
|
|
|
static int br_dev_init(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
2014-10-03 19:29:18 +04:00
|
|
|
int err;
|
2011-04-04 18:03:32 +04:00
|
|
|
|
2014-02-13 23:46:28 +04:00
|
|
|
br->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
2011-04-04 18:03:32 +04:00
|
|
|
if (!br->stats)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-10-03 19:29:18 +04:00
|
|
|
err = br_vlan_init(br);
|
|
|
|
if (err)
|
|
|
|
free_percpu(br->stats);
|
|
|
|
|
|
|
|
return err;
|
2011-04-04 18:03:32 +04:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static int br_dev_open(struct net_device *dev)
|
|
|
|
{
|
2005-05-30 01:15:17 +04:00
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-04-22 10:31:16 +04:00
|
|
|
netdev_update_features(dev);
|
2005-05-30 01:15:17 +04:00
|
|
|
netif_start_queue(dev);
|
|
|
|
br_stp_enable_bridge(br);
|
2010-02-28 11:49:38 +03:00
|
|
|
br_multicast_open(br);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void br_dev_set_multicast_list(struct net_device *dev)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-05-16 17:59:20 +04:00
|
|
|
static void br_dev_change_rx_flags(struct net_device *dev, int change)
|
|
|
|
{
|
|
|
|
if (change & IFF_PROMISC)
|
|
|
|
br_manage_promisc(netdev_priv(dev));
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static int br_dev_stop(struct net_device *dev)
|
|
|
|
{
|
2010-02-28 11:49:38 +03:00
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
|
|
|
|
br_stp_disable_bridge(br);
|
|
|
|
br_multicast_stop(br);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-08 01:58:56 +04:00
|
|
|
static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev,
|
|
|
|
struct rtnl_link_stats64 *stats)
|
2010-03-02 16:32:09 +03:00
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
2014-01-04 09:57:59 +04:00
|
|
|
struct pcpu_sw_netstats tmp, sum = { 0 };
|
2010-03-02 16:32:09 +03:00
|
|
|
unsigned int cpu;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
2010-06-24 00:00:48 +04:00
|
|
|
unsigned int start;
|
2014-01-04 09:57:59 +04:00
|
|
|
const struct pcpu_sw_netstats *bstats
|
2010-03-02 16:32:09 +03:00
|
|
|
= per_cpu_ptr(br->stats, cpu);
|
2010-06-24 00:00:48 +04:00
|
|
|
do {
|
2014-03-14 08:26:42 +04:00
|
|
|
start = u64_stats_fetch_begin_irq(&bstats->syncp);
|
2010-06-24 00:00:48 +04:00
|
|
|
memcpy(&tmp, bstats, sizeof(tmp));
|
2014-03-14 08:26:42 +04:00
|
|
|
} while (u64_stats_fetch_retry_irq(&bstats->syncp, start));
|
2010-06-24 00:00:48 +04:00
|
|
|
sum.tx_bytes += tmp.tx_bytes;
|
|
|
|
sum.tx_packets += tmp.tx_packets;
|
|
|
|
sum.rx_bytes += tmp.rx_bytes;
|
|
|
|
sum.rx_packets += tmp.rx_packets;
|
2010-03-02 16:32:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
stats->tx_bytes = sum.tx_bytes;
|
|
|
|
stats->tx_packets = sum.tx_packets;
|
|
|
|
stats->rx_bytes = sum.rx_bytes;
|
|
|
|
stats->rx_packets = sum.rx_packets;
|
|
|
|
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static int br_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
{
|
2008-07-31 03:27:55 +04:00
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
if (new_mtu < 68 || new_mtu > br_min_mtu(br))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dev->mtu = new_mtu;
|
2008-07-31 03:27:55 +04:00
|
|
|
|
2014-09-18 13:29:03 +04:00
|
|
|
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
2008-07-31 03:27:55 +04:00
|
|
|
/* remember the MTU in the rtable for PMTU */
|
2010-12-09 08:16:57 +03:00
|
|
|
dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);
|
2008-07-31 03:27:55 +04:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-04-09 22:49:58 +04:00
|
|
|
/* Allow setting mac address to any valid ethernet address. */
|
2005-12-22 05:51:49 +03:00
|
|
|
static int br_set_mac_address(struct net_device *dev, void *p)
|
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
struct sockaddr *addr = p;
|
2007-04-09 22:49:58 +04:00
|
|
|
|
|
|
|
if (!is_valid_ether_addr(addr->sa_data))
|
2012-02-21 06:07:52 +04:00
|
|
|
return -EADDRNOTAVAIL;
|
2005-12-22 05:51:49 +03:00
|
|
|
|
|
|
|
spin_lock_bh(&br->lock);
|
bridge: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 22:56:49 +04:00
|
|
|
if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) {
|
2014-02-07 11:48:20 +04:00
|
|
|
/* Mac address will be changed in br_stp_change_bridge_id(). */
|
2011-12-08 11:17:49 +04:00
|
|
|
br_stp_change_bridge_id(br, addr->sa_data);
|
|
|
|
}
|
2005-12-22 05:51:49 +03:00
|
|
|
spin_unlock_bh(&br->lock);
|
|
|
|
|
2007-04-09 22:49:58 +04:00
|
|
|
return 0;
|
2005-12-22 05:51:49 +03:00
|
|
|
}
|
|
|
|
|
2005-12-22 06:00:58 +03:00
|
|
|
static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
{
|
2013-01-06 04:44:26 +04:00
|
|
|
strlcpy(info->driver, "bridge", sizeof(info->driver));
|
|
|
|
strlcpy(info->version, BR_VERSION, sizeof(info->version));
|
|
|
|
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
|
|
|
|
strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
|
2005-12-22 06:00:58 +03:00
|
|
|
}
|
|
|
|
|
2011-11-15 19:29:55 +04:00
|
|
|
static netdev_features_t br_fix_features(struct net_device *dev,
|
|
|
|
netdev_features_t features)
|
2005-12-22 06:00:58 +03:00
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
|
2011-04-22 10:31:16 +04:00
|
|
|
return br_features_recompute(br, features);
|
2010-10-20 17:56:08 +04:00
|
|
|
}
|
|
|
|
|
2010-05-06 11:48:24 +04:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
2010-06-10 20:12:50 +04:00
|
|
|
static void br_poll_controller(struct net_device *br_dev)
|
2010-05-06 11:48:24 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-06-10 20:12:50 +04:00
|
|
|
static void br_netpoll_cleanup(struct net_device *dev)
|
2010-05-06 11:48:24 +04:00
|
|
|
{
|
2010-06-10 20:12:50 +04:00
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
2012-08-10 05:24:44 +04:00
|
|
|
struct net_bridge_port *p;
|
2010-05-06 11:48:24 +04:00
|
|
|
|
2012-08-10 05:24:44 +04:00
|
|
|
list_for_each_entry(p, &br->port_list, list)
|
2010-06-10 20:12:50 +04:00
|
|
|
br_netpoll_disable(p);
|
2010-05-06 11:48:24 +04:00
|
|
|
}
|
|
|
|
|
2014-03-28 02:36:38 +04:00
|
|
|
static int __br_netpoll_enable(struct net_bridge_port *p)
|
2014-02-07 03:00:52 +04:00
|
|
|
{
|
|
|
|
struct netpoll *np;
|
|
|
|
int err;
|
|
|
|
|
2014-03-28 02:36:38 +04:00
|
|
|
np = kzalloc(sizeof(*p->np), GFP_KERNEL);
|
2014-02-07 03:00:52 +04:00
|
|
|
if (!np)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-03-28 02:36:38 +04:00
|
|
|
err = __netpoll_setup(np, p->dev);
|
2014-02-07 03:00:52 +04:00
|
|
|
if (err) {
|
|
|
|
kfree(np);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->np = np;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-03-28 02:36:38 +04:00
|
|
|
int br_netpoll_enable(struct net_bridge_port *p)
|
2014-02-07 03:00:52 +04:00
|
|
|
{
|
|
|
|
if (!p->br->dev->npinfo)
|
|
|
|
return 0;
|
|
|
|
|
2014-03-28 02:36:38 +04:00
|
|
|
return __br_netpoll_enable(p);
|
2014-02-07 03:00:52 +04:00
|
|
|
}
|
|
|
|
|
2014-03-28 02:36:38 +04:00
|
|
|
static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
|
2010-05-06 11:48:24 +04:00
|
|
|
{
|
2010-05-10 13:31:08 +04:00
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
2012-08-10 05:24:44 +04:00
|
|
|
struct net_bridge_port *p;
|
2010-06-10 20:12:50 +04:00
|
|
|
int err = 0;
|
2010-05-06 11:48:24 +04:00
|
|
|
|
2012-08-10 05:24:44 +04:00
|
|
|
list_for_each_entry(p, &br->port_list, list) {
|
2010-06-10 20:12:50 +04:00
|
|
|
if (!p->dev)
|
|
|
|
continue;
|
2014-03-28 02:36:38 +04:00
|
|
|
err = __br_netpoll_enable(p);
|
2010-06-10 20:12:50 +04:00
|
|
|
if (err)
|
|
|
|
goto fail;
|
2010-05-06 11:48:24 +04:00
|
|
|
}
|
2010-06-10 20:12:50 +04:00
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
br_netpoll_cleanup(dev);
|
|
|
|
goto out;
|
2010-05-06 11:48:24 +04:00
|
|
|
}
|
|
|
|
|
2010-06-10 20:12:50 +04:00
|
|
|
void br_netpoll_disable(struct net_bridge_port *p)
|
2010-05-06 11:48:24 +04:00
|
|
|
{
|
2010-06-10 20:12:50 +04:00
|
|
|
struct netpoll *np = p->np;
|
|
|
|
|
|
|
|
if (!np)
|
|
|
|
return;
|
|
|
|
|
|
|
|
p->np = NULL;
|
|
|
|
|
2013-02-11 14:25:30 +04:00
|
|
|
__netpoll_free_async(np);
|
2010-05-06 11:48:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2011-02-13 12:33:42 +03:00
|
|
|
static int br_add_slave(struct net_device *dev, struct net_device *slave_dev)
|
|
|
|
|
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
|
|
|
|
return br_add_if(br, slave_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int br_del_slave(struct net_device *dev, struct net_device *slave_dev)
|
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
|
|
|
|
return br_del_if(br, slave_dev);
|
|
|
|
}
|
|
|
|
|
2008-11-20 08:49:00 +03:00
|
|
|
static const struct ethtool_ops br_ethtool_ops = {
|
2008-08-16 06:51:07 +04:00
|
|
|
.get_drvinfo = br_getinfo,
|
|
|
|
.get_link = ethtool_op_get_link,
|
2005-12-22 06:00:58 +03:00
|
|
|
};
|
|
|
|
|
2008-11-20 08:49:00 +03:00
|
|
|
static const struct net_device_ops br_netdev_ops = {
|
|
|
|
.ndo_open = br_dev_open,
|
|
|
|
.ndo_stop = br_dev_stop,
|
2011-04-04 18:03:32 +04:00
|
|
|
.ndo_init = br_dev_init,
|
2008-11-21 07:14:53 +03:00
|
|
|
.ndo_start_xmit = br_dev_xmit,
|
2010-06-24 00:00:48 +04:00
|
|
|
.ndo_get_stats64 = br_get_stats64,
|
2008-11-21 07:14:53 +03:00
|
|
|
.ndo_set_mac_address = br_set_mac_address,
|
2011-08-16 10:29:01 +04:00
|
|
|
.ndo_set_rx_mode = br_dev_set_multicast_list,
|
2014-05-16 17:59:20 +04:00
|
|
|
.ndo_change_rx_flags = br_dev_change_rx_flags,
|
2008-11-21 07:14:53 +03:00
|
|
|
.ndo_change_mtu = br_change_mtu,
|
|
|
|
.ndo_do_ioctl = br_dev_ioctl,
|
2010-05-06 11:48:24 +04:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
2010-06-10 20:12:50 +04:00
|
|
|
.ndo_netpoll_setup = br_netpoll_setup,
|
2010-05-06 11:48:24 +04:00
|
|
|
.ndo_netpoll_cleanup = br_netpoll_cleanup,
|
|
|
|
.ndo_poll_controller = br_poll_controller,
|
|
|
|
#endif
|
2011-02-13 12:33:42 +03:00
|
|
|
.ndo_add_slave = br_add_slave,
|
|
|
|
.ndo_del_slave = br_del_slave,
|
2011-04-22 10:31:16 +04:00
|
|
|
.ndo_fix_features = br_fix_features,
|
2012-04-15 10:43:56 +04:00
|
|
|
.ndo_fdb_add = br_fdb_add,
|
|
|
|
.ndo_fdb_del = br_fdb_delete,
|
|
|
|
.ndo_fdb_dump = br_fdb_dump,
|
2012-10-24 12:12:57 +04:00
|
|
|
.ndo_bridge_getlink = br_getlink,
|
|
|
|
.ndo_bridge_setlink = br_setlink,
|
2013-02-13 16:00:12 +04:00
|
|
|
.ndo_bridge_dellink = br_dellink,
|
2015-07-31 09:03:26 +03:00
|
|
|
.ndo_features_check = passthru_features_check,
|
2008-11-20 08:49:00 +03:00
|
|
|
};
|
|
|
|
|
2010-03-02 16:32:09 +03:00
|
|
|
static void br_dev_free(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
|
|
|
|
free_percpu(br->stats);
|
|
|
|
free_netdev(dev);
|
|
|
|
}
|
|
|
|
|
2011-04-04 18:03:32 +04:00
|
|
|
static struct device_type br_type = {
|
|
|
|
.name = "bridge",
|
|
|
|
};
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
void br_dev_setup(struct net_device *dev)
|
|
|
|
{
|
2011-04-04 18:03:32 +04:00
|
|
|
struct net_bridge *br = netdev_priv(dev);
|
|
|
|
|
2012-02-15 10:45:40 +04:00
|
|
|
eth_hw_addr_random(dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
ether_setup(dev);
|
|
|
|
|
2008-11-20 08:49:00 +03:00
|
|
|
dev->netdev_ops = &br_netdev_ops;
|
2010-03-02 16:32:09 +03:00
|
|
|
dev->destructor = br_dev_free;
|
2014-05-11 04:12:32 +04:00
|
|
|
dev->ethtool_ops = &br_ethtool_ops;
|
2011-04-04 18:03:32 +04:00
|
|
|
SET_NETDEV_DEVTYPE(dev, &br_type);
|
2015-08-18 11:30:37 +03:00
|
|
|
dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE;
|
2005-12-22 06:00:58 +03:00
|
|
|
|
2013-05-22 11:49:34 +04:00
|
|
|
dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL |
|
2014-06-10 15:59:22 +04:00
|
|
|
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
|
|
|
|
dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_STAG_TX;
|
2013-05-22 11:49:34 +04:00
|
|
|
dev->vlan_features = COMMON_FEATURES;
|
2011-04-04 18:03:32 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-02-23 12:05:25 +04:00
|
|
|
ether_addr_copy(br->group_addr, eth_reserved_addr_base);
|
2011-04-04 18:03:32 +04:00
|
|
|
|
|
|
|
br->stp_enabled = BR_NO_STP;
|
2011-10-03 22:14:46 +04:00
|
|
|
br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
|
2014-06-10 15:59:24 +04:00
|
|
|
br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
|
2011-10-03 22:14:46 +04:00
|
|
|
|
2011-04-04 18:03:32 +04:00
|
|
|
br->designated_root = br->bridge_id;
|
|
|
|
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;
|
2015-09-23 18:39:16 +03:00
|
|
|
br->ageing_time = BR_DEFAULT_AGEING_TIME;
|
2011-04-04 18:03:32 +04:00
|
|
|
|
|
|
|
br_netfilter_rtable_init(br);
|
|
|
|
br_stp_timer_init(br);
|
|
|
|
br_multicast_init(br);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|