| /* |
| *************************************************************************** |
| * Copyright (c) 2015-2016, 2018-2021, The Linux Foundation. All rights reserved. |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all copies. |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| *************************************************************************** |
| */ |
| |
| #include <linux/version.h> |
| #include <linux/etherdevice.h> |
| #include <linux/if_ether.h> |
| #include <net/genetlink.h> |
| #include <nss_api_if.h> |
| #include <nss_nl_if.h> |
| #include "nss_nlcmn_if.h" |
| #include "nss_nl.h" |
| #include "nss_nlgre_redir_if.h" |
| #include "nss_nlgre_redir_cmn.h" |
| |
| static struct nss_nlgre_redir_cmn_tun_data tun_data[NSS_NLGRE_REDIR_CMN_MAX_TUNNELS]; |
| static const struct net_device_ops gre_redir_netdev_ops; |
| static DEFINE_SPINLOCK(lock); |
| |
| /* |
| * nss_nlgre_redir_cmn_get_tun_data() |
| * Returns the tun_data after checking for lock |
| */ |
| static struct nss_nlgre_redir_cmn_tun_data nss_nlgre_redir_cmn_get_tun_data(struct net_device *dev) |
| { |
| struct nss_nlgre_redir_cmn_tun_data dummy_tun_data = {0}; |
| int index; |
| |
| spin_lock(&lock); |
| for (index = 0; index < NSS_NLGRE_REDIR_CMN_MAX_TUNNELS; index++) { |
| if (dev != tun_data[index].dev) { |
| continue; |
| } |
| |
| spin_unlock(&lock); |
| return tun_data[index]; |
| } |
| |
| spin_unlock(&lock); |
| return dummy_tun_data; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_get_next_free_tun() |
| * Returns the next free tunnel available |
| */ |
| static int nss_nlgre_redir_cmn_get_next_free_tun(void) |
| { |
| int index; |
| |
| spin_lock(&lock); |
| for (index = 0; index < NSS_NLGRE_REDIR_CMN_MAX_TUNNELS ; index++) { |
| if (!tun_data[index].enable) { |
| spin_unlock(&lock); |
| return index; |
| } |
| } |
| |
| spin_unlock(&lock); |
| nss_nl_error("Max tunnel count exceeded: %d\n", index); |
| return -1; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_set_tun_data() |
| * Set the tun_data value to value passed |
| */ |
| static bool nss_nlgre_redir_cmn_set_tun_data(struct nss_nlgre_redir_cmn_tun_data *data, int index) |
| { |
| if (!data) { |
| nss_nl_error("data is NULL\n"); |
| return false; |
| } |
| |
| spin_lock(&lock); |
| tun_data[index] = *data; |
| spin_unlock(&lock); |
| return true; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_init_tun_data() |
| * Initializes the tun_data |
| */ |
| static void nss_nlgre_redir_cmn_init_tun_data(struct nss_nlgre_redir_cmn_tun_data *tun_data) |
| { |
| tun_data->dev = NULL; |
| tun_data->enable = false; |
| tun_data->host_inner_ifnum = -1; |
| tun_data->wifi_offl_inner_ifnum = -1; |
| tun_data->sjack_inner_ifnum = -1; |
| tun_data->outer_ifnum = -1; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_deinit_tun_data() |
| * Deinitialize private data for the given index. |
| */ |
| static bool nss_nlgre_redir_cmn_deinit_tun_data(struct nss_nlgre_redir_cmn_tun_data *tun_data, int index) |
| { |
| struct nss_ctx_instance *nss_ctx; |
| |
| nss_ctx = nss_gre_redir_get_context(); |
| tun_data->dev = NULL; |
| tun_data->enable = false; |
| tun_data->host_inner_ifnum = -1; |
| tun_data->wifi_offl_inner_ifnum = -1; |
| tun_data->sjack_inner_ifnum = -1; |
| tun_data->outer_ifnum = -1; |
| |
| if (!nss_nlgre_redir_cmn_set_tun_data(tun_data, index)) { |
| nss_nl_error("%px: Unable to set tun_data\n", nss_ctx); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_host_data_cb() |
| * Data callback for host offload inner node. |
| */ |
| static void nss_nlgre_redir_cmn_host_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi) |
| { |
| struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context(); |
| |
| if (!skb) { |
| nss_nl_trace("%px: SKB is NULL\n", nss_ctx); |
| return; |
| } |
| |
| nss_nl_trace("%px: Exception packet on host inner:\n", skb); |
| nss_nlgre_redir_cmn_print_hex_dump(skb); |
| skb->protocol = eth_type_trans(skb, netdev); |
| netif_receive_skb(skb); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_wifi_offl_data_cb() |
| * Data callback for wifi offload inner node. |
| */ |
| static void nss_nlgre_redir_cmn_wifi_offl_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi) |
| { |
| struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context(); |
| |
| if (!skb) { |
| nss_nl_warn("%px: SKB is NULL\n", nss_ctx); |
| return; |
| } |
| |
| nss_nl_trace("%px: Exception packet on wifi offld inner:\n", skb); |
| nss_nlgre_redir_cmn_print_hex_dump(skb); |
| skb->protocol = eth_type_trans(skb, netdev); |
| netif_receive_skb(skb); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_sjack_data_cb |
| * Data callback for sjack inner node. |
| */ |
| static void nss_nlgre_redir_cmn_sjack_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi) |
| { |
| nss_nl_trace("%px: Exception packet on sjack inner node:\n", skb); |
| nss_nlgre_redir_cmn_print_hex_dump(skb); |
| dev_kfree_skb(skb); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_outer_data_cb() |
| * Data callback for outer node. |
| */ |
| static void nss_nlgre_redir_cmn_outer_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi) |
| { |
| nss_nl_trace("%px: Exception packet on outer node:\n", skb); |
| nss_nlgre_redir_cmn_print_hex_dump(skb); |
| dev_kfree_skb(skb); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_map_unmap_msg_cb() |
| * HLOS->NSS message completion callback. |
| */ |
| static void nss_nlgre_redir_cmn_map_unmap_msg_cb(void *app_data, struct nss_cmn_msg *cmnmsg) |
| { |
| struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context(); |
| nss_nl_info("%px: callback gre_redir tunnel msg from NSS\n", nss_ctx); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_interface_alloc_and_register() |
| * Allocates nodes and registers callbacks |
| */ |
| static int nss_nlgre_redir_cmn_interface_alloc_and_register(struct nss_nlgre_redir_cmn_tun_data *tun_data, struct net_device *dev) |
| { |
| struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context(); |
| |
| tun_data->host_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev, |
| nss_nlgre_redir_cmn_host_data_cb, |
| NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER, dev); |
| nss_nl_info("%px: host_inner = %d\n", nss_ctx, tun_data->host_inner_ifnum); |
| if (tun_data->host_inner_ifnum == -1) { |
| nss_nl_error("%px: Unable to allocate and register wifi host inner interface\n", nss_ctx); |
| return -1; |
| } |
| |
| tun_data->wifi_offl_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev, |
| nss_nlgre_redir_cmn_wifi_offl_data_cb, |
| NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER, dev); |
| nss_nl_info("%px: wifi_inner = %d\n", nss_ctx, tun_data->wifi_offl_inner_ifnum); |
| if (tun_data->wifi_offl_inner_ifnum == -1) { |
| nss_nl_error("%px: Unable to allocate and register wifi offload inner interface\n", nss_ctx); |
| return -1; |
| } |
| |
| tun_data->sjack_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev, |
| nss_nlgre_redir_cmn_sjack_data_cb, |
| NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER, dev); |
| nss_nl_info("%px: sjack_inner = %d\n", nss_ctx, tun_data->sjack_inner_ifnum); |
| if (tun_data->sjack_inner_ifnum == -1) { |
| nss_nl_error("%px: Unable to allocate and register sjack inner interface\n", nss_ctx); |
| return -1; |
| } |
| |
| tun_data->outer_ifnum = nss_gre_redir_alloc_and_register_node(dev, |
| nss_nlgre_redir_cmn_outer_data_cb, |
| NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER, dev); |
| nss_nl_info("%px: outer = %d\n", nss_ctx, tun_data->outer_ifnum); |
| if (tun_data->outer_ifnum == -1) { |
| nss_nl_error("%px: Unable to allocate and register outer interface\n", nss_ctx); |
| return -1; |
| } |
| |
| return 0; |
| |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_open_interface() |
| * Used when the interface is opened for use. |
| */ |
| static int nss_nlgre_redir_cmn_open_interface(struct net_device *dev) |
| { |
| struct nss_gre_redir_cmn_ndev_priv *priv; |
| priv = netdev_priv(dev); |
| priv->gre_seq = 0; |
| netif_start_queue(dev); |
| netif_carrier_on(dev); |
| return 0; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_close_interace() |
| * Used when the interface is closed. |
| */ |
| static int nss_nlgre_redir_cmn_close_interface(struct net_device *dev) |
| { |
| netif_stop_queue (dev); |
| netif_carrier_off(dev); |
| return 0; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_xmit_data() |
| * Used when the interface is used for transmit data. |
| */ |
| static netdev_tx_t nss_nlgre_redir_cmn_xmit_data(struct sk_buff *skb, struct net_device *dev) |
| { |
| struct nss_gre_redir_encap_per_pkt_metadata *meta_data_encap = NULL; |
| struct nss_gre_redir_cmn_ndev_priv *priv; |
| uint32_t ifnum, ret = 0; |
| struct nss_ctx_instance *nss_ctx; |
| |
| nss_ctx = nss_gre_redir_get_context(); |
| priv = netdev_priv(dev); |
| ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER); |
| |
| /* |
| * Initializing the start of skb with offset of metadata |
| */ |
| *(skb->head) = NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET; |
| |
| /* |
| * Configuring gre_redir meta data. |
| */ |
| meta_data_encap = (struct nss_gre_redir_encap_per_pkt_metadata *)(skb->head + NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET); |
| memset(meta_data_encap, 0, sizeof(struct nss_gre_redir_encap_per_pkt_metadata)); |
| meta_data_encap->gre_flags = 0; |
| meta_data_encap->gre_prio = 0; |
| meta_data_encap->gre_seq = ++priv->gre_seq; |
| meta_data_encap->gre_tunnel_id = 10; |
| meta_data_encap->ip_dscp = 0; |
| meta_data_encap->ip_df_override = 0; |
| meta_data_encap->ipsec_pattern = 0; |
| |
| nss_ctx = nss_gre_redir_get_context(); |
| ret = nss_gre_redir_tx_buf(nss_ctx, skb, ifnum); |
| if (ret != NSS_TX_SUCCESS) { |
| nss_nl_error("%px: Transmit failed and returned with %d\n", nss_ctx, ret); |
| dev_kfree_skb_any(skb); |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_stats64_get() |
| * Used to get link statistics. |
| */ |
| static struct rtnl_link_stats64 *nss_nlgre_redir_cmn_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) |
| { |
| struct nss_gre_redir_tunnel_stats get_stats; |
| bool found = false; |
| int i; |
| |
| for (i = 0; i < NSS_GRE_REDIR_MAX_INTERFACES; i++) { |
| if (!nss_gre_redir_stats_get(i, &get_stats)) { |
| continue; |
| } |
| |
| if (get_stats.dev != dev) { |
| continue; |
| } |
| |
| found = true; |
| break; |
| } |
| |
| if (found == false) |
| return NULL; |
| |
| stats->tx_bytes = get_stats.tstats.tx_bytes; |
| stats->tx_packets = get_stats.tstats.tx_packets; |
| stats->rx_bytes = get_stats.tstats.rx_bytes; |
| stats->rx_packets = get_stats.tstats.rx_packets; |
| for (i = 0;i < ARRAY_SIZE(get_stats.tstats.rx_dropped); i++) { |
| stats->rx_dropped += get_stats.tstats.rx_dropped[i]; |
| } |
| |
| stats->tx_dropped = get_stats.tstats.tx_dropped; |
| |
| return stats; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_dev_stats64 |
| * Report packet statistics to linux |
| */ |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) |
| static struct rtnl_link_stats64 *nss_nlgre_redir_cmn_dev_stats64(struct net_device *dev, |
| struct rtnl_link_stats64 *stats) |
| { |
| return nss_nlgre_redir_cmn_get_stats64(dev, stats); |
| } |
| #else |
| static void nss_nlgre_redir_cmn_dev_stats64(struct net_device *dev, |
| struct rtnl_link_stats64 *stats) |
| { |
| nss_nlgre_redir_cmn_get_stats64(dev, stats); |
| } |
| #endif |
| |
| /* |
| * nss_nlgre_redir_cmn_set_mac_address() |
| * Sets the mac address of netdev |
| */ |
| static int nss_nlgre_redir_cmn_set_mac_address(struct net_device *dev, void *p) |
| { |
| struct sockaddr *addr = (struct sockaddr *)p; |
| |
| if (!is_valid_ether_addr(addr->sa_data)) { |
| nss_nl_error("%pM: MAC address validation failed\n", addr->sa_data); |
| return -EINVAL; |
| } |
| |
| memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); |
| return 0; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_netdev_destructor() |
| * Unregisters and free the net device |
| */ |
| static void nss_nlgre_redir_cmn_netdev_destructor(struct net_device *dev) |
| { |
| nss_nl_info("Gre_redir tunnel device freed %s\n", dev->name); |
| free_netdev(dev); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_dev_setup() |
| * To setup the netdevice |
| */ |
| static void nss_nlgre_redir_cmn_dev_setup(struct net_device *dev) |
| { |
| ether_setup(dev); |
| dev->needed_headroom = NSS_NLGRE_REDIR_CMN_NEEDED_HEADROOM; |
| dev->netdev_ops = &gre_redir_netdev_ops; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) |
| dev->destructor = nss_nlgre_redir_cmn_netdev_destructor; |
| #else |
| dev->priv_destructor = nss_nlgre_redir_cmn_netdev_destructor; |
| #endif |
| eth_hw_addr_random(dev); |
| } |
| |
| /* |
| * net_device_ops |
| * Netdevice operations. |
| */ |
| static const struct net_device_ops gre_redir_netdev_ops = { |
| .ndo_open = nss_nlgre_redir_cmn_open_interface, |
| .ndo_stop = nss_nlgre_redir_cmn_close_interface, |
| .ndo_start_xmit = nss_nlgre_redir_cmn_xmit_data, |
| .ndo_get_stats64 = nss_nlgre_redir_cmn_dev_stats64, |
| .ndo_set_mac_address = nss_nlgre_redir_cmn_set_mac_address, |
| }; |
| |
| /* |
| * nss_nlgre_redir_cmn_print_hex_dump() |
| * To print hex dump of packet received |
| */ |
| void nss_nlgre_redir_cmn_print_hex_dump(struct sk_buff *skb) |
| { |
| int16_t dump_sz = (skb->len < NSS_NLGRE_REDIR_PKT_DUMP_SZ) ? skb->len : NSS_NLGRE_REDIR_PKT_DUMP_SZ; |
| |
| dump_sz -= NSS_NLGRE_REDIR_PKT_DUMP_OFFSET; |
| if (dump_sz > 0) { |
| /* |
| * Enable dynamic debug to print |
| */ |
| print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, dump_sz); |
| return; |
| } |
| |
| nss_nl_warn("Could not print packet skb->len=%d, DUMP_SZ=%d, DUMP_OFFSET=%d\n", |
| skb->len, NSS_NLGRE_REDIR_PKT_DUMP_SZ, NSS_NLGRE_REDIR_PKT_DUMP_OFFSET); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_mode_str_to_enum() |
| * Returns the type of mode |
| */ |
| enum nss_nlgre_redir_cmn_mode_type nss_nlgre_redir_cmn_mode_str_to_enum(char *mode) |
| { |
| if (!mode) |
| return NSS_NLGRE_REDIR_CMN_MODE_TYPE_UNKNOWN; |
| if (!strncmp(mode, "wifi", NSS_NLGRE_REDIR_MODE_MAX_SZ)) |
| return NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI; |
| if (!strncmp(mode, "split", NSS_NLGRE_REDIR_MODE_MAX_SZ)) |
| return NSS_NLGRE_REDIR_CMN_MODE_TYPE_SPLIT; |
| |
| return NSS_NLGRE_REDIR_CMN_MODE_TYPE_UNKNOWN; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_get_tun_ifnum() |
| * Returns the interface number of the net device |
| */ |
| int32_t nss_nlgre_redir_cmn_get_tun_ifnum(enum nss_nlgre_redir_cmn_mode_type type, struct net_device *dev) |
| { |
| struct nss_nlgre_redir_cmn_tun_data tun_data; |
| |
| if (!dev) { |
| nss_nl_error("net_dev is NULL\n"); |
| return -1; |
| } |
| |
| tun_data = nss_nlgre_redir_cmn_get_tun_data(dev); |
| if (!tun_data.dev) { |
| nss_nl_error("Invalid tun_data: %px\n", tun_data.dev); |
| return -1; |
| } |
| |
| switch(type) { |
| case NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI: |
| return tun_data.wifi_offl_inner_ifnum; |
| case NSS_NLGRE_REDIR_CMN_MODE_TYPE_SPLIT: |
| return NSS_ETH_RX_INTERFACE; |
| default: |
| nss_nl_error("Wrong mode type: %d\n", type); |
| return -1; |
| } |
| |
| return -1; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_get_tun_type() |
| * Returns the type of tunnel we'll operate in |
| */ |
| enum nss_nlgre_redir_cmn_tun_type nss_nlgre_redir_cmn_get_tun_type(char *tun_type) |
| { |
| if (!tun_type) |
| return NSS_NLGRE_REDIR_CMN_TUN_TYPE_UNKNOWN; |
| if (!strncmp(tun_type, "tun", NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ)) |
| return NSS_NLGRE_REDIR_CMN_TUN_TYPE_TUN; |
| if (!strncmp(tun_type, "dtun", NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ)) |
| return NSS_NLGRE_REDIR_CMN_TUN_TYPE_DTUN; |
| if (!strncmp(tun_type, "split", NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ)) |
| return NSS_NLGRE_REDIR_CMN_TUN_TYPE_SPLIT; |
| |
| return NSS_NLGRE_REDIR_CMN_TUN_TYPE_UNKNOWN; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_get_tun_data_index() |
| * Returns index in array of private data. |
| */ |
| int nss_nlgre_redir_cmn_get_tun_data_index(struct net_device *dev) |
| { |
| struct nss_ctx_instance *nss_ctx; |
| uint32_t iter; |
| |
| nss_ctx = nss_gre_redir_get_context(); |
| if (!dev) { |
| nss_nl_error("%px: Dev is NULL\n", nss_ctx); |
| return -1; |
| } |
| |
| spin_lock(&lock); |
| for (iter = 0; iter < NSS_NLGRE_REDIR_CMN_MAX_TUNNELS; iter++) { |
| if (tun_data[iter].dev != dev) { |
| continue; |
| } |
| |
| spin_unlock(&lock); |
| return iter; |
| } |
| |
| spin_unlock(&lock); |
| return -1; |
| } |
| |
| /* |
| * nss_gre_redir_unregister_and_deallocate() |
| * Unregisters and deallocates corresponding dev and node. |
| */ |
| bool nss_nlgre_redir_cmn_unregister_and_deallocate(struct net_device *dev, uint32_t type) |
| { |
| nss_tx_status_t status; |
| int ifnum; |
| bool ret; |
| |
| ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, type); |
| if (ifnum == -1) { |
| nss_nl_error("%px: unable to get NSS interface for net device %s of type %d\n", dev, dev->name, type); |
| return false; |
| } |
| |
| ret = nss_gre_redir_unregister_if(ifnum); |
| if (!ret) { |
| nss_nl_error("%px: Unable to unregister interface %d\n", dev, ret); |
| return false; |
| } |
| |
| status = nss_dynamic_interface_dealloc_node(ifnum, type); |
| if (status != NSS_TX_SUCCESS) { |
| nss_nl_error("%px: Unable to deallocate node %d\n", dev, status); |
| return false; |
| } |
| |
| nss_nl_trace("%s: Sucessfully unregistered and deallocated %d\n", dev->name, ifnum); |
| return true; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc |
| * Find out the interfaces to be deallocated |
| */ |
| void nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc(struct nss_nlgre_redir_cmn_tun_data *tun_data) |
| { |
| struct nss_ctx_instance *nss_ctx; |
| |
| nss_ctx = nss_gre_redir_get_context(); |
| if (tun_data->sjack_inner_ifnum != -1) { |
| if(!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER)) { |
| nss_nl_error("%px: Unable to unregister and deallocate node of type %d\n", nss_ctx, |
| NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER); |
| } |
| } |
| |
| if (tun_data->wifi_offl_inner_ifnum != -1) { |
| if (!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER)) { |
| nss_nl_error("%px: Unable to unregister and deallocate node of type %d\n", nss_ctx, |
| NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER); |
| } |
| } |
| |
| if (tun_data->host_inner_ifnum != -1) { |
| if (!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER)) { |
| nss_nl_error("%px: Unable to unregister and deallocate node of type %d\n", nss_ctx, |
| NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER); |
| } |
| } |
| |
| if (tun_data->outer_ifnum != -1) { |
| if (!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER)) { |
| nss_nl_error("%px: Unable to unregister and deallocate node of type %d\n", nss_ctx, |
| NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER); |
| } |
| } |
| |
| nss_nl_trace("%s: Sucessfully unregistered and deallocated\n", tun_data->dev->name); |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_destroy_tun() |
| * Unregisters and deallocs dynamic interfaces. |
| */ |
| int nss_nlgre_redir_cmn_destroy_tun(struct net_device *dev) |
| { |
| struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context(); |
| struct nss_nlgre_redir_cmn_tun_data tun_data; |
| int index; |
| |
| dev_hold(dev); |
| tun_data = nss_nlgre_redir_cmn_get_tun_data(dev); |
| if (!tun_data.dev) { |
| nss_nl_error("%px: Invalid tun data\n", nss_ctx); |
| return -1; |
| } |
| |
| index = nss_nlgre_redir_cmn_get_tun_data_index(tun_data.dev); |
| if (index < NSS_NLGRE_REDIR_CMN_MIN_TUNNELS || index >= NSS_NLGRE_REDIR_CMN_MAX_TUNNELS) { |
| nss_nl_error("%px: index out of bound %d\n", nss_ctx, index); |
| return -1; |
| } |
| |
| nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc(&tun_data); |
| nss_nlgre_redir_cmn_deinit_tun_data(&tun_data, index); |
| dev_put(dev); |
| unregister_netdev(dev); |
| nss_nl_info("%px: Successfully destroyed gretun = gretun%d tunnel\n", dev, index); |
| return index; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_create_tun() |
| * Allocates netdevice and configures tunnel. |
| */ |
| struct net_device *nss_nlgre_redir_cmn_create_tun(uint32_t sip[4], uint32_t dip[4], uint8_t iptype) |
| { |
| struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context(); |
| struct nss_gre_redir_outer_configure_msg ngrocm = {0}; |
| struct nss_gre_redir_inner_configure_msg ngrm = {0}; |
| struct nss_nlgre_redir_cmn_tun_data tun_data; |
| struct nss_gre_redir_cmn_ndev_priv *priv; |
| struct net_device *dev; |
| nss_tx_status_t status; |
| int tun_idx = -1, ret; |
| |
| tun_idx = nss_nlgre_redir_cmn_get_next_free_tun(); |
| if (tun_idx == -1) { |
| nss_nl_error("Unable to allocate any tunnel\n"); |
| return NULL; |
| } |
| |
| /* |
| * Initializes the tun_data |
| */ |
| nss_nlgre_redir_cmn_init_tun_data(&tun_data); |
| dev = alloc_netdev(sizeof(*priv), "gretun%d", NET_NAME_UNKNOWN, nss_nlgre_redir_cmn_dev_setup); |
| if (!dev) { |
| nss_nl_error("Unable to allocate netdev\n"); |
| return NULL; |
| } |
| |
| if (register_netdev(dev)) { |
| nss_nl_warn("Unable to register netdev %s\n", dev->name); |
| free_netdev(dev); |
| goto fail; |
| } |
| |
| /* |
| * Dynamic interface allocation. |
| */ |
| ret = nss_nlgre_redir_cmn_interface_alloc_and_register(&tun_data, dev); |
| if (ret == -1) { |
| nss_nl_error("%px: Unable to allocate and register gre_redir nodes\n", nss_ctx); |
| unregister_netdev(dev); |
| goto fail; |
| } |
| |
| memcpy(ngrm.ip_src_addr, sip, sizeof(ngrm.ip_src_addr)); |
| memcpy(ngrm.ip_dest_addr, dip, sizeof(ngrm.ip_dest_addr)); |
| |
| /* |
| * TODO: Dynamic assignment of values from userspace |
| * ip_df_policy value currently hard coded. This needs to be supplied from userspace. |
| */ |
| ngrm.ip_hdr_type = iptype; |
| ngrm.ip_df_policy = 0; |
| ngrm.gre_version = 0; |
| ngrm.ip_ttl = NSS_NLGRE_REDIR_CMN_IP_TTL; |
| ngrm.except_outerif = tun_data.outer_ifnum; |
| |
| /* |
| * TODO: Dynamic assignment of values from userspace |
| * rps_hint value currently hard coded. This needs to be supplied from userspace. |
| */ |
| ngrocm.ip_hdr_type = iptype; |
| ngrocm.rps_hint = 0; |
| ngrocm.except_hostif = tun_data.host_inner_ifnum; |
| ngrocm.except_offlif = tun_data.wifi_offl_inner_ifnum; |
| ngrocm.except_sjackif = tun_data.sjack_inner_ifnum; |
| |
| status = nss_gre_redir_configure_inner_node(tun_data.host_inner_ifnum, &ngrm); |
| if (status != NSS_TX_SUCCESS) { |
| nss_nl_warn("%px: unable to configure host inner node %d\n", nss_ctx, tun_data.host_inner_ifnum); |
| unregister_netdev(dev); |
| goto fail; |
| } |
| |
| status = nss_gre_redir_configure_inner_node(tun_data.wifi_offl_inner_ifnum, &ngrm); |
| if (status != NSS_TX_SUCCESS) { |
| nss_nl_warn("%px: unable to configure wifi offload inner node %d\n", nss_ctx, tun_data.host_inner_ifnum); |
| unregister_netdev(dev); |
| goto fail; |
| } |
| |
| status = nss_gre_redir_configure_inner_node(tun_data.sjack_inner_ifnum, &ngrm); |
| if (status != NSS_TX_SUCCESS) { |
| nss_nl_warn("%px: unable to configure sjack inner node %d\n", nss_ctx, tun_data.sjack_inner_ifnum); |
| unregister_netdev(dev); |
| goto fail; |
| } |
| |
| status = nss_gre_redir_configure_outer_node(tun_data.outer_ifnum, &ngrocm); |
| if (status != NSS_TX_SUCCESS) { |
| nss_nl_warn("%px: unable to configure outer node %d\n", nss_ctx, tun_data.host_inner_ifnum); |
| unregister_netdev(dev); |
| goto fail; |
| } |
| |
| tun_data.enable = true; |
| tun_data.dev = dev; |
| if (!nss_nlgre_redir_cmn_set_tun_data(&tun_data, tun_idx)) { |
| nss_nl_error("%px: Unable to set tun data\n", nss_ctx); |
| unregister_netdev(dev); |
| goto fail; |
| } |
| |
| return dev; |
| fail: |
| nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc(&tun_data); |
| nss_nlgre_redir_cmn_deinit_tun_data(&tun_data, tun_idx); |
| return NULL; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_get_dev_ifnum() |
| * Returns the interface number by dev and type |
| */ |
| int32_t nss_nlgre_redir_cmn_get_dev_ifnum(char *dev_name) |
| { |
| struct net_device *dev; |
| uint32_t ifnum; |
| |
| if (!dev_name) { |
| nss_nl_error("dev_name is NULL\n"); |
| return -1; |
| } |
| |
| /* |
| * Get the dev reference |
| */ |
| dev = dev_get_by_name(&init_net, dev_name); |
| if (!dev) { |
| nss_nl_error("Invalid parameter: %s\n", dev_name); |
| return -ENODEV; |
| } |
| |
| ifnum = nss_cmn_get_interface_number_by_dev(dev); |
| dev_put(dev); |
| return ifnum; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_map_interface() |
| * Map nss interface to tunnel ID. |
| */ |
| int nss_nlgre_redir_cmn_map_interface(uint32_t nexthop_nssif, uint16_t lag_en, struct nss_nlgre_redir_map *map_params) |
| { |
| struct nss_gre_redir_msg ngrm; |
| struct nss_ctx_instance *nss_ctx; |
| nss_tx_status_t ret; |
| uint32_t vap_nss_if; |
| uint8_t tun_type; |
| uint32_t len; |
| |
| len = sizeof(struct nss_gre_redir_msg) - sizeof(struct nss_cmn_msg); |
| nss_ctx = nss_gre_redir_get_context(); |
| tun_type = nss_nlgre_redir_cmn_get_tun_type(map_params->tun_type); |
| vap_nss_if = nss_nlgre_redir_cmn_get_dev_ifnum(map_params->vap_nss_if); |
| if ((vap_nss_if >= NSS_DYNAMIC_IF_START+NSS_MAX_DYNAMIC_INTERFACES) || (vap_nss_if < NSS_DYNAMIC_IF_START)) { |
| nss_nl_error("%px: vap_nss_if is out of valid range for vap: %d\n", nss_ctx, vap_nss_if); |
| return -1; |
| } |
| |
| if (map_params->rid >= NSS_NLGRE_REDIR_CMN_RADIO_ID_MAX) { |
| nss_nl_error("%px: radio_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if); |
| return -1; |
| } |
| |
| if (map_params->vid >= NSS_NLGRE_REDIR_CMN_VAP_ID_MAX) { |
| nss_nl_error("%px: vap_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if); |
| return -1; |
| } |
| |
| nss_cmn_msg_init(&ngrm.cm, NSS_GRE_REDIR_INTERFACE, NSS_GRE_REDIR_TX_INTERFACE_MAP_MSG, |
| len, nss_nlgre_redir_cmn_map_unmap_msg_cb, NULL); |
| |
| ngrm.msg.interface_map.vap_nssif = vap_nss_if; |
| ngrm.msg.interface_map.radio_id = map_params->rid; |
| ngrm.msg.interface_map.vap_id = map_params->vid; |
| ngrm.msg.interface_map.tunnel_type = tun_type; |
| ngrm.msg.interface_map.ipsec_pattern = map_params->ipsec_sa_pattern; |
| ngrm.msg.interface_map.lag_en = lag_en; |
| ngrm.msg.interface_map.nexthop_nssif = nexthop_nssif; |
| |
| ret = nss_gre_redir_tx_msg_sync(nss_ctx, &ngrm); |
| if (ret != NSS_TX_SUCCESS) { |
| nss_nl_error("%px: Tx to firmware failed\n", nss_ctx); |
| return -1; |
| } |
| |
| nss_nl_info("%px: Successfully transmitted msg to firmware\n", nss_ctx); |
| return 0; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_unmap_interface() |
| * Interface unmap message. |
| */ |
| int nss_nlgre_redir_cmn_unmap_interface(struct nss_nlgre_redir_unmap *unmap_params) |
| { |
| struct nss_ctx_instance *nss_ctx; |
| struct nss_gre_redir_msg ngrm; |
| uint32_t vap_nss_if, len; |
| nss_tx_status_t ret; |
| |
| len = sizeof(struct nss_gre_redir_msg) - sizeof(struct nss_cmn_msg); |
| vap_nss_if = nss_nlgre_redir_cmn_get_dev_ifnum(unmap_params->vap_nss_if); |
| nss_ctx = nss_gre_redir_get_context(); |
| |
| if ((vap_nss_if >= NSS_DYNAMIC_IF_START+NSS_MAX_DYNAMIC_INTERFACES) || (vap_nss_if < NSS_DYNAMIC_IF_START)) { |
| nss_nl_error("%px: vap_nss_if is out of valid range for vap: %d\n", nss_ctx, vap_nss_if); |
| return -1; |
| } |
| |
| if (unmap_params->rid >= NSS_NLGRE_REDIR_CMN_RADIO_ID_MAX) { |
| nss_nl_error("%px: radio_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if); |
| return -1; |
| } |
| |
| if (unmap_params->vid >= NSS_NLGRE_REDIR_CMN_VAP_ID_MAX) { |
| nss_nl_error("%px: vap_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if); |
| return -1; |
| } |
| |
| nss_cmn_msg_init(&ngrm.cm, NSS_GRE_REDIR_INTERFACE, NSS_GRE_REDIR_TX_INTERFACE_UNMAP_MSG, |
| len, nss_nlgre_redir_cmn_map_unmap_msg_cb, NULL); |
| ngrm.msg.interface_unmap.vap_nssif = vap_nss_if; |
| ngrm.msg.interface_unmap.radio_id = unmap_params->rid; |
| ngrm.msg.interface_unmap.vap_id = unmap_params->vid; |
| |
| ret = nss_gre_redir_tx_msg_sync(nss_ctx, &ngrm); |
| if (ret != NSS_TX_SUCCESS) { |
| nss_nl_error("%px: Tx to firmware failed\n", nss_ctx); |
| return -1; |
| } |
| |
| nss_nl_info("%px: Successfully transmitted msg to firmware\n", nss_ctx); |
| return 0; |
| } |
| |
| /* |
| * nss_nlgre_redir_cmn_set_next_hop() |
| * Sets next hop as gre-redir for wifi. |
| */ |
| int nss_nlgre_redir_cmn_set_next_hop(uint32_t next_dev_ifnum, struct nss_nlgre_redir_set_next *setnext_params) |
| { |
| struct nss_ctx_instance *nss_ctx; |
| nss_tx_status_t ret; |
| int ifnumber; |
| void *ctx; |
| |
| nss_ctx = nss_gre_redir_get_context(); |
| ifnumber = nss_nlgre_redir_cmn_get_dev_ifnum(setnext_params->dev_name); |
| if (ifnumber == -1) { |
| nss_nl_error("%px: Unable to find NSS interface for net device %s\n", nss_ctx, setnext_params->dev_name); |
| return -1; |
| } |
| |
| nss_nl_info("%px: next hop interface number is %d\n", nss_ctx, next_dev_ifnum); |
| ctx = nss_wifi_get_context(); |
| |
| ret = nss_wifi_vdev_set_next_hop(ctx, ifnumber, next_dev_ifnum); |
| if (ret != NSS_TX_SUCCESS) { |
| nss_nl_error("%px: wifi drv api failed to set next hop\n", nss_ctx); |
| return -1; |
| } |
| |
| nss_nl_info("%px: Successfully set the next hop\n", nss_ctx); |
| return 0; |
| } |