Commit 93197b13 authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge_vlan'

Vlad Yasevich says:

====================
VLAN filtering/VLAN aware bridge

Changes since v10
* Updated implemenation of ndo_fdb_del in emulex and qlogic drivers.

Changes since v9:
* series re-ordering so make functionality more distinct.  Basic vlan
  filtering is patches 1-4.  Support for PVID/untagged vlans is patches
  5 and 6.  VLAN support for FDB/MDB is patches 7-11.  Patch 12 is
  still additional egress policy.
* Slight simplification to code that extracts the VID from skb.  Since we
  now depend on the vlan module, at the time of input skb_tci is guaranteed
  to be set if the packet had 8021q header.  We can simply refere to it.
* Changed the opaque 'parent' pointer from prior patches to a union so we
  can be much more explicit in our assignments.
* Lots of additional testing with STP turned on.  No issues were observed.

Changes since v8:
* Unified vlans_to_* calls into a single interface
* Fixed the rest of the issues report by Michal Miroslaw
* Fixed a bug where fdb entries were not created for all added vlans.

Changes since v7:
* Rebases on the latest net-next and removed the vlan wrapper patch from
the series.
* Fixed a crash in br_fdb_add/br_fdb_delete.

Changes since v6:
* VLANs are now stored in a VLAN bitmap per port.  This allows for O(1)
lookup at ingress and egress.  We simply check to see if the bit associated
with the vlan id is set in the map.  The drawback to this approach is that
it wastes some space when there is only a small number of VLANs.
* In addition to the build time configuration option, VLAN filtering also has
a configuration paramter in sysfs.  By default the filtering is turned off
and all traffic is permitted.  When the filtring is turned on, we do strict
matching to the filter configured.  Thus, if there is no configuration, all
packets are rejected.  This was done to make the behavior more streight
forward.  Without this (and if egress policy patch is rejected), the
decision for how to forward untagged traffic that was not filtered at ingress
is almost impossible to make.  It would not be right to deliver to every
port that has PVID set as, each port may have a different PVID.
* Separate egress policy bitmap patch has been isolated and is provided last
in the series.  This has been a more contentious piece of functionality and I
wanted to isolate it so that it could easily be dropped and not block the whole
series.

Changes since v5:
 - Pulled VLAN filtering into its own file and made it a configuration options.
 - Made new vlan filtering option dependent on VLAN_8021Q.
 - Got rid of HW filter inlines and moved then vlan_core.c.
   (All of the above suggested by Stephen Hemminger)

Changes since v4:
 - Pull per-port vlan data into its own structures and give it to the bridge
   device thus making bridge device behave like a regular port for vlan
   configuration.
 - Add a per-vlan 'untagged' bitmap that determins egress policy.  If a port
   is part of this bitmap, traffic egresses untagged.
 - PVID is now used for ingress policy only.  Incomming frames without VLAN tag
   are assigned to the PVID vlan.  Egress is determined via bitmap memberships.
 - Allow for incremental config of a vlan.  Now, PVID and untagged memberships
   may be set on existing vlans.  They however can NOT be cleared separately.
 - VLAN deletion is now done via RTM_DELLINK command for PF_BRIDGE family.
   This cleans up the netlink interface.

Changes since v3:
 - Re-integrated compiler problems that got left out last time.  Appologies.
 - checkpatches.pl errors fixed

Changes since v2:
 - Added inline functiosn to manimulate vlan hw filters and re-use in 8021q
   and bridge code.
 - Use rtnl_dereference (Michael Tsirkin)
 - Remove synchronize_net() call (Eric Dumazet)
 - Fix NULL ptr deref bug I introduced in br_ifinfo_notify.

Changes since v1:
 - Fixed some forwarding bugs.
 - Add vlan to local fdb entries.  New local entries are created per vlan
   to facilite correct forwarding to bridge interface.
 - Allow configuration of vlans directly on the bridge master device
   in addition to ports.

Changes since rfc v2:
 - Per-port vlan bitmap is gone and is replaced with a vlan list.
 - Added bridge vlan list, which is referenced by each port.  Entries in
   the birdge vlan list have port bitmap that shows which port are parts
   of which vlan.
 - Netlink API changes.
 - Dropped sysfs support for now.  If people think this is really usefull,
   can add it back.
 - Support for native/untagged vlans.

Changes since rfc v1:
 - Comments addressed regarding formatting and RCU usage
 - iocts have been removed and changed over the netlink interface.
 - Added support of user added ndb entries.
 - changed sysfs interface to export a bitmap.  Also added a write interface.
   I am not sure how much I like it, but it made my testing easier/faster.  I
   might change the write interface to take text instead of binary.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 22222997 35e03f3a
