rtnl_lock();
err = switchdev_port_attr_set(asw->dev, &asw->attr);
- BUG_ON(err);
+ if (err && err != -EOPNOTSUPP)
+ netdev_err(asw->dev, "failed (err=%d) to set attribute (id=%d)\n",
+ err, asw->attr.id);
rtnl_unlock();
dev_put(asw->dev);
}
EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
+/**
+ * switchdev_port_obj_dump - Dump port objects
+ *
+ * @dev: port device
+ * @obj: object to dump
+ */
+int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj)
+{
+ const struct switchdev_ops *ops = dev->switchdev_ops;
+ struct net_device *lower_dev;
+ struct list_head *iter;
+ int err = -EOPNOTSUPP;
+
+ if (ops && ops->switchdev_port_obj_dump)
+ return ops->switchdev_port_obj_dump(dev, obj);
+
+ /* Switch device port(s) may be stacked under
+ * bond/team/vlan dev, so recurse down to dump objects on
+ * first port at bottom of stack.
+ */
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter) {
+ err = switchdev_port_obj_dump(lower_dev, obj);
+ break;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
+
static DEFINE_MUTEX(switchdev_mutex);
static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
return err;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
- attr.brport_flags, mask, nlflags);
+ attr.u.brport_flags, mask, nlflags);
}
EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink);
return err;
if (flag)
- attr.brport_flags |= brport_flag;
+ attr.u.brport_flags |= brport_flag;
else
- attr.brport_flags &= ~brport_flag;
+ attr.u.brport_flags &= ~brport_flag;
return switchdev_port_attr_set(dev, &attr);
}
struct switchdev_obj obj = {
.id = SWITCHDEV_OBJ_PORT_VLAN,
};
+ struct switchdev_obj_vlan *vlan = &obj.u.vlan;
int rem;
int err;
if (nla_len(attr) != sizeof(struct bridge_vlan_info))
return -EINVAL;
vinfo = nla_data(attr);
- obj.vlan.flags = vinfo->flags;
+ vlan->flags = vinfo->flags;
if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
- if (obj.vlan.vid_start)
+ if (vlan->vid_begin)
return -EINVAL;
- obj.vlan.vid_start = vinfo->vid;
+ vlan->vid_begin = vinfo->vid;
} else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
- if (!obj.vlan.vid_start)
+ if (!vlan->vid_begin)
return -EINVAL;
- obj.vlan.vid_end = vinfo->vid;
- if (obj.vlan.vid_end <= obj.vlan.vid_start)
+ vlan->vid_end = vinfo->vid;
+ if (vlan->vid_end <= vlan->vid_begin)
return -EINVAL;
err = f(dev, &obj);
if (err)
return err;
- memset(&obj.vlan, 0, sizeof(obj.vlan));
+ memset(vlan, 0, sizeof(*vlan));
} else {
- if (obj.vlan.vid_start)
+ if (vlan->vid_begin)
return -EINVAL;
- obj.vlan.vid_start = vinfo->vid;
- obj.vlan.vid_end = vinfo->vid;
+ vlan->vid_begin = vinfo->vid;
+ vlan->vid_end = vinfo->vid;
err = f(dev, &obj);
if (err)
return err;
- memset(&obj.vlan, 0, sizeof(obj.vlan));
+ memset(vlan, 0, sizeof(*vlan));
}
}
}
EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
+/**
+ * switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port
+ *
+ * @ndmsg: netlink hdr
+ * @nlattr: netlink attributes
+ * @dev: port device
+ * @addr: MAC address to add
+ * @vid: VLAN to add
+ *
+ * Add FDB entry to switch device.
+ */
+int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+ struct net_device *dev, const unsigned char *addr,
+ u16 vid, u16 nlm_flags)
+{
+ struct switchdev_obj obj = {
+ .id = SWITCHDEV_OBJ_PORT_FDB,
+ .u.fdb = {
+ .addr = addr,
+ .vid = vid,
+ },
+ };
+
+ return switchdev_port_obj_add(dev, &obj);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
+
+/**
+ * switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port
+ *
+ * @ndmsg: netlink hdr
+ * @nlattr: netlink attributes
+ * @dev: port device
+ * @addr: MAC address to delete
+ * @vid: VLAN to delete
+ *
+ * Delete FDB entry from switch device.
+ */
+int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
+ struct net_device *dev, const unsigned char *addr,
+ u16 vid)
+{
+ struct switchdev_obj obj = {
+ .id = SWITCHDEV_OBJ_PORT_FDB,
+ .u.fdb = {
+ .addr = addr,
+ .vid = vid,
+ },
+ };
+
+ return switchdev_port_obj_del(dev, &obj);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
+
+struct switchdev_fdb_dump {
+ struct switchdev_obj obj;
+ struct sk_buff *skb;
+ struct netlink_callback *cb;
+ int idx;
+};
+
+static int switchdev_port_fdb_dump_cb(struct net_device *dev,
+ struct switchdev_obj *obj)
+{
+ struct switchdev_fdb_dump *dump =
+ container_of(obj, struct switchdev_fdb_dump, obj);
+ u32 portid = NETLINK_CB(dump->cb->skb).portid;
+ u32 seq = dump->cb->nlh->nlmsg_seq;
+ struct nlmsghdr *nlh;
+ struct ndmsg *ndm;
+
+ if (dump->idx < dump->cb->args[0])
+ goto skip;
+
+ nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
+ sizeof(*ndm), NLM_F_MULTI);
+ 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 = dev->ifindex;
+ ndm->ndm_state = NUD_REACHABLE;
+
+ if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
+ goto nla_put_failure;
+
+ if (obj->u.fdb.vid && nla_put_u16(dump->skb, NDA_VLAN, obj->u.fdb.vid))
+ goto nla_put_failure;
+
+ nlmsg_end(dump->skb, nlh);
+
+skip:
+ dump->idx++;
+ return 0;
+
+nla_put_failure:
+ nlmsg_cancel(dump->skb, nlh);
+ return -EMSGSIZE;
+}
+
+/**
+ * switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries
+ *
+ * @skb: netlink skb
+ * @cb: netlink callback
+ * @dev: port device
+ * @filter_dev: filter device
+ * @idx:
+ *
+ * Delete FDB entry from switch device.
+ */
+int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+ struct net_device *dev,
+ struct net_device *filter_dev, int idx)
+{
+ struct switchdev_fdb_dump dump = {
+ .obj = {
+ .id = SWITCHDEV_OBJ_PORT_FDB,
+ .cb = switchdev_port_fdb_dump_cb,
+ },
+ .skb = skb,
+ .cb = cb,
+ .idx = idx,
+ };
+ int err;
+
+ err = switchdev_port_obj_dump(dev, &dump.obj);
+ if (err)
+ return err;
+
+ return dump.idx;
+}
+EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
+
static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
{
const struct switchdev_ops *ops = dev->switchdev_ops;
return NULL;
if (nhsel > 0) {
- if (prev_attr.ppid.id_len != attr.ppid.id_len)
+ if (prev_attr.u.ppid.id_len != attr.u.ppid.id_len)
return NULL;
- if (memcmp(prev_attr.ppid.id, attr.ppid.id,
- attr.ppid.id_len))
+ if (memcmp(prev_attr.u.ppid.id, attr.u.ppid.id,
+ attr.u.ppid.id_len))
return NULL;
}
}
/**
- * switchdev_fib_ipv4_add - Add IPv4 route entry to switch
+ * switchdev_fib_ipv4_add - Add/modify switch IPv4 route entry
*
* @dst: route's IPv4 destination address
* @dst_len: destination address length (prefix length)
* @nlflags: netlink flags passed in (NLM_F_*)
* @tb_id: route table ID
*
- * Add IPv4 route entry to switch device.
+ * Add/modify switch IPv4 route entry.
*/
int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
u8 tos, u8 type, u32 nlflags, u32 tb_id)
{
struct switchdev_obj fib_obj = {
.id = SWITCHDEV_OBJ_IPV4_FIB,
- .ipv4_fib = {
- .dst = htonl(dst),
+ .u.ipv4_fib = {
+ .dst = dst,
.dst_len = dst_len,
.fi = fi,
.tos = tos,
err = switchdev_port_obj_add(dev, &fib_obj);
if (!err)
- fi->fib_flags |= RTNH_F_EXTERNAL;
+ fi->fib_flags |= RTNH_F_OFFLOAD;
- return err;
+ return err == -EOPNOTSUPP ? 0 : err;
}
EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
{
struct switchdev_obj fib_obj = {
.id = SWITCHDEV_OBJ_IPV4_FIB,
- .ipv4_fib = {
- .dst = htonl(dst),
+ .u.ipv4_fib = {
+ .dst = dst,
.dst_len = dst_len,
.fi = fi,
.tos = tos,
struct net_device *dev;
int err = 0;
- if (!(fi->fib_flags & RTNH_F_EXTERNAL))
+ if (!(fi->fib_flags & RTNH_F_OFFLOAD))
return 0;
dev = switchdev_get_dev_by_nhs(fi);
err = switchdev_port_obj_del(dev, &fib_obj);
if (!err)
- fi->fib_flags &= ~RTNH_F_EXTERNAL;
+ fi->fib_flags &= ~RTNH_F_OFFLOAD;
- return err;
+ return err == -EOPNOTSUPP ? 0 : err;
}
EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del);