rocker: implement ndo_fdb_dump
authorJiri Pirko <jiri@resnulli.us>
Fri, 28 Nov 2014 13:34:29 +0000 (14:34 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 3 Dec 2014 04:01:26 +0000 (20:01 -0800)
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/rocker/rocker.c

index 6c15aa1e4b60452c44d6acfcd0e40cf9816e2235..fea49e8cf8ef09b927207993d04888f2ae961af6 100644 (file)
@@ -3587,6 +3587,78 @@ static int rocker_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
        return rocker_port_fdb(rocker_port, addr, vlan_id, flags);
 }
 
+static int rocker_fdb_fill_info(struct sk_buff *skb,
+                               struct rocker_port *rocker_port,
+                               const unsigned char *addr, u16 vid,
+                               u32 portid, u32 seq, int type,
+                               unsigned int flags)
+{
+       struct nlmsghdr *nlh;
+       struct ndmsg *ndm;
+
+       nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
+       if (!nlh)
+               return -EMSGSIZE;
+
+       ndm = nlmsg_data(nlh);
+       ndm->ndm_family  = AF_BRIDGE;
+       ndm->ndm_pad1    = 0;
+       ndm->ndm_pad2    = 0;
+       ndm->ndm_flags   = NTF_SELF;
+       ndm->ndm_type    = 0;
+       ndm->ndm_ifindex = rocker_port->dev->ifindex;
+       ndm->ndm_state   = NUD_REACHABLE;
+
+       if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
+               goto nla_put_failure;
+
+       if (vid && nla_put_u16(skb, NDA_VLAN, vid))
+               goto nla_put_failure;
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static int rocker_port_fdb_dump(struct sk_buff *skb,
+                               struct netlink_callback *cb,
+                               struct net_device *dev,
+                               struct net_device *filter_dev,
+                               int idx)
+{
+       struct rocker_port *rocker_port = netdev_priv(dev);
+       struct rocker *rocker = rocker_port->rocker;
+       struct rocker_fdb_tbl_entry *found;
+       struct hlist_node *tmp;
+       int bkt;
+       unsigned long lock_flags;
+       const unsigned char *addr;
+       u16 vid;
+       int err;
+
+       spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
+       hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
+               if (found->key.lport != rocker_port->lport)
+                       continue;
+               if (idx < cb->args[0])
+                       goto skip;
+               addr = found->key.addr;
+               vid = rocker_port_vlan_to_vid(rocker_port, found->key.vlan_id);
+               err = rocker_fdb_fill_info(skb, rocker_port, addr, vid,
+                                          NETLINK_CB(cb->skb).portid,
+                                          cb->nlh->nlmsg_seq,
+                                          RTM_NEWNEIGH, NLM_F_MULTI);
+               if (err < 0)
+                       break;
+skip:
+               ++idx;
+       }
+       spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
+       return idx;
+}
+
 static int rocker_port_switch_parent_id_get(struct net_device *dev,
                                            struct netdev_phys_item_id *psid)
 {
@@ -3614,6 +3686,7 @@ static const struct net_device_ops rocker_port_netdev_ops = {
        .ndo_vlan_rx_kill_vid           = rocker_port_vlan_rx_kill_vid,
        .ndo_fdb_add                    = rocker_port_fdb_add,
        .ndo_fdb_del                    = rocker_port_fdb_del,
+       .ndo_fdb_dump                   = rocker_port_fdb_dump,
        .ndo_switch_parent_id_get       = rocker_port_switch_parent_id_get,
        .ndo_switch_port_stp_update     = rocker_port_switch_port_stp_update,
 };