--- zzzz-none-000/linux-5.15.111/net/openvswitch/datapath.c 2023-05-11 14:00:40.000000000 +0000 +++ puma7-atom-6670-761/linux-5.15.111/net/openvswitch/datapath.c 2024-02-07 10:23:30.000000000 +0000 @@ -3,6 +3,16 @@ * Copyright (c) 2007-2014 Nicira, Inc. */ +/* + * Includes Inango Systems Ltd’s changes/modifications dated: 2021. + * Changed/modified portions - Copyright (c) 2021 , Inango Systems Ltd. + */ + +/* + Includes MaxLinear's changes dated: 2021, 2022, 2023. + Changed portions - Copyright 2021-2023 MaxLinear, Inc. +*/ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -38,6 +48,7 @@ #include #include +#include "am_pp.h" #include "datapath.h" #include "flow.h" #include "flow_table.h" @@ -46,9 +57,97 @@ #include "openvswitch_trace.h" #include "vport-internal_dev.h" #include "vport-netdev.h" +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +#include +#endif unsigned int ovs_net_id __read_mostly; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, void __user *data, int cmd); +EXPORT_SYMBOL(ovs_dp_ioctl_hook); + +int (*ovs_dp_mac_addr_hook)(struct net_device *dev, void *p); +EXPORT_SYMBOL(ovs_dp_mac_addr_hook); + +int (*ovs_dp_mtu_hook)(struct net_device *dev, int mtu); +EXPORT_SYMBOL(ovs_dp_mtu_hook); + +int (*ovs_dp_add_del_port_hook)(struct net_device *br_dev, struct net_device *p_dev, int add); +EXPORT_SYMBOL(ovs_dp_add_del_port_hook); + +int (*ovs_dp_br_changelink_hook)(struct vport *vport, struct nlattr *tb[], struct nlattr *data[]); +EXPORT_SYMBOL(ovs_dp_br_changelink_hook); + +struct net_device *(*ovs_dp_br_get_netdev_hook)(void); +EXPORT_SYMBOL(ovs_dp_br_get_netdev_hook); + +int (*ovs_dp_br_brc_add_bridge_netlink_hook)(struct net *net, struct net_device *dev); +EXPORT_SYMBOL(ovs_dp_br_brc_add_bridge_netlink_hook); + +void (*ovs_dp_br_brc_del_bridge_netlink_hook)(struct net *net, struct net_device *dev); +EXPORT_SYMBOL(ovs_dp_br_brc_del_bridge_netlink_hook); + +int (*ovs_dp_br_port_slave_changelink_hook)(struct vport *vport, struct net_device *br_dev, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *ack); +EXPORT_SYMBOL(ovs_dp_br_port_slave_changelink_hook); + +int (*ovs_dp_br_fill_info_hook)(struct vport *vport, struct sk_buff *skb, const struct net_device *br_dev); +EXPORT_SYMBOL(ovs_dp_br_fill_info_hook); + +int (*ovs_dp_br_fill_ifinfo_hook)(struct vport *vport, struct sk_buff *skb, const struct net_device *dev, u32 pid, u32 seq, int event, unsigned int flags); +EXPORT_SYMBOL(ovs_dp_br_fill_ifinfo_hook); + +int (*ovs_dp_br_port_fill_slave_info_hook)(struct vport *vport, struct sk_buff *skb, const struct net_device *br_dev, const struct net_device *dev); +EXPORT_SYMBOL(ovs_dp_br_port_fill_slave_info_hook); + +int (*ovs_dp_br_setlink_hook)(struct vport *vport, struct net_device *dev, struct nlmsghdr *nlh, u16 flags); +EXPORT_SYMBOL(ovs_dp_br_setlink_hook); + +int (*ovs_dp_br_bridge_setup)(struct vport *vport, int add); +EXPORT_SYMBOL(ovs_dp_br_bridge_setup); + +int (*ovs_dp_br_bridge_port_setup)(struct vport *br_vport, struct vport *vport, int add); +EXPORT_SYMBOL(ovs_dp_br_bridge_port_setup); + +struct rtnl_link_ops * (*ovs_dp_get_rtnl_link_ops_hook)(void); +EXPORT_SYMBOL(ovs_dp_get_rtnl_link_ops_hook); + +int (*ovs_dp_sysfs_hook)(struct net_device *dev, unsigned long *ul_value, int cmd, int oper); +EXPORT_SYMBOL(ovs_dp_sysfs_hook); + +void (*ovs_dp_dev_init)(struct vport *vport); +EXPORT_SYMBOL(ovs_dp_dev_init); + +void (*ovs_dp_dev_open)(struct vport *vport); +EXPORT_SYMBOL(ovs_dp_dev_open); + +void (*ovs_dp_dev_stop)(struct vport *vport); +EXPORT_SYMBOL(ovs_dp_dev_stop); + +int (*ovs_dp_dev_set_mtu_set_by_user)(struct net_device *dev, int is_set_by_user); +EXPORT_SYMBOL(ovs_dp_dev_set_mtu_set_by_user); + +int (*ovs_dp_multicast_add_group)(struct vport *vport, struct br_ip *group, unsigned char *mac); +EXPORT_SYMBOL(ovs_dp_multicast_add_group); + +int (*ovs_dp_multicast_del_group)(struct vport *vport, struct br_ip *group, unsigned char *mac); +EXPORT_SYMBOL(ovs_dp_multicast_del_group); + +int (*ovs_dp_sysfs_string_hook)(struct net_device *dev, char *ustring, int cmd); +EXPORT_SYMBOL(ovs_dp_sysfs_string_hook); + +int (*ovs_get_fdb_entries)(struct net_device *dev, void *user, + unsigned long maxnum, unsigned long offset, bool is_user_buf); +EXPORT_SYMBOL(ovs_get_fdb_entries); + +int (*brc_handle_event_hook)(struct net_device *dev, unsigned long event); +EXPORT_SYMBOL(brc_handle_event_hook); +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + +static struct vport *lookup_vport(struct net *net, + const struct ovs_header *ovs_header, + struct nlattr *a[OVS_VPORT_ATTR_MAX + 1]); + static struct genl_family dp_packet_genl_family; static struct genl_family dp_flow_genl_family; static struct genl_family dp_datapath_genl_family; @@ -160,6 +259,94 @@ return ifindex; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static size_t br_nlmsg_size(void) +{ + return NLMSG_ALIGN(sizeof(struct ifinfomsg)) + + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ + + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ + + nla_total_size(4) /* IFLA_MASTER */ + + nla_total_size(4) /* IFLA_MTU */ + + nla_total_size(1); /* IFLA_OPERSTATE */ +} + +static int dp_fill_ifinfo(struct sk_buff *skb, + const struct vport *port, + int event, unsigned int flags) +{ + struct datapath *dp = port->dp; + struct ifinfomsg *hdr; + struct nlmsghdr *nlh; + struct net_device *upper_dev; + + if (!port->ops->get_ifindex) + return -ENODEV; + rtnl_lock(); + upper_dev = netdev_master_upper_dev_get(port->dev); + rtnl_unlock(); + + nlh = nlmsg_put(skb, 0, 0, event, sizeof(*hdr), flags); + if (nlh == NULL) + return -EMSGSIZE; + + hdr = nlmsg_data(nlh); + hdr->ifi_family = AF_BRIDGE; + hdr->__ifi_pad = 0; + hdr->ifi_type = ARPHRD_ETHER; + hdr->ifi_index = port->ops->get_ifindex(port); + hdr->ifi_flags = port->ops->get_dev_flags(port); + hdr->ifi_change = 0; + + if (nla_put_string(skb, IFLA_IFNAME, port->ops->get_name(port)) || + nla_put_u32(skb, IFLA_MASTER, upper_dev ? upper_dev->ifindex : get_dpifindex(dp)) || + nla_put_u32(skb, IFLA_MTU, port->ops->get_mtu(port)) || +#ifdef IFLA_OPERSTATE + nla_put_u8(skb, IFLA_OPERSTATE, + port->ops->is_running(port) ? port->ops->get_operstate(port) : IF_OPER_DOWN) || +#endif + nla_put(skb, IFLA_ADDRESS, ETH_ALEN, port->ops->get_addr(port))) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static void dp_ifinfo_notify(int event, struct vport *port) +{ + struct sk_buff *skb; + int err; + + skb = nlmsg_new(br_nlmsg_size(), GFP_KERNEL); + if (!skb) { + err = -ENOBUFS; + goto err; + } + + err = dp_fill_ifinfo(skb, port, event, 0); + if (err < 0) { + if (err == -ENODEV) { + goto out; + } else { + /* -EMSGSIZE implies BUG in br_nlmsg_size() */ + WARN_ON(err == -EMSGSIZE); + goto err; + } + } + + rtnl_notify(skb, ovs_dp_get_net(port->dp), 0, RTNLGRP_LINK, NULL, GFP_KERNEL); + + return; +err: + rtnl_set_sk_err(ovs_dp_get_net(port->dp), RTNLGRP_LINK, err); +out: + kfree_skb(skb); +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + static void destroy_dp_rcu(struct rcu_head *rcu) { struct datapath *dp = container_of(rcu, struct datapath, rcu); @@ -204,6 +391,9 @@ struct hlist_head *head = vport_hash_bucket(dp, vport->port_no); hlist_add_head_rcu(&vport->dp_hash_node, head); +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + dp_ifinfo_notify(RTM_NEWLINK, vport); +#endif } return vport; } @@ -212,6 +402,16 @@ { ASSERT_OVSL(); +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + OVS_LOG_DBG("detach vport %s from datapath\n", p->dev->name); + if (p->type != OVS_VPORT_TYPE_INTERNAL) + ovs_dp_sysfs_del_if(p); + else + ovs_dp_sysfs_del_bridge(p); + + dp_ifinfo_notify(RTM_DELLINK, p); +#endif + /* First drop references to device. */ hlist_del_rcu(&p->dp_hash_node); @@ -268,7 +468,7 @@ ovs_flow_stats_update(flow, key->tp.flags, skb); sf_acts = rcu_dereference(flow->sf_acts); - error = ovs_execute_actions(dp, skb, sf_acts, key); + error = ovs_execute_actions(dp, skb, sf_acts, flow /*key, &flow->id*/); if (unlikely(error)) net_dbg_ratelimited("ovs: action execution error on datapath %s: %d\n", ovs_dp_name(dp), error); @@ -647,7 +847,7 @@ sf_acts = rcu_dereference(flow->sf_acts); local_bh_disable(); - err = ovs_execute_actions(dp, packet, sf_acts, &flow->key); + err = ovs_execute_actions(dp, packet, sf_acts, flow/*&flow->key, &flow->id*/); local_bh_enable(); rcu_read_unlock(); @@ -783,6 +983,7 @@ unsigned long used; ovs_flow_stats_get(flow, &stats, &used, &tcp_flags); + am_get_session_stats(flow, &stats, &used); if (used && nla_put_u64_64bit(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used), @@ -803,6 +1004,15 @@ } /* Called with ovs_mutex or RCU read lock. */ +static int ovs_flow_cmd_fill_props(const struct sw_flow *flow, struct sk_buff *skb) +{ + if (nla_put_u32(skb, OVS_FLOW_ATTR_TIMEOUT, flow->session_timeout)) + return -EMSGSIZE; + + return 0; +} + +/* Called with ovs_mutex or RCU read lock. */ static int ovs_flow_cmd_fill_actions(const struct sw_flow *flow, struct sk_buff *skb, int skb_orig_len) { @@ -878,6 +1088,10 @@ if (err) goto error; + err = ovs_flow_cmd_fill_props(flow, skb); + if (err) + goto error; + if (should_fill_actions(ufid_flags)) { err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len); if (err) @@ -988,6 +1202,7 @@ ovs_flow_mask_key(&new_flow->key, key, true, &mask); + new_flow->flow_type = nla_get_u32(a[OVS_FLOW_ATTR_FLOW_TYPE]); /* Extract flow identifier. */ error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], key, log); @@ -1030,6 +1245,7 @@ acts = NULL; goto err_unlock_ovs; } + am_create_session(dp, new_flow, a[OVS_FLOW_ATTR_PROACTIVE_FLAG]); if (unlikely(reply)) { error = ovs_flow_cmd_fill_info(new_flow, @@ -1072,6 +1288,7 @@ /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); + am_mod_session(dp, flow, old_acts); if (unlikely(reply)) { error = ovs_flow_cmd_fill_info(flow, @@ -1243,6 +1460,7 @@ if (likely(acts)) { old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); + am_mod_session(dp, flow, old_acts); if (unlikely(reply)) { error = ovs_flow_cmd_fill_info(flow, @@ -1258,6 +1476,7 @@ reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info, OVS_FLOW_CMD_SET, false, ufid_flags); + am_mod_session(dp, flow, NULL); if (IS_ERR(reply)) { error = PTR_ERR(reply); @@ -1392,6 +1611,7 @@ } ovs_flow_tbl_remove(&dp->table, flow); + am_remove_session(flow); ovs_unlock(); reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts, @@ -1479,6 +1699,7 @@ [OVS_FLOW_ATTR_PROBE] = { .type = NLA_FLAG }, [OVS_FLOW_ATTR_UFID] = { .type = NLA_UNSPEC, .len = 1 }, [OVS_FLOW_ATTR_UFID_FLAGS] = { .type = NLA_U32 }, + [OVS_FLOW_ATTR_PROACTIVE_FLAG] = { .type = NLA_FLAG }, }; static const struct genl_small_ops dp_flow_genl_ops[] = { @@ -1851,6 +2072,43 @@ return err; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static void detach_internal_ifaces(struct vport *vport_master) { + struct datapath *dp; + int i; + struct vport *vport_next, *vport; + LIST_HEAD(head); + + if (!vport_master){ + pr_err("try to detach slave intrfaces from null master interface\n"); + return; + } + + if (vport_master->type != OVS_VPORT_TYPE_INTERNAL) + return; + + rtnl_lock(); + dp = vport_master->dp; + for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { + struct hlist_node *n; + + hlist_for_each_entry_safe(vport_next, n, &dp->ports[i], dp_hash_node) + if (vport_next->port_no != OVSP_LOCAL){ + if (netdev_master_upper_dev_get(vport_next->dev) == vport_master->dev){ + OVS_LOG_DBG("detach slave interface %s (master: %s)\n", vport_next->dev->name, vport_master->dev->name); + list_add(&vport_next->detach_list, &head); + } + } + } + rtnl_unlock(); + + list_for_each_entry_safe(vport, vport_next, &head, detach_list) { + list_del(&vport->detach_list); + ovs_dp_detach_port(vport); + } +} +#endif + /* Called with ovs_mutex. */ static void __dp_destroy(struct datapath *dp) { @@ -2004,6 +2262,362 @@ return skb->len; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static inline size_t rtnl_fdb_nlmsg_size(void) +{ + return NLMSG_ALIGN(sizeof(struct ndmsg)) + + nla_total_size(ETH_ALEN) + /* NDA_LLADDR */ + nla_total_size(sizeof(u16)) + /* NDA_VLAN */ + 0; +} + +static int ovs_vport_cmd_newneigh(struct sk_buff *skbn, struct genl_info *info) +{ + struct nlattr **a = info->attrs; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + struct sk_buff *skb; + struct net *net; + struct ovs_header *ovs_header = info->userhdr; + u8 mac_addr[ETH_ALEN]; + int err; + struct vport *input_vport; + int if_index; + + if (a[OVS_VPORT_ATTR_MAC]) { + struct nlattr *mac_attr = a[OVS_VPORT_ATTR_MAC]; + memcpy(mac_addr, nla_data(mac_attr), nla_len(mac_attr)); + } else { + return -EBADMSG; + } + net = sock_net(skbn->sk); + + rcu_read_lock(); + input_vport = lookup_vport(net, ovs_header, a); + err = PTR_ERR(input_vport); + if (IS_ERR(input_vport)) + goto error; + if (input_vport->dev) + if_index = input_vport->dev->ifindex; + else { + err = -ENETDOWN; + goto error; + } + rcu_read_unlock(); + + skb = nlmsg_new(rtnl_fdb_nlmsg_size(), GFP_ATOMIC); + nlh = nlmsg_put(skb, 0, 0, RTM_NEWNEIGH, sizeof(*ndm), 0); + + 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 = if_index; + ndm->ndm_state = NUD_REACHABLE; + + if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &mac_addr)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; + +error: + rcu_read_unlock(); + return err; +} + +static int ovs_vport_cmd_delneigh(struct sk_buff *skbn, struct genl_info *info) +{ + struct nlattr **a = info->attrs; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + struct sk_buff *skb; + struct net *net; + struct ovs_header *ovs_header = info->userhdr; + u8 mac_addr[ETH_ALEN]; + int err; + struct vport *input_vport; + int if_index; + + if (a[OVS_VPORT_ATTR_MAC]) { + struct nlattr *mac_attr = a[OVS_VPORT_ATTR_MAC]; + memcpy(mac_addr, nla_data(mac_attr), nla_len(mac_attr)); + } else { + return -EBADMSG; + } + net = sock_net(skbn->sk); + + rcu_read_lock(); + input_vport = lookup_vport(net, ovs_header, a); + err = PTR_ERR(input_vport); + if (IS_ERR(input_vport)) + goto error; + if (input_vport->dev) + if_index = input_vport->dev->ifindex; + else { + err = -ENETDOWN; + goto error; + } + rcu_read_unlock(); + + skb = nlmsg_new(rtnl_fdb_nlmsg_size(), GFP_ATOMIC); + nlh = nlmsg_put(skb, 0, 0, RTM_DELNEIGH, sizeof(*ndm), 0); + + 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 = if_index; + ndm->ndm_state = NUD_FAILED; + + if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &mac_addr)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; + +error: + rcu_read_unlock(); + return err; +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + +static int ovs_vport_cmd_mcast_fill_msg(struct vport* vport, struct genl_info *info, + struct pp_am_multicast_event_msg *msg, struct br_ip *group, + unsigned char *mac) +{ + struct nlattr **a = info->attrs; + struct nlattr *ip_attr; + + memset(msg, 0, sizeof(struct pp_am_multicast_event_msg)); + memset(group, 0, sizeof(struct br_ip)); + + msg->ifindex = vport->dev->ifindex; + + if (a[OVS_VPORT_ATTR_IPV4]) { + ip_attr = a[OVS_VPORT_ATTR_IPV4]; + memcpy(&msg->ip.ipv4, nla_data(ip_attr), nla_len(ip_attr)); + msg->ip.eth_proto = htons(ETH_P_IP); + group->dst.ip4 = msg->ip.ipv4.s_addr; + group->proto = msg->ip.eth_proto; + } else if (a[OVS_VPORT_ATTR_IPV6]) { + ip_attr = a[OVS_VPORT_ATTR_IPV6]; + memcpy(&msg->ip.ipv6, nla_data(ip_attr), nla_len(ip_attr)); + msg->ip.eth_proto = htons(ETH_P_IPV6); + group->dst.ip6 = msg->ip.ipv6; + group->proto = msg->ip.eth_proto; + } else { + return -EINVAL; + } + + if (a[OVS_VPORT_ATTR_VLAN]) { + group->vid = nla_get_u16(a[OVS_VPORT_ATTR_VLAN]); + } else { + group->vid = 0; + } + +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + if (a[OVS_VPORT_ATTR_MAC]) { + memcpy(mac, nla_data(a[OVS_VPORT_ATTR_MAC]), nla_len(a[OVS_VPORT_ATTR_MAC])); + } +#endif + + return 0; +} + +static int ovs_vport_cmd_addmcastgrp(struct sk_buff *skb, struct genl_info *info) +{ + struct pp_am_multicast_event_msg msg; + int err = -1; + struct vport *vport; + struct nlattr **a = info->attrs; + struct br_ip group; + unsigned char mac[ETH_ALEN]; + + ovs_lock(); + + vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); + err = PTR_ERR(vport); + if (IS_ERR(vport)) + goto err; + + err = ovs_vport_cmd_mcast_fill_msg(vport, info, &msg, &group, mac); + if (err) + goto err; + + am_port_event(PP_AM_MULTICAST_JOIN, &msg); + +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + if (ovs_dp_multicast_add_group) + err = ovs_dp_multicast_add_group(vport, &group, mac); +#endif + +err: + ovs_unlock(); + return err; +} + +static int ovs_vport_cmd_delmcastgrp(struct sk_buff *skb, struct genl_info *info) +{ + struct pp_am_multicast_event_msg msg; + int err = -1; + struct vport *vport; + struct nlattr **a = info->attrs; + struct br_ip group; + unsigned char mac[ETH_ALEN]; + + ovs_lock(); + + vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); + err = PTR_ERR(vport); + if (IS_ERR(vport)) + goto err; + + err = ovs_vport_cmd_mcast_fill_msg(vport, info, &msg, &group, mac); + if (err) + goto err; + + am_port_event(PP_AM_MULTICAST_LEAVE, &msg); + +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + if (ovs_dp_multicast_del_group) + err = ovs_dp_multicast_del_group(vport, &group, mac); +#endif + +err: + ovs_unlock(); + return err; +} + +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +uint32_t set_vport_flags(struct vport *vport, uint32_t flags) +{ + uint32_t old_flags; + + old_flags = vport->flags; + vport->flags |= flags; + + return old_flags; +} + +uint32_t del_vport_flags(struct vport *vport, uint32_t flags) +{ + uint32_t old_flags; + + old_flags = vport->flags; + vport->flags &= ~flags; + + return old_flags; +} + +static int ovs_vport_cmd_set_flags(struct sk_buff *skb, struct genl_info *info) +{ + int err = -1; + struct vport *vport; + struct nlattr **a = info->attrs; + uint32_t flags; + + ovs_lock(); + + vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); + err = PTR_ERR(vport); + if (IS_ERR(vport)) { + ovs_unlock(); + return err; + } + + if (a[OVS_VPORT_ATTR_FLAGS]) { + flags = nla_get_u32(a[OVS_VPORT_ATTR_FLAGS]); + } else { + flags = 0; + } + + set_vport_flags(vport, flags); + + ovs_unlock(); + + return 0; +} + +static int ovs_vport_cmd_del_flags(struct sk_buff *skb, struct genl_info *info) +{ + int err = -1; + struct vport *vport; + struct nlattr **a = info->attrs; + uint32_t flags; + + ovs_lock(); + + vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); + err = PTR_ERR(vport); + if (IS_ERR(vport)) { + ovs_unlock(); + return err; + } + + if (a[OVS_VPORT_ATTR_FLAGS]) { + flags = nla_get_u32(a[OVS_VPORT_ATTR_FLAGS]); + } else { + flags = 0; + } + + del_vport_flags(vport, flags); + + ovs_unlock(); + + return 0; +} + +static int ovs_vport_cmd_set_carrier(struct sk_buff *skb, struct genl_info *info) +{ + int err = -1; + struct vport *vport; + struct nlattr **a = info->attrs; + uint32_t carrier; + + ovs_lock(); + + vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); + err = PTR_ERR(vport); + if (IS_ERR(vport)){ + ovs_unlock(); + return err; + } + + if (a[OVS_VPORT_ATTR_FLAGS]) { + carrier = nla_get_u32(a[OVS_VPORT_ATTR_FLAGS]); + } else { + ovs_unlock(); + return -1; + } + if (carrier) { + netif_carrier_on(vport->dev); + } else { + netif_carrier_off(vport->dev); + } + + ovs_unlock(); + + return 0; +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 }, @@ -2198,6 +2812,79 @@ } } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +/* Function to get the vport structure through its bridge name*/ +struct vport* get_vport_by_bridge(struct datapath *dp, const char *bridge) +{ + int i; + + for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { + struct vport *vport; + struct hlist_node *n; + + hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) { + if (!(strcmp(vport->dev->name, bridge))) + return vport; + } + } + + return NULL; +} + +static int ovs_brcompat_port_setup(struct vport *vport, struct vport_parms *parms){ + struct vport *br_vport; + + if (!ovs_dp_br_bridge_port_setup) + return -EOPNOTSUPP; + + if (!parms->bridge_name || !*parms->bridge_name) + return -EINVAL; + + br_vport = get_vport_by_bridge(vport->dp, parms->bridge_name); + + if (br_vport == NULL) + return -EINVAL; + + return ovs_dp_br_bridge_port_setup(br_vport, vport, 1); + + return -EOPNOTSUPP; +} + +static int ovs_dp_brcompat_init(struct vport *vport, struct vport_parms *parms) +{ + int err = 0; + + if (vport->port_no == OVSP_LOCAL) { + return err; + } + + if (parms->type != OVS_VPORT_TYPE_INTERNAL) { + err = ovs_brcompat_port_setup(vport, parms); + } + + return err; +} + +static int ovs_brcompat_bridge_destroy(struct vport *vport) +{ + if (ovs_dp_br_bridge_setup) { + return ovs_dp_br_bridge_setup( vport, 0); + } + return -EOPNOTSUPP; +} + +static int ovs_dp_brcompat_destroy(struct vport *vport) +{ + int err = 0; + + if (vport->type == OVS_VPORT_TYPE_INTERNAL) { + err = ovs_brcompat_bridge_destroy(vport); + } + + return err; +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; @@ -2206,6 +2893,10 @@ struct sk_buff *reply; struct vport *vport; struct datapath *dp; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + char *bridgeName = NULL; + struct vport *br; +#endif unsigned int new_headroom; u32 port_no; int err; @@ -2216,6 +2907,11 @@ if (a[OVS_VPORT_ATTR_IFINDEX]) return -EOPNOTSUPP; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + if (!!a[OVS_VPORT_ATTR_BRNAME]) + bridgeName = nla_data(a[OVS_VPORT_ATTR_BRNAME]); +#endif + port_no = a[OVS_VPORT_ATTR_PORT_NO] ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0; if (port_no >= DP_MAX_PORTS) @@ -2255,6 +2951,9 @@ parms.dp = dp; parms.port_no = port_no; parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + parms.bridge_name = bridgeName; +#endif vport = new_vport(&parms); err = PTR_ERR(vport); @@ -2264,6 +2963,25 @@ goto exit_unlock_free; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + ovs_dp_brcompat_init(vport, &parms); + vport->type = parms.type; + if (parms.type == OVS_VPORT_TYPE_INTERNAL) { + ovs_dp_sysfs_add_bridge(vport->dp, vport); + } + else{ + if (!bridgeName || !*bridgeName) + goto cont_wo_sysfs; + + br = get_vport_by_bridge(vport->dp, bridgeName); + + if (br == NULL) + goto cont_wo_sysfs; + ovs_dp_sysfs_add_if(vport, br); + } + +cont_wo_sysfs: +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW, GFP_KERNEL); @@ -2360,6 +3078,10 @@ if (IS_ERR(vport)) goto exit_unlock_free; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + OVS_LOG_DBG("Request to detach port %s (type: %d)\n", vport->dev->name, vport->type); +#endif + if (vport->port_no == OVSP_LOCAL) { err = -EINVAL; goto exit_unlock_free; @@ -2376,6 +3098,13 @@ update_headroom = true; netdev_reset_rx_headroom(vport->dev); + +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + if (vport->type == OVS_VPORT_TYPE_INTERNAL) { + detach_internal_ifaces(vport); + } + ovs_dp_brcompat_destroy(vport); +#endif ovs_dp_detach_port(vport); if (update_headroom) { @@ -2493,6 +3222,15 @@ [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, [OVS_VPORT_ATTR_IFINDEX] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_NETNSID] = { .type = NLA_S32 }, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + [OVS_VPORT_ATTR_MAC] = { .type = NLA_UNSPEC }, +#endif + [OVS_VPORT_ATTR_IPV4] = { .type = NLA_UNSPEC }, + [OVS_VPORT_ATTR_IPV6] = { .type = NLA_UNSPEC }, + [OVS_VPORT_ATTR_VLAN] = { .type = NLA_U16 }, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + [OVS_VPORT_ATTR_FLAGS] = { .type = NLA_U32 }, +#endif }; static const struct genl_small_ops dp_vport_genl_ops[] = { @@ -2517,6 +3255,55 @@ .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .doit = ovs_vport_cmd_set, }, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + { .cmd = OVS_VPORT_CMD_NEWNEIGH, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ + .doit = ovs_vport_cmd_newneigh, + }, + { .cmd = OVS_VPORT_CMD_DELNEIGH, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ + .doit = ovs_vport_cmd_delneigh, + }, +#endif + { .cmd = OVS_VPORT_CMD_ADDMCASTGRP, +#ifdef HAVE_GENL_VALIDATE_FLAGS + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +#endif + .flags = 0, /* OK for unprivileged users. */ + .doit = ovs_vport_cmd_addmcastgrp, + }, + { .cmd = OVS_VPORT_CMD_DELMCASTGRP, +#ifdef HAVE_GENL_VALIDATE_FLAGS + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +#endif + .flags = 0, /* OK for unprivileged users. */ + .doit = ovs_vport_cmd_delmcastgrp, + }, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + { .cmd = OVS_VPORT_CMD_SET_FLAGS, +#ifdef HAVE_GENL_VALIDATE_FLAGS + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +#endif + .flags = 0, /* OK for unprivileged users. */ + .doit = ovs_vport_cmd_set_flags, + }, + { .cmd = OVS_VPORT_CMD_DEL_FLAGS, +#ifdef HAVE_GENL_VALIDATE_FLAGS + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +#endif + .flags = 0, /* OK for unprivileged users. */ + .doit = ovs_vport_cmd_del_flags, + }, + { .cmd = OVS_VPORT_CMD_SET_CARRIER, +#ifdef HAVE_GENL_VALIDATE_FLAGS + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +#endif + .flags = 0, /* OK for unprivileged users. */ + .doit = ovs_vport_cmd_set_carrier, + }, +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ }; struct genl_family dp_vport_genl_family __ro_after_init = {