| @@ -1540,6 +1540,85 @@ out_err: · 1539 unmodified lines |
| 1540 | return false; | 1540 | return false; |
| 1541 | } | 1541 | } |
| 1542 | | 1542 | |
| | 1543 | static size_t rtnl_vlan_nlmsg_size(void) |
| | 1544 | { |
| | 1545 | return NLMSG_ALIGN(sizeof(struct br_vlan_msg)) |
| | 1546 | + nla_total_size(0) /* BRIDGE_VLANDB_ENTRY */ |
| | 1547 | + nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_ENTRY_RANGE */ |
| | 1548 | + nla_total_size(sizeof(struct bridge_vlan_info)); /* BRIDGE_VLANDB_ENTRY_INFO */ |
| | 1549 | } |
| | 1550 | |
| | 1551 | void br_vlan_notify(const struct net_bridge *br, |
| | 1552 | const struct net_bridge_port *p, |
| | 1553 | u16 vid, u16 vid_range, |
| | 1554 | int cmd) |
| | 1555 | { |
| | 1556 | struct net_bridge_vlan_group *vg; |
| | 1557 | struct net_bridge_vlan *v; |
| | 1558 | struct br_vlan_msg *bvm; |
| | 1559 | struct nlmsghdr *nlh; |
| | 1560 | struct sk_buff *skb; |
| | 1561 | int err = -ENOBUFS; |
| | 1562 | struct net *net; |
| | 1563 | u16 flags = 0; |
| | 1564 | int ifindex; |
| | 1565 | |
| | 1566 | /* right now notifications are done only with rtnl held */ |
| | 1567 | ASSERT_RTNL(); |
| | 1568 | |
| | 1569 | if (p) { |
| | 1570 | ifindex = p->dev->ifindex; |
| | 1571 | vg = nbp_vlan_group(p); |
| | 1572 | net = dev_net(p->dev); |
| | 1573 | } else { |
| | 1574 | ifindex = br->dev->ifindex; |
| | 1575 | vg = br_vlan_group(br); |
| | 1576 | net = dev_net(br->dev); |
| | 1577 | } |
| | 1578 | |
| | 1579 | skb = nlmsg_new(rtnl_vlan_nlmsg_size(), GFP_KERNEL); |
| | 1580 | if (!skb) |
| | 1581 | goto out_err; |
| | 1582 | |
| | 1583 | err = -EMSGSIZE; |
| | 1584 | nlh = nlmsg_put(skb, 0, 0, cmd, sizeof(*bvm), 0); |
| | 1585 | if (!nlh) |
| | 1586 | goto out_err; |
| | 1587 | bvm = nlmsg_data(nlh); |
| | 1588 | memset(bvm, 0, sizeof(*bvm)); |
| | 1589 | bvm->family = AF_BRIDGE; |
| | 1590 | bvm->ifindex = ifindex; |
| | 1591 | |
| | 1592 | switch (cmd) { |
| | 1593 | case RTM_NEWVLAN: |
| | 1594 | /* need to find the vlan due to flags/options */ |
| | 1595 | v = br_vlan_find(vg, vid); |
| | 1596 | if (!v || !br_vlan_should_use(v)) |
| | 1597 | goto out_kfree; |
| | 1598 | |
| | 1599 | flags = v->flags; |
| | 1600 | if (br_get_pvid(vg) == v->vid) |
| | 1601 | flags |= BRIDGE_VLAN_INFO_PVID; |
| | 1602 | break; |
| | 1603 | case RTM_DELVLAN: |
| | 1604 | break; |
| | 1605 | default: |
| | 1606 | goto out_kfree; |
| | 1607 | } |
| | 1608 | |
| | 1609 | if (!br_vlan_fill_vids(skb, vid, vid_range, flags)) |
| | 1610 | goto out_err; |
| | 1611 | |
| | 1612 | nlmsg_end(skb, nlh); |
| | 1613 | rtnl_notify(skb, net, 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL); |
| | 1614 | return; |
| | 1615 | |
| | 1616 | out_err: |
| | 1617 | rtnl_set_sk_err(net, RTNLGRP_BRVLAN, err); |
| | 1618 | out_kfree: |
| | 1619 | kfree_skb(skb); |
| | 1620 | } |
| | 1621 | |
| 1543 | /* check if v_curr can enter a range ending in range_end */ | 1622 | /* check if v_curr can enter a range ending in range_end */ |
| 1544 | static bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, | 1623 | static bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, |
| 1545 | const struct net_bridge_vlan *range_end) | 1624 | const struct net_bridge_vlan *range_end) |