diff options
Diffstat (limited to 'net/bridge')
-rw-r--r-- | net/bridge/br_fdb.c | 7 | ||||
-rw-r--r-- | net/bridge/br_if.c | 1 | ||||
-rw-r--r-- | net/bridge/br_netfilter.c | 12 | ||||
-rw-r--r-- | net/bridge/br_netlink.c | 251 | ||||
-rw-r--r-- | net/bridge/br_private.h | 4 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 4 | ||||
-rw-r--r-- | net/bridge/netfilter/ebt_vlan.c | 4 | ||||
-rw-r--r-- | net/bridge/netfilter/ebtables.c | 2 |
8 files changed, 214 insertions, 71 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index cc36e59db7d7..e6e0372bc3cd 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -686,6 +686,9 @@ int br_fdb_dump(struct sk_buff *skb, if (!(dev->priv_flags & IFF_EBRIDGE)) goto out; + if (!filter_dev) + idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); + for (i = 0; i < BR_HASH_SIZE; i++) { struct net_bridge_fdb_entry *f; @@ -697,7 +700,7 @@ int br_fdb_dump(struct sk_buff *skb, (!f->dst || f->dst->dev != filter_dev)) { if (filter_dev != dev) goto skip; - /* !f->dst is a speacial case for bridge + /* !f->dst is a special case for bridge * It means the MAC belongs to the bridge * Therefore need a little more filtering * we only want to dump the !f->dst case @@ -705,6 +708,8 @@ int br_fdb_dump(struct sk_buff *skb, if (f->dst) goto skip; } + if (!filter_dev && f->dst) + goto skip; if (fdb_fill_info(skb, br, f, NETLINK_CB(cb->skb).portid, diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index ed307db7a12b..81e49fb73169 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -424,6 +424,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br, features = netdev_increment_features(features, p->dev->features, mask); } + features = netdev_add_tso_features(features, mask); return features; } diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index c190d22b6b3d..65728e0dc4ff 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -66,17 +66,17 @@ static int brnf_pass_vlan_indev __read_mostly = 0; #endif #define IS_IP(skb) \ - (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_IP)) + (!skb_vlan_tag_present(skb) && skb->protocol == htons(ETH_P_IP)) #define IS_IPV6(skb) \ - (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_IPV6)) + (!skb_vlan_tag_present(skb) && skb->protocol == htons(ETH_P_IPV6)) #define IS_ARP(skb) \ - (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_ARP)) + (!skb_vlan_tag_present(skb) && skb->protocol == htons(ETH_P_ARP)) static inline __be16 vlan_proto(const struct sk_buff *skb) { - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) return skb->protocol; else if (skb->protocol == htons(ETH_P_8021Q)) return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; @@ -436,11 +436,11 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct struct net_device *vlan, *br; br = bridge_parent(dev); - if (brnf_pass_vlan_indev == 0 || !vlan_tx_tag_present(skb)) + if (brnf_pass_vlan_indev == 0 || !skb_vlan_tag_present(skb)) return br; vlan = __vlan_find_dev_deep_rcu(br, skb->vlan_proto, - vlan_tx_tag_get(skb) & VLAN_VID_MASK); + skb_vlan_tag_get(skb) & VLAN_VID_MASK); return vlan ? vlan : br; } diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 9f5eb55a4d3a..66ece91ee165 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -67,6 +67,120 @@ static int br_port_fill_attrs(struct sk_buff *skb, return 0; } +static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start, + u16 vid_end, u16 flags) +{ + struct bridge_vlan_info vinfo; + + if ((vid_end - vid_start) > 0) { + /* add range to skb */ + vinfo.vid = vid_start; + vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_BEGIN; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + + vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + + vinfo.vid = vid_end; + vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_END; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + } else { + vinfo.vid = vid_start; + vinfo.flags = flags; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb, + const struct net_port_vlans *pv) +{ + u16 vid_range_start = 0, vid_range_end = 0; + u16 vid_range_flags = 0; + u16 pvid, vid, flags; + int err = 0; + + /* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan + * and mark vlan info with begin and end flags + * if vlaninfo represents a range + */ + pvid = br_get_pvid(pv); + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + flags = 0; + if (vid == pvid) + flags |= BRIDGE_VLAN_INFO_PVID; + + if (test_bit(vid, pv->untagged_bitmap)) + flags |= BRIDGE_VLAN_INFO_UNTAGGED; + + if (vid_range_start == 0) { + goto initvars; + } else if ((vid - vid_range_end) == 1 && + flags == vid_range_flags) { + vid_range_end = vid; + continue; + } else { + err = br_fill_ifvlaninfo_range(skb, vid_range_start, + vid_range_end, + vid_range_flags); + if (err) + return err; + } + +initvars: + vid_range_start = vid; + vid_range_end = vid; + vid_range_flags = flags; + } + + if (vid_range_start != 0) { + /* Call it once more to send any left over vlans */ + err = br_fill_ifvlaninfo_range(skb, vid_range_start, + vid_range_end, + vid_range_flags); + if (err) + return err; + } + + return 0; +} + +static int br_fill_ifvlaninfo(struct sk_buff *skb, + const struct net_port_vlans *pv) +{ + struct bridge_vlan_info vinfo; + u16 pvid, vid; + + pvid = br_get_pvid(pv); + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + 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; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + /* * Create one netlink message for one interface * Contains port and master info as well as carrier and bridge state. @@ -121,12 +235,11 @@ static int br_fill_ifinfo(struct sk_buff *skb, } /* Check if the VID information is requested */ - if (filter_mask & RTEXT_FILTER_BRVLAN) { - struct nlattr *af; + if ((filter_mask & RTEXT_FILTER_BRVLAN) || + (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) { const struct net_port_vlans *pv; - struct bridge_vlan_info vinfo; - u16 vid; - u16 pvid; + struct nlattr *af; + int err; if (port) pv = nbp_get_vlan_info(port); @@ -140,21 +253,12 @@ static int br_fill_ifinfo(struct sk_buff *skb, if (!af) goto nla_put_failure; - pvid = br_get_pvid(pv); - for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { - 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; - } - + if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) + err = br_fill_ifvlaninfo_compressed(skb, pv); + else + err = br_fill_ifvlaninfo(skb, pv); + if (err) + goto nla_put_failure; nla_nest_end(skb, af); } @@ -209,7 +313,8 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, int err = 0; struct net_bridge_port *port = br_port_get_rtnl(dev); - if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN)) + if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN) && + !(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) goto out; err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI, @@ -218,57 +323,89 @@ out: return err; } -static 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_vlan_info(struct net_bridge *br, struct net_bridge_port *p, + int cmd, struct bridge_vlan_info *vinfo) +{ + int err = 0; + + 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); + } + 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 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]; + struct bridge_vlan_info *vinfo_start = NULL; + struct bridge_vlan_info *vinfo = NULL; + struct nlattr *attr; int err = 0; + int rem; - err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy); - if (err) - return err; + nla_for_each_nested(attr, af_spec, rem) { + if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) + continue; + if (nla_len(attr) != sizeof(struct bridge_vlan_info)) + return -EINVAL; + vinfo = nla_data(attr); + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + if (vinfo_start) + return -EINVAL; + vinfo_start = vinfo; + continue; + } - if (tb[IFLA_BRIDGE_VLAN_INFO]) { - struct bridge_vlan_info *vinfo; + if (vinfo_start) { + struct bridge_vlan_info tmp_vinfo; + int v; - vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]); + if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + return -EINVAL; - if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) - return -EINVAL; + if (vinfo->vid <= vinfo_start->vid) + return -EINVAL; + + memcpy(&tmp_vinfo, vinfo_start, + sizeof(struct bridge_vlan_info)); - switch (cmd) { - case RTM_SETLINK: - if (p) { - err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); + for (v = vinfo_start->vid; v <= vinfo->vid; v++) { + tmp_vinfo.vid = v; + err = br_vlan_info(br, p, cmd, &tmp_vinfo); 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); - - 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; + } + vinfo_start = NULL; + } else { + err = br_vlan_info(br, p, cmd, vinfo); } + if (err) + break; } return err; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index aea3d1339b3f..d808d766334d 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -628,8 +628,8 @@ 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; + if (skb_vlan_tag_present(skb)) + *vid = skb_vlan_tag_get(skb) & VLAN_VID_MASK; else { *vid = 0; err = -EINVAL; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 97b8ddf57363..13013fe8db24 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -187,7 +187,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, * sent from vlan device on the bridge device, it does not have * HW accelerated vlan tag. */ - if (unlikely(!vlan_tx_tag_present(skb) && + if (unlikely(!skb_vlan_tag_present(skb) && skb->protocol == proto)) { skb = skb_vlan_untag(skb); if (unlikely(!skb)) @@ -200,7 +200,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, /* Protocol-mismatch, empty out vlan_tci for new tag */ skb_push(skb, ETH_HLEN); skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, - vlan_tx_tag_get(skb)); + skb_vlan_tag_get(skb)); if (unlikely(!skb)) return false; diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index 8d3f8c7651f0..618568888128 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -45,8 +45,8 @@ ebt_vlan_mt(const struct sk_buff *skb, struct xt_action_param *par) /* VLAN encapsulated Type/Length field, given from orig frame */ __be16 encap; - if (vlan_tx_tag_present(skb)) { - TCI = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + TCI = skb_vlan_tag_get(skb); encap = skb->protocol; } else { const struct vlan_hdr *fp; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index d9a8c05d995d..91180a7fc943 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -133,7 +133,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb, __be16 ethproto; int verdict, i; - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) ethproto = htons(ETH_P_8021Q); else ethproto = h->h_proto; |