| @@ -1540,6 +1540,85 @@ out_err: · 1539 unmodified lines |
| 1540 | 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 | 1622 | /* check if v_curr can enter a range ending in range_end */ |
| 1544 | 1623 | static bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, |
| 1545 | 1624 | const struct net_bridge_vlan *range_end) |