|
@@ -201,6 +201,105 @@ out:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
|
|
+ struct net_device *dev,
|
|
|
+ const unsigned char *addr, u16 vid, u16 nlm_flags)
|
|
|
+{
|
|
|
+ struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
+ struct dsa_switch *ds = p->parent;
|
|
|
+ int ret = -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if (ds->drv->fdb_add)
|
|
|
+ ret = ds->drv->fdb_add(ds, p->port, addr, vid);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
|
|
|
+ struct net_device *dev,
|
|
|
+ const unsigned char *addr, u16 vid)
|
|
|
+{
|
|
|
+ struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
+ struct dsa_switch *ds = p->parent;
|
|
|
+ int ret = -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if (ds->drv->fdb_del)
|
|
|
+ ret = ds->drv->fdb_del(ds, p->port, addr, vid);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb,
|
|
|
+ const unsigned char *addr, u16 vid,
|
|
|
+ bool is_static,
|
|
|
+ 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_EXT_LEARNED;
|
|
|
+ ndm->ndm_type = 0;
|
|
|
+ ndm->ndm_ifindex = dev->ifindex;
|
|
|
+ ndm->ndm_state = is_static ? NUD_NOARP : 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;
|
|
|
+
|
|
|
+ nlmsg_end(skb, nlh);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+nla_put_failure:
|
|
|
+ nlmsg_cancel(skb, nlh);
|
|
|
+ return -EMSGSIZE;
|
|
|
+}
|
|
|
+
|
|
|
+/* Dump information about entries, in response to GETNEIGH */
|
|
|
+static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
|
|
|
+ struct net_device *dev,
|
|
|
+ struct net_device *filter_dev, int idx)
|
|
|
+{
|
|
|
+ struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
+ struct dsa_switch *ds = p->parent;
|
|
|
+ unsigned char addr[ETH_ALEN] = { 0 };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!ds->drv->fdb_getnext)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ for (; ; idx++) {
|
|
|
+ bool is_static;
|
|
|
+
|
|
|
+ ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (idx < cb->args[0])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = dsa_slave_fill_info(dev, skb, addr, 0,
|
|
|
+ is_static,
|
|
|
+ NETLINK_CB(cb->skb).portid,
|
|
|
+ cb->nlh->nlmsg_seq,
|
|
|
+ RTM_NEWNEIGH, NLM_F_MULTI);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return idx;
|
|
|
+}
|
|
|
+
|
|
|
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
|
{
|
|
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
|
@@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
|
|
|
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
|
|
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
|
|
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
|
|
+ .ndo_fdb_add = dsa_slave_fdb_add,
|
|
|
+ .ndo_fdb_del = dsa_slave_fdb_del,
|
|
|
+ .ndo_fdb_dump = dsa_slave_fdb_dump,
|
|
|
.ndo_do_ioctl = dsa_slave_ioctl,
|
|
|
};
|
|
|
|