On Sat, Dec 15, 2018 at 2:20 PM David Ahern <d...@cumulusnetworks.com> wrote: > > On 12/14/18 9:14 PM, Roopa Prabhu wrote: > > @@ -4021,6 +4033,171 @@ static int rtnl_fdb_dump(struct sk_buff *skb, > > struct netlink_callback *cb) > > return skb->len; > > } > > > > +static int valid_fdb_get_strict(const struct nlmsghdr *nlh, > > + struct nlattr **tb, u8 *ndm_flags, > > + int *br_idx, int *brport_idx, u8 **addr, > > + u16 *vid, struct netlink_ext_ack *extack) > > +{ > > + struct ndmsg *ndm; > > + int err, i; > > + > > + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { > > + NL_SET_ERR_MSG(extack, "Invalid header for fdb get request"); > > + return -EINVAL; > > + } > > + > > + ndm = nlmsg_data(nlh); > > + if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || > > + ndm->ndm_type) { > > + NL_SET_ERR_MSG(extack, "Invalid values in header for fdb get > > request"); > > + return -EINVAL; > > + } > > + > > + if (ndm->ndm_flags & ~(NTF_MASTER | NTF_SELF)) { > > + NL_SET_ERR_MSG(extack, "Invalid flags in header for fdb get > > request"); > > + return -EINVAL; > > + } > > + > > + err = nlmsg_parse_strict(nlh, sizeof(struct ndmsg), tb, NDA_MAX, > > + nda_policy, extack); > > + if (err < 0) > > + return err; > > + > > + *ndm_flags = ndm->ndm_flags; > > + *brport_idx = ndm->ndm_ifindex; > > + for (i = 0; i <= NDA_MAX; ++i) { > > + if (!tb[i]) > > + continue; > > + > > + switch (i) { > > + case NDA_MASTER: > > + if (nla_len(tb[i]) != sizeof(u32)) { > > This is checked by the policy during the nlsmsg_parse. > > > + NL_SET_ERR_MSG(extack, "Invalid MASTER > > attribute in fdb get request"); > > + return -EINVAL; > > + } > > + *br_idx = nla_get_u32(tb[i]); > > + break; > > + case NDA_LLADDR: > > + if (!tb[i] || nla_len(tb[i]) != ETH_ALEN) { > > The nla_len check is needed here, but '!tb[i]' is redundant with the > check after the for statement. > > > + NL_SET_ERR_MSG(extack, "Invalid address in > > fdb get request"); > > + return -EINVAL; > > + } > > + *addr = nla_data(tb[i]); > > + break; > > + case NDA_VLAN: > > + err = fdb_vid_parse(tb[i], vid, extack); > > + if (err) > > + return err; > > + break; > > + case NDA_VNI: > > + if (nla_len(tb[i]) != sizeof(u32)) { > > And here the len is already checked by the policy. > > > + NL_SET_ERR_MSG(extack, "Invalid VNI in fdb > > get request"); > > + return err; > > + } > > + break;
ack to all the above. I will remove the check and keep the NDA_VNI case stmt empty. (ideally vxlan driver should be checking all the vxlan attributes. tb[] is passed to the vxlan ndo. this is consistent with all other fdb operations) > > + default: > > + NL_SET_ERR_MSG(extack, "Unsupported attribute in fdb > > get request"); > > + return -EINVAL; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int rtnl_fdb_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, > > + struct netlink_ext_ack *extack) > > +{ > > + struct net_device *dev = NULL, *br_dev = NULL; > > + const struct net_device_ops *ops = NULL; > > + struct net *net = sock_net(in_skb->sk); > > + struct nlattr *tb[NDA_MAX + 1]; > > + struct sk_buff *skb; > > + int brport_idx = 0; > > + u8 ndm_flags = 0; > > + int br_idx = 0; > > + u8 *addr = NULL; > > + u16 vid = 0; > > + int err; > > + > > + err = valid_fdb_get_strict(nlh, tb, &ndm_flags, &br_idx, > > + &brport_idx, &addr, &vid, extack); > > + if (err < 0) > > + return err; > > + > > + if (brport_idx) { > > + dev = __dev_get_by_index(net, brport_idx); > > + if (!dev) { > > + NL_SET_ERR_MSG(extack, "Unknown device ifindex"); > > + return -ENODEV; > > + } > > + } > > + > > + if (br_idx) { > > + if (dev) { > > + NL_SET_ERR_MSG(extack, "Master and device are > > mutually exclusive"); > > + return -EINVAL; > > + } > > + > > + br_dev = __dev_get_by_index(net, br_idx); > > + if (!br_dev) { > > + NL_SET_ERR_MSG(extack, "Invalid master ifindex"); > > + return -EINVAL; > > + } > > + ops = br_dev->netdev_ops; > > + } > > + > > + if (dev) { > > + if (!ndm_flags || (ndm_flags & NTF_MASTER)) { > > + if (!(dev->priv_flags & IFF_BRIDGE_PORT)) { > > + NL_SET_ERR_MSG(extack, "Device is not a > > bridge port"); > > + return -EINVAL; > > + } > > + br_dev = netdev_master_upper_dev_get(dev); > > + if (!br_dev) { > > + NL_SET_ERR_MSG(extack, "Master of device not > > found"); > > + return -EINVAL; > > + } > > + ops = br_dev->netdev_ops; > > + } else { > > + if (!(ndm_flags & NTF_SELF)) { > > + NL_SET_ERR_MSG(extack, "Missing NTF_SELF"); > > + return -EINVAL; > > + } > > + ops = dev->netdev_ops; > > + } > > + } > > + > > + if (!br_dev && !dev) { > > + NL_SET_ERR_MSG(extack, "No device specified"); > > + return -ENODEV; > > + } > > + > > + if (!ops || !ops->ndo_fdb_get) { > > + NL_SET_ERR_MSG(extack, "Fdb get operation not supported by > > device"); > > + return -EOPNOTSUPP; > > + } > > + > > + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); > > + if (!skb) > > + return -ENOBUFS; > > + > > + if (br_dev) > > + err = ops->ndo_fdb_get(skb, tb, br_dev, addr, vid, > > + NETLINK_CB(in_skb).portid, > > + nlh->nlmsg_seq, extack); > > + else > > + err = ops->ndo_fdb_get(skb, tb, dev, addr, vid, > > + NETLINK_CB(in_skb).portid, > > + nlh->nlmsg_seq, extack); > > The above can be simplified to one ndo_fdb_get call. > > if (br_dev) > dev = br_dev; > > err = ops->ndo_fdb_get(skb, tb, dev, addr, vid, > NETLINK_CB(in_skb).portid, > nlh->nlmsg_seq, extack); > > Or unless I am missing something, br_dev as a separate variable is not > really needed and it would simplify things just a bit. > > ack. I did have it this way but after a few iterations moved it..don't remember now. Will move it to the above > > > + if (err) > > + goto out; > > + > > + return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); > > +out: > > + kfree_skb(skb); > > + return err; > > +} > > + > > static int brport_nla_put_flag(struct sk_buff *skb, u32 flags, u32 mask, > > unsigned int attrnum, unsigned int flag) > > { > > @@ -5081,7 +5258,7 @@ void __init rtnetlink_init(void) > > > > rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, 0); > > rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, 0); > > - rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, 0); > > + rtnl_register(PF_BRIDGE, RTM_GETNEIGH, rtnl_fdb_get, rtnl_fdb_dump, > > 0); > > > > rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, 0); > > rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, 0); > > >