18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2007-2014 Nicira, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 118c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 128c2ecf20Sopenharmony_ci#include <linux/in.h> 138c2ecf20Sopenharmony_ci#include <linux/ip.h> 148c2ecf20Sopenharmony_ci#include <linux/jhash.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/time.h> 178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/genetlink.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/kthread.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci#include <linux/percpu.h> 238c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 248c2ecf20Sopenharmony_ci#include <linux/tcp.h> 258c2ecf20Sopenharmony_ci#include <linux/udp.h> 268c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 278c2ecf20Sopenharmony_ci#include <linux/wait.h> 288c2ecf20Sopenharmony_ci#include <asm/div64.h> 298c2ecf20Sopenharmony_ci#include <linux/highmem.h> 308c2ecf20Sopenharmony_ci#include <linux/netfilter_bridge.h> 318c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv4.h> 328c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 338c2ecf20Sopenharmony_ci#include <linux/list.h> 348c2ecf20Sopenharmony_ci#include <linux/openvswitch.h> 358c2ecf20Sopenharmony_ci#include <linux/rculist.h> 368c2ecf20Sopenharmony_ci#include <linux/dmi.h> 378c2ecf20Sopenharmony_ci#include <net/genetlink.h> 388c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 398c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "datapath.h" 428c2ecf20Sopenharmony_ci#include "flow.h" 438c2ecf20Sopenharmony_ci#include "flow_table.h" 448c2ecf20Sopenharmony_ci#include "flow_netlink.h" 458c2ecf20Sopenharmony_ci#include "meter.h" 468c2ecf20Sopenharmony_ci#include "vport-internal_dev.h" 478c2ecf20Sopenharmony_ci#include "vport-netdev.h" 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciunsigned int ovs_net_id __read_mostly; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct genl_family dp_packet_genl_family; 528c2ecf20Sopenharmony_cistatic struct genl_family dp_flow_genl_family; 538c2ecf20Sopenharmony_cistatic struct genl_family dp_datapath_genl_family; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct nla_policy flow_policy[]; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct genl_multicast_group ovs_dp_flow_multicast_group = { 588c2ecf20Sopenharmony_ci .name = OVS_FLOW_MCGROUP, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const struct genl_multicast_group ovs_dp_datapath_multicast_group = { 628c2ecf20Sopenharmony_ci .name = OVS_DATAPATH_MCGROUP, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic const struct genl_multicast_group ovs_dp_vport_multicast_group = { 668c2ecf20Sopenharmony_ci .name = OVS_VPORT_MCGROUP, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Check if need to build a reply message. 708c2ecf20Sopenharmony_ci * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */ 718c2ecf20Sopenharmony_cistatic bool ovs_must_notify(struct genl_family *family, struct genl_info *info, 728c2ecf20Sopenharmony_ci unsigned int group) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return info->nlhdr->nlmsg_flags & NLM_F_ECHO || 758c2ecf20Sopenharmony_ci genl_has_listeners(family, genl_info_net(info), group); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void ovs_notify(struct genl_family *family, 798c2ecf20Sopenharmony_ci struct sk_buff *skb, struct genl_info *info) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci genl_notify(family, skb, info, 0, GFP_KERNEL); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/** 858c2ecf20Sopenharmony_ci * DOC: Locking: 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * All writes e.g. Writes to device state (add/remove datapath, port, set 888c2ecf20Sopenharmony_ci * operations on vports, etc.), Writes to other state (flow table 898c2ecf20Sopenharmony_ci * modifications, set miscellaneous datapath parameters, etc.) are protected 908c2ecf20Sopenharmony_ci * by ovs_lock. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Reads are protected by RCU. 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * There are a few special cases (mostly stats) that have their own 958c2ecf20Sopenharmony_ci * synchronization but they nest under all of above and don't interact with 968c2ecf20Sopenharmony_ci * each other. 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * The RTNL lock nests inside ovs_mutex. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ovs_mutex); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_civoid ovs_lock(void) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci mutex_lock(&ovs_mutex); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_civoid ovs_unlock(void) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci mutex_unlock(&ovs_mutex); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#ifdef CONFIG_LOCKDEP 1148c2ecf20Sopenharmony_ciint lockdep_ovsl_is_held(void) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci if (debug_locks) 1178c2ecf20Sopenharmony_ci return lockdep_is_held(&ovs_mutex); 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci return 1; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci#endif 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct vport *new_vport(const struct vport_parms *); 1248c2ecf20Sopenharmony_cistatic int queue_gso_packets(struct datapath *dp, struct sk_buff *, 1258c2ecf20Sopenharmony_ci const struct sw_flow_key *, 1268c2ecf20Sopenharmony_ci const struct dp_upcall_info *, 1278c2ecf20Sopenharmony_ci uint32_t cutlen); 1288c2ecf20Sopenharmony_cistatic int queue_userspace_packet(struct datapath *dp, struct sk_buff *, 1298c2ecf20Sopenharmony_ci const struct sw_flow_key *, 1308c2ecf20Sopenharmony_ci const struct dp_upcall_info *, 1318c2ecf20Sopenharmony_ci uint32_t cutlen); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void ovs_dp_masks_rebalance(struct work_struct *work); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* Must be called with rcu_read_lock or ovs_mutex. */ 1368c2ecf20Sopenharmony_ciconst char *ovs_dp_name(const struct datapath *dp) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL); 1398c2ecf20Sopenharmony_ci return ovs_vport_name(vport); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int get_dpifindex(const struct datapath *dp) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct vport *local; 1458c2ecf20Sopenharmony_ci int ifindex; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci rcu_read_lock(); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci local = ovs_vport_rcu(dp, OVSP_LOCAL); 1508c2ecf20Sopenharmony_ci if (local) 1518c2ecf20Sopenharmony_ci ifindex = local->dev->ifindex; 1528c2ecf20Sopenharmony_ci else 1538c2ecf20Sopenharmony_ci ifindex = 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rcu_read_unlock(); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return ifindex; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void destroy_dp_rcu(struct rcu_head *rcu) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct datapath *dp = container_of(rcu, struct datapath, rcu); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ovs_flow_tbl_destroy(&dp->table); 1658c2ecf20Sopenharmony_ci free_percpu(dp->stats_percpu); 1668c2ecf20Sopenharmony_ci kfree(dp->ports); 1678c2ecf20Sopenharmony_ci ovs_meters_exit(dp); 1688c2ecf20Sopenharmony_ci kfree(dp); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic struct hlist_head *vport_hash_bucket(const struct datapath *dp, 1728c2ecf20Sopenharmony_ci u16 port_no) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)]; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* Called with ovs_mutex or RCU read lock. */ 1788c2ecf20Sopenharmony_cistruct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct vport *vport; 1818c2ecf20Sopenharmony_ci struct hlist_head *head; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci head = vport_hash_bucket(dp, port_no); 1848c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(vport, head, dp_hash_node, 1858c2ecf20Sopenharmony_ci lockdep_ovsl_is_held()) { 1868c2ecf20Sopenharmony_ci if (vport->port_no == port_no) 1878c2ecf20Sopenharmony_ci return vport; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci return NULL; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* Called with ovs_mutex. */ 1938c2ecf20Sopenharmony_cistatic struct vport *new_vport(const struct vport_parms *parms) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct vport *vport; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci vport = ovs_vport_add(parms); 1988c2ecf20Sopenharmony_ci if (!IS_ERR(vport)) { 1998c2ecf20Sopenharmony_ci struct datapath *dp = parms->dp; 2008c2ecf20Sopenharmony_ci struct hlist_head *head = vport_hash_bucket(dp, vport->port_no); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci hlist_add_head_rcu(&vport->dp_hash_node, head); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return vport; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_civoid ovs_dp_detach_port(struct vport *p) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci ASSERT_OVSL(); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* First drop references to device. */ 2128c2ecf20Sopenharmony_ci hlist_del_rcu(&p->dp_hash_node); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Then destroy it. */ 2158c2ecf20Sopenharmony_ci ovs_vport_del(p); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* Must be called with rcu_read_lock. */ 2198c2ecf20Sopenharmony_civoid ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci const struct vport *p = OVS_CB(skb)->input_vport; 2228c2ecf20Sopenharmony_ci struct datapath *dp = p->dp; 2238c2ecf20Sopenharmony_ci struct sw_flow *flow; 2248c2ecf20Sopenharmony_ci struct sw_flow_actions *sf_acts; 2258c2ecf20Sopenharmony_ci struct dp_stats_percpu *stats; 2268c2ecf20Sopenharmony_ci u64 *stats_counter; 2278c2ecf20Sopenharmony_ci u32 n_mask_hit; 2288c2ecf20Sopenharmony_ci u32 n_cache_hit; 2298c2ecf20Sopenharmony_ci int error; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci stats = this_cpu_ptr(dp->stats_percpu); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Look up flow. */ 2348c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb), 2358c2ecf20Sopenharmony_ci &n_mask_hit, &n_cache_hit); 2368c2ecf20Sopenharmony_ci if (unlikely(!flow)) { 2378c2ecf20Sopenharmony_ci struct dp_upcall_info upcall; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci memset(&upcall, 0, sizeof(upcall)); 2408c2ecf20Sopenharmony_ci upcall.cmd = OVS_PACKET_CMD_MISS; 2418c2ecf20Sopenharmony_ci upcall.portid = ovs_vport_find_upcall_portid(p, skb); 2428c2ecf20Sopenharmony_ci upcall.mru = OVS_CB(skb)->mru; 2438c2ecf20Sopenharmony_ci error = ovs_dp_upcall(dp, skb, key, &upcall, 0); 2448c2ecf20Sopenharmony_ci switch (error) { 2458c2ecf20Sopenharmony_ci case 0: 2468c2ecf20Sopenharmony_ci case -EAGAIN: 2478c2ecf20Sopenharmony_ci case -ERESTARTSYS: 2488c2ecf20Sopenharmony_ci case -EINTR: 2498c2ecf20Sopenharmony_ci consume_skb(skb); 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci kfree_skb(skb); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci stats_counter = &stats->n_missed; 2568c2ecf20Sopenharmony_ci goto out; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ovs_flow_stats_update(flow, key->tp.flags, skb); 2608c2ecf20Sopenharmony_ci sf_acts = rcu_dereference(flow->sf_acts); 2618c2ecf20Sopenharmony_ci error = ovs_execute_actions(dp, skb, sf_acts, key); 2628c2ecf20Sopenharmony_ci if (unlikely(error)) 2638c2ecf20Sopenharmony_ci net_dbg_ratelimited("ovs: action execution error on datapath %s: %d\n", 2648c2ecf20Sopenharmony_ci ovs_dp_name(dp), error); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci stats_counter = &stats->n_hit; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciout: 2698c2ecf20Sopenharmony_ci /* Update datapath statistics. */ 2708c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 2718c2ecf20Sopenharmony_ci (*stats_counter)++; 2728c2ecf20Sopenharmony_ci stats->n_mask_hit += n_mask_hit; 2738c2ecf20Sopenharmony_ci stats->n_cache_hit += n_cache_hit; 2748c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciint ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, 2788c2ecf20Sopenharmony_ci const struct sw_flow_key *key, 2798c2ecf20Sopenharmony_ci const struct dp_upcall_info *upcall_info, 2808c2ecf20Sopenharmony_ci uint32_t cutlen) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct dp_stats_percpu *stats; 2838c2ecf20Sopenharmony_ci int err; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (upcall_info->portid == 0) { 2868c2ecf20Sopenharmony_ci err = -ENOTCONN; 2878c2ecf20Sopenharmony_ci goto err; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!skb_is_gso(skb)) 2918c2ecf20Sopenharmony_ci err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci err = queue_gso_packets(dp, skb, key, upcall_info, cutlen); 2948c2ecf20Sopenharmony_ci if (err) 2958c2ecf20Sopenharmony_ci goto err; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cierr: 3008c2ecf20Sopenharmony_ci stats = this_cpu_ptr(dp->stats_percpu); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 3038c2ecf20Sopenharmony_ci stats->n_lost++; 3048c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return err; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, 3108c2ecf20Sopenharmony_ci const struct sw_flow_key *key, 3118c2ecf20Sopenharmony_ci const struct dp_upcall_info *upcall_info, 3128c2ecf20Sopenharmony_ci uint32_t cutlen) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci unsigned int gso_type = skb_shinfo(skb)->gso_type; 3158c2ecf20Sopenharmony_ci struct sw_flow_key later_key; 3168c2ecf20Sopenharmony_ci struct sk_buff *segs, *nskb; 3178c2ecf20Sopenharmony_ci int err; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*OVS_CB(skb)) > SKB_GSO_CB_OFFSET); 3208c2ecf20Sopenharmony_ci segs = __skb_gso_segment(skb, NETIF_F_SG, false); 3218c2ecf20Sopenharmony_ci if (IS_ERR(segs)) 3228c2ecf20Sopenharmony_ci return PTR_ERR(segs); 3238c2ecf20Sopenharmony_ci if (segs == NULL) 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (gso_type & SKB_GSO_UDP) { 3278c2ecf20Sopenharmony_ci /* The initial flow key extracted by ovs_flow_key_extract() 3288c2ecf20Sopenharmony_ci * in this case is for a first fragment, so we need to 3298c2ecf20Sopenharmony_ci * properly mark later fragments. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci later_key = *key; 3328c2ecf20Sopenharmony_ci later_key.ip.frag = OVS_FRAG_TYPE_LATER; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Queue all of the segments. */ 3368c2ecf20Sopenharmony_ci skb_list_walk_safe(segs, skb, nskb) { 3378c2ecf20Sopenharmony_ci if (gso_type & SKB_GSO_UDP && skb != segs) 3388c2ecf20Sopenharmony_ci key = &later_key; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); 3418c2ecf20Sopenharmony_ci if (err) 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Free all of the segments. */ 3478c2ecf20Sopenharmony_ci skb_list_walk_safe(segs, skb, nskb) { 3488c2ecf20Sopenharmony_ci if (err) 3498c2ecf20Sopenharmony_ci kfree_skb(skb); 3508c2ecf20Sopenharmony_ci else 3518c2ecf20Sopenharmony_ci consume_skb(skb); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic size_t upcall_msg_size(const struct dp_upcall_info *upcall_info, 3578c2ecf20Sopenharmony_ci unsigned int hdrlen, int actions_attrlen) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci size_t size = NLMSG_ALIGN(sizeof(struct ovs_header)) 3608c2ecf20Sopenharmony_ci + nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */ 3618c2ecf20Sopenharmony_ci + nla_total_size(ovs_key_attr_size()) /* OVS_PACKET_ATTR_KEY */ 3628c2ecf20Sopenharmony_ci + nla_total_size(sizeof(unsigned int)) /* OVS_PACKET_ATTR_LEN */ 3638c2ecf20Sopenharmony_ci + nla_total_size(sizeof(u64)); /* OVS_PACKET_ATTR_HASH */ 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* OVS_PACKET_ATTR_USERDATA */ 3668c2ecf20Sopenharmony_ci if (upcall_info->userdata) 3678c2ecf20Sopenharmony_ci size += NLA_ALIGN(upcall_info->userdata->nla_len); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */ 3708c2ecf20Sopenharmony_ci if (upcall_info->egress_tun_info) 3718c2ecf20Sopenharmony_ci size += nla_total_size(ovs_tun_key_attr_size()); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* OVS_PACKET_ATTR_ACTIONS */ 3748c2ecf20Sopenharmony_ci if (upcall_info->actions_len) 3758c2ecf20Sopenharmony_ci size += nla_total_size(actions_attrlen); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* OVS_PACKET_ATTR_MRU */ 3788c2ecf20Sopenharmony_ci if (upcall_info->mru) 3798c2ecf20Sopenharmony_ci size += nla_total_size(sizeof(upcall_info->mru)); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return size; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void pad_packet(struct datapath *dp, struct sk_buff *skb) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci if (!(dp->user_features & OVS_DP_F_UNALIGNED)) { 3878c2ecf20Sopenharmony_ci size_t plen = NLA_ALIGN(skb->len) - skb->len; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (plen > 0) 3908c2ecf20Sopenharmony_ci skb_put_zero(skb, plen); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, 3958c2ecf20Sopenharmony_ci const struct sw_flow_key *key, 3968c2ecf20Sopenharmony_ci const struct dp_upcall_info *upcall_info, 3978c2ecf20Sopenharmony_ci uint32_t cutlen) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct ovs_header *upcall; 4008c2ecf20Sopenharmony_ci struct sk_buff *nskb = NULL; 4018c2ecf20Sopenharmony_ci struct sk_buff *user_skb = NULL; /* to be queued to userspace */ 4028c2ecf20Sopenharmony_ci struct nlattr *nla; 4038c2ecf20Sopenharmony_ci size_t len; 4048c2ecf20Sopenharmony_ci unsigned int hlen; 4058c2ecf20Sopenharmony_ci int err, dp_ifindex; 4068c2ecf20Sopenharmony_ci u64 hash; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dp_ifindex = get_dpifindex(dp); 4098c2ecf20Sopenharmony_ci if (!dp_ifindex) 4108c2ecf20Sopenharmony_ci return -ENODEV; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 4138c2ecf20Sopenharmony_ci nskb = skb_clone(skb, GFP_ATOMIC); 4148c2ecf20Sopenharmony_ci if (!nskb) 4158c2ecf20Sopenharmony_ci return -ENOMEM; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci nskb = __vlan_hwaccel_push_inside(nskb); 4188c2ecf20Sopenharmony_ci if (!nskb) 4198c2ecf20Sopenharmony_ci return -ENOMEM; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci skb = nskb; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (nla_attr_size(skb->len) > USHRT_MAX) { 4258c2ecf20Sopenharmony_ci err = -EFBIG; 4268c2ecf20Sopenharmony_ci goto out; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Complete checksum if needed */ 4308c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL && 4318c2ecf20Sopenharmony_ci (err = skb_csum_hwoffload_help(skb, 0))) 4328c2ecf20Sopenharmony_ci goto out; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Older versions of OVS user space enforce alignment of the last 4358c2ecf20Sopenharmony_ci * Netlink attribute to NLA_ALIGNTO which would require extensive 4368c2ecf20Sopenharmony_ci * padding logic. Only perform zerocopy if padding is not required. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci if (dp->user_features & OVS_DP_F_UNALIGNED) 4398c2ecf20Sopenharmony_ci hlen = skb_zerocopy_headlen(skb); 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci hlen = skb->len; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci len = upcall_msg_size(upcall_info, hlen - cutlen, 4448c2ecf20Sopenharmony_ci OVS_CB(skb)->acts_origlen); 4458c2ecf20Sopenharmony_ci user_skb = genlmsg_new(len, GFP_ATOMIC); 4468c2ecf20Sopenharmony_ci if (!user_skb) { 4478c2ecf20Sopenharmony_ci err = -ENOMEM; 4488c2ecf20Sopenharmony_ci goto out; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci upcall = genlmsg_put(user_skb, 0, 0, &dp_packet_genl_family, 4528c2ecf20Sopenharmony_ci 0, upcall_info->cmd); 4538c2ecf20Sopenharmony_ci if (!upcall) { 4548c2ecf20Sopenharmony_ci err = -EINVAL; 4558c2ecf20Sopenharmony_ci goto out; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci upcall->dp_ifindex = dp_ifindex; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci err = ovs_nla_put_key(key, key, OVS_PACKET_ATTR_KEY, false, user_skb); 4608c2ecf20Sopenharmony_ci if (err) 4618c2ecf20Sopenharmony_ci goto out; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (upcall_info->userdata) 4648c2ecf20Sopenharmony_ci __nla_put(user_skb, OVS_PACKET_ATTR_USERDATA, 4658c2ecf20Sopenharmony_ci nla_len(upcall_info->userdata), 4668c2ecf20Sopenharmony_ci nla_data(upcall_info->userdata)); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (upcall_info->egress_tun_info) { 4698c2ecf20Sopenharmony_ci nla = nla_nest_start_noflag(user_skb, 4708c2ecf20Sopenharmony_ci OVS_PACKET_ATTR_EGRESS_TUN_KEY); 4718c2ecf20Sopenharmony_ci if (!nla) { 4728c2ecf20Sopenharmony_ci err = -EMSGSIZE; 4738c2ecf20Sopenharmony_ci goto out; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci err = ovs_nla_put_tunnel_info(user_skb, 4768c2ecf20Sopenharmony_ci upcall_info->egress_tun_info); 4778c2ecf20Sopenharmony_ci if (err) 4788c2ecf20Sopenharmony_ci goto out; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci nla_nest_end(user_skb, nla); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (upcall_info->actions_len) { 4848c2ecf20Sopenharmony_ci nla = nla_nest_start_noflag(user_skb, OVS_PACKET_ATTR_ACTIONS); 4858c2ecf20Sopenharmony_ci if (!nla) { 4868c2ecf20Sopenharmony_ci err = -EMSGSIZE; 4878c2ecf20Sopenharmony_ci goto out; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci err = ovs_nla_put_actions(upcall_info->actions, 4908c2ecf20Sopenharmony_ci upcall_info->actions_len, 4918c2ecf20Sopenharmony_ci user_skb); 4928c2ecf20Sopenharmony_ci if (!err) 4938c2ecf20Sopenharmony_ci nla_nest_end(user_skb, nla); 4948c2ecf20Sopenharmony_ci else 4958c2ecf20Sopenharmony_ci nla_nest_cancel(user_skb, nla); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Add OVS_PACKET_ATTR_MRU */ 4998c2ecf20Sopenharmony_ci if (upcall_info->mru && 5008c2ecf20Sopenharmony_ci nla_put_u16(user_skb, OVS_PACKET_ATTR_MRU, upcall_info->mru)) { 5018c2ecf20Sopenharmony_ci err = -ENOBUFS; 5028c2ecf20Sopenharmony_ci goto out; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Add OVS_PACKET_ATTR_LEN when packet is truncated */ 5068c2ecf20Sopenharmony_ci if (cutlen > 0 && 5078c2ecf20Sopenharmony_ci nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) { 5088c2ecf20Sopenharmony_ci err = -ENOBUFS; 5098c2ecf20Sopenharmony_ci goto out; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Add OVS_PACKET_ATTR_HASH */ 5138c2ecf20Sopenharmony_ci hash = skb_get_hash_raw(skb); 5148c2ecf20Sopenharmony_ci if (skb->sw_hash) 5158c2ecf20Sopenharmony_ci hash |= OVS_PACKET_HASH_SW_BIT; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (skb->l4_hash) 5188c2ecf20Sopenharmony_ci hash |= OVS_PACKET_HASH_L4_BIT; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (nla_put(user_skb, OVS_PACKET_ATTR_HASH, sizeof (u64), &hash)) { 5218c2ecf20Sopenharmony_ci err = -ENOBUFS; 5228c2ecf20Sopenharmony_ci goto out; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Only reserve room for attribute header, packet data is added 5268c2ecf20Sopenharmony_ci * in skb_zerocopy() */ 5278c2ecf20Sopenharmony_ci if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) { 5288c2ecf20Sopenharmony_ci err = -ENOBUFS; 5298c2ecf20Sopenharmony_ci goto out; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci nla->nla_len = nla_attr_size(skb->len - cutlen); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen); 5348c2ecf20Sopenharmony_ci if (err) 5358c2ecf20Sopenharmony_ci goto out; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Pad OVS_PACKET_ATTR_PACKET if linear copy was performed */ 5388c2ecf20Sopenharmony_ci pad_packet(dp, user_skb); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); 5438c2ecf20Sopenharmony_ci user_skb = NULL; 5448c2ecf20Sopenharmony_ciout: 5458c2ecf20Sopenharmony_ci if (err) 5468c2ecf20Sopenharmony_ci skb_tx_error(skb); 5478c2ecf20Sopenharmony_ci consume_skb(user_skb); 5488c2ecf20Sopenharmony_ci consume_skb(nskb); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return err; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 5568c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 5578c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 5588c2ecf20Sopenharmony_ci struct sw_flow_actions *acts; 5598c2ecf20Sopenharmony_ci struct sk_buff *packet; 5608c2ecf20Sopenharmony_ci struct sw_flow *flow; 5618c2ecf20Sopenharmony_ci struct sw_flow_actions *sf_acts; 5628c2ecf20Sopenharmony_ci struct datapath *dp; 5638c2ecf20Sopenharmony_ci struct vport *input_vport; 5648c2ecf20Sopenharmony_ci u16 mru = 0; 5658c2ecf20Sopenharmony_ci u64 hash; 5668c2ecf20Sopenharmony_ci int len; 5678c2ecf20Sopenharmony_ci int err; 5688c2ecf20Sopenharmony_ci bool log = !a[OVS_PACKET_ATTR_PROBE]; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci err = -EINVAL; 5718c2ecf20Sopenharmony_ci if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] || 5728c2ecf20Sopenharmony_ci !a[OVS_PACKET_ATTR_ACTIONS]) 5738c2ecf20Sopenharmony_ci goto err; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci len = nla_len(a[OVS_PACKET_ATTR_PACKET]); 5768c2ecf20Sopenharmony_ci packet = __dev_alloc_skb(NET_IP_ALIGN + len, GFP_KERNEL); 5778c2ecf20Sopenharmony_ci err = -ENOMEM; 5788c2ecf20Sopenharmony_ci if (!packet) 5798c2ecf20Sopenharmony_ci goto err; 5808c2ecf20Sopenharmony_ci skb_reserve(packet, NET_IP_ALIGN); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Set packet's mru */ 5858c2ecf20Sopenharmony_ci if (a[OVS_PACKET_ATTR_MRU]) { 5868c2ecf20Sopenharmony_ci mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]); 5878c2ecf20Sopenharmony_ci packet->ignore_df = 1; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci OVS_CB(packet)->mru = mru; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (a[OVS_PACKET_ATTR_HASH]) { 5928c2ecf20Sopenharmony_ci hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci __skb_set_hash(packet, hash & 0xFFFFFFFFULL, 5958c2ecf20Sopenharmony_ci !!(hash & OVS_PACKET_HASH_SW_BIT), 5968c2ecf20Sopenharmony_ci !!(hash & OVS_PACKET_HASH_L4_BIT)); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* Build an sw_flow for sending this packet. */ 6008c2ecf20Sopenharmony_ci flow = ovs_flow_alloc(); 6018c2ecf20Sopenharmony_ci err = PTR_ERR(flow); 6028c2ecf20Sopenharmony_ci if (IS_ERR(flow)) 6038c2ecf20Sopenharmony_ci goto err_kfree_skb; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci err = ovs_flow_key_extract_userspace(net, a[OVS_PACKET_ATTR_KEY], 6068c2ecf20Sopenharmony_ci packet, &flow->key, log); 6078c2ecf20Sopenharmony_ci if (err) 6088c2ecf20Sopenharmony_ci goto err_flow_free; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci err = ovs_nla_copy_actions(net, a[OVS_PACKET_ATTR_ACTIONS], 6118c2ecf20Sopenharmony_ci &flow->key, &acts, log); 6128c2ecf20Sopenharmony_ci if (err) 6138c2ecf20Sopenharmony_ci goto err_flow_free; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci rcu_assign_pointer(flow->sf_acts, acts); 6168c2ecf20Sopenharmony_ci packet->priority = flow->key.phy.priority; 6178c2ecf20Sopenharmony_ci packet->mark = flow->key.phy.skb_mark; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci rcu_read_lock(); 6208c2ecf20Sopenharmony_ci dp = get_dp_rcu(net, ovs_header->dp_ifindex); 6218c2ecf20Sopenharmony_ci err = -ENODEV; 6228c2ecf20Sopenharmony_ci if (!dp) 6238c2ecf20Sopenharmony_ci goto err_unlock; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci input_vport = ovs_vport_rcu(dp, flow->key.phy.in_port); 6268c2ecf20Sopenharmony_ci if (!input_vport) 6278c2ecf20Sopenharmony_ci input_vport = ovs_vport_rcu(dp, OVSP_LOCAL); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!input_vport) 6308c2ecf20Sopenharmony_ci goto err_unlock; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci packet->dev = input_vport->dev; 6338c2ecf20Sopenharmony_ci OVS_CB(packet)->input_vport = input_vport; 6348c2ecf20Sopenharmony_ci sf_acts = rcu_dereference(flow->sf_acts); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci local_bh_disable(); 6378c2ecf20Sopenharmony_ci err = ovs_execute_actions(dp, packet, sf_acts, &flow->key); 6388c2ecf20Sopenharmony_ci local_bh_enable(); 6398c2ecf20Sopenharmony_ci rcu_read_unlock(); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci ovs_flow_free(flow, false); 6428c2ecf20Sopenharmony_ci return err; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cierr_unlock: 6458c2ecf20Sopenharmony_ci rcu_read_unlock(); 6468c2ecf20Sopenharmony_cierr_flow_free: 6478c2ecf20Sopenharmony_ci ovs_flow_free(flow, false); 6488c2ecf20Sopenharmony_cierr_kfree_skb: 6498c2ecf20Sopenharmony_ci kfree_skb(packet); 6508c2ecf20Sopenharmony_cierr: 6518c2ecf20Sopenharmony_ci return err; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = { 6558c2ecf20Sopenharmony_ci [OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN }, 6568c2ecf20Sopenharmony_ci [OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED }, 6578c2ecf20Sopenharmony_ci [OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED }, 6588c2ecf20Sopenharmony_ci [OVS_PACKET_ATTR_PROBE] = { .type = NLA_FLAG }, 6598c2ecf20Sopenharmony_ci [OVS_PACKET_ATTR_MRU] = { .type = NLA_U16 }, 6608c2ecf20Sopenharmony_ci [OVS_PACKET_ATTR_HASH] = { .type = NLA_U64 }, 6618c2ecf20Sopenharmony_ci}; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic const struct genl_small_ops dp_packet_genl_ops[] = { 6648c2ecf20Sopenharmony_ci { .cmd = OVS_PACKET_CMD_EXECUTE, 6658c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6668c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 6678c2ecf20Sopenharmony_ci .doit = ovs_packet_cmd_execute 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci}; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic struct genl_family dp_packet_genl_family __ro_after_init = { 6728c2ecf20Sopenharmony_ci .hdrsize = sizeof(struct ovs_header), 6738c2ecf20Sopenharmony_ci .name = OVS_PACKET_FAMILY, 6748c2ecf20Sopenharmony_ci .version = OVS_PACKET_VERSION, 6758c2ecf20Sopenharmony_ci .maxattr = OVS_PACKET_ATTR_MAX, 6768c2ecf20Sopenharmony_ci .policy = packet_policy, 6778c2ecf20Sopenharmony_ci .netnsok = true, 6788c2ecf20Sopenharmony_ci .parallel_ops = true, 6798c2ecf20Sopenharmony_ci .small_ops = dp_packet_genl_ops, 6808c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(dp_packet_genl_ops), 6818c2ecf20Sopenharmony_ci .module = THIS_MODULE, 6828c2ecf20Sopenharmony_ci}; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats, 6858c2ecf20Sopenharmony_ci struct ovs_dp_megaflow_stats *mega_stats) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci int i; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci memset(mega_stats, 0, sizeof(*mega_stats)); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci stats->n_flows = ovs_flow_tbl_count(&dp->table); 6928c2ecf20Sopenharmony_ci mega_stats->n_masks = ovs_flow_tbl_num_masks(&dp->table); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci stats->n_hit = stats->n_missed = stats->n_lost = 0; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 6978c2ecf20Sopenharmony_ci const struct dp_stats_percpu *percpu_stats; 6988c2ecf20Sopenharmony_ci struct dp_stats_percpu local_stats; 6998c2ecf20Sopenharmony_ci unsigned int start; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci percpu_stats = per_cpu_ptr(dp->stats_percpu, i); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci do { 7048c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&percpu_stats->syncp); 7058c2ecf20Sopenharmony_ci local_stats = *percpu_stats; 7068c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start)); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci stats->n_hit += local_stats.n_hit; 7098c2ecf20Sopenharmony_ci stats->n_missed += local_stats.n_missed; 7108c2ecf20Sopenharmony_ci stats->n_lost += local_stats.n_lost; 7118c2ecf20Sopenharmony_ci mega_stats->n_mask_hit += local_stats.n_mask_hit; 7128c2ecf20Sopenharmony_ci mega_stats->n_cache_hit += local_stats.n_cache_hit; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic bool should_fill_key(const struct sw_flow_id *sfid, uint32_t ufid_flags) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci return ovs_identifier_is_ufid(sfid) && 7198c2ecf20Sopenharmony_ci !(ufid_flags & OVS_UFID_F_OMIT_KEY); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic bool should_fill_mask(uint32_t ufid_flags) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci return !(ufid_flags & OVS_UFID_F_OMIT_MASK); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic bool should_fill_actions(uint32_t ufid_flags) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci return !(ufid_flags & OVS_UFID_F_OMIT_ACTIONS); 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts, 7338c2ecf20Sopenharmony_ci const struct sw_flow_id *sfid, 7348c2ecf20Sopenharmony_ci uint32_t ufid_flags) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci size_t len = NLMSG_ALIGN(sizeof(struct ovs_header)); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* OVS_FLOW_ATTR_UFID, or unmasked flow key as fallback 7398c2ecf20Sopenharmony_ci * see ovs_nla_put_identifier() 7408c2ecf20Sopenharmony_ci */ 7418c2ecf20Sopenharmony_ci if (sfid && ovs_identifier_is_ufid(sfid)) 7428c2ecf20Sopenharmony_ci len += nla_total_size(sfid->ufid_len); 7438c2ecf20Sopenharmony_ci else 7448c2ecf20Sopenharmony_ci len += nla_total_size(ovs_key_attr_size()); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* OVS_FLOW_ATTR_KEY */ 7478c2ecf20Sopenharmony_ci if (!sfid || should_fill_key(sfid, ufid_flags)) 7488c2ecf20Sopenharmony_ci len += nla_total_size(ovs_key_attr_size()); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* OVS_FLOW_ATTR_MASK */ 7518c2ecf20Sopenharmony_ci if (should_fill_mask(ufid_flags)) 7528c2ecf20Sopenharmony_ci len += nla_total_size(ovs_key_attr_size()); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* OVS_FLOW_ATTR_ACTIONS */ 7558c2ecf20Sopenharmony_ci if (should_fill_actions(ufid_flags)) 7568c2ecf20Sopenharmony_ci len += nla_total_size(acts->orig_len); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return len 7598c2ecf20Sopenharmony_ci + nla_total_size_64bit(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */ 7608c2ecf20Sopenharmony_ci + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */ 7618c2ecf20Sopenharmony_ci + nla_total_size_64bit(8); /* OVS_FLOW_ATTR_USED */ 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/* Called with ovs_mutex or RCU read lock. */ 7658c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_fill_stats(const struct sw_flow *flow, 7668c2ecf20Sopenharmony_ci struct sk_buff *skb) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct ovs_flow_stats stats; 7698c2ecf20Sopenharmony_ci __be16 tcp_flags; 7708c2ecf20Sopenharmony_ci unsigned long used; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci ovs_flow_stats_get(flow, &stats, &used, &tcp_flags); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (used && 7758c2ecf20Sopenharmony_ci nla_put_u64_64bit(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used), 7768c2ecf20Sopenharmony_ci OVS_FLOW_ATTR_PAD)) 7778c2ecf20Sopenharmony_ci return -EMSGSIZE; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (stats.n_packets && 7808c2ecf20Sopenharmony_ci nla_put_64bit(skb, OVS_FLOW_ATTR_STATS, 7818c2ecf20Sopenharmony_ci sizeof(struct ovs_flow_stats), &stats, 7828c2ecf20Sopenharmony_ci OVS_FLOW_ATTR_PAD)) 7838c2ecf20Sopenharmony_ci return -EMSGSIZE; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if ((u8)ntohs(tcp_flags) && 7868c2ecf20Sopenharmony_ci nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, (u8)ntohs(tcp_flags))) 7878c2ecf20Sopenharmony_ci return -EMSGSIZE; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/* Called with ovs_mutex or RCU read lock. */ 7938c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_fill_actions(const struct sw_flow *flow, 7948c2ecf20Sopenharmony_ci struct sk_buff *skb, int skb_orig_len) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct nlattr *start; 7978c2ecf20Sopenharmony_ci int err; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if 8008c2ecf20Sopenharmony_ci * this is the first flow to be dumped into 'skb'. This is unusual for 8018c2ecf20Sopenharmony_ci * Netlink but individual action lists can be longer than 8028c2ecf20Sopenharmony_ci * NLMSG_GOODSIZE and thus entirely undumpable if we didn't do this. 8038c2ecf20Sopenharmony_ci * The userspace caller can always fetch the actions separately if it 8048c2ecf20Sopenharmony_ci * really wants them. (Most userspace callers in fact don't care.) 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * This can only fail for dump operations because the skb is always 8078c2ecf20Sopenharmony_ci * properly sized for single flows. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci start = nla_nest_start_noflag(skb, OVS_FLOW_ATTR_ACTIONS); 8108c2ecf20Sopenharmony_ci if (start) { 8118c2ecf20Sopenharmony_ci const struct sw_flow_actions *sf_acts; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci sf_acts = rcu_dereference_ovsl(flow->sf_acts); 8148c2ecf20Sopenharmony_ci err = ovs_nla_put_actions(sf_acts->actions, 8158c2ecf20Sopenharmony_ci sf_acts->actions_len, skb); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (!err) 8188c2ecf20Sopenharmony_ci nla_nest_end(skb, start); 8198c2ecf20Sopenharmony_ci else { 8208c2ecf20Sopenharmony_ci if (skb_orig_len) 8218c2ecf20Sopenharmony_ci return err; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci nla_nest_cancel(skb, start); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } else if (skb_orig_len) { 8268c2ecf20Sopenharmony_ci return -EMSGSIZE; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci return 0; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/* Called with ovs_mutex or RCU read lock. */ 8338c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex, 8348c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 portid, 8358c2ecf20Sopenharmony_ci u32 seq, u32 flags, u8 cmd, u32 ufid_flags) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci const int skb_orig_len = skb->len; 8388c2ecf20Sopenharmony_ci struct ovs_header *ovs_header; 8398c2ecf20Sopenharmony_ci int err; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, 8428c2ecf20Sopenharmony_ci flags, cmd); 8438c2ecf20Sopenharmony_ci if (!ovs_header) 8448c2ecf20Sopenharmony_ci return -EMSGSIZE; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ovs_header->dp_ifindex = dp_ifindex; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci err = ovs_nla_put_identifier(flow, skb); 8498c2ecf20Sopenharmony_ci if (err) 8508c2ecf20Sopenharmony_ci goto error; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (should_fill_key(&flow->id, ufid_flags)) { 8538c2ecf20Sopenharmony_ci err = ovs_nla_put_masked_key(flow, skb); 8548c2ecf20Sopenharmony_ci if (err) 8558c2ecf20Sopenharmony_ci goto error; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (should_fill_mask(ufid_flags)) { 8598c2ecf20Sopenharmony_ci err = ovs_nla_put_mask(flow, skb); 8608c2ecf20Sopenharmony_ci if (err) 8618c2ecf20Sopenharmony_ci goto error; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci err = ovs_flow_cmd_fill_stats(flow, skb); 8658c2ecf20Sopenharmony_ci if (err) 8668c2ecf20Sopenharmony_ci goto error; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (should_fill_actions(ufid_flags)) { 8698c2ecf20Sopenharmony_ci err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len); 8708c2ecf20Sopenharmony_ci if (err) 8718c2ecf20Sopenharmony_ci goto error; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci genlmsg_end(skb, ovs_header); 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cierror: 8788c2ecf20Sopenharmony_ci genlmsg_cancel(skb, ovs_header); 8798c2ecf20Sopenharmony_ci return err; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci/* May not be called with RCU read lock. */ 8838c2ecf20Sopenharmony_cistatic struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts, 8848c2ecf20Sopenharmony_ci const struct sw_flow_id *sfid, 8858c2ecf20Sopenharmony_ci struct genl_info *info, 8868c2ecf20Sopenharmony_ci bool always, 8878c2ecf20Sopenharmony_ci uint32_t ufid_flags) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct sk_buff *skb; 8908c2ecf20Sopenharmony_ci size_t len; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (!always && !ovs_must_notify(&dp_flow_genl_family, info, 0)) 8938c2ecf20Sopenharmony_ci return NULL; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci len = ovs_flow_cmd_msg_size(acts, sfid, ufid_flags); 8968c2ecf20Sopenharmony_ci skb = genlmsg_new(len, GFP_KERNEL); 8978c2ecf20Sopenharmony_ci if (!skb) 8988c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return skb; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci/* Called with ovs_mutex. */ 9048c2ecf20Sopenharmony_cistatic struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow, 9058c2ecf20Sopenharmony_ci int dp_ifindex, 9068c2ecf20Sopenharmony_ci struct genl_info *info, u8 cmd, 9078c2ecf20Sopenharmony_ci bool always, u32 ufid_flags) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct sk_buff *skb; 9108c2ecf20Sopenharmony_ci int retval; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), 9138c2ecf20Sopenharmony_ci &flow->id, info, always, ufid_flags); 9148c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(skb)) 9158c2ecf20Sopenharmony_ci return skb; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb, 9188c2ecf20Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 9198c2ecf20Sopenharmony_ci cmd, ufid_flags); 9208c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(retval < 0)) { 9218c2ecf20Sopenharmony_ci kfree_skb(skb); 9228c2ecf20Sopenharmony_ci skb = ERR_PTR(retval); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci return skb; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 9308c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 9318c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 9328c2ecf20Sopenharmony_ci struct sw_flow *flow = NULL, *new_flow; 9338c2ecf20Sopenharmony_ci struct sw_flow_mask mask; 9348c2ecf20Sopenharmony_ci struct sk_buff *reply; 9358c2ecf20Sopenharmony_ci struct datapath *dp; 9368c2ecf20Sopenharmony_ci struct sw_flow_key *key; 9378c2ecf20Sopenharmony_ci struct sw_flow_actions *acts; 9388c2ecf20Sopenharmony_ci struct sw_flow_match match; 9398c2ecf20Sopenharmony_ci u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); 9408c2ecf20Sopenharmony_ci int error; 9418c2ecf20Sopenharmony_ci bool log = !a[OVS_FLOW_ATTR_PROBE]; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Must have key and actions. */ 9448c2ecf20Sopenharmony_ci error = -EINVAL; 9458c2ecf20Sopenharmony_ci if (!a[OVS_FLOW_ATTR_KEY]) { 9468c2ecf20Sopenharmony_ci OVS_NLERR(log, "Flow key attr not present in new flow."); 9478c2ecf20Sopenharmony_ci goto error; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci if (!a[OVS_FLOW_ATTR_ACTIONS]) { 9508c2ecf20Sopenharmony_ci OVS_NLERR(log, "Flow actions attr not present in new flow."); 9518c2ecf20Sopenharmony_ci goto error; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* Most of the time we need to allocate a new flow, do it before 9558c2ecf20Sopenharmony_ci * locking. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci new_flow = ovs_flow_alloc(); 9588c2ecf20Sopenharmony_ci if (IS_ERR(new_flow)) { 9598c2ecf20Sopenharmony_ci error = PTR_ERR(new_flow); 9608c2ecf20Sopenharmony_ci goto error; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* Extract key. */ 9648c2ecf20Sopenharmony_ci key = kzalloc(sizeof(*key), GFP_KERNEL); 9658c2ecf20Sopenharmony_ci if (!key) { 9668c2ecf20Sopenharmony_ci error = -ENOMEM; 9678c2ecf20Sopenharmony_ci goto err_kfree_flow; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ovs_match_init(&match, key, false, &mask); 9718c2ecf20Sopenharmony_ci error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], 9728c2ecf20Sopenharmony_ci a[OVS_FLOW_ATTR_MASK], log); 9738c2ecf20Sopenharmony_ci if (error) 9748c2ecf20Sopenharmony_ci goto err_kfree_key; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci ovs_flow_mask_key(&new_flow->key, key, true, &mask); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* Extract flow identifier. */ 9798c2ecf20Sopenharmony_ci error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], 9808c2ecf20Sopenharmony_ci key, log); 9818c2ecf20Sopenharmony_ci if (error) 9828c2ecf20Sopenharmony_ci goto err_kfree_key; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* Validate actions. */ 9858c2ecf20Sopenharmony_ci error = ovs_nla_copy_actions(net, a[OVS_FLOW_ATTR_ACTIONS], 9868c2ecf20Sopenharmony_ci &new_flow->key, &acts, log); 9878c2ecf20Sopenharmony_ci if (error) { 9888c2ecf20Sopenharmony_ci OVS_NLERR(log, "Flow actions may not be safe on all matching packets."); 9898c2ecf20Sopenharmony_ci goto err_kfree_key; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci reply = ovs_flow_cmd_alloc_info(acts, &new_flow->id, info, false, 9938c2ecf20Sopenharmony_ci ufid_flags); 9948c2ecf20Sopenharmony_ci if (IS_ERR(reply)) { 9958c2ecf20Sopenharmony_ci error = PTR_ERR(reply); 9968c2ecf20Sopenharmony_ci goto err_kfree_acts; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci ovs_lock(); 10008c2ecf20Sopenharmony_ci dp = get_dp(net, ovs_header->dp_ifindex); 10018c2ecf20Sopenharmony_ci if (unlikely(!dp)) { 10028c2ecf20Sopenharmony_ci error = -ENODEV; 10038c2ecf20Sopenharmony_ci goto err_unlock_ovs; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* Check if this is a duplicate flow */ 10078c2ecf20Sopenharmony_ci if (ovs_identifier_is_ufid(&new_flow->id)) 10088c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_ufid(&dp->table, &new_flow->id); 10098c2ecf20Sopenharmony_ci if (!flow) 10108c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup(&dp->table, key); 10118c2ecf20Sopenharmony_ci if (likely(!flow)) { 10128c2ecf20Sopenharmony_ci rcu_assign_pointer(new_flow->sf_acts, acts); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Put flow in bucket. */ 10158c2ecf20Sopenharmony_ci error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask); 10168c2ecf20Sopenharmony_ci if (unlikely(error)) { 10178c2ecf20Sopenharmony_ci acts = NULL; 10188c2ecf20Sopenharmony_ci goto err_unlock_ovs; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (unlikely(reply)) { 10228c2ecf20Sopenharmony_ci error = ovs_flow_cmd_fill_info(new_flow, 10238c2ecf20Sopenharmony_ci ovs_header->dp_ifindex, 10248c2ecf20Sopenharmony_ci reply, info->snd_portid, 10258c2ecf20Sopenharmony_ci info->snd_seq, 0, 10268c2ecf20Sopenharmony_ci OVS_FLOW_CMD_NEW, 10278c2ecf20Sopenharmony_ci ufid_flags); 10288c2ecf20Sopenharmony_ci BUG_ON(error < 0); 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci ovs_unlock(); 10318c2ecf20Sopenharmony_ci } else { 10328c2ecf20Sopenharmony_ci struct sw_flow_actions *old_acts; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* Bail out if we're not allowed to modify an existing flow. 10358c2ecf20Sopenharmony_ci * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL 10368c2ecf20Sopenharmony_ci * because Generic Netlink treats the latter as a dump 10378c2ecf20Sopenharmony_ci * request. We also accept NLM_F_EXCL in case that bug ever 10388c2ecf20Sopenharmony_ci * gets fixed. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ci if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE 10418c2ecf20Sopenharmony_ci | NLM_F_EXCL))) { 10428c2ecf20Sopenharmony_ci error = -EEXIST; 10438c2ecf20Sopenharmony_ci goto err_unlock_ovs; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci /* The flow identifier has to be the same for flow updates. 10468c2ecf20Sopenharmony_ci * Look for any overlapping flow. 10478c2ecf20Sopenharmony_ci */ 10488c2ecf20Sopenharmony_ci if (unlikely(!ovs_flow_cmp(flow, &match))) { 10498c2ecf20Sopenharmony_ci if (ovs_identifier_is_key(&flow->id)) 10508c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_exact(&dp->table, 10518c2ecf20Sopenharmony_ci &match); 10528c2ecf20Sopenharmony_ci else /* UFID matches but key is different */ 10538c2ecf20Sopenharmony_ci flow = NULL; 10548c2ecf20Sopenharmony_ci if (!flow) { 10558c2ecf20Sopenharmony_ci error = -ENOENT; 10568c2ecf20Sopenharmony_ci goto err_unlock_ovs; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci /* Update actions. */ 10608c2ecf20Sopenharmony_ci old_acts = ovsl_dereference(flow->sf_acts); 10618c2ecf20Sopenharmony_ci rcu_assign_pointer(flow->sf_acts, acts); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (unlikely(reply)) { 10648c2ecf20Sopenharmony_ci error = ovs_flow_cmd_fill_info(flow, 10658c2ecf20Sopenharmony_ci ovs_header->dp_ifindex, 10668c2ecf20Sopenharmony_ci reply, info->snd_portid, 10678c2ecf20Sopenharmony_ci info->snd_seq, 0, 10688c2ecf20Sopenharmony_ci OVS_FLOW_CMD_NEW, 10698c2ecf20Sopenharmony_ci ufid_flags); 10708c2ecf20Sopenharmony_ci BUG_ON(error < 0); 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci ovs_unlock(); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci ovs_nla_free_flow_actions_rcu(old_acts); 10758c2ecf20Sopenharmony_ci ovs_flow_free(new_flow, false); 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (reply) 10798c2ecf20Sopenharmony_ci ovs_notify(&dp_flow_genl_family, reply, info); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci kfree(key); 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cierr_unlock_ovs: 10858c2ecf20Sopenharmony_ci ovs_unlock(); 10868c2ecf20Sopenharmony_ci kfree_skb(reply); 10878c2ecf20Sopenharmony_cierr_kfree_acts: 10888c2ecf20Sopenharmony_ci ovs_nla_free_flow_actions(acts); 10898c2ecf20Sopenharmony_cierr_kfree_key: 10908c2ecf20Sopenharmony_ci kfree(key); 10918c2ecf20Sopenharmony_cierr_kfree_flow: 10928c2ecf20Sopenharmony_ci ovs_flow_free(new_flow, false); 10938c2ecf20Sopenharmony_cierror: 10948c2ecf20Sopenharmony_ci return error; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/* Factor out action copy to avoid "Wframe-larger-than=1024" warning. */ 10988c2ecf20Sopenharmony_cistatic noinline_for_stack 10998c2ecf20Sopenharmony_cistruct sw_flow_actions *get_flow_actions(struct net *net, 11008c2ecf20Sopenharmony_ci const struct nlattr *a, 11018c2ecf20Sopenharmony_ci const struct sw_flow_key *key, 11028c2ecf20Sopenharmony_ci const struct sw_flow_mask *mask, 11038c2ecf20Sopenharmony_ci bool log) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct sw_flow_actions *acts; 11068c2ecf20Sopenharmony_ci struct sw_flow_key masked_key; 11078c2ecf20Sopenharmony_ci int error; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci ovs_flow_mask_key(&masked_key, key, true, mask); 11108c2ecf20Sopenharmony_ci error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log); 11118c2ecf20Sopenharmony_ci if (error) { 11128c2ecf20Sopenharmony_ci OVS_NLERR(log, 11138c2ecf20Sopenharmony_ci "Actions may not be safe on all matching packets"); 11148c2ecf20Sopenharmony_ci return ERR_PTR(error); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci return acts; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci/* Factor out match-init and action-copy to avoid 11218c2ecf20Sopenharmony_ci * "Wframe-larger-than=1024" warning. Because mask is only 11228c2ecf20Sopenharmony_ci * used to get actions, we new a function to save some 11238c2ecf20Sopenharmony_ci * stack space. 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * If there are not key and action attrs, we return 0 11268c2ecf20Sopenharmony_ci * directly. In the case, the caller will also not use the 11278c2ecf20Sopenharmony_ci * match as before. If there is action attr, we try to get 11288c2ecf20Sopenharmony_ci * actions and save them to *acts. Before returning from 11298c2ecf20Sopenharmony_ci * the function, we reset the match->mask pointer. Because 11308c2ecf20Sopenharmony_ci * we should not to return match object with dangling reference 11318c2ecf20Sopenharmony_ci * to mask. 11328c2ecf20Sopenharmony_ci * */ 11338c2ecf20Sopenharmony_cistatic noinline_for_stack int 11348c2ecf20Sopenharmony_ciovs_nla_init_match_and_action(struct net *net, 11358c2ecf20Sopenharmony_ci struct sw_flow_match *match, 11368c2ecf20Sopenharmony_ci struct sw_flow_key *key, 11378c2ecf20Sopenharmony_ci struct nlattr **a, 11388c2ecf20Sopenharmony_ci struct sw_flow_actions **acts, 11398c2ecf20Sopenharmony_ci bool log) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct sw_flow_mask mask; 11428c2ecf20Sopenharmony_ci int error = 0; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (a[OVS_FLOW_ATTR_KEY]) { 11458c2ecf20Sopenharmony_ci ovs_match_init(match, key, true, &mask); 11468c2ecf20Sopenharmony_ci error = ovs_nla_get_match(net, match, a[OVS_FLOW_ATTR_KEY], 11478c2ecf20Sopenharmony_ci a[OVS_FLOW_ATTR_MASK], log); 11488c2ecf20Sopenharmony_ci if (error) 11498c2ecf20Sopenharmony_ci goto error; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (a[OVS_FLOW_ATTR_ACTIONS]) { 11538c2ecf20Sopenharmony_ci if (!a[OVS_FLOW_ATTR_KEY]) { 11548c2ecf20Sopenharmony_ci OVS_NLERR(log, 11558c2ecf20Sopenharmony_ci "Flow key attribute not present in set flow."); 11568c2ecf20Sopenharmony_ci error = -EINVAL; 11578c2ecf20Sopenharmony_ci goto error; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci *acts = get_flow_actions(net, a[OVS_FLOW_ATTR_ACTIONS], key, 11618c2ecf20Sopenharmony_ci &mask, log); 11628c2ecf20Sopenharmony_ci if (IS_ERR(*acts)) { 11638c2ecf20Sopenharmony_ci error = PTR_ERR(*acts); 11648c2ecf20Sopenharmony_ci goto error; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* On success, error is 0. */ 11698c2ecf20Sopenharmony_cierror: 11708c2ecf20Sopenharmony_ci match->mask = NULL; 11718c2ecf20Sopenharmony_ci return error; 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 11778c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 11788c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 11798c2ecf20Sopenharmony_ci struct sw_flow_key key; 11808c2ecf20Sopenharmony_ci struct sw_flow *flow; 11818c2ecf20Sopenharmony_ci struct sk_buff *reply = NULL; 11828c2ecf20Sopenharmony_ci struct datapath *dp; 11838c2ecf20Sopenharmony_ci struct sw_flow_actions *old_acts = NULL, *acts = NULL; 11848c2ecf20Sopenharmony_ci struct sw_flow_match match; 11858c2ecf20Sopenharmony_ci struct sw_flow_id sfid; 11868c2ecf20Sopenharmony_ci u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); 11878c2ecf20Sopenharmony_ci int error = 0; 11888c2ecf20Sopenharmony_ci bool log = !a[OVS_FLOW_ATTR_PROBE]; 11898c2ecf20Sopenharmony_ci bool ufid_present; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci ufid_present = ovs_nla_get_ufid(&sfid, a[OVS_FLOW_ATTR_UFID], log); 11928c2ecf20Sopenharmony_ci if (!a[OVS_FLOW_ATTR_KEY] && !ufid_present) { 11938c2ecf20Sopenharmony_ci OVS_NLERR(log, 11948c2ecf20Sopenharmony_ci "Flow set message rejected, Key attribute missing."); 11958c2ecf20Sopenharmony_ci return -EINVAL; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci error = ovs_nla_init_match_and_action(net, &match, &key, a, 11998c2ecf20Sopenharmony_ci &acts, log); 12008c2ecf20Sopenharmony_ci if (error) 12018c2ecf20Sopenharmony_ci goto error; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (acts) { 12048c2ecf20Sopenharmony_ci /* Can allocate before locking if have acts. */ 12058c2ecf20Sopenharmony_ci reply = ovs_flow_cmd_alloc_info(acts, &sfid, info, false, 12068c2ecf20Sopenharmony_ci ufid_flags); 12078c2ecf20Sopenharmony_ci if (IS_ERR(reply)) { 12088c2ecf20Sopenharmony_ci error = PTR_ERR(reply); 12098c2ecf20Sopenharmony_ci goto err_kfree_acts; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci ovs_lock(); 12148c2ecf20Sopenharmony_ci dp = get_dp(net, ovs_header->dp_ifindex); 12158c2ecf20Sopenharmony_ci if (unlikely(!dp)) { 12168c2ecf20Sopenharmony_ci error = -ENODEV; 12178c2ecf20Sopenharmony_ci goto err_unlock_ovs; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci /* Check that the flow exists. */ 12208c2ecf20Sopenharmony_ci if (ufid_present) 12218c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_ufid(&dp->table, &sfid); 12228c2ecf20Sopenharmony_ci else 12238c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); 12248c2ecf20Sopenharmony_ci if (unlikely(!flow)) { 12258c2ecf20Sopenharmony_ci error = -ENOENT; 12268c2ecf20Sopenharmony_ci goto err_unlock_ovs; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* Update actions, if present. */ 12308c2ecf20Sopenharmony_ci if (likely(acts)) { 12318c2ecf20Sopenharmony_ci old_acts = ovsl_dereference(flow->sf_acts); 12328c2ecf20Sopenharmony_ci rcu_assign_pointer(flow->sf_acts, acts); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (unlikely(reply)) { 12358c2ecf20Sopenharmony_ci error = ovs_flow_cmd_fill_info(flow, 12368c2ecf20Sopenharmony_ci ovs_header->dp_ifindex, 12378c2ecf20Sopenharmony_ci reply, info->snd_portid, 12388c2ecf20Sopenharmony_ci info->snd_seq, 0, 12398c2ecf20Sopenharmony_ci OVS_FLOW_CMD_SET, 12408c2ecf20Sopenharmony_ci ufid_flags); 12418c2ecf20Sopenharmony_ci BUG_ON(error < 0); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci } else { 12448c2ecf20Sopenharmony_ci /* Could not alloc without acts before locking. */ 12458c2ecf20Sopenharmony_ci reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, 12468c2ecf20Sopenharmony_ci info, OVS_FLOW_CMD_SET, false, 12478c2ecf20Sopenharmony_ci ufid_flags); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (IS_ERR(reply)) { 12508c2ecf20Sopenharmony_ci error = PTR_ERR(reply); 12518c2ecf20Sopenharmony_ci goto err_unlock_ovs; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci /* Clear stats. */ 12568c2ecf20Sopenharmony_ci if (a[OVS_FLOW_ATTR_CLEAR]) 12578c2ecf20Sopenharmony_ci ovs_flow_stats_clear(flow); 12588c2ecf20Sopenharmony_ci ovs_unlock(); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci if (reply) 12618c2ecf20Sopenharmony_ci ovs_notify(&dp_flow_genl_family, reply, info); 12628c2ecf20Sopenharmony_ci if (old_acts) 12638c2ecf20Sopenharmony_ci ovs_nla_free_flow_actions_rcu(old_acts); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cierr_unlock_ovs: 12688c2ecf20Sopenharmony_ci ovs_unlock(); 12698c2ecf20Sopenharmony_ci kfree_skb(reply); 12708c2ecf20Sopenharmony_cierr_kfree_acts: 12718c2ecf20Sopenharmony_ci ovs_nla_free_flow_actions(acts); 12728c2ecf20Sopenharmony_cierror: 12738c2ecf20Sopenharmony_ci return error; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 12798c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 12808c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 12818c2ecf20Sopenharmony_ci struct sw_flow_key key; 12828c2ecf20Sopenharmony_ci struct sk_buff *reply; 12838c2ecf20Sopenharmony_ci struct sw_flow *flow; 12848c2ecf20Sopenharmony_ci struct datapath *dp; 12858c2ecf20Sopenharmony_ci struct sw_flow_match match; 12868c2ecf20Sopenharmony_ci struct sw_flow_id ufid; 12878c2ecf20Sopenharmony_ci u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); 12888c2ecf20Sopenharmony_ci int err = 0; 12898c2ecf20Sopenharmony_ci bool log = !a[OVS_FLOW_ATTR_PROBE]; 12908c2ecf20Sopenharmony_ci bool ufid_present; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log); 12938c2ecf20Sopenharmony_ci if (a[OVS_FLOW_ATTR_KEY]) { 12948c2ecf20Sopenharmony_ci ovs_match_init(&match, &key, true, NULL); 12958c2ecf20Sopenharmony_ci err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], NULL, 12968c2ecf20Sopenharmony_ci log); 12978c2ecf20Sopenharmony_ci } else if (!ufid_present) { 12988c2ecf20Sopenharmony_ci OVS_NLERR(log, 12998c2ecf20Sopenharmony_ci "Flow get message rejected, Key attribute missing."); 13008c2ecf20Sopenharmony_ci err = -EINVAL; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci if (err) 13038c2ecf20Sopenharmony_ci return err; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci ovs_lock(); 13068c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 13078c2ecf20Sopenharmony_ci if (!dp) { 13088c2ecf20Sopenharmony_ci err = -ENODEV; 13098c2ecf20Sopenharmony_ci goto unlock; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (ufid_present) 13138c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_ufid(&dp->table, &ufid); 13148c2ecf20Sopenharmony_ci else 13158c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); 13168c2ecf20Sopenharmony_ci if (!flow) { 13178c2ecf20Sopenharmony_ci err = -ENOENT; 13188c2ecf20Sopenharmony_ci goto unlock; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info, 13228c2ecf20Sopenharmony_ci OVS_FLOW_CMD_GET, true, ufid_flags); 13238c2ecf20Sopenharmony_ci if (IS_ERR(reply)) { 13248c2ecf20Sopenharmony_ci err = PTR_ERR(reply); 13258c2ecf20Sopenharmony_ci goto unlock; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci ovs_unlock(); 13298c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 13308c2ecf20Sopenharmony_ciunlock: 13318c2ecf20Sopenharmony_ci ovs_unlock(); 13328c2ecf20Sopenharmony_ci return err; 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 13388c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 13398c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 13408c2ecf20Sopenharmony_ci struct sw_flow_key key; 13418c2ecf20Sopenharmony_ci struct sk_buff *reply; 13428c2ecf20Sopenharmony_ci struct sw_flow *flow = NULL; 13438c2ecf20Sopenharmony_ci struct datapath *dp; 13448c2ecf20Sopenharmony_ci struct sw_flow_match match; 13458c2ecf20Sopenharmony_ci struct sw_flow_id ufid; 13468c2ecf20Sopenharmony_ci u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); 13478c2ecf20Sopenharmony_ci int err; 13488c2ecf20Sopenharmony_ci bool log = !a[OVS_FLOW_ATTR_PROBE]; 13498c2ecf20Sopenharmony_ci bool ufid_present; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log); 13528c2ecf20Sopenharmony_ci if (a[OVS_FLOW_ATTR_KEY]) { 13538c2ecf20Sopenharmony_ci ovs_match_init(&match, &key, true, NULL); 13548c2ecf20Sopenharmony_ci err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], 13558c2ecf20Sopenharmony_ci NULL, log); 13568c2ecf20Sopenharmony_ci if (unlikely(err)) 13578c2ecf20Sopenharmony_ci return err; 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci ovs_lock(); 13618c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 13628c2ecf20Sopenharmony_ci if (unlikely(!dp)) { 13638c2ecf20Sopenharmony_ci err = -ENODEV; 13648c2ecf20Sopenharmony_ci goto unlock; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci if (unlikely(!a[OVS_FLOW_ATTR_KEY] && !ufid_present)) { 13688c2ecf20Sopenharmony_ci err = ovs_flow_tbl_flush(&dp->table); 13698c2ecf20Sopenharmony_ci goto unlock; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci if (ufid_present) 13738c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_ufid(&dp->table, &ufid); 13748c2ecf20Sopenharmony_ci else 13758c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); 13768c2ecf20Sopenharmony_ci if (unlikely(!flow)) { 13778c2ecf20Sopenharmony_ci err = -ENOENT; 13788c2ecf20Sopenharmony_ci goto unlock; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci ovs_flow_tbl_remove(&dp->table, flow); 13828c2ecf20Sopenharmony_ci ovs_unlock(); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts, 13858c2ecf20Sopenharmony_ci &flow->id, info, false, ufid_flags); 13868c2ecf20Sopenharmony_ci if (likely(reply)) { 13878c2ecf20Sopenharmony_ci if (!IS_ERR(reply)) { 13888c2ecf20Sopenharmony_ci rcu_read_lock(); /*To keep RCU checker happy. */ 13898c2ecf20Sopenharmony_ci err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, 13908c2ecf20Sopenharmony_ci reply, info->snd_portid, 13918c2ecf20Sopenharmony_ci info->snd_seq, 0, 13928c2ecf20Sopenharmony_ci OVS_FLOW_CMD_DEL, 13938c2ecf20Sopenharmony_ci ufid_flags); 13948c2ecf20Sopenharmony_ci rcu_read_unlock(); 13958c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(err < 0)) { 13968c2ecf20Sopenharmony_ci kfree_skb(reply); 13978c2ecf20Sopenharmony_ci goto out_free; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci ovs_notify(&dp_flow_genl_family, reply, info); 14018c2ecf20Sopenharmony_ci } else { 14028c2ecf20Sopenharmony_ci netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, 14038c2ecf20Sopenharmony_ci PTR_ERR(reply)); 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ciout_free: 14088c2ecf20Sopenharmony_ci ovs_flow_free(flow, true); 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ciunlock: 14118c2ecf20Sopenharmony_ci ovs_unlock(); 14128c2ecf20Sopenharmony_ci return err; 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_cistatic int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci struct nlattr *a[__OVS_FLOW_ATTR_MAX]; 14188c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); 14198c2ecf20Sopenharmony_ci struct table_instance *ti; 14208c2ecf20Sopenharmony_ci struct datapath *dp; 14218c2ecf20Sopenharmony_ci u32 ufid_flags; 14228c2ecf20Sopenharmony_ci int err; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci err = genlmsg_parse_deprecated(cb->nlh, &dp_flow_genl_family, a, 14258c2ecf20Sopenharmony_ci OVS_FLOW_ATTR_MAX, flow_policy, NULL); 14268c2ecf20Sopenharmony_ci if (err) 14278c2ecf20Sopenharmony_ci return err; 14288c2ecf20Sopenharmony_ci ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci rcu_read_lock(); 14318c2ecf20Sopenharmony_ci dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); 14328c2ecf20Sopenharmony_ci if (!dp) { 14338c2ecf20Sopenharmony_ci rcu_read_unlock(); 14348c2ecf20Sopenharmony_ci return -ENODEV; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci ti = rcu_dereference(dp->table.ti); 14388c2ecf20Sopenharmony_ci for (;;) { 14398c2ecf20Sopenharmony_ci struct sw_flow *flow; 14408c2ecf20Sopenharmony_ci u32 bucket, obj; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci bucket = cb->args[0]; 14438c2ecf20Sopenharmony_ci obj = cb->args[1]; 14448c2ecf20Sopenharmony_ci flow = ovs_flow_tbl_dump_next(ti, &bucket, &obj); 14458c2ecf20Sopenharmony_ci if (!flow) 14468c2ecf20Sopenharmony_ci break; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb, 14498c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 14508c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 14518c2ecf20Sopenharmony_ci OVS_FLOW_CMD_GET, ufid_flags) < 0) 14528c2ecf20Sopenharmony_ci break; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci cb->args[0] = bucket; 14558c2ecf20Sopenharmony_ci cb->args[1] = obj; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci rcu_read_unlock(); 14588c2ecf20Sopenharmony_ci return skb->len; 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cistatic const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = { 14628c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED }, 14638c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_MASK] = { .type = NLA_NESTED }, 14648c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED }, 14658c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG }, 14668c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_PROBE] = { .type = NLA_FLAG }, 14678c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_UFID] = { .type = NLA_UNSPEC, .len = 1 }, 14688c2ecf20Sopenharmony_ci [OVS_FLOW_ATTR_UFID_FLAGS] = { .type = NLA_U32 }, 14698c2ecf20Sopenharmony_ci}; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_cistatic const struct genl_small_ops dp_flow_genl_ops[] = { 14728c2ecf20Sopenharmony_ci { .cmd = OVS_FLOW_CMD_NEW, 14738c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 14748c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 14758c2ecf20Sopenharmony_ci .doit = ovs_flow_cmd_new 14768c2ecf20Sopenharmony_ci }, 14778c2ecf20Sopenharmony_ci { .cmd = OVS_FLOW_CMD_DEL, 14788c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 14798c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 14808c2ecf20Sopenharmony_ci .doit = ovs_flow_cmd_del 14818c2ecf20Sopenharmony_ci }, 14828c2ecf20Sopenharmony_ci { .cmd = OVS_FLOW_CMD_GET, 14838c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 14848c2ecf20Sopenharmony_ci .flags = 0, /* OK for unprivileged users. */ 14858c2ecf20Sopenharmony_ci .doit = ovs_flow_cmd_get, 14868c2ecf20Sopenharmony_ci .dumpit = ovs_flow_cmd_dump 14878c2ecf20Sopenharmony_ci }, 14888c2ecf20Sopenharmony_ci { .cmd = OVS_FLOW_CMD_SET, 14898c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 14908c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 14918c2ecf20Sopenharmony_ci .doit = ovs_flow_cmd_set, 14928c2ecf20Sopenharmony_ci }, 14938c2ecf20Sopenharmony_ci}; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic struct genl_family dp_flow_genl_family __ro_after_init = { 14968c2ecf20Sopenharmony_ci .hdrsize = sizeof(struct ovs_header), 14978c2ecf20Sopenharmony_ci .name = OVS_FLOW_FAMILY, 14988c2ecf20Sopenharmony_ci .version = OVS_FLOW_VERSION, 14998c2ecf20Sopenharmony_ci .maxattr = OVS_FLOW_ATTR_MAX, 15008c2ecf20Sopenharmony_ci .policy = flow_policy, 15018c2ecf20Sopenharmony_ci .netnsok = true, 15028c2ecf20Sopenharmony_ci .parallel_ops = true, 15038c2ecf20Sopenharmony_ci .small_ops = dp_flow_genl_ops, 15048c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(dp_flow_genl_ops), 15058c2ecf20Sopenharmony_ci .mcgrps = &ovs_dp_flow_multicast_group, 15068c2ecf20Sopenharmony_ci .n_mcgrps = 1, 15078c2ecf20Sopenharmony_ci .module = THIS_MODULE, 15088c2ecf20Sopenharmony_ci}; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic size_t ovs_dp_cmd_msg_size(void) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci size_t msgsize = NLMSG_ALIGN(sizeof(struct ovs_header)); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci msgsize += nla_total_size(IFNAMSIZ); 15158c2ecf20Sopenharmony_ci msgsize += nla_total_size_64bit(sizeof(struct ovs_dp_stats)); 15168c2ecf20Sopenharmony_ci msgsize += nla_total_size_64bit(sizeof(struct ovs_dp_megaflow_stats)); 15178c2ecf20Sopenharmony_ci msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */ 15188c2ecf20Sopenharmony_ci msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_MASKS_CACHE_SIZE */ 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return msgsize; 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci/* Called with ovs_mutex. */ 15248c2ecf20Sopenharmony_cistatic int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, 15258c2ecf20Sopenharmony_ci u32 portid, u32 seq, u32 flags, u8 cmd) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci struct ovs_header *ovs_header; 15288c2ecf20Sopenharmony_ci struct ovs_dp_stats dp_stats; 15298c2ecf20Sopenharmony_ci struct ovs_dp_megaflow_stats dp_megaflow_stats; 15308c2ecf20Sopenharmony_ci int err; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci ovs_header = genlmsg_put(skb, portid, seq, &dp_datapath_genl_family, 15338c2ecf20Sopenharmony_ci flags, cmd); 15348c2ecf20Sopenharmony_ci if (!ovs_header) 15358c2ecf20Sopenharmony_ci goto error; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci ovs_header->dp_ifindex = get_dpifindex(dp); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci err = nla_put_string(skb, OVS_DP_ATTR_NAME, ovs_dp_name(dp)); 15408c2ecf20Sopenharmony_ci if (err) 15418c2ecf20Sopenharmony_ci goto nla_put_failure; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci get_dp_stats(dp, &dp_stats, &dp_megaflow_stats); 15448c2ecf20Sopenharmony_ci if (nla_put_64bit(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats), 15458c2ecf20Sopenharmony_ci &dp_stats, OVS_DP_ATTR_PAD)) 15468c2ecf20Sopenharmony_ci goto nla_put_failure; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (nla_put_64bit(skb, OVS_DP_ATTR_MEGAFLOW_STATS, 15498c2ecf20Sopenharmony_ci sizeof(struct ovs_dp_megaflow_stats), 15508c2ecf20Sopenharmony_ci &dp_megaflow_stats, OVS_DP_ATTR_PAD)) 15518c2ecf20Sopenharmony_ci goto nla_put_failure; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (nla_put_u32(skb, OVS_DP_ATTR_USER_FEATURES, dp->user_features)) 15548c2ecf20Sopenharmony_ci goto nla_put_failure; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (nla_put_u32(skb, OVS_DP_ATTR_MASKS_CACHE_SIZE, 15578c2ecf20Sopenharmony_ci ovs_flow_tbl_masks_cache_size(&dp->table))) 15588c2ecf20Sopenharmony_ci goto nla_put_failure; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci genlmsg_end(skb, ovs_header); 15618c2ecf20Sopenharmony_ci return 0; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_cinla_put_failure: 15648c2ecf20Sopenharmony_ci genlmsg_cancel(skb, ovs_header); 15658c2ecf20Sopenharmony_cierror: 15668c2ecf20Sopenharmony_ci return -EMSGSIZE; 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_cistatic struct sk_buff *ovs_dp_cmd_alloc_info(void) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci return genlmsg_new(ovs_dp_cmd_msg_size(), GFP_KERNEL); 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci/* Called with rcu_read_lock or ovs_mutex. */ 15758c2ecf20Sopenharmony_cistatic struct datapath *lookup_datapath(struct net *net, 15768c2ecf20Sopenharmony_ci const struct ovs_header *ovs_header, 15778c2ecf20Sopenharmony_ci struct nlattr *a[OVS_DP_ATTR_MAX + 1]) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci struct datapath *dp; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (!a[OVS_DP_ATTR_NAME]) 15828c2ecf20Sopenharmony_ci dp = get_dp(net, ovs_header->dp_ifindex); 15838c2ecf20Sopenharmony_ci else { 15848c2ecf20Sopenharmony_ci struct vport *vport; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci vport = ovs_vport_locate(net, nla_data(a[OVS_DP_ATTR_NAME])); 15878c2ecf20Sopenharmony_ci dp = vport && vport->port_no == OVSP_LOCAL ? vport->dp : NULL; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci return dp ? dp : ERR_PTR(-ENODEV); 15908c2ecf20Sopenharmony_ci} 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_cistatic void ovs_dp_reset_user_features(struct sk_buff *skb, 15938c2ecf20Sopenharmony_ci struct genl_info *info) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci struct datapath *dp; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci dp = lookup_datapath(sock_net(skb->sk), info->userhdr, 15988c2ecf20Sopenharmony_ci info->attrs); 15998c2ecf20Sopenharmony_ci if (IS_ERR(dp)) 16008c2ecf20Sopenharmony_ci return; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci pr_warn("%s: Dropping previously announced user features\n", 16038c2ecf20Sopenharmony_ci ovs_dp_name(dp)); 16048c2ecf20Sopenharmony_ci dp->user_features = 0; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(tc_recirc_sharing_support); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_cistatic int ovs_dp_change(struct datapath *dp, struct nlattr *a[]) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci u32 user_features = 0; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (a[OVS_DP_ATTR_USER_FEATURES]) { 16148c2ecf20Sopenharmony_ci user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if (user_features & ~(OVS_DP_F_VPORT_PIDS | 16178c2ecf20Sopenharmony_ci OVS_DP_F_UNALIGNED | 16188c2ecf20Sopenharmony_ci OVS_DP_F_TC_RECIRC_SHARING)) 16198c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci#if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 16228c2ecf20Sopenharmony_ci if (user_features & OVS_DP_F_TC_RECIRC_SHARING) 16238c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16248c2ecf20Sopenharmony_ci#endif 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci if (a[OVS_DP_ATTR_MASKS_CACHE_SIZE]) { 16288c2ecf20Sopenharmony_ci int err; 16298c2ecf20Sopenharmony_ci u32 cache_size; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci cache_size = nla_get_u32(a[OVS_DP_ATTR_MASKS_CACHE_SIZE]); 16328c2ecf20Sopenharmony_ci err = ovs_flow_tbl_masks_cache_resize(&dp->table, cache_size); 16338c2ecf20Sopenharmony_ci if (err) 16348c2ecf20Sopenharmony_ci return err; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci dp->user_features = user_features; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci if (dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) 16408c2ecf20Sopenharmony_ci static_branch_enable(&tc_recirc_sharing_support); 16418c2ecf20Sopenharmony_ci else 16428c2ecf20Sopenharmony_ci static_branch_disable(&tc_recirc_sharing_support); 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci return 0; 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cistatic int ovs_dp_stats_init(struct datapath *dp) 16488c2ecf20Sopenharmony_ci{ 16498c2ecf20Sopenharmony_ci dp->stats_percpu = netdev_alloc_pcpu_stats(struct dp_stats_percpu); 16508c2ecf20Sopenharmony_ci if (!dp->stats_percpu) 16518c2ecf20Sopenharmony_ci return -ENOMEM; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci return 0; 16548c2ecf20Sopenharmony_ci} 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_cistatic int ovs_dp_vport_init(struct datapath *dp) 16578c2ecf20Sopenharmony_ci{ 16588c2ecf20Sopenharmony_ci int i; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci dp->ports = kmalloc_array(DP_VPORT_HASH_BUCKETS, 16618c2ecf20Sopenharmony_ci sizeof(struct hlist_head), 16628c2ecf20Sopenharmony_ci GFP_KERNEL); 16638c2ecf20Sopenharmony_ci if (!dp->ports) 16648c2ecf20Sopenharmony_ci return -ENOMEM; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) 16678c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&dp->ports[i]); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci return 0; 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) 16738c2ecf20Sopenharmony_ci{ 16748c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 16758c2ecf20Sopenharmony_ci struct vport_parms parms; 16768c2ecf20Sopenharmony_ci struct sk_buff *reply; 16778c2ecf20Sopenharmony_ci struct datapath *dp; 16788c2ecf20Sopenharmony_ci struct vport *vport; 16798c2ecf20Sopenharmony_ci struct ovs_net *ovs_net; 16808c2ecf20Sopenharmony_ci int err; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci err = -EINVAL; 16838c2ecf20Sopenharmony_ci if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID]) 16848c2ecf20Sopenharmony_ci goto err; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci reply = ovs_dp_cmd_alloc_info(); 16878c2ecf20Sopenharmony_ci if (!reply) 16888c2ecf20Sopenharmony_ci return -ENOMEM; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci err = -ENOMEM; 16918c2ecf20Sopenharmony_ci dp = kzalloc(sizeof(*dp), GFP_KERNEL); 16928c2ecf20Sopenharmony_ci if (dp == NULL) 16938c2ecf20Sopenharmony_ci goto err_destroy_reply; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci ovs_dp_set_net(dp, sock_net(skb->sk)); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci /* Allocate table. */ 16988c2ecf20Sopenharmony_ci err = ovs_flow_tbl_init(&dp->table); 16998c2ecf20Sopenharmony_ci if (err) 17008c2ecf20Sopenharmony_ci goto err_destroy_dp; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci err = ovs_dp_stats_init(dp); 17038c2ecf20Sopenharmony_ci if (err) 17048c2ecf20Sopenharmony_ci goto err_destroy_table; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci err = ovs_dp_vport_init(dp); 17078c2ecf20Sopenharmony_ci if (err) 17088c2ecf20Sopenharmony_ci goto err_destroy_stats; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci err = ovs_meters_init(dp); 17118c2ecf20Sopenharmony_ci if (err) 17128c2ecf20Sopenharmony_ci goto err_destroy_ports; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* Set up our datapath device. */ 17158c2ecf20Sopenharmony_ci parms.name = nla_data(a[OVS_DP_ATTR_NAME]); 17168c2ecf20Sopenharmony_ci parms.type = OVS_VPORT_TYPE_INTERNAL; 17178c2ecf20Sopenharmony_ci parms.options = NULL; 17188c2ecf20Sopenharmony_ci parms.dp = dp; 17198c2ecf20Sopenharmony_ci parms.port_no = OVSP_LOCAL; 17208c2ecf20Sopenharmony_ci parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID]; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci /* So far only local changes have been made, now need the lock. */ 17238c2ecf20Sopenharmony_ci ovs_lock(); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci err = ovs_dp_change(dp, a); 17268c2ecf20Sopenharmony_ci if (err) 17278c2ecf20Sopenharmony_ci goto err_unlock_and_destroy_meters; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci vport = new_vport(&parms); 17308c2ecf20Sopenharmony_ci if (IS_ERR(vport)) { 17318c2ecf20Sopenharmony_ci err = PTR_ERR(vport); 17328c2ecf20Sopenharmony_ci if (err == -EBUSY) 17338c2ecf20Sopenharmony_ci err = -EEXIST; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (err == -EEXIST) { 17368c2ecf20Sopenharmony_ci /* An outdated user space instance that does not understand 17378c2ecf20Sopenharmony_ci * the concept of user_features has attempted to create a new 17388c2ecf20Sopenharmony_ci * datapath and is likely to reuse it. Drop all user features. 17398c2ecf20Sopenharmony_ci */ 17408c2ecf20Sopenharmony_ci if (info->genlhdr->version < OVS_DP_VER_FEATURES) 17418c2ecf20Sopenharmony_ci ovs_dp_reset_user_features(skb, info); 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci goto err_unlock_and_destroy_meters; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, 17488c2ecf20Sopenharmony_ci info->snd_seq, 0, OVS_DP_CMD_NEW); 17498c2ecf20Sopenharmony_ci BUG_ON(err < 0); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id); 17528c2ecf20Sopenharmony_ci list_add_tail_rcu(&dp->list_node, &ovs_net->dps); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci ovs_unlock(); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci ovs_notify(&dp_datapath_genl_family, reply, info); 17578c2ecf20Sopenharmony_ci return 0; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_cierr_unlock_and_destroy_meters: 17608c2ecf20Sopenharmony_ci ovs_unlock(); 17618c2ecf20Sopenharmony_ci ovs_meters_exit(dp); 17628c2ecf20Sopenharmony_cierr_destroy_ports: 17638c2ecf20Sopenharmony_ci kfree(dp->ports); 17648c2ecf20Sopenharmony_cierr_destroy_stats: 17658c2ecf20Sopenharmony_ci free_percpu(dp->stats_percpu); 17668c2ecf20Sopenharmony_cierr_destroy_table: 17678c2ecf20Sopenharmony_ci ovs_flow_tbl_destroy(&dp->table); 17688c2ecf20Sopenharmony_cierr_destroy_dp: 17698c2ecf20Sopenharmony_ci kfree(dp); 17708c2ecf20Sopenharmony_cierr_destroy_reply: 17718c2ecf20Sopenharmony_ci kfree_skb(reply); 17728c2ecf20Sopenharmony_cierr: 17738c2ecf20Sopenharmony_ci return err; 17748c2ecf20Sopenharmony_ci} 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci/* Called with ovs_mutex. */ 17778c2ecf20Sopenharmony_cistatic void __dp_destroy(struct datapath *dp) 17788c2ecf20Sopenharmony_ci{ 17798c2ecf20Sopenharmony_ci struct flow_table *table = &dp->table; 17808c2ecf20Sopenharmony_ci int i; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { 17838c2ecf20Sopenharmony_ci struct vport *vport; 17848c2ecf20Sopenharmony_ci struct hlist_node *n; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) 17878c2ecf20Sopenharmony_ci if (vport->port_no != OVSP_LOCAL) 17888c2ecf20Sopenharmony_ci ovs_dp_detach_port(vport); 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci list_del_rcu(&dp->list_node); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* OVSP_LOCAL is datapath internal port. We need to make sure that 17948c2ecf20Sopenharmony_ci * all ports in datapath are destroyed first before freeing datapath. 17958c2ecf20Sopenharmony_ci */ 17968c2ecf20Sopenharmony_ci ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci /* Flush sw_flow in the tables. RCU cb only releases resource 17998c2ecf20Sopenharmony_ci * such as dp, ports and tables. That may avoid some issues 18008c2ecf20Sopenharmony_ci * such as RCU usage warning. 18018c2ecf20Sopenharmony_ci */ 18028c2ecf20Sopenharmony_ci table_instance_flow_flush(table, ovsl_dereference(table->ti), 18038c2ecf20Sopenharmony_ci ovsl_dereference(table->ufid_ti)); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* RCU destroy the ports, meters and flow tables. */ 18068c2ecf20Sopenharmony_ci call_rcu(&dp->rcu, destroy_dp_rcu); 18078c2ecf20Sopenharmony_ci} 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_cistatic int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci struct sk_buff *reply; 18128c2ecf20Sopenharmony_ci struct datapath *dp; 18138c2ecf20Sopenharmony_ci int err; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci reply = ovs_dp_cmd_alloc_info(); 18168c2ecf20Sopenharmony_ci if (!reply) 18178c2ecf20Sopenharmony_ci return -ENOMEM; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci ovs_lock(); 18208c2ecf20Sopenharmony_ci dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); 18218c2ecf20Sopenharmony_ci err = PTR_ERR(dp); 18228c2ecf20Sopenharmony_ci if (IS_ERR(dp)) 18238c2ecf20Sopenharmony_ci goto err_unlock_free; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, 18268c2ecf20Sopenharmony_ci info->snd_seq, 0, OVS_DP_CMD_DEL); 18278c2ecf20Sopenharmony_ci BUG_ON(err < 0); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci __dp_destroy(dp); 18308c2ecf20Sopenharmony_ci ovs_unlock(); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci ovs_notify(&dp_datapath_genl_family, reply, info); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci return 0; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_cierr_unlock_free: 18378c2ecf20Sopenharmony_ci ovs_unlock(); 18388c2ecf20Sopenharmony_ci kfree_skb(reply); 18398c2ecf20Sopenharmony_ci return err; 18408c2ecf20Sopenharmony_ci} 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_cistatic int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci struct sk_buff *reply; 18458c2ecf20Sopenharmony_ci struct datapath *dp; 18468c2ecf20Sopenharmony_ci int err; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci reply = ovs_dp_cmd_alloc_info(); 18498c2ecf20Sopenharmony_ci if (!reply) 18508c2ecf20Sopenharmony_ci return -ENOMEM; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci ovs_lock(); 18538c2ecf20Sopenharmony_ci dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); 18548c2ecf20Sopenharmony_ci err = PTR_ERR(dp); 18558c2ecf20Sopenharmony_ci if (IS_ERR(dp)) 18568c2ecf20Sopenharmony_ci goto err_unlock_free; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci err = ovs_dp_change(dp, info->attrs); 18598c2ecf20Sopenharmony_ci if (err) 18608c2ecf20Sopenharmony_ci goto err_unlock_free; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, 18638c2ecf20Sopenharmony_ci info->snd_seq, 0, OVS_DP_CMD_SET); 18648c2ecf20Sopenharmony_ci BUG_ON(err < 0); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci ovs_unlock(); 18678c2ecf20Sopenharmony_ci ovs_notify(&dp_datapath_genl_family, reply, info); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci return 0; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_cierr_unlock_free: 18728c2ecf20Sopenharmony_ci ovs_unlock(); 18738c2ecf20Sopenharmony_ci kfree_skb(reply); 18748c2ecf20Sopenharmony_ci return err; 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci struct sk_buff *reply; 18808c2ecf20Sopenharmony_ci struct datapath *dp; 18818c2ecf20Sopenharmony_ci int err; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci reply = ovs_dp_cmd_alloc_info(); 18848c2ecf20Sopenharmony_ci if (!reply) 18858c2ecf20Sopenharmony_ci return -ENOMEM; 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci ovs_lock(); 18888c2ecf20Sopenharmony_ci dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); 18898c2ecf20Sopenharmony_ci if (IS_ERR(dp)) { 18908c2ecf20Sopenharmony_ci err = PTR_ERR(dp); 18918c2ecf20Sopenharmony_ci goto err_unlock_free; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, 18948c2ecf20Sopenharmony_ci info->snd_seq, 0, OVS_DP_CMD_GET); 18958c2ecf20Sopenharmony_ci BUG_ON(err < 0); 18968c2ecf20Sopenharmony_ci ovs_unlock(); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_cierr_unlock_free: 19018c2ecf20Sopenharmony_ci ovs_unlock(); 19028c2ecf20Sopenharmony_ci kfree_skb(reply); 19038c2ecf20Sopenharmony_ci return err; 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id); 19098c2ecf20Sopenharmony_ci struct datapath *dp; 19108c2ecf20Sopenharmony_ci int skip = cb->args[0]; 19118c2ecf20Sopenharmony_ci int i = 0; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci ovs_lock(); 19148c2ecf20Sopenharmony_ci list_for_each_entry(dp, &ovs_net->dps, list_node) { 19158c2ecf20Sopenharmony_ci if (i >= skip && 19168c2ecf20Sopenharmony_ci ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid, 19178c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 19188c2ecf20Sopenharmony_ci OVS_DP_CMD_GET) < 0) 19198c2ecf20Sopenharmony_ci break; 19208c2ecf20Sopenharmony_ci i++; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci ovs_unlock(); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci cb->args[0] = i; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci return skb->len; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cistatic const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { 19308c2ecf20Sopenharmony_ci [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, 19318c2ecf20Sopenharmony_ci [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 }, 19328c2ecf20Sopenharmony_ci [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 }, 19338c2ecf20Sopenharmony_ci [OVS_DP_ATTR_MASKS_CACHE_SIZE] = NLA_POLICY_RANGE(NLA_U32, 0, 19348c2ecf20Sopenharmony_ci PCPU_MIN_UNIT_SIZE / sizeof(struct mask_cache_entry)), 19358c2ecf20Sopenharmony_ci}; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cistatic const struct genl_small_ops dp_datapath_genl_ops[] = { 19388c2ecf20Sopenharmony_ci { .cmd = OVS_DP_CMD_NEW, 19398c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 19408c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 19418c2ecf20Sopenharmony_ci .doit = ovs_dp_cmd_new 19428c2ecf20Sopenharmony_ci }, 19438c2ecf20Sopenharmony_ci { .cmd = OVS_DP_CMD_DEL, 19448c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 19458c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 19468c2ecf20Sopenharmony_ci .doit = ovs_dp_cmd_del 19478c2ecf20Sopenharmony_ci }, 19488c2ecf20Sopenharmony_ci { .cmd = OVS_DP_CMD_GET, 19498c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 19508c2ecf20Sopenharmony_ci .flags = 0, /* OK for unprivileged users. */ 19518c2ecf20Sopenharmony_ci .doit = ovs_dp_cmd_get, 19528c2ecf20Sopenharmony_ci .dumpit = ovs_dp_cmd_dump 19538c2ecf20Sopenharmony_ci }, 19548c2ecf20Sopenharmony_ci { .cmd = OVS_DP_CMD_SET, 19558c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 19568c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 19578c2ecf20Sopenharmony_ci .doit = ovs_dp_cmd_set, 19588c2ecf20Sopenharmony_ci }, 19598c2ecf20Sopenharmony_ci}; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_cistatic struct genl_family dp_datapath_genl_family __ro_after_init = { 19628c2ecf20Sopenharmony_ci .hdrsize = sizeof(struct ovs_header), 19638c2ecf20Sopenharmony_ci .name = OVS_DATAPATH_FAMILY, 19648c2ecf20Sopenharmony_ci .version = OVS_DATAPATH_VERSION, 19658c2ecf20Sopenharmony_ci .maxattr = OVS_DP_ATTR_MAX, 19668c2ecf20Sopenharmony_ci .policy = datapath_policy, 19678c2ecf20Sopenharmony_ci .netnsok = true, 19688c2ecf20Sopenharmony_ci .parallel_ops = true, 19698c2ecf20Sopenharmony_ci .small_ops = dp_datapath_genl_ops, 19708c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(dp_datapath_genl_ops), 19718c2ecf20Sopenharmony_ci .mcgrps = &ovs_dp_datapath_multicast_group, 19728c2ecf20Sopenharmony_ci .n_mcgrps = 1, 19738c2ecf20Sopenharmony_ci .module = THIS_MODULE, 19748c2ecf20Sopenharmony_ci}; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci/* Called with ovs_mutex or RCU read lock. */ 19778c2ecf20Sopenharmony_cistatic int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, 19788c2ecf20Sopenharmony_ci struct net *net, u32 portid, u32 seq, 19798c2ecf20Sopenharmony_ci u32 flags, u8 cmd, gfp_t gfp) 19808c2ecf20Sopenharmony_ci{ 19818c2ecf20Sopenharmony_ci struct ovs_header *ovs_header; 19828c2ecf20Sopenharmony_ci struct ovs_vport_stats vport_stats; 19838c2ecf20Sopenharmony_ci int err; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci ovs_header = genlmsg_put(skb, portid, seq, &dp_vport_genl_family, 19868c2ecf20Sopenharmony_ci flags, cmd); 19878c2ecf20Sopenharmony_ci if (!ovs_header) 19888c2ecf20Sopenharmony_ci return -EMSGSIZE; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci ovs_header->dp_ifindex = get_dpifindex(vport->dp); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || 19938c2ecf20Sopenharmony_ci nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || 19948c2ecf20Sopenharmony_ci nla_put_string(skb, OVS_VPORT_ATTR_NAME, 19958c2ecf20Sopenharmony_ci ovs_vport_name(vport)) || 19968c2ecf20Sopenharmony_ci nla_put_u32(skb, OVS_VPORT_ATTR_IFINDEX, vport->dev->ifindex)) 19978c2ecf20Sopenharmony_ci goto nla_put_failure; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci if (!net_eq(net, dev_net(vport->dev))) { 20008c2ecf20Sopenharmony_ci int id = peernet2id_alloc(net, dev_net(vport->dev), gfp); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id)) 20038c2ecf20Sopenharmony_ci goto nla_put_failure; 20048c2ecf20Sopenharmony_ci } 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci ovs_vport_get_stats(vport, &vport_stats); 20078c2ecf20Sopenharmony_ci if (nla_put_64bit(skb, OVS_VPORT_ATTR_STATS, 20088c2ecf20Sopenharmony_ci sizeof(struct ovs_vport_stats), &vport_stats, 20098c2ecf20Sopenharmony_ci OVS_VPORT_ATTR_PAD)) 20108c2ecf20Sopenharmony_ci goto nla_put_failure; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci if (ovs_vport_get_upcall_portids(vport, skb)) 20138c2ecf20Sopenharmony_ci goto nla_put_failure; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci err = ovs_vport_get_options(vport, skb); 20168c2ecf20Sopenharmony_ci if (err == -EMSGSIZE) 20178c2ecf20Sopenharmony_ci goto error; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci genlmsg_end(skb, ovs_header); 20208c2ecf20Sopenharmony_ci return 0; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_cinla_put_failure: 20238c2ecf20Sopenharmony_ci err = -EMSGSIZE; 20248c2ecf20Sopenharmony_cierror: 20258c2ecf20Sopenharmony_ci genlmsg_cancel(skb, ovs_header); 20268c2ecf20Sopenharmony_ci return err; 20278c2ecf20Sopenharmony_ci} 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_cistatic struct sk_buff *ovs_vport_cmd_alloc_info(void) 20308c2ecf20Sopenharmony_ci{ 20318c2ecf20Sopenharmony_ci return nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci/* Called with ovs_mutex, only via ovs_dp_notify_wq(). */ 20358c2ecf20Sopenharmony_cistruct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net, 20368c2ecf20Sopenharmony_ci u32 portid, u32 seq, u8 cmd) 20378c2ecf20Sopenharmony_ci{ 20388c2ecf20Sopenharmony_ci struct sk_buff *skb; 20398c2ecf20Sopenharmony_ci int retval; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 20428c2ecf20Sopenharmony_ci if (!skb) 20438c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd, 20468c2ecf20Sopenharmony_ci GFP_KERNEL); 20478c2ecf20Sopenharmony_ci BUG_ON(retval < 0); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci return skb; 20508c2ecf20Sopenharmony_ci} 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci/* Called with ovs_mutex or RCU read lock. */ 20538c2ecf20Sopenharmony_cistatic struct vport *lookup_vport(struct net *net, 20548c2ecf20Sopenharmony_ci const struct ovs_header *ovs_header, 20558c2ecf20Sopenharmony_ci struct nlattr *a[OVS_VPORT_ATTR_MAX + 1]) 20568c2ecf20Sopenharmony_ci{ 20578c2ecf20Sopenharmony_ci struct datapath *dp; 20588c2ecf20Sopenharmony_ci struct vport *vport; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (a[OVS_VPORT_ATTR_IFINDEX]) 20618c2ecf20Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 20628c2ecf20Sopenharmony_ci if (a[OVS_VPORT_ATTR_NAME]) { 20638c2ecf20Sopenharmony_ci vport = ovs_vport_locate(net, nla_data(a[OVS_VPORT_ATTR_NAME])); 20648c2ecf20Sopenharmony_ci if (!vport) 20658c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 20668c2ecf20Sopenharmony_ci if (ovs_header->dp_ifindex && 20678c2ecf20Sopenharmony_ci ovs_header->dp_ifindex != get_dpifindex(vport->dp)) 20688c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 20698c2ecf20Sopenharmony_ci return vport; 20708c2ecf20Sopenharmony_ci } else if (a[OVS_VPORT_ATTR_PORT_NO]) { 20718c2ecf20Sopenharmony_ci u32 port_no = nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci if (port_no >= DP_MAX_PORTS) 20748c2ecf20Sopenharmony_ci return ERR_PTR(-EFBIG); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci dp = get_dp(net, ovs_header->dp_ifindex); 20778c2ecf20Sopenharmony_ci if (!dp) 20788c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci vport = ovs_vport_ovsl_rcu(dp, port_no); 20818c2ecf20Sopenharmony_ci if (!vport) 20828c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 20838c2ecf20Sopenharmony_ci return vport; 20848c2ecf20Sopenharmony_ci } else 20858c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci} 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_cistatic unsigned int ovs_get_max_headroom(struct datapath *dp) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci unsigned int dev_headroom, max_headroom = 0; 20928c2ecf20Sopenharmony_ci struct net_device *dev; 20938c2ecf20Sopenharmony_ci struct vport *vport; 20948c2ecf20Sopenharmony_ci int i; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { 20978c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node, 20988c2ecf20Sopenharmony_ci lockdep_ovsl_is_held()) { 20998c2ecf20Sopenharmony_ci dev = vport->dev; 21008c2ecf20Sopenharmony_ci dev_headroom = netdev_get_fwd_headroom(dev); 21018c2ecf20Sopenharmony_ci if (dev_headroom > max_headroom) 21028c2ecf20Sopenharmony_ci max_headroom = dev_headroom; 21038c2ecf20Sopenharmony_ci } 21048c2ecf20Sopenharmony_ci } 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci return max_headroom; 21078c2ecf20Sopenharmony_ci} 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci/* Called with ovs_mutex */ 21108c2ecf20Sopenharmony_cistatic void ovs_update_headroom(struct datapath *dp, unsigned int new_headroom) 21118c2ecf20Sopenharmony_ci{ 21128c2ecf20Sopenharmony_ci struct vport *vport; 21138c2ecf20Sopenharmony_ci int i; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci dp->max_headroom = new_headroom; 21168c2ecf20Sopenharmony_ci for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { 21178c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node, 21188c2ecf20Sopenharmony_ci lockdep_ovsl_is_held()) 21198c2ecf20Sopenharmony_ci netdev_set_rx_headroom(vport->dev, new_headroom); 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci} 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_cistatic int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 21268c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 21278c2ecf20Sopenharmony_ci struct vport_parms parms; 21288c2ecf20Sopenharmony_ci struct sk_buff *reply; 21298c2ecf20Sopenharmony_ci struct vport *vport; 21308c2ecf20Sopenharmony_ci struct datapath *dp; 21318c2ecf20Sopenharmony_ci unsigned int new_headroom; 21328c2ecf20Sopenharmony_ci u32 port_no; 21338c2ecf20Sopenharmony_ci int err; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] || 21368c2ecf20Sopenharmony_ci !a[OVS_VPORT_ATTR_UPCALL_PID]) 21378c2ecf20Sopenharmony_ci return -EINVAL; 21388c2ecf20Sopenharmony_ci if (a[OVS_VPORT_ATTR_IFINDEX]) 21398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci port_no = a[OVS_VPORT_ATTR_PORT_NO] 21428c2ecf20Sopenharmony_ci ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0; 21438c2ecf20Sopenharmony_ci if (port_no >= DP_MAX_PORTS) 21448c2ecf20Sopenharmony_ci return -EFBIG; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci reply = ovs_vport_cmd_alloc_info(); 21478c2ecf20Sopenharmony_ci if (!reply) 21488c2ecf20Sopenharmony_ci return -ENOMEM; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci ovs_lock(); 21518c2ecf20Sopenharmony_cirestart: 21528c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 21538c2ecf20Sopenharmony_ci err = -ENODEV; 21548c2ecf20Sopenharmony_ci if (!dp) 21558c2ecf20Sopenharmony_ci goto exit_unlock_free; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (port_no) { 21588c2ecf20Sopenharmony_ci vport = ovs_vport_ovsl(dp, port_no); 21598c2ecf20Sopenharmony_ci err = -EBUSY; 21608c2ecf20Sopenharmony_ci if (vport) 21618c2ecf20Sopenharmony_ci goto exit_unlock_free; 21628c2ecf20Sopenharmony_ci } else { 21638c2ecf20Sopenharmony_ci for (port_no = 1; ; port_no++) { 21648c2ecf20Sopenharmony_ci if (port_no >= DP_MAX_PORTS) { 21658c2ecf20Sopenharmony_ci err = -EFBIG; 21668c2ecf20Sopenharmony_ci goto exit_unlock_free; 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci vport = ovs_vport_ovsl(dp, port_no); 21698c2ecf20Sopenharmony_ci if (!vport) 21708c2ecf20Sopenharmony_ci break; 21718c2ecf20Sopenharmony_ci } 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci parms.name = nla_data(a[OVS_VPORT_ATTR_NAME]); 21758c2ecf20Sopenharmony_ci parms.type = nla_get_u32(a[OVS_VPORT_ATTR_TYPE]); 21768c2ecf20Sopenharmony_ci parms.options = a[OVS_VPORT_ATTR_OPTIONS]; 21778c2ecf20Sopenharmony_ci parms.dp = dp; 21788c2ecf20Sopenharmony_ci parms.port_no = port_no; 21798c2ecf20Sopenharmony_ci parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci vport = new_vport(&parms); 21828c2ecf20Sopenharmony_ci err = PTR_ERR(vport); 21838c2ecf20Sopenharmony_ci if (IS_ERR(vport)) { 21848c2ecf20Sopenharmony_ci if (err == -EAGAIN) 21858c2ecf20Sopenharmony_ci goto restart; 21868c2ecf20Sopenharmony_ci goto exit_unlock_free; 21878c2ecf20Sopenharmony_ci } 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), 21908c2ecf20Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 21918c2ecf20Sopenharmony_ci OVS_VPORT_CMD_NEW, GFP_KERNEL); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci new_headroom = netdev_get_fwd_headroom(vport->dev); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci if (new_headroom > dp->max_headroom) 21968c2ecf20Sopenharmony_ci ovs_update_headroom(dp, new_headroom); 21978c2ecf20Sopenharmony_ci else 21988c2ecf20Sopenharmony_ci netdev_set_rx_headroom(vport->dev, dp->max_headroom); 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci BUG_ON(err < 0); 22018c2ecf20Sopenharmony_ci ovs_unlock(); 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci ovs_notify(&dp_vport_genl_family, reply, info); 22048c2ecf20Sopenharmony_ci return 0; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ciexit_unlock_free: 22078c2ecf20Sopenharmony_ci ovs_unlock(); 22088c2ecf20Sopenharmony_ci kfree_skb(reply); 22098c2ecf20Sopenharmony_ci return err; 22108c2ecf20Sopenharmony_ci} 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_cistatic int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) 22138c2ecf20Sopenharmony_ci{ 22148c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 22158c2ecf20Sopenharmony_ci struct sk_buff *reply; 22168c2ecf20Sopenharmony_ci struct vport *vport; 22178c2ecf20Sopenharmony_ci int err; 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci reply = ovs_vport_cmd_alloc_info(); 22208c2ecf20Sopenharmony_ci if (!reply) 22218c2ecf20Sopenharmony_ci return -ENOMEM; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci ovs_lock(); 22248c2ecf20Sopenharmony_ci vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); 22258c2ecf20Sopenharmony_ci err = PTR_ERR(vport); 22268c2ecf20Sopenharmony_ci if (IS_ERR(vport)) 22278c2ecf20Sopenharmony_ci goto exit_unlock_free; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci if (a[OVS_VPORT_ATTR_TYPE] && 22308c2ecf20Sopenharmony_ci nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type) { 22318c2ecf20Sopenharmony_ci err = -EINVAL; 22328c2ecf20Sopenharmony_ci goto exit_unlock_free; 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci if (a[OVS_VPORT_ATTR_OPTIONS]) { 22368c2ecf20Sopenharmony_ci err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]); 22378c2ecf20Sopenharmony_ci if (err) 22388c2ecf20Sopenharmony_ci goto exit_unlock_free; 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci if (a[OVS_VPORT_ATTR_UPCALL_PID]) { 22438c2ecf20Sopenharmony_ci struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID]; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci err = ovs_vport_set_upcall_portids(vport, ids); 22468c2ecf20Sopenharmony_ci if (err) 22478c2ecf20Sopenharmony_ci goto exit_unlock_free; 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), 22518c2ecf20Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 22528c2ecf20Sopenharmony_ci OVS_VPORT_CMD_SET, GFP_KERNEL); 22538c2ecf20Sopenharmony_ci BUG_ON(err < 0); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci ovs_unlock(); 22568c2ecf20Sopenharmony_ci ovs_notify(&dp_vport_genl_family, reply, info); 22578c2ecf20Sopenharmony_ci return 0; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ciexit_unlock_free: 22608c2ecf20Sopenharmony_ci ovs_unlock(); 22618c2ecf20Sopenharmony_ci kfree_skb(reply); 22628c2ecf20Sopenharmony_ci return err; 22638c2ecf20Sopenharmony_ci} 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_cistatic int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) 22668c2ecf20Sopenharmony_ci{ 22678c2ecf20Sopenharmony_ci bool update_headroom = false; 22688c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 22698c2ecf20Sopenharmony_ci struct sk_buff *reply; 22708c2ecf20Sopenharmony_ci struct datapath *dp; 22718c2ecf20Sopenharmony_ci struct vport *vport; 22728c2ecf20Sopenharmony_ci unsigned int new_headroom; 22738c2ecf20Sopenharmony_ci int err; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci reply = ovs_vport_cmd_alloc_info(); 22768c2ecf20Sopenharmony_ci if (!reply) 22778c2ecf20Sopenharmony_ci return -ENOMEM; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci ovs_lock(); 22808c2ecf20Sopenharmony_ci vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); 22818c2ecf20Sopenharmony_ci err = PTR_ERR(vport); 22828c2ecf20Sopenharmony_ci if (IS_ERR(vport)) 22838c2ecf20Sopenharmony_ci goto exit_unlock_free; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci if (vport->port_no == OVSP_LOCAL) { 22868c2ecf20Sopenharmony_ci err = -EINVAL; 22878c2ecf20Sopenharmony_ci goto exit_unlock_free; 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), 22918c2ecf20Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 22928c2ecf20Sopenharmony_ci OVS_VPORT_CMD_DEL, GFP_KERNEL); 22938c2ecf20Sopenharmony_ci BUG_ON(err < 0); 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci /* the vport deletion may trigger dp headroom update */ 22968c2ecf20Sopenharmony_ci dp = vport->dp; 22978c2ecf20Sopenharmony_ci if (netdev_get_fwd_headroom(vport->dev) == dp->max_headroom) 22988c2ecf20Sopenharmony_ci update_headroom = true; 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci netdev_reset_rx_headroom(vport->dev); 23018c2ecf20Sopenharmony_ci ovs_dp_detach_port(vport); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci if (update_headroom) { 23048c2ecf20Sopenharmony_ci new_headroom = ovs_get_max_headroom(dp); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci if (new_headroom < dp->max_headroom) 23078c2ecf20Sopenharmony_ci ovs_update_headroom(dp, new_headroom); 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci ovs_unlock(); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci ovs_notify(&dp_vport_genl_family, reply, info); 23128c2ecf20Sopenharmony_ci return 0; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ciexit_unlock_free: 23158c2ecf20Sopenharmony_ci ovs_unlock(); 23168c2ecf20Sopenharmony_ci kfree_skb(reply); 23178c2ecf20Sopenharmony_ci return err; 23188c2ecf20Sopenharmony_ci} 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_cistatic int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info) 23218c2ecf20Sopenharmony_ci{ 23228c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 23238c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 23248c2ecf20Sopenharmony_ci struct sk_buff *reply; 23258c2ecf20Sopenharmony_ci struct vport *vport; 23268c2ecf20Sopenharmony_ci int err; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci reply = ovs_vport_cmd_alloc_info(); 23298c2ecf20Sopenharmony_ci if (!reply) 23308c2ecf20Sopenharmony_ci return -ENOMEM; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci rcu_read_lock(); 23338c2ecf20Sopenharmony_ci vport = lookup_vport(sock_net(skb->sk), ovs_header, a); 23348c2ecf20Sopenharmony_ci err = PTR_ERR(vport); 23358c2ecf20Sopenharmony_ci if (IS_ERR(vport)) 23368c2ecf20Sopenharmony_ci goto exit_unlock_free; 23378c2ecf20Sopenharmony_ci err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), 23388c2ecf20Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 23398c2ecf20Sopenharmony_ci OVS_VPORT_CMD_GET, GFP_ATOMIC); 23408c2ecf20Sopenharmony_ci BUG_ON(err < 0); 23418c2ecf20Sopenharmony_ci rcu_read_unlock(); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ciexit_unlock_free: 23468c2ecf20Sopenharmony_ci rcu_read_unlock(); 23478c2ecf20Sopenharmony_ci kfree_skb(reply); 23488c2ecf20Sopenharmony_ci return err; 23498c2ecf20Sopenharmony_ci} 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_cistatic int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) 23528c2ecf20Sopenharmony_ci{ 23538c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); 23548c2ecf20Sopenharmony_ci struct datapath *dp; 23558c2ecf20Sopenharmony_ci int bucket = cb->args[0], skip = cb->args[1]; 23568c2ecf20Sopenharmony_ci int i, j = 0; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci rcu_read_lock(); 23598c2ecf20Sopenharmony_ci dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); 23608c2ecf20Sopenharmony_ci if (!dp) { 23618c2ecf20Sopenharmony_ci rcu_read_unlock(); 23628c2ecf20Sopenharmony_ci return -ENODEV; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci for (i = bucket; i < DP_VPORT_HASH_BUCKETS; i++) { 23658c2ecf20Sopenharmony_ci struct vport *vport; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci j = 0; 23688c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node) { 23698c2ecf20Sopenharmony_ci if (j >= skip && 23708c2ecf20Sopenharmony_ci ovs_vport_cmd_fill_info(vport, skb, 23718c2ecf20Sopenharmony_ci sock_net(skb->sk), 23728c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 23738c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 23748c2ecf20Sopenharmony_ci NLM_F_MULTI, 23758c2ecf20Sopenharmony_ci OVS_VPORT_CMD_GET, 23768c2ecf20Sopenharmony_ci GFP_ATOMIC) < 0) 23778c2ecf20Sopenharmony_ci goto out; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci j++; 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci skip = 0; 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ciout: 23848c2ecf20Sopenharmony_ci rcu_read_unlock(); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci cb->args[0] = i; 23878c2ecf20Sopenharmony_ci cb->args[1] = j; 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci return skb->len; 23908c2ecf20Sopenharmony_ci} 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_cistatic void ovs_dp_masks_rebalance(struct work_struct *work) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci struct ovs_net *ovs_net = container_of(work, struct ovs_net, 23958c2ecf20Sopenharmony_ci masks_rebalance.work); 23968c2ecf20Sopenharmony_ci struct datapath *dp; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci ovs_lock(); 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci list_for_each_entry(dp, &ovs_net->dps, list_node) 24018c2ecf20Sopenharmony_ci ovs_flow_masks_rebalance(&dp->table); 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci ovs_unlock(); 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci schedule_delayed_work(&ovs_net->masks_rebalance, 24068c2ecf20Sopenharmony_ci msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL)); 24078c2ecf20Sopenharmony_ci} 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_cistatic const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = { 24108c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, 24118c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) }, 24128c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 }, 24138c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 }, 24148c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_UNSPEC }, 24158c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, 24168c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_IFINDEX] = { .type = NLA_U32 }, 24178c2ecf20Sopenharmony_ci [OVS_VPORT_ATTR_NETNSID] = { .type = NLA_S32 }, 24188c2ecf20Sopenharmony_ci}; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_cistatic const struct genl_small_ops dp_vport_genl_ops[] = { 24218c2ecf20Sopenharmony_ci { .cmd = OVS_VPORT_CMD_NEW, 24228c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 24238c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 24248c2ecf20Sopenharmony_ci .doit = ovs_vport_cmd_new 24258c2ecf20Sopenharmony_ci }, 24268c2ecf20Sopenharmony_ci { .cmd = OVS_VPORT_CMD_DEL, 24278c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 24288c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 24298c2ecf20Sopenharmony_ci .doit = ovs_vport_cmd_del 24308c2ecf20Sopenharmony_ci }, 24318c2ecf20Sopenharmony_ci { .cmd = OVS_VPORT_CMD_GET, 24328c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 24338c2ecf20Sopenharmony_ci .flags = 0, /* OK for unprivileged users. */ 24348c2ecf20Sopenharmony_ci .doit = ovs_vport_cmd_get, 24358c2ecf20Sopenharmony_ci .dumpit = ovs_vport_cmd_dump 24368c2ecf20Sopenharmony_ci }, 24378c2ecf20Sopenharmony_ci { .cmd = OVS_VPORT_CMD_SET, 24388c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 24398c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ 24408c2ecf20Sopenharmony_ci .doit = ovs_vport_cmd_set, 24418c2ecf20Sopenharmony_ci }, 24428c2ecf20Sopenharmony_ci}; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_cistruct genl_family dp_vport_genl_family __ro_after_init = { 24458c2ecf20Sopenharmony_ci .hdrsize = sizeof(struct ovs_header), 24468c2ecf20Sopenharmony_ci .name = OVS_VPORT_FAMILY, 24478c2ecf20Sopenharmony_ci .version = OVS_VPORT_VERSION, 24488c2ecf20Sopenharmony_ci .maxattr = OVS_VPORT_ATTR_MAX, 24498c2ecf20Sopenharmony_ci .policy = vport_policy, 24508c2ecf20Sopenharmony_ci .netnsok = true, 24518c2ecf20Sopenharmony_ci .parallel_ops = true, 24528c2ecf20Sopenharmony_ci .small_ops = dp_vport_genl_ops, 24538c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(dp_vport_genl_ops), 24548c2ecf20Sopenharmony_ci .mcgrps = &ovs_dp_vport_multicast_group, 24558c2ecf20Sopenharmony_ci .n_mcgrps = 1, 24568c2ecf20Sopenharmony_ci .module = THIS_MODULE, 24578c2ecf20Sopenharmony_ci}; 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_cistatic struct genl_family * const dp_genl_families[] = { 24608c2ecf20Sopenharmony_ci &dp_datapath_genl_family, 24618c2ecf20Sopenharmony_ci &dp_vport_genl_family, 24628c2ecf20Sopenharmony_ci &dp_flow_genl_family, 24638c2ecf20Sopenharmony_ci &dp_packet_genl_family, 24648c2ecf20Sopenharmony_ci &dp_meter_genl_family, 24658c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 24668c2ecf20Sopenharmony_ci &dp_ct_limit_genl_family, 24678c2ecf20Sopenharmony_ci#endif 24688c2ecf20Sopenharmony_ci}; 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_cistatic void dp_unregister_genl(int n_families) 24718c2ecf20Sopenharmony_ci{ 24728c2ecf20Sopenharmony_ci int i; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci for (i = 0; i < n_families; i++) 24758c2ecf20Sopenharmony_ci genl_unregister_family(dp_genl_families[i]); 24768c2ecf20Sopenharmony_ci} 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_cistatic int __init dp_register_genl(void) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci int err; 24818c2ecf20Sopenharmony_ci int i; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) { 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci err = genl_register_family(dp_genl_families[i]); 24868c2ecf20Sopenharmony_ci if (err) 24878c2ecf20Sopenharmony_ci goto error; 24888c2ecf20Sopenharmony_ci } 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci return 0; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_cierror: 24938c2ecf20Sopenharmony_ci dp_unregister_genl(i); 24948c2ecf20Sopenharmony_ci return err; 24958c2ecf20Sopenharmony_ci} 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_cistatic int __net_init ovs_init_net(struct net *net) 24988c2ecf20Sopenharmony_ci{ 24998c2ecf20Sopenharmony_ci struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 25008c2ecf20Sopenharmony_ci int err; 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ovs_net->dps); 25038c2ecf20Sopenharmony_ci INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq); 25048c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ovs_net->masks_rebalance, ovs_dp_masks_rebalance); 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci err = ovs_ct_init(net); 25078c2ecf20Sopenharmony_ci if (err) 25088c2ecf20Sopenharmony_ci return err; 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci schedule_delayed_work(&ovs_net->masks_rebalance, 25118c2ecf20Sopenharmony_ci msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL)); 25128c2ecf20Sopenharmony_ci return 0; 25138c2ecf20Sopenharmony_ci} 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_cistatic void __net_exit list_vports_from_net(struct net *net, struct net *dnet, 25168c2ecf20Sopenharmony_ci struct list_head *head) 25178c2ecf20Sopenharmony_ci{ 25188c2ecf20Sopenharmony_ci struct ovs_net *ovs_net = net_generic(net, ovs_net_id); 25198c2ecf20Sopenharmony_ci struct datapath *dp; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci list_for_each_entry(dp, &ovs_net->dps, list_node) { 25228c2ecf20Sopenharmony_ci int i; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { 25258c2ecf20Sopenharmony_ci struct vport *vport; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci hlist_for_each_entry(vport, &dp->ports[i], dp_hash_node) { 25288c2ecf20Sopenharmony_ci if (vport->ops->type != OVS_VPORT_TYPE_INTERNAL) 25298c2ecf20Sopenharmony_ci continue; 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci if (dev_net(vport->dev) == dnet) 25328c2ecf20Sopenharmony_ci list_add(&vport->detach_list, head); 25338c2ecf20Sopenharmony_ci } 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci} 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_cistatic void __net_exit ovs_exit_net(struct net *dnet) 25398c2ecf20Sopenharmony_ci{ 25408c2ecf20Sopenharmony_ci struct datapath *dp, *dp_next; 25418c2ecf20Sopenharmony_ci struct ovs_net *ovs_net = net_generic(dnet, ovs_net_id); 25428c2ecf20Sopenharmony_ci struct vport *vport, *vport_next; 25438c2ecf20Sopenharmony_ci struct net *net; 25448c2ecf20Sopenharmony_ci LIST_HEAD(head); 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci ovs_lock(); 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci ovs_ct_exit(dnet); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node) 25518c2ecf20Sopenharmony_ci __dp_destroy(dp); 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci down_read(&net_rwsem); 25548c2ecf20Sopenharmony_ci for_each_net(net) 25558c2ecf20Sopenharmony_ci list_vports_from_net(net, dnet, &head); 25568c2ecf20Sopenharmony_ci up_read(&net_rwsem); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci /* Detach all vports from given namespace. */ 25598c2ecf20Sopenharmony_ci list_for_each_entry_safe(vport, vport_next, &head, detach_list) { 25608c2ecf20Sopenharmony_ci list_del(&vport->detach_list); 25618c2ecf20Sopenharmony_ci ovs_dp_detach_port(vport); 25628c2ecf20Sopenharmony_ci } 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci ovs_unlock(); 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ovs_net->masks_rebalance); 25678c2ecf20Sopenharmony_ci cancel_work_sync(&ovs_net->dp_notify_work); 25688c2ecf20Sopenharmony_ci} 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_cistatic struct pernet_operations ovs_net_ops = { 25718c2ecf20Sopenharmony_ci .init = ovs_init_net, 25728c2ecf20Sopenharmony_ci .exit = ovs_exit_net, 25738c2ecf20Sopenharmony_ci .id = &ovs_net_id, 25748c2ecf20Sopenharmony_ci .size = sizeof(struct ovs_net), 25758c2ecf20Sopenharmony_ci}; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_cistatic int __init dp_init(void) 25788c2ecf20Sopenharmony_ci{ 25798c2ecf20Sopenharmony_ci int err; 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > 25828c2ecf20Sopenharmony_ci sizeof_field(struct sk_buff, cb)); 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci pr_info("Open vSwitch switching datapath\n"); 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci err = action_fifos_init(); 25878c2ecf20Sopenharmony_ci if (err) 25888c2ecf20Sopenharmony_ci goto error; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci err = ovs_internal_dev_rtnl_link_register(); 25918c2ecf20Sopenharmony_ci if (err) 25928c2ecf20Sopenharmony_ci goto error_action_fifos_exit; 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci err = ovs_flow_init(); 25958c2ecf20Sopenharmony_ci if (err) 25968c2ecf20Sopenharmony_ci goto error_unreg_rtnl_link; 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci err = ovs_vport_init(); 25998c2ecf20Sopenharmony_ci if (err) 26008c2ecf20Sopenharmony_ci goto error_flow_exit; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci err = register_pernet_device(&ovs_net_ops); 26038c2ecf20Sopenharmony_ci if (err) 26048c2ecf20Sopenharmony_ci goto error_vport_exit; 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&ovs_dp_device_notifier); 26078c2ecf20Sopenharmony_ci if (err) 26088c2ecf20Sopenharmony_ci goto error_netns_exit; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci err = ovs_netdev_init(); 26118c2ecf20Sopenharmony_ci if (err) 26128c2ecf20Sopenharmony_ci goto error_unreg_notifier; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci err = dp_register_genl(); 26158c2ecf20Sopenharmony_ci if (err < 0) 26168c2ecf20Sopenharmony_ci goto error_unreg_netdev; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci return 0; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_cierror_unreg_netdev: 26218c2ecf20Sopenharmony_ci ovs_netdev_exit(); 26228c2ecf20Sopenharmony_cierror_unreg_notifier: 26238c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ovs_dp_device_notifier); 26248c2ecf20Sopenharmony_cierror_netns_exit: 26258c2ecf20Sopenharmony_ci unregister_pernet_device(&ovs_net_ops); 26268c2ecf20Sopenharmony_cierror_vport_exit: 26278c2ecf20Sopenharmony_ci ovs_vport_exit(); 26288c2ecf20Sopenharmony_cierror_flow_exit: 26298c2ecf20Sopenharmony_ci ovs_flow_exit(); 26308c2ecf20Sopenharmony_cierror_unreg_rtnl_link: 26318c2ecf20Sopenharmony_ci ovs_internal_dev_rtnl_link_unregister(); 26328c2ecf20Sopenharmony_cierror_action_fifos_exit: 26338c2ecf20Sopenharmony_ci action_fifos_exit(); 26348c2ecf20Sopenharmony_cierror: 26358c2ecf20Sopenharmony_ci return err; 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_cistatic void dp_cleanup(void) 26398c2ecf20Sopenharmony_ci{ 26408c2ecf20Sopenharmony_ci dp_unregister_genl(ARRAY_SIZE(dp_genl_families)); 26418c2ecf20Sopenharmony_ci ovs_netdev_exit(); 26428c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ovs_dp_device_notifier); 26438c2ecf20Sopenharmony_ci unregister_pernet_device(&ovs_net_ops); 26448c2ecf20Sopenharmony_ci rcu_barrier(); 26458c2ecf20Sopenharmony_ci ovs_vport_exit(); 26468c2ecf20Sopenharmony_ci ovs_flow_exit(); 26478c2ecf20Sopenharmony_ci ovs_internal_dev_rtnl_link_unregister(); 26488c2ecf20Sopenharmony_ci action_fifos_exit(); 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cimodule_init(dp_init); 26528c2ecf20Sopenharmony_cimodule_exit(dp_cleanup); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Open vSwitch switching datapath"); 26558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 26568c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(OVS_DATAPATH_FAMILY); 26578c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY); 26588c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY); 26598c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY); 26608c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(OVS_METER_FAMILY); 26618c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(OVS_CT_LIMIT_FAMILY); 2662