blob: 2517622161f95eec86fd46052b48f28f90cbf298 [file] [log] [blame]
/*
***************************************************************************
* 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;
}