...@@ -7002,7 +7002,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -7002,7 +7002,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
return err; return err;
} }
static int ixgbe_ndo_fdb_del(struct ndmsg *ndm, static int ixgbe_ndo_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr) const unsigned char *addr)
{ {
...@@ -7079,7 +7079,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, ...@@ -7079,7 +7079,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
} }
static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev) struct net_device *dev,
u32 filter_mask)
{ {
struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_adapter *adapter = netdev_priv(dev);
u16 mode; u16 mode;
......
...@@ -1959,6 +1959,7 @@ static int mlx4_en_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -1959,6 +1959,7 @@ static int mlx4_en_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
} }
static int mlx4_en_fdb_del(struct ndmsg *ndm, static int mlx4_en_fdb_del(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr) const unsigned char *addr)
{ {
......
...@@ -247,8 +247,8 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p) ...@@ -247,8 +247,8 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)
return 0; return 0;
} }
static int qlcnic_fdb_del(struct ndmsg *ndm, struct net_device *netdev, static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
const unsigned char *addr) struct net_device *netdev, const unsigned char *addr)
{ {
struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_adapter *adapter = netdev_priv(netdev);
int err = -EOPNOTSUPP; int err = -EOPNOTSUPP;
......
...@@ -599,7 +599,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -599,7 +599,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
return err; return err;
} }
static int macvlan_fdb_del(struct ndmsg *ndm, static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr) const unsigned char *addr)
{ {
......
...@@ -393,7 +393,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -393,7 +393,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
} }
/* Delete entry (via netlink) */ /* Delete entry (via netlink) */
static int vxlan_fdb_delete(struct ndmsg *ndm, struct net_device *dev, static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr) const unsigned char *addr)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
......
...@@ -884,7 +884,8 @@ struct netdev_fcoe_hbainfo { ...@@ -884,7 +884,8 @@ struct netdev_fcoe_hbainfo {
* struct net_device *dev, * struct net_device *dev,
* const unsigned char *addr, u16 flags) * const unsigned char *addr, u16 flags)
* Adds an FDB entry to dev for addr. * Adds an FDB entry to dev for addr.
* int (*ndo_fdb_del)(struct ndmsg *ndm, struct net_device *dev, * int (*ndo_fdb_del)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev,
* const unsigned char *addr) * const unsigned char *addr)
* Deletes the FDB entry from dev coresponding to addr. * Deletes the FDB entry from dev coresponding to addr.
* int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb, * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
...@@ -1008,6 +1009,7 @@ struct net_device_ops { ...@@ -1008,6 +1009,7 @@ struct net_device_ops {
const unsigned char *addr, const unsigned char *addr,
u16 flags); u16 flags);
int (*ndo_fdb_del)(struct ndmsg *ndm, int (*ndo_fdb_del)(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr); const unsigned char *addr);
int (*ndo_fdb_dump)(struct sk_buff *skb, int (*ndo_fdb_dump)(struct sk_buff *skb,
...@@ -1019,7 +1021,10 @@ struct net_device_ops { ...@@ -1019,7 +1021,10 @@ struct net_device_ops {
struct nlmsghdr *nlh); struct nlmsghdr *nlh);
int (*ndo_bridge_getlink)(struct sk_buff *skb, int (*ndo_bridge_getlink)(struct sk_buff *skb,
u32 pid, u32 seq, u32 pid, u32 seq,
struct net_device *dev); struct net_device *dev,
u32 filter_mask);
int (*ndo_bridge_dellink)(struct net_device *dev,
struct nlmsghdr *nlh);
int (*ndo_change_carrier)(struct net_device *dev, int (*ndo_change_carrier)(struct net_device *dev,
bool new_carrier); bool new_carrier);
}; };
......
...@@ -108,15 +108,26 @@ struct __fdb_entry { ...@@ -108,15 +108,26 @@ struct __fdb_entry {
* [IFLA_AF_SPEC] = { * [IFLA_AF_SPEC] = {
* [IFLA_BRIDGE_FLAGS] * [IFLA_BRIDGE_FLAGS]
* [IFLA_BRIDGE_MODE] * [IFLA_BRIDGE_MODE]
* [IFLA_BRIDGE_VLAN_INFO]
* } * }
*/ */
enum { enum {
IFLA_BRIDGE_FLAGS, IFLA_BRIDGE_FLAGS,
IFLA_BRIDGE_MODE, IFLA_BRIDGE_MODE,
IFLA_BRIDGE_VLAN_INFO,
__IFLA_BRIDGE_MAX, __IFLA_BRIDGE_MAX,
}; };
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */
#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */
#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */
struct bridge_vlan_info {
u16 flags;
u16 vid;
};
/* Bridge multicast database attributes /* Bridge multicast database attributes
* [MDBA_MDB] = { * [MDBA_MDB] = {
* [MDBA_MDB_ENTRY] = { * [MDBA_MDB_ENTRY] = {
......
...@@ -20,6 +20,7 @@ enum { ...@@ -20,6 +20,7 @@ enum {
NDA_LLADDR, NDA_LLADDR,
NDA_CACHEINFO, NDA_CACHEINFO,
NDA_PROBES, NDA_PROBES,
NDA_VLAN,
__NDA_MAX __NDA_MAX
}; };
......
...@@ -630,6 +630,7 @@ struct tcamsg { ...@@ -630,6 +630,7 @@ struct tcamsg {
/* New extended info filters for IFLA_EXT_MASK */ /* New extended info filters for IFLA_EXT_MASK */
#define RTEXT_FILTER_VF (1 << 0) #define RTEXT_FILTER_VF (1 << 0)
#define RTEXT_FILTER_BRVLAN (1 << 1)
/* End of information exported to user level */ /* End of information exported to user level */
......
...@@ -144,6 +144,7 @@ struct sk_buff *vlan_untag(struct sk_buff *skb) ...@@ -144,6 +144,7 @@ struct sk_buff *vlan_untag(struct sk_buff *skb)
kfree_skb(skb); kfree_skb(skb);
return NULL; return NULL;
} }
EXPORT_SYMBOL(vlan_untag);
/* /*
......
...@@ -46,3 +46,17 @@ config BRIDGE_IGMP_SNOOPING ...@@ -46,3 +46,17 @@ config BRIDGE_IGMP_SNOOPING
Say N to exclude this support and reduce the binary size. Say N to exclude this support and reduce the binary size.
If unsure, say Y. If unsure, say Y.
config BRIDGE_VLAN_FILTERING
bool "VLAN filtering"
depends on BRIDGE
depends on VLAN_8021Q
default n
---help---
If you say Y here, then the Ethernet bridge will be able selectively
receive and forward traffic based on VLAN information in the packet
any VLAN information configured on the bridge port or bridge device.
Say N to exclude this support and reduce the binary size.
If unsure, say Y.
...@@ -14,4 +14,6 @@ bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o ...@@ -14,4 +14,6 @@ bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
...@@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
struct net_bridge_fdb_entry *dst; struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst; struct net_bridge_mdb_entry *mdst;
struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
u16 vid = 0;
rcu_read_lock(); rcu_read_lock();
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
...@@ -45,6 +46,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -45,6 +46,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
brstats->tx_bytes += skb->len; brstats->tx_bytes += skb->len;
u64_stats_update_end(&brstats->syncp); u64_stats_update_end(&brstats->syncp);
if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))
goto out;
BR_INPUT_SKB_CB(skb)->brdev = dev; BR_INPUT_SKB_CB(skb)->brdev = dev;
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
...@@ -67,7 +71,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -67,7 +71,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
br_multicast_deliver(mdst, skb); br_multicast_deliver(mdst, skb);
else else
br_flood_deliver(br, skb); br_flood_deliver(br, skb);
} else if ((dst = __br_fdb_get(br, dest)) != NULL) } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL)
br_deliver(dst->dst, skb); br_deliver(dst->dst, skb);
else else
br_flood_deliver(br, skb); br_flood_deliver(br, skb);
...@@ -313,6 +317,7 @@ static const struct net_device_ops br_netdev_ops = { ...@@ -313,6 +317,7 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_fdb_dump = br_fdb_dump, .ndo_fdb_dump = br_fdb_dump,
.ndo_bridge_getlink = br_getlink, .ndo_bridge_getlink = br_getlink,
.ndo_bridge_setlink = br_setlink, .ndo_bridge_setlink = br_setlink,
.ndo_bridge_dellink = br_dellink,
}; };
static void br_dev_free(struct net_device *dev) static void br_dev_free(struct net_device *dev)
......
This diff is collapsed.
...@@ -31,6 +31,7 @@ static inline int should_deliver(const struct net_bridge_port *p, ...@@ -31,6 +31,7 @@ static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb) const struct sk_buff *skb)
{ {
return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) &&
p->state == BR_STATE_FORWARDING); p->state == BR_STATE_FORWARDING);
} }
...@@ -63,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb) ...@@ -63,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb)
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
if (!skb)
return;
skb->dev = to->dev; skb->dev = to->dev;
if (unlikely(netpoll_tx_running(to->br->dev))) { if (unlikely(netpoll_tx_running(to->br->dev))) {
...@@ -88,6 +93,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) ...@@ -88,6 +93,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
return; return;
} }
skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
if (!skb)
return;
indev = skb->dev; indev = skb->dev;
skb->dev = to->dev; skb->dev = to->dev;
skb_forward_csum(skb); skb_forward_csum(skb);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <net/sock.h> #include <net/sock.h>
#include <linux/if_vlan.h>
#include "br_private.h" #include "br_private.h"
...@@ -139,6 +140,7 @@ static void del_nbp(struct net_bridge_port *p) ...@@ -139,6 +140,7 @@ static void del_nbp(struct net_bridge_port *p)
br_ifinfo_notify(RTM_DELLINK, p); br_ifinfo_notify(RTM_DELLINK, p);
nbp_vlan_flush(p);
br_fdb_delete_by_port(br, p, 1); br_fdb_delete_by_port(br, p, 1);
list_del_rcu(&p->list); list_del_rcu(&p->list);
...@@ -395,7 +397,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -395,7 +397,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
dev_set_mtu(br->dev, br_min_mtu(br)); dev_set_mtu(br->dev, br_min_mtu(br));
if (br_fdb_insert(br, p, dev->dev_addr)) if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n"); netdev_err(dev, "failed insert local address bridge forwarding table\n");
kobject_uevent(&p->kobj, KOBJ_ADD); kobject_uevent(&p->kobj, KOBJ_ADD);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/rculist.h>
#include "br_private.h" #include "br_private.h"
/* Hook for brouter */ /* Hook for brouter */
...@@ -34,6 +35,20 @@ static int br_pass_frame_up(struct sk_buff *skb) ...@@ -34,6 +35,20 @@ static int br_pass_frame_up(struct sk_buff *skb)
brstats->rx_bytes += skb->len; brstats->rx_bytes += skb->len;
u64_stats_update_end(&brstats->syncp); u64_stats_update_end(&brstats->syncp);
/* Bridge is just like any other port. Make sure the
* packet is allowed except in promisc modue when someone
* may be running packet capture.
*/
if (!(brdev->flags & IFF_PROMISC) &&
!br_allowed_egress(br, br_get_vlan_info(br), skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
skb = br_handle_vlan(br, br_get_vlan_info(br), skb);
if (!skb)
return NET_RX_DROP;
indev = skb->dev; indev = skb->dev;
skb->dev = brdev; skb->dev = brdev;
...@@ -50,13 +65,17 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -50,13 +65,17 @@ int br_handle_frame_finish(struct sk_buff *skb)
struct net_bridge_fdb_entry *dst; struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst; struct net_bridge_mdb_entry *mdst;
struct sk_buff *skb2; struct sk_buff *skb2;
u16 vid = 0;
if (!p || p->state == BR_STATE_DISABLED) if (!p || p->state == BR_STATE_DISABLED)
goto drop; goto drop;
if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid))
goto drop;
/* insert into forwarding database after filtering to avoid spoofing */ /* insert into forwarding database after filtering to avoid spoofing */
br = p->br; br = p->br;
br_fdb_update(br, p, eth_hdr(skb)->h_source); br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
br_multicast_rcv(br, p, skb)) br_multicast_rcv(br, p, skb))
...@@ -91,7 +110,8 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -91,7 +110,8 @@ int br_handle_frame_finish(struct sk_buff *skb)
skb2 = skb; skb2 = skb;
br->dev->stats.multicast++; br->dev->stats.multicast++;
} else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { } else if ((dst = __br_fdb_get(br, dest, vid)) &&
dst->is_local) {
skb2 = skb; skb2 = skb;
/* Do not forward the packet since it's local. */ /* Do not forward the packet since it's local. */
skb = NULL; skb = NULL;
...@@ -119,8 +139,10 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -119,8 +139,10 @@ int br_handle_frame_finish(struct sk_buff *skb)
static int br_handle_local_finish(struct sk_buff *skb) static int br_handle_local_finish(struct sk_buff *skb)
{ {
struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge_port *p = br_port_get_rcu(skb->dev);
u16 vid = 0;
br_fdb_update(p->br, p, eth_hdr(skb)->h_source); br_vlan_get_tag(skb, &vid);
br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid);
return 0; /* process further */ return 0; /* process further */
} }
......
...@@ -39,6 +39,8 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) ...@@ -39,6 +39,8 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
{ {
if (a->proto != b->proto) if (a->proto != b->proto)
return 0; return 0;
if (a->vid != b->vid)
return 0;
switch (a->proto) { switch (a->proto) {
case htons(ETH_P_IP): case htons(ETH_P_IP):
return a->u.ip4 == b->u.ip4; return a->u.ip4 == b->u.ip4;
...@@ -50,16 +52,19 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) ...@@ -50,16 +52,19 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
return 0; return 0;
} }
static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip) static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip,
__u16 vid)
{ {
return jhash_1word(mdb->secret, (__force u32)ip) & (mdb->max - 1); return jhash_2words((__force u32)ip, vid, mdb->secret) & (mdb->max - 1);
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb, static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb,
const struct in6_addr *ip) const struct in6_addr *ip,
__u16 vid)
{ {
return jhash2((__force u32 *)ip->s6_addr32, 4, mdb->secret) & (mdb->max - 1); return jhash_2words(ipv6_addr_hash(ip), vid,
mdb->secret) & (mdb->max - 1);
} }
#endif #endif
...@@ -68,10 +73,10 @@ static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb, ...@@ -68,10 +73,10 @@ static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
{ {
switch (ip->proto) { switch (ip->proto) {
case htons(ETH_P_IP): case htons(ETH_P_IP):
return __br_ip4_hash(mdb, ip->u.ip4); return __br_ip4_hash(mdb, ip->u.ip4, ip->vid);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6): case htons(ETH_P_IPV6):
return __br_ip6_hash(mdb, &ip->u.ip6); return __br_ip6_hash(mdb, &ip->u.ip6, ip->vid);
#endif #endif
} }
return 0; return 0;
...@@ -101,24 +106,27 @@ struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, ...@@ -101,24 +106,27 @@ struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb,
} }
static struct net_bridge_mdb_entry *br_mdb_ip4_get( static struct net_bridge_mdb_entry *br_mdb_ip4_get(
struct net_bridge_mdb_htable *mdb, __be32 dst) struct net_bridge_mdb_htable *mdb, __be32 dst, __u16 vid)
{ {
struct br_ip br_dst; struct br_ip br_dst;
br_dst.u.ip4 = dst; br_dst.u.ip4 = dst;
br_dst.proto = htons(ETH_P_IP); br_dst.proto = htons(ETH_P_IP);
br_dst.vid = vid;
return br_mdb_ip_get(mdb, &br_dst); return br_mdb_ip_get(mdb, &br_dst);
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
static struct net_bridge_mdb_entry *br_mdb_ip6_get( static struct net_bridge_mdb_entry *br_mdb_ip6_get(
struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst) struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst,
__u16 vid)
{ {
struct br_ip br_dst; struct br_ip br_dst;
br_dst.u.ip6 = *dst; br_dst.u.ip6 = *dst;
br_dst.proto = htons(ETH_P_IPV6); br_dst.proto = htons(ETH_P_IPV6);
br_dst.vid = vid;
return br_mdb_ip_get(mdb, &br_dst); return br_mdb_ip_get(mdb, &br_dst);
} }
...@@ -694,7 +702,8 @@ static int br_multicast_add_group(struct net_bridge *br, ...@@ -694,7 +702,8 @@ static int br_multicast_add_group(struct net_bridge *br,
static int br_ip4_multicast_add_group(struct net_bridge *br, static int br_ip4_multicast_add_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
__be32 group) __be32 group,
__u16 vid)
{ {
struct br_ip br_group; struct br_ip br_group;
...@@ -703,6 +712,7 @@ static int br_ip4_multicast_add_group(struct net_bridge *br, ...@@ -703,6 +712,7 @@ static int br_ip4_multicast_add_group(struct net_bridge *br,
br_group.u.ip4 = group; br_group.u.ip4 = group;
br_group.proto = htons(ETH_P_IP); br_group.proto = htons(ETH_P_IP);
br_group.vid = vid;
return br_multicast_add_group(br, port, &br_group); return br_multicast_add_group(br, port, &br_group);
} }
...@@ -710,7 +720,8 @@ static int br_ip4_multicast_add_group(struct net_bridge *br, ...@@ -710,7 +720,8 @@ static int br_ip4_multicast_add_group(struct net_bridge *br,
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
static int br_ip6_multicast_add_group(struct net_bridge *br, static int br_ip6_multicast_add_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
const struct in6_addr *group) const struct in6_addr *group,
__u16 vid)
{ {
struct br_ip br_group; struct br_ip br_group;
...@@ -719,6 +730,7 @@ static int br_ip6_multicast_add_group(struct net_bridge *br, ...@@ -719,6 +730,7 @@ static int br_ip6_multicast_add_group(struct net_bridge *br,
br_group.u.ip6 = *group; br_group.u.ip6 = *group;
br_group.proto = htons(ETH_P_IPV6); br_group.proto = htons(ETH_P_IPV6);
br_group.vid = vid;
return br_multicast_add_group(br, port, &br_group); return br_multicast_add_group(br, port, &br_group);
} }
...@@ -895,10 +907,12 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, ...@@ -895,10 +907,12 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
int type; int type;
int err = 0; int err = 0;
__be32 group; __be32 group;
u16 vid = 0;
if (!pskb_may_pull(skb, sizeof(*ih))) if (!pskb_may_pull(skb, sizeof(*ih)))
return -EINVAL; return -EINVAL;
br_vlan_get_tag(skb, &vid);
ih = igmpv3_report_hdr(skb); ih = igmpv3_report_hdr(skb);
num = ntohs(ih->ngrec); num = ntohs(ih->ngrec);
len = sizeof(*ih); len = sizeof(*ih);
...@@ -930,7 +944,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, ...@@ -930,7 +944,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
continue; continue;
} }
err = br_ip4_multicast_add_group(br, port, group); err = br_ip4_multicast_add_group(br, port, group, vid);
if (err) if (err)
break; break;
} }
...@@ -949,10 +963,12 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, ...@@ -949,10 +963,12 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
int len; int len;
int num; int num;
int err = 0; int err = 0;
u16 vid = 0;
if (!pskb_may_pull(skb, sizeof(*icmp6h))) if (!pskb_may_pull(skb, sizeof(*icmp6h)))
return -EINVAL; return -EINVAL;
br_vlan_get_tag(skb, &vid);
icmp6h = icmp6_hdr(skb); icmp6h = icmp6_hdr(skb);
num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
len = sizeof(*icmp6h); len = sizeof(*icmp6h);
...@@ -990,7 +1006,8 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, ...@@ -990,7 +1006,8 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
continue; continue;
} }
err = br_ip6_multicast_add_group(br, port, &grec->grec_mca); err = br_ip6_multicast_add_group(br, port, &grec->grec_mca,
vid);
if (!err) if (!err)
break; break;
} }
...@@ -1074,6 +1091,7 @@ static int br_ip4_multicast_query(struct net_bridge *br, ...@@ -1074,6 +1091,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
unsigned long now = jiffies; unsigned long now = jiffies;
__be32 group; __be32 group;
int err = 0; int err = 0;
u16 vid = 0;
spin_lock(&br->multicast_lock); spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || if (!netif_running(br->dev) ||
...@@ -1108,7 +1126,8 @@ static int br_ip4_multicast_query(struct net_bridge *br, ...@@ -1108,7 +1126,8 @@ static int br_ip4_multicast_query(struct net_bridge *br,
if (!group) if (!group)
goto out; goto out;
mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group); br_vlan_get_tag(skb, &vid);
mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
if (!mp) if (!mp)
goto out; goto out;
...@@ -1149,6 +1168,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, ...@@ -1149,6 +1168,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
unsigned long now = jiffies; unsigned long now = jiffies;
const struct in6_addr *group = NULL; const struct in6_addr *group = NULL;
int err = 0; int err = 0;
u16 vid = 0;
spin_lock(&br->multicast_lock); spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || if (!netif_running(br->dev) ||
...@@ -1180,7 +1200,8 @@ static int br_ip6_multicast_query(struct net_bridge *br, ...@@ -1180,7 +1200,8 @@ static int br_ip6_multicast_query(struct net_bridge *br,
if (!group) if (!group)
goto out; goto out;
mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group); br_vlan_get_tag(skb, &vid);
mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
if (!mp) if (!mp)
goto out; goto out;
...@@ -1286,7 +1307,8 @@ static void br_multicast_leave_group(struct net_bridge *br, ...@@ -1286,7 +1307,8 @@ static void br_multicast_leave_group(struct net_bridge *br,
static void br_ip4_multicast_leave_group(struct net_bridge *br, static void br_ip4_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
__be32 group) __be32 group,
__u16 vid)
{ {
struct br_ip br_group; struct br_ip br_group;
...@@ -1295,6 +1317,7 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br, ...@@ -1295,6 +1317,7 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
br_group.u.ip4 = group; br_group.u.ip4 = group;
br_group.proto = htons(ETH_P_IP); br_group.proto = htons(ETH_P_IP);
br_group.vid = vid;
br_multicast_leave_group(br, port, &br_group); br_multicast_leave_group(br, port, &br_group);
} }
...@@ -1302,7 +1325,8 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br, ...@@ -1302,7 +1325,8 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
static void br_ip6_multicast_leave_group(struct net_bridge *br, static void br_ip6_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
const struct in6_addr *group) const struct in6_addr *group,
__u16 vid)
{ {
struct br_ip br_group; struct br_ip br_group;
...@@ -1311,6 +1335,7 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br, ...@@ -1311,6 +1335,7 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
br_group.u.ip6 = *group; br_group.u.ip6 = *group;
br_group.proto = htons(ETH_P_IPV6); br_group.proto = htons(ETH_P_IPV6);
br_group.vid = vid;
br_multicast_leave_group(br, port, &br_group); br_multicast_leave_group(br, port, &br_group);
} }
...@@ -1326,6 +1351,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ...@@ -1326,6 +1351,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
unsigned int len; unsigned int len;
unsigned int offset; unsigned int offset;
int err; int err;
u16 vid = 0;
/* We treat OOM as packet loss for now. */ /* We treat OOM as packet loss for now. */
if (!pskb_may_pull(skb, sizeof(*iph))) if (!pskb_may_pull(skb, sizeof(*iph)))
...@@ -1386,6 +1412,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ...@@ -1386,6 +1412,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
err = 0; err = 0;
br_vlan_get_tag(skb2, &vid);
BR_INPUT_SKB_CB(skb)->igmp = 1; BR_INPUT_SKB_CB(skb)->igmp = 1;
ih = igmp_hdr(skb2); ih = igmp_hdr(skb2);
...@@ -1393,7 +1420,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ...@@ -1393,7 +1420,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
case IGMP_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_MEMBERSHIP_REPORT:
case IGMPV2_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT:
BR_INPUT_SKB_CB(skb)->mrouters_only = 1; BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
err = br_ip4_multicast_add_group(br, port, ih->group); err = br_ip4_multicast_add_group(br, port, ih->group, vid);
break; break;
case IGMPV3_HOST_MEMBERSHIP_REPORT: case IGMPV3_HOST_MEMBERSHIP_REPORT:
err = br_ip4_multicast_igmp3_report(br, port, skb2); err = br_ip4_multicast_igmp3_report(br, port, skb2);
...@@ -1402,7 +1429,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ...@@ -1402,7 +1429,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
err = br_ip4_multicast_query(br, port, skb2); err = br_ip4_multicast_query(br, port, skb2);
break; break;
case IGMP_HOST_LEAVE_MESSAGE: case IGMP_HOST_LEAVE_MESSAGE:
br_ip4_multicast_leave_group(br, port, ih->group); br_ip4_multicast_leave_group(br, port, ih->group, vid);
break; break;
} }
...@@ -1427,6 +1454,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1427,6 +1454,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
unsigned int len; unsigned int len;
int offset; int offset;
int err; int err;
u16 vid = 0;
if (!pskb_may_pull(skb, sizeof(*ip6h))) if (!pskb_may_pull(skb, sizeof(*ip6h)))
return -EINVAL; return -EINVAL;
...@@ -1510,6 +1538,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1510,6 +1538,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
err = 0; err = 0;
br_vlan_get_tag(skb, &vid);
BR_INPUT_SKB_CB(skb)->igmp = 1; BR_INPUT_SKB_CB(skb)->igmp = 1;
switch (icmp6_type) { switch (icmp6_type) {
...@@ -1522,7 +1551,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1522,7 +1551,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
} }
mld = (struct mld_msg *)skb_transport_header(skb2); mld = (struct mld_msg *)skb_transport_header(skb2);
BR_INPUT_SKB_CB(skb)->mrouters_only = 1; BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
err = br_ip6_multicast_add_group(br, port, &mld->mld_mca); err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
break; break;
} }
case ICMPV6_MLD2_REPORT: case ICMPV6_MLD2_REPORT:
...@@ -1539,7 +1568,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1539,7 +1568,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
goto out; goto out;
} }
mld = (struct mld_msg *)skb_transport_header(skb2); mld = (struct mld_msg *)skb_transport_header(skb2);
br_ip6_multicast_leave_group(br, port, &mld->mld_mca); br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid);
} }
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
#include <uapi/linux/if_bridge.h>
#include "br_private.h" #include "br_private.h"
#include "br_private_stp.h" #include "br_private_stp.h"
...@@ -64,15 +65,21 @@ static int br_port_fill_attrs(struct sk_buff *skb, ...@@ -64,15 +65,21 @@ static int br_port_fill_attrs(struct sk_buff *skb,
* Create one netlink message for one interface * Create one netlink message for one interface
* Contains port and master info as well as carrier and bridge state. * 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, static int br_fill_ifinfo(struct sk_buff *skb,
u32 pid, u32 seq, int event, unsigned int flags) const struct net_bridge_port *port,
u32 pid, u32 seq, int event, unsigned int flags,
u32 filter_mask, const struct net_device *dev)
{ {
const struct net_bridge *br = port->br; const struct net_bridge *br;
const struct net_device *dev = port->dev;
struct ifinfomsg *hdr; struct ifinfomsg *hdr;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
if (port)
br = port->br;
else
br = netdev_priv(dev);
br_debug(br, "br_fill_info event %d port %s master %s\n", br_debug(br, "br_fill_info event %d port %s master %s\n",
event, dev->name, br->dev->name); event, dev->name, br->dev->name);
...@@ -98,7 +105,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por ...@@ -98,7 +105,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
nla_put_u32(skb, IFLA_LINK, dev->iflink))) nla_put_u32(skb, IFLA_LINK, dev->iflink)))
goto nla_put_failure; goto nla_put_failure;
if (event == RTM_NEWLINK) { if (event == RTM_NEWLINK && port) {
struct nlattr *nest struct nlattr *nest
= nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED); = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED);
...@@ -107,6 +114,48 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por ...@@ -107,6 +114,48 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
} }
/* Check if the VID information is requested */
if (filter_mask & RTEXT_FILTER_BRVLAN) {
struct nlattr *af;
const struct net_port_vlans *pv;
struct bridge_vlan_info vinfo;
u16 vid;
u16 pvid;
if (port)
pv = nbp_get_vlan_info(port);
else
pv = br_get_vlan_info(br);
if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN))
goto done;
af = nla_nest_start(skb, IFLA_AF_SPEC);
if (!af)
goto nla_put_failure;
pvid = br_get_pvid(pv);
for (vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
vid < BR_VLAN_BITMAP_LEN;
vid = find_next_bit(pv->vlan_bitmap,
BR_VLAN_BITMAP_LEN, vid+1)) {
vinfo.vid = vid;
vinfo.flags = 0;
if (vid == pvid)
vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
if (test_bit(vid, pv->untagged_bitmap))
vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
}
nla_nest_end(skb, af);
}
done:
return nlmsg_end(skb, nlh); return nlmsg_end(skb, nlh);
nla_put_failure: nla_put_failure:
...@@ -119,10 +168,14 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por ...@@ -119,10 +168,14 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
*/ */
void br_ifinfo_notify(int event, struct net_bridge_port *port) void br_ifinfo_notify(int event, struct net_bridge_port *port)
{ {
struct net *net = dev_net(port->dev); struct net *net;
struct sk_buff *skb; struct sk_buff *skb;
int err = -ENOBUFS; int err = -ENOBUFS;
if (!port)
return;
net = dev_net(port->dev);
br_debug(port->br, "port %u(%s) event %d\n", br_debug(port->br, "port %u(%s) event %d\n",
(unsigned int)port->port_no, port->dev->name, event); (unsigned int)port->port_no, port->dev->name, event);
...@@ -130,7 +183,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port) ...@@ -130,7 +183,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
if (skb == NULL) if (skb == NULL)
goto errout; goto errout;
err = br_fill_ifinfo(skb, port, 0, 0, event, 0); err = br_fill_ifinfo(skb, port, 0, 0, event, 0, 0, port->dev);
if (err < 0) { if (err < 0) {
/* -EMSGSIZE implies BUG in br_nlmsg_size() */ /* -EMSGSIZE implies BUG in br_nlmsg_size() */
WARN_ON(err == -EMSGSIZE); WARN_ON(err == -EMSGSIZE);
...@@ -144,24 +197,85 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port) ...@@ -144,24 +197,85 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
rtnl_set_sk_err(net, RTNLGRP_LINK, err); rtnl_set_sk_err(net, RTNLGRP_LINK, err);
} }
/* /*
* Dump information about all ports, in response to GETLINK * Dump information about all ports, in response to GETLINK
*/ */
int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev) struct net_device *dev, u32 filter_mask)
{ {
int err = 0; int err = 0;
struct net_bridge_port *port = br_port_get_rcu(dev); struct net_bridge_port *port = br_port_get_rcu(dev);
/* not a bridge port */ /* not a bridge port and */
if (!port) if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN))
goto out; goto out;
err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI); err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI,
filter_mask, dev);
out: out:
return err; return err;
} }
const struct nla_policy ifla_br_policy[IFLA_MAX+1] = {
[IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 },
[IFLA_BRIDGE_MODE] = { .type = NLA_U16 },
[IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY,
.len = sizeof(struct bridge_vlan_info), },
};
static int br_afspec(struct net_bridge *br,
struct net_bridge_port *p,
struct nlattr *af_spec,
int cmd)
{
struct nlattr *tb[IFLA_BRIDGE_MAX+1];
int err = 0;
err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy);
if (err)
return err;
if (tb[IFLA_BRIDGE_VLAN_INFO]) {
struct bridge_vlan_info *vinfo;
vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
if (vinfo->vid >= VLAN_N_VID)
return -EINVAL;
switch (cmd) {
case RTM_SETLINK:
if (p) {
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
if (err)
break;
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
err = br_vlan_add(p->br, vinfo->vid,
vinfo->flags);
} else
err = br_vlan_add(br, vinfo->vid, vinfo->flags);
if (err)
break;
break;
case RTM_DELLINK:
if (p) {
nbp_vlan_delete(p, vinfo->vid);
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
br_vlan_delete(p->br, vinfo->vid);
} else
br_vlan_delete(br, vinfo->vid);
break;
}
}
return err;
}
static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
[IFLA_BRPORT_STATE] = { .type = NLA_U8 }, [IFLA_BRPORT_STATE] = { .type = NLA_U8 },
[IFLA_BRPORT_COST] = { .type = NLA_U32 }, [IFLA_BRPORT_COST] = { .type = NLA_U32 },
...@@ -241,6 +355,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) ...@@ -241,6 +355,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
{ {
struct ifinfomsg *ifm; struct ifinfomsg *ifm;
struct nlattr *protinfo; struct nlattr *protinfo;
struct nlattr *afspec;
struct net_bridge_port *p; struct net_bridge_port *p;
struct nlattr *tb[IFLA_BRPORT_MAX + 1]; struct nlattr *tb[IFLA_BRPORT_MAX + 1];
int err; int err;
...@@ -248,13 +363,18 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) ...@@ -248,13 +363,18 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
ifm = nlmsg_data(nlh); ifm = nlmsg_data(nlh);
protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
if (!protinfo) afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
if (!protinfo && !afspec)
return 0; return 0;
p = br_port_get_rtnl(dev); p = br_port_get_rtnl(dev);
if (!p) /* We want to accept dev as bridge itself if the AF_SPEC
* is set to see if someone is setting vlan info on the brigde
*/
if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec))
return -EINVAL; return -EINVAL;
if (p && protinfo) {
if (protinfo->nla_type & NLA_F_NESTED) { if (protinfo->nla_type & NLA_F_NESTED) {
err = nla_parse_nested(tb, IFLA_BRPORT_MAX, err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
protinfo, ifla_brport_policy); protinfo, ifla_brport_policy);
...@@ -273,13 +393,46 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) ...@@ -273,13 +393,46 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
err = br_set_port_state(p, nla_get_u8(protinfo)); err = br_set_port_state(p, nla_get_u8(protinfo));
spin_unlock_bh(&p->br->lock); spin_unlock_bh(&p->br->lock);
} }
if (err)
goto out;
}
if (afspec) {
err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
afspec, RTM_SETLINK);
}
if (err == 0) if (err == 0)
br_ifinfo_notify(RTM_NEWLINK, p); br_ifinfo_notify(RTM_NEWLINK, p);
out:
return err; return err;
} }
/* Delete port information */
int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
{
struct ifinfomsg *ifm;
struct nlattr *afspec;
struct net_bridge_port *p;
int err;
ifm = nlmsg_data(nlh);
afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
if (!afspec)
return 0;
p = br_port_get_rtnl(dev);
/* We want to accept dev as bridge itself as well */
if (!p && !(dev->priv_flags & IFF_EBRIDGE))
return -EINVAL;
err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
afspec, RTM_DELLINK);
return err;
}
static int br_validate(struct nlattr *tb[], struct nlattr *data[]) static int br_validate(struct nlattr *tb[], struct nlattr *data[])
{ {
if (tb[IFLA_ADDRESS]) { if (tb[IFLA_ADDRESS]) {
...@@ -292,6 +445,29 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[]) ...@@ -292,6 +445,29 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[])
return 0; return 0;
} }
static size_t br_get_link_af_size(const struct net_device *dev)
{
struct net_port_vlans *pv;
if (br_port_exists(dev))
pv = nbp_get_vlan_info(br_port_get_rcu(dev));
else if (dev->priv_flags & IFF_EBRIDGE)
pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
else
return 0;
if (!pv)
return 0;
/* Each VLAN is returned in bridge_vlan_info along with flags */
return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
}
struct rtnl_af_ops br_af_ops = {
.family = AF_BRIDGE,
.get_link_af_size = br_get_link_af_size,
};
struct rtnl_link_ops br_link_ops __read_mostly = { struct rtnl_link_ops br_link_ops __read_mostly = {
.kind = "bridge", .kind = "bridge",
.priv_size = sizeof(struct net_bridge), .priv_size = sizeof(struct net_bridge),
...@@ -305,11 +481,18 @@ int __init br_netlink_init(void) ...@@ -305,11 +481,18 @@ int __init br_netlink_init(void)
int err; int err;
br_mdb_init(); br_mdb_init();
err = rtnl_link_register(&br_link_ops); err = rtnl_af_register(&br_af_ops);
if (err) if (err)
goto out; goto out;
err = rtnl_link_register(&br_link_ops);
if (err)
goto out_af;
return 0; return 0;
out_af:
rtnl_af_unregister(&br_af_ops);
out: out:
br_mdb_uninit(); br_mdb_uninit();
return err; return err;
...@@ -318,5 +501,6 @@ int __init br_netlink_init(void) ...@@ -318,5 +501,6 @@ int __init br_netlink_init(void)
void __exit br_netlink_fini(void) void __exit br_netlink_fini(void)
{ {
br_mdb_uninit(); br_mdb_uninit();
rtnl_af_unregister(&br_af_ops);
rtnl_link_unregister(&br_link_ops); rtnl_link_unregister(&br_link_ops);
} }
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/netpoll.h> #include <linux/netpoll.h>
#include <linux/u64_stats_sync.h> #include <linux/u64_stats_sync.h>
#include <net/route.h> #include <net/route.h>
#include <linux/if_vlan.h>
#define BR_HASH_BITS 8 #define BR_HASH_BITS 8
#define BR_HASH_SIZE (1 << BR_HASH_BITS) #define BR_HASH_SIZE (1 << BR_HASH_BITS)
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
#define BR_PORT_BITS 10 #define BR_PORT_BITS 10
#define BR_MAX_PORTS (1<<BR_PORT_BITS) #define BR_MAX_PORTS (1<<BR_PORT_BITS)
#define BR_VLAN_BITMAP_LEN BITS_TO_LONGS(VLAN_N_VID)
#define BR_VERSION "2.3" #define BR_VERSION "2.3"
...@@ -61,6 +63,20 @@ struct br_ip ...@@ -61,6 +63,20 @@ struct br_ip
#endif #endif
} u; } u;
__be16 proto; __be16 proto;
__u16 vid;
};
struct net_port_vlans {
u16 port_idx;
u16 pvid;
union {
struct net_bridge_port *port;
struct net_bridge *br;
} parent;
struct rcu_head rcu;
unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN];
unsigned long untagged_bitmap[BR_VLAN_BITMAP_LEN];
u16 num_vlans;
}; };
struct net_bridge_fdb_entry struct net_bridge_fdb_entry
...@@ -74,6 +90,7 @@ struct net_bridge_fdb_entry ...@@ -74,6 +90,7 @@ struct net_bridge_fdb_entry
mac_addr addr; mac_addr addr;
unsigned char is_local; unsigned char is_local;
unsigned char is_static; unsigned char is_static;
__u16 vlan_id;
}; };
struct net_bridge_port_group { struct net_bridge_port_group {
...@@ -156,6 +173,9 @@ struct net_bridge_port ...@@ -156,6 +173,9 @@ struct net_bridge_port
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
struct netpoll *np; struct netpoll *np;
#endif #endif
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
struct net_port_vlans __rcu *vlan_info;
#endif
}; };
#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT) #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
...@@ -257,6 +277,10 @@ struct net_bridge ...@@ -257,6 +277,10 @@ struct net_bridge
struct timer_list topology_change_timer; struct timer_list topology_change_timer;
struct timer_list gc_timer; struct timer_list gc_timer;
struct kobject *ifobj; struct kobject *ifobj;
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
u8 vlan_enabled;
struct net_port_vlans __rcu *vlan_info;
#endif
}; };
struct br_input_skb_cb { struct br_input_skb_cb {
...@@ -352,18 +376,22 @@ extern void br_fdb_cleanup(unsigned long arg); ...@@ -352,18 +376,22 @@ extern void br_fdb_cleanup(unsigned long arg);
extern void br_fdb_delete_by_port(struct net_bridge *br, extern void br_fdb_delete_by_port(struct net_bridge *br,
const struct net_bridge_port *p, int do_all); const struct net_bridge_port *p, int do_all);
extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
const unsigned char *addr); const unsigned char *addr,
__u16 vid);
extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, extern int br_fdb_fillbuf(struct net_bridge *br, void *buf,
unsigned long count, unsigned long off); unsigned long count, unsigned long off);
extern int br_fdb_insert(struct net_bridge *br, extern int br_fdb_insert(struct net_bridge *br,
struct net_bridge_port *source, struct net_bridge_port *source,
const unsigned char *addr); const unsigned char *addr,
u16 vid);
extern void br_fdb_update(struct net_bridge *br, extern void br_fdb_update(struct net_bridge *br,
struct net_bridge_port *source, struct net_bridge_port *source,
const unsigned char *addr); const unsigned char *addr,
u16 vid);
extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid);
extern int br_fdb_delete(struct ndmsg *ndm, extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr); const unsigned char *addr);
extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[],
...@@ -531,6 +559,142 @@ static inline void br_mdb_uninit(void) ...@@ -531,6 +559,142 @@ static inline void br_mdb_uninit(void)
} }
#endif #endif
/* br_vlan.c */
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
struct sk_buff *skb, u16 *vid);
extern bool br_allowed_egress(struct net_bridge *br,
const struct net_port_vlans *v,
const struct sk_buff *skb);
extern struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_port_vlans *v,
struct sk_buff *skb);
extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
extern int br_vlan_delete(struct net_bridge *br, u16 vid);
extern void br_vlan_flush(struct net_bridge *br);
extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
extern void nbp_vlan_flush(struct net_bridge_port *port);
extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);
static inline struct net_port_vlans *br_get_vlan_info(
const struct net_bridge *br)
{
return rcu_dereference_rtnl(br->vlan_info);
}
static inline struct net_port_vlans *nbp_get_vlan_info(
const struct net_bridge_port *p)
{
return rcu_dereference_rtnl(p->vlan_info);
}
/* Since bridge now depends on 8021Q module, but the time bridge sees the
* skb, the vlan tag will always be present if the frame was tagged.
*/
static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid)
{
int err = 0;
if (vlan_tx_tag_present(skb))
*vid = vlan_tx_tag_get(skb) & VLAN_VID_MASK;
else {
*vid = 0;
err = -EINVAL;
}
return err;
}
static inline u16 br_get_pvid(const struct net_port_vlans *v)
{
/* Return just the VID if it is set, or VLAN_N_VID (invalid vid) if
* vid wasn't set
*/
smp_rmb();
return (v->pvid & VLAN_TAG_PRESENT) ?
(v->pvid & ~VLAN_TAG_PRESENT) :
VLAN_N_VID;
}
#else
static inline bool br_allowed_ingress(struct net_bridge *br,
struct net_port_vlans *v,
struct sk_buff *skb,
u16 *vid)
{
return true;
}
static inline bool br_allowed_egress(struct net_bridge *br,
const struct net_port_vlans *v,
const struct sk_buff *skb)
{
return true;
}
static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_port_vlans *v,
struct sk_buff *skb)
{
return skb;
}
static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
{
return -EOPNOTSUPP;
}
static inline int br_vlan_delete(struct net_bridge *br, u16 vid)
{
return -EOPNOTSUPP;
}
static inline void br_vlan_flush(struct net_bridge *br)
{
}
static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
{
return -EOPNOTSUPP;
}
static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
{
return -EOPNOTSUPP;
}
static inline void nbp_vlan_flush(struct net_bridge_port *port)
{
}
static inline struct net_port_vlans *br_get_vlan_info(
const struct net_bridge *br)
{
return NULL;
}
static inline struct net_port_vlans *nbp_get_vlan_info(
const struct net_bridge_port *p)
{
return NULL;
}
static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
{
return false;
}
static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
{
return 0;
}
static inline u16 br_get_pvid(const struct net_port_vlans *v)
{
return VLAN_N_VID; /* Returns invalid vid */
}
#endif
/* br_netfilter.c */ /* br_netfilter.c */
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
extern int br_netfilter_init(void); extern int br_netfilter_init(void);
...@@ -591,8 +755,9 @@ extern int br_netlink_init(void); ...@@ -591,8 +755,9 @@ extern int br_netlink_init(void);
extern void br_netlink_fini(void); extern void br_netlink_fini(void);
extern void br_ifinfo_notify(int event, struct net_bridge_port *port); extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg);
extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev); struct net_device *dev, u32 filter_mask);
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
/* br_sysfs_if.c */ /* br_sysfs_if.c */
......
...@@ -691,6 +691,24 @@ static ssize_t store_nf_call_arptables( ...@@ -691,6 +691,24 @@ static ssize_t store_nf_call_arptables(
static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR, static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR,
show_nf_call_arptables, store_nf_call_arptables); show_nf_call_arptables, store_nf_call_arptables);
#endif #endif
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
static ssize_t show_vlan_filtering(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct net_bridge *br = to_bridge(d);
return sprintf(buf, "%d\n", br->vlan_enabled);
}
static ssize_t store_vlan_filtering(struct device *d,
struct device_attribute *attr,
const char *buf, size_t len)
{
return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
}
static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR,
show_vlan_filtering, store_vlan_filtering);
#endif
static struct attribute *bridge_attrs[] = { static struct attribute *bridge_attrs[] = {
&dev_attr_forward_delay.attr, &dev_attr_forward_delay.attr,
...@@ -731,6 +749,9 @@ static struct attribute *bridge_attrs[] = { ...@@ -731,6 +749,9 @@ static struct attribute *bridge_attrs[] = {
&dev_attr_nf_call_iptables.attr, &dev_attr_nf_call_iptables.attr,
&dev_attr_nf_call_ip6tables.attr, &dev_attr_nf_call_ip6tables.attr,
&dev_attr_nf_call_arptables.attr, &dev_attr_nf_call_arptables.attr,
#endif
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
&dev_attr_vlan_filtering.attr,
#endif #endif
NULL NULL
}; };
......
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include "br_private.h"
static void __vlan_add_pvid(struct net_port_vlans *v, u16 vid)
{
if (v->pvid == vid)
return;
smp_wmb();
v->pvid = vid;
}
static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
{
if (v->pvid != vid)
return;
smp_wmb();
v->pvid = 0;
}
static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
{
if (flags & BRIDGE_VLAN_INFO_PVID)
__vlan_add_pvid(v, vid);
if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
set_bit(vid, v->untagged_bitmap);
}
static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
{
struct net_bridge_port *p = NULL;
struct net_bridge *br;
struct net_device *dev;
int err;
if (test_bit(vid, v->vlan_bitmap)) {
__vlan_add_flags(v, vid, flags);
return 0;
}
if (vid) {
if (v->port_idx) {
p = v->parent.port;
br = p->br;
dev = p->dev;
} else {
br = v->parent.br;
dev = br->dev;
}
if (p && (dev->features & NETIF_F_HW_VLAN_FILTER)) {
/* Add VLAN to the device filter if it is supported.
* Stricly speaking, this is not necessary now, since
* devices are made promiscuous by the bridge, but if
* that ever changes this code will allow tagged
* traffic to enter the bridge.
*/
err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
if (err)
return err;
}
err = br_fdb_insert(br, p, dev->dev_addr, vid);
if (err) {
br_err(br, "failed insert local address into bridge "
"forwarding table\n");
goto out_filt;
}
}
set_bit(vid, v->vlan_bitmap);
v->num_vlans++;
__vlan_add_flags(v, vid, flags);
return 0;
out_filt:
if (p && (dev->features & NETIF_F_HW_VLAN_FILTER))
dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
return err;
}
static int __vlan_del(struct net_port_vlans *v, u16 vid)
{
if (!test_bit(vid, v->vlan_bitmap))
return -EINVAL;
__vlan_delete_pvid(v, vid);
clear_bit(vid, v->untagged_bitmap);
if (v->port_idx && vid) {
struct net_device *dev = v->parent.port->dev;
if (dev->features & NETIF_F_HW_VLAN_FILTER)
dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
}
clear_bit(vid, v->vlan_bitmap);
v->num_vlans--;
if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
if (v->port_idx)
rcu_assign_pointer(v->parent.port->vlan_info, NULL);
else
rcu_assign_pointer(v->parent.br->vlan_info, NULL);
kfree_rcu(v, rcu);
}
return 0;
}
static void __vlan_flush(struct net_port_vlans *v)
{
smp_wmb();
v->pvid = 0;
bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
if (v->port_idx)
rcu_assign_pointer(v->parent.port->vlan_info, NULL);
else
rcu_assign_pointer(v->parent.br->vlan_info, NULL);
kfree_rcu(v, rcu);
}
/* Strip the tag from the packet. Will return skb with tci set 0. */
static struct sk_buff *br_vlan_untag(struct sk_buff *skb)
{
if (skb->protocol != htons(ETH_P_8021Q)) {
skb->vlan_tci = 0;
return skb;
}
skb->vlan_tci = 0;
skb = vlan_untag(skb);
if (skb)
skb->vlan_tci = 0;
return skb;
}
struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_port_vlans *pv,
struct sk_buff *skb)
{
u16 vid;
if (!br->vlan_enabled)
goto out;
/* At this point, we know that the frame was filtered and contains
* a valid vlan id. If the vlan id is set in the untagged bitmap,
* send untagged; otherwise, send taged.
*/
br_vlan_get_tag(skb, &vid);
if (test_bit(vid, pv->untagged_bitmap))
skb = br_vlan_untag(skb);
else {
/* Egress policy says "send tagged". If output device
* is the bridge, we need to add the VLAN header
* ourselves since we'll be going through the RX path.
* Sending to ports puts the frame on the TX path and
* we let dev_hard_start_xmit() add the header.
*/
if (skb->protocol != htons(ETH_P_8021Q) &&
pv->port_idx == 0) {
/* vlan_put_tag expects skb->data to point to
* mac header.
*/
skb_push(skb, ETH_HLEN);
skb = __vlan_put_tag(skb, skb->vlan_tci);
if (!skb)
goto out;
/* put skb->data back to where it was */
skb_pull(skb, ETH_HLEN);
skb->vlan_tci = 0;
}
}
out:
return skb;
}
/* Called under RCU */
bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
struct sk_buff *skb, u16 *vid)
{
/* If VLAN filtering is disabled on the bridge, all packets are
* permitted.
*/
if (!br->vlan_enabled)
return true;
/* If there are no vlan in the permitted list, all packets are
* rejected.
*/
if (!v)
return false;
if (br_vlan_get_tag(skb, vid)) {
u16 pvid = br_get_pvid(v);
/* Frame did not have a tag. See if pvid is set
* on this port. That tells us which vlan untagged
* traffic belongs to.
*/
if (pvid == VLAN_N_VID)
return false;
/* PVID is set on this port. Any untagged ingress
* frame is considered to belong to this vlan.
*/
__vlan_hwaccel_put_tag(skb, pvid);
return true;
}
/* Frame had a valid vlan tag. See if vlan is allowed */
if (test_bit(*vid, v->vlan_bitmap))
return true;
return false;
}
/* Called under RCU. */
bool br_allowed_egress(struct net_bridge *br,
const struct net_port_vlans *v,
const struct sk_buff *skb)
{
u16 vid;
if (!br->vlan_enabled)
return true;
if (!v)
return false;
br_vlan_get_tag(skb, &vid);
if (test_bit(vid, v->vlan_bitmap))
return true;
return false;
}
/* Must be protected by RTNL */
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
{
struct net_port_vlans *pv = NULL;
int err;
ASSERT_RTNL();
pv = rtnl_dereference(br->vlan_info);
if (pv)
return __vlan_add(pv, vid, flags);
/* Create port vlan infomration
*/
pv = kzalloc(sizeof(*pv), GFP_KERNEL);
if (!pv)
return -ENOMEM;
pv->parent.br = br;
err = __vlan_add(pv, vid, flags);
if (err)
goto out;
rcu_assign_pointer(br->vlan_info, pv);
return 0;
out:
kfree(pv);
return err;
}
/* Must be protected by RTNL */
int br_vlan_delete(struct net_bridge *br, u16 vid)
{
struct net_port_vlans *pv;
ASSERT_RTNL();
pv = rtnl_dereference(br->vlan_info);
if (!pv)
return -EINVAL;
if (vid) {
/* If the VID !=0 remove fdb for this vid. VID 0 is special
* in that it's the default and is always there in the fdb.
*/
spin_lock_bh(&br->hash_lock);
fdb_delete_by_addr(br, br->dev->dev_addr, vid);
spin_unlock_bh(&br->hash_lock);
}
__vlan_del(pv, vid);
return 0;
}
void br_vlan_flush(struct net_bridge *br)
{
struct net_port_vlans *pv;
ASSERT_RTNL();
pv = rtnl_dereference(br->vlan_info);
if (!pv)
return;
__vlan_flush(pv);
}
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
{
if (!rtnl_trylock())
return restart_syscall();
if (br->vlan_enabled == val)
goto unlock;
br->vlan_enabled = val;
unlock:
rtnl_unlock();
return 0;
}
/* Must be protected by RTNL */
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
{
struct net_port_vlans *pv = NULL;
int err;
ASSERT_RTNL();
pv = rtnl_dereference(port->vlan_info);
if (pv)
return __vlan_add(pv, vid, flags);
/* Create port vlan infomration
*/
pv = kzalloc(sizeof(*pv), GFP_KERNEL);
if (!pv) {
err = -ENOMEM;
goto clean_up;
}
pv->port_idx = port->port_no;
pv->parent.port = port;
err = __vlan_add(pv, vid, flags);
if (err)
goto clean_up;
rcu_assign_pointer(port->vlan_info, pv);
return 0;
clean_up:
kfree(pv);
return err;
}
/* Must be protected by RTNL */
int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
{
struct net_port_vlans *pv;
ASSERT_RTNL();
pv = rtnl_dereference(port->vlan_info);
if (!pv)
return -EINVAL;
if (vid) {
/* If the VID !=0 remove fdb for this vid. VID 0 is special
* in that it's the default and is always there in the fdb.
*/
spin_lock_bh(&port->br->hash_lock);
fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
spin_unlock_bh(&port->br->hash_lock);
}
return __vlan_del(pv, vid);
}
void nbp_vlan_flush(struct net_bridge_port *port)
{
struct net_port_vlans *pv;
ASSERT_RTNL();
pv = rtnl_dereference(port->vlan_info);
if (!pv)
return;
__vlan_flush(pv);
}
bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
{
struct net_port_vlans *pv;
bool found = false;
rcu_read_lock();
pv = rcu_dereference(port->vlan_info);
if (!pv)
goto out;
if (test_bit(vid, pv->vlan_bitmap))
found = true;
out:
rcu_read_unlock();
return found;
}
...@@ -2119,13 +2119,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -2119,13 +2119,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct ndmsg *ndm; struct ndmsg *ndm;
struct nlattr *llattr; struct nlattr *tb[NDA_MAX+1];
struct net_device *dev; struct net_device *dev;
int err = -EINVAL; int err = -EINVAL;
__u8 *addr; __u8 *addr;
if (nlmsg_len(nlh) < sizeof(*ndm)) if (!capable(CAP_NET_ADMIN))
return -EINVAL; return -EPERM;
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
if (err < 0)
return err;
ndm = nlmsg_data(nlh); ndm = nlmsg_data(nlh);
if (ndm->ndm_ifindex == 0) { if (ndm->ndm_ifindex == 0) {
...@@ -2139,13 +2143,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -2139,13 +2143,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return -ENODEV; return -ENODEV;
} }
llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid address\n");
pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n"); return -EINVAL;
}
addr = nla_data(tb[NDA_LLADDR]);
if (!is_valid_ether_addr(addr)) {
pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ether address\n");
return -EINVAL; return -EINVAL;
} }
addr = nla_data(llattr);
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
/* Support fdb on master device the net/bridge default case */ /* Support fdb on master device the net/bridge default case */
...@@ -2155,7 +2163,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -2155,7 +2163,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
const struct net_device_ops *ops = br_dev->netdev_ops; const struct net_device_ops *ops = br_dev->netdev_ops;
if (ops->ndo_fdb_del) if (ops->ndo_fdb_del)
err = ops->ndo_fdb_del(ndm, dev, addr); err = ops->ndo_fdb_del(ndm, tb, dev, addr);
if (err) if (err)
goto out; goto out;
...@@ -2165,7 +2173,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -2165,7 +2173,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
/* Embedded bridge, macvlan, and any other device support */ /* Embedded bridge, macvlan, and any other device support */
if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) { if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) {
err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr); err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
if (!err) { if (!err) {
rtnl_fdb_notify(dev, addr, RTM_DELNEIGH); rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
...@@ -2315,6 +2323,13 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2315,6 +2323,13 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
int idx = 0; int idx = 0;
u32 portid = NETLINK_CB(cb->skb).portid; u32 portid = NETLINK_CB(cb->skb).portid;
u32 seq = cb->nlh->nlmsg_seq; u32 seq = cb->nlh->nlmsg_seq;
struct nlattr *extfilt;
u32 filter_mask = 0;
extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct rtgenmsg),
IFLA_EXT_MASK);
if (extfilt)
filter_mask = nla_get_u32(extfilt);
rcu_read_lock(); rcu_read_lock();
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
...@@ -2324,14 +2339,15 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2324,14 +2339,15 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
if (idx >= cb->args[0] && if (idx >= cb->args[0] &&
br_dev->netdev_ops->ndo_bridge_getlink( br_dev->netdev_ops->ndo_bridge_getlink(
skb, portid, seq, dev) < 0) skb, portid, seq, dev, filter_mask) < 0)
break; break;
idx++; idx++;
} }
if (ops->ndo_bridge_getlink) { if (ops->ndo_bridge_getlink) {
if (idx >= cb->args[0] && if (idx >= cb->args[0] &&
ops->ndo_bridge_getlink(skb, portid, seq, dev) < 0) ops->ndo_bridge_getlink(skb, portid, seq, dev,
filter_mask) < 0)
break; break;
idx++; idx++;
} }
...@@ -2372,14 +2388,14 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags) ...@@ -2372,14 +2388,14 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) &&
br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
if (err < 0) if (err < 0)
goto errout; goto errout;
} }
if ((flags & BRIDGE_FLAGS_SELF) && if ((flags & BRIDGE_FLAGS_SELF) &&
dev->netdev_ops->ndo_bridge_getlink) { dev->netdev_ops->ndo_bridge_getlink) {
err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0);
if (err < 0) if (err < 0)
goto errout; goto errout;
} }
...@@ -2464,6 +2480,77 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2464,6 +2480,77 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
return err; return err;
} }
static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
void *arg)
{
struct net *net = sock_net(skb->sk);
struct ifinfomsg *ifm;
struct net_device *dev;
struct nlattr *br_spec, *attr = NULL;
int rem, err = -EOPNOTSUPP;
u16 oflags, flags = 0;
bool have_flags = false;
if (nlmsg_len(nlh) < sizeof(*ifm))
return -EINVAL;
ifm = nlmsg_data(nlh);
if (ifm->ifi_family != AF_BRIDGE)
return -EPFNOSUPPORT;
dev = __dev_get_by_index(net, ifm->ifi_index);
if (!dev) {
pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n");
return -ENODEV;
}
br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
if (br_spec) {
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
have_flags = true;
flags = nla_get_u16(attr);
break;
}
}
}
oflags = flags;
if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
if (!br_dev || !br_dev->netdev_ops->ndo_bridge_dellink) {
err = -EOPNOTSUPP;
goto out;
}
err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
if (err)
goto out;
flags &= ~BRIDGE_FLAGS_MASTER;
}
if ((flags & BRIDGE_FLAGS_SELF)) {
if (!dev->netdev_ops->ndo_bridge_dellink)
err = -EOPNOTSUPP;
else
err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
if (!err)
flags &= ~BRIDGE_FLAGS_SELF;
}
if (have_flags)
memcpy(nla_data(attr), &flags, sizeof(flags));
/* Generate event to notify upper layer of bridge change */
if (!err)
err = rtnl_bridge_notify(dev, oflags);
out:
return err;
}
/* Protected by RTNL sempahore. */ /* Protected by RTNL sempahore. */
static struct rtattr **rta_buf; static struct rtattr **rta_buf;
static int rtattr_max; static int rtattr_max;
...@@ -2647,6 +2734,7 @@ void __init rtnetlink_init(void) ...@@ -2647,6 +2734,7 @@ void __init rtnetlink_init(void)
rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL);
rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment