blob: 4861f354b878f3b36c04f5b1f5bee38f4c9da8a1 [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2019-2020, 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.
**************************************************************************
*/
/*
* nss_eogremgr.c
* NSS EOGRE manager
*/
#include <nss_api_if.h>
#include <nss_cmn.h>
#include "nss_connmgr_gre_public.h"
#include <linux/of.h>
#include "nss_eogremgr.h"
#include "nss_eogremgr_priv.h"
/*
* Mapping table from tunnel-id to rule/GRE netdev
*/
struct nss_eogremgr_tunnel {
union {
struct nss_ipv4_rule_create_msg v4; /* IPv4 rule structure. */
struct nss_ipv6_rule_create_msg v6; /* IPv6 rule structure. */
} ip_rule;
bool is_ipv4; /* Set when tunnel is ipv4. */
struct net_device *gre_dev; /* Store the gre netdev associated with the tunnel. */
};
struct nss_eogremgr_tunnel *tunnels[NSS_EOGREMGR_MAX_TUNNELS] = {NULL};
/*
* nss_eogremgr_get_if_num_inner()
* Get the GRE inner interface number associated with the tunnel.
*/
int nss_eogremgr_get_if_num_inner(uint32_t tunnel_id)
{
int inner_ifnum;
/*
* Check if the tunnel exists.
*/
if (!tunnels[tunnel_id]) {
nss_eogremgr_warn("Could not find tunnel data, for tunnel id : %d\n", tunnel_id);
return -1;
}
/*
* Find inner ifnum for the GRE netdev.
*/
inner_ifnum = nss_cmn_get_interface_number_by_dev_and_type(
tunnels[tunnel_id]->gre_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_INNER);
if (inner_ifnum < 0) {
nss_eogremgr_warn("No GRE inner interface registered for tunnel id : %d\n", tunnel_id);
return -1;
}
/*
* Append the core-id to the ifnum.
*/
return nss_gre_ifnum_with_core_id(inner_ifnum);
}
EXPORT_SYMBOL(nss_eogremgr_get_if_num_inner);
/*
* nss_eogremgr_tunnel_disable()
* Function to disable eogre tunnel.
*/
nss_eogremgr_status_t nss_eogremgr_tunnel_disable(uint32_t tunnel_id)
{
int ret;
/*
* Check if the tunnel exists.
*/
if (!tunnels[tunnel_id]) {
nss_eogremgr_warn("Could not find tunnel data for tunnel id : %d\n", tunnel_id);
return NSS_EOGRE_ERR_NETDEV_DISABLE_FAILED;
}
/*
* Notify GRE client to disable GRE tunnel.
*/
ret = nss_connmgr_gre_dev_close(tunnels[tunnel_id]->gre_dev);
if (ret != 0) {
nss_eogremgr_warn("%s: Unable to disable tunnel:%d\n\n",
tunnels[tunnel_id]->gre_dev->name, tunnel_id);
return NSS_EOGRE_ERR_NETDEV_DISABLE_FAILED;
}
return NSS_EOGRE_SUCCESS;
}
EXPORT_SYMBOL(nss_eogremgr_tunnel_disable);
/*
* nss_eogremgr_tunnel_enable()
* Function to enable eogre tunnel.
*/
nss_eogremgr_status_t nss_eogremgr_tunnel_enable(uint32_t tunnel_id)
{
int ret;
/*
* Check if the tunnel exists.
*/
if (!tunnels[tunnel_id]) {
nss_eogremgr_warn("Could not find tunnel data for tunnel id : %d\n", tunnel_id);
return NSS_EOGRE_ERR_NETDEV_ENABLE_FAILED;
}
/*
* Notify GRE client to enable GRE tunnel.
*/
ret = nss_connmgr_gre_dev_open(tunnels[tunnel_id]->gre_dev);
if (ret != 0) {
nss_eogremgr_warn("%s: Unable to enable tunnel:%d\n",
tunnels[tunnel_id]->gre_dev->name, tunnel_id);
return NSS_EOGRE_ERR_NETDEV_ENABLE_FAILED;
}
return NSS_EOGRE_SUCCESS;
}
EXPORT_SYMBOL(nss_eogremgr_tunnel_enable);
/*
* nss_eogremgr_tunnel_destroy_ipv6_rule()
* Destroy a given IPv6 connection in the NSS
*/
static nss_tx_status_t nss_eogremgr_tunnel_destroy_ipv6_rule(struct nss_ctx_instance *nss_ctx, struct nss_ipv6_5tuple *ni5t)
{
struct nss_ipv6_msg nim;
nss_tx_status_t status;
nss_eogremgr_info("%px: ctx: Destroy IPv6: %pI6:%u, %pI6:%u, p: %d\n", nss_ctx,
&ni5t->flow_ip, ni5t->flow_ident, &ni5t->return_ip, ni5t->return_ident, ni5t->protocol);
nss_ipv6_msg_init(&nim, NSS_IPV6_RX_INTERFACE, NSS_IPV6_TX_DESTROY_RULE_MSG,
sizeof(struct nss_ipv6_rule_destroy_msg), NULL, NULL);
nim.msg.rule_destroy.tuple = *ni5t;
status = nss_ipv6_tx_sync(nss_ctx, &nim);
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("%px: Destroy IPv6 message failed %d\n", nss_ctx, status);
}
return status;
}
/*
* nss_eogremgr_tunnel_destroy_ipv4_rule()
* Destroy a given IPv4 connection in the NSS
*/
static nss_tx_status_t nss_eogremgr_tunnel_destroy_ipv4_rule(struct nss_ctx_instance *nss_ctx, struct nss_ipv4_5tuple *ni5t)
{
struct nss_ipv4_msg nim;
nss_tx_status_t status;
nss_eogremgr_info("%px: ctx: Destroy IPv4: %pI4h :%u, %pI4h :%u, p: %d\n", nss_ctx,
&ni5t->flow_ip, ni5t->flow_ident, &ni5t->return_ip, ni5t->return_ident, ni5t->protocol);
nss_ipv4_msg_init(&nim, NSS_IPV4_RX_INTERFACE, NSS_IPV4_TX_DESTROY_RULE_MSG,
sizeof(struct nss_ipv4_rule_destroy_msg), NULL, NULL);
nim.msg.rule_destroy.tuple = *ni5t;
status = nss_ipv4_tx_sync(nss_ctx, &nim);
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("%px: Destroy IPv4 message failed %d\n", nss_ctx, status);
}
return status;
}
/*
* nss_eogremgr_tunnel_create_ipv6_rule()
* Create a given IPv6 connection in the NSS
*/
static nss_tx_status_t nss_eogremgr_tunnel_create_ipv6_rule(struct nss_ctx_instance *nss_ctx, struct nss_ipv6_rule_create_msg *nircm)
{
struct nss_ipv6_msg nim;
nss_tx_status_t status;
nss_ipv6_msg_init(&nim, NSS_IPV6_RX_INTERFACE, NSS_IPV6_TX_CREATE_RULE_MSG,
sizeof(struct nss_ipv6_rule_create_msg), NULL, NULL);
nim.msg.rule_create = *nircm;
status = nss_ipv6_tx_sync(nss_ctx, &nim);
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("%px: Create IPv6 message failed %d\n", nss_ctx, status);
}
return status;
}
/*
* nss_eogremgr_tunnel_create_ipv4_rule()
* Create a given IPv4 connection in the NSS
*/
static nss_tx_status_t nss_eogremgr_tunnel_create_ipv4_rule(struct nss_ctx_instance *nss_ctx, struct nss_ipv4_rule_create_msg *nircm)
{
struct nss_ipv4_msg nim;
nss_tx_status_t status;
nss_ipv4_msg_init(&nim, NSS_IPV4_RX_INTERFACE, NSS_IPV4_TX_CREATE_RULE_MSG,
sizeof(struct nss_ipv4_rule_create_msg), NULL, NULL);
nim.msg.rule_create = *nircm;
status = nss_ipv4_tx_sync(nss_ctx, &nim);
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("%px: Create IPv4 message failed %d\n", nss_ctx, status);
}
return status;
}
/*
* nss_eogremgr_tunnel_destroy()
* Destroy the EoGRE tunnel.
*/
nss_eogremgr_status_t nss_eogremgr_tunnel_destroy(uint32_t tunnel_id)
{
struct nss_ctx_instance *nss_ctx_v4, *nss_ctx_v6;
nss_tx_status_t status;
enum nss_connmgr_gre_err_codes err_code_gre;
/*
* Check if tunnel_id is in range.
*/
if (tunnel_id >= NSS_EOGREMGR_MAX_TUNNELS) {
nss_eogremgr_warn("Invalid param tunnel_id: %d max: %d\n",
tunnel_id, NSS_EOGREMGR_MAX_TUNNELS);
return NSS_EOGRE_ERR_INVALID_TUNNEL_ID;
}
/*
* Check if the tunnel_id exists.
*/
if (!tunnels[tunnel_id]) {
nss_eogremgr_warn("Could not find tunnel data for tunnel id : %d\n", tunnel_id);
return NSS_EOGRE_ERR_INVALID_TUNNEL_ID;
}
/*
* Destroy IPv4/IPv6 rule
*/
if (tunnels[tunnel_id]->is_ipv4) {
nss_ctx_v4 = nss_ipv4_get_mgr();
if (!nss_ctx_v4) {
nss_eogremgr_warn("Tunnel: %d: could not get IPv4 ctx\n", tunnel_id);
return NSS_EOGRE_ERR_BAD_NSS_CTX;
}
status = nss_eogremgr_tunnel_destroy_ipv4_rule(nss_ctx_v4, &tunnels[tunnel_id]->ip_rule.v4.tuple);
} else {
nss_ctx_v6 = nss_ipv6_get_mgr();
if (!nss_ctx_v6) {
nss_eogremgr_warn("Tunnel: %d: could not get IPv6 ctx\n", tunnel_id);
return NSS_EOGRE_ERR_BAD_NSS_CTX;
}
status = nss_eogremgr_tunnel_destroy_ipv6_rule(nss_ctx_v6, &tunnels[tunnel_id]->ip_rule.v6.tuple);
}
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("Destroy IP rule failed for tunnel: %d\n", tunnel_id);
return NSS_EOGRE_ERR_DESTROY_IP_RULE_FAILED;
}
/*
* Destroy GRE tunnel
*/
err_code_gre = nss_connmgr_gre_destroy_interface(tunnels[tunnel_id]->gre_dev);
if (err_code_gre != GRE_SUCCESS) {
nss_eogremgr_warn("Could not destroy GRE interface: %d, err=%d\n", tunnel_id, err_code_gre);
/*
* Add the ipv4/upv6 rule back, since tunnel is not destroyed.
*/
if (tunnels[tunnel_id]->is_ipv4) {
status = nss_eogremgr_tunnel_create_ipv4_rule(
nss_ctx_v4, &tunnels[tunnel_id]->ip_rule.v4);
} else {
status = nss_eogremgr_tunnel_create_ipv6_rule(
nss_ctx_v6, &tunnels[tunnel_id]->ip_rule.v6);
}
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("IPv4/IPv6 rule create from nss_eogremgr_tunnel_destroy() failed with status: %d\n",
status);
return NSS_EOGRE_ERR_CREATE_IP_RULE_FAILED;
}
return NSS_EOGRE_TUNNEL_DESTROY_FAILED;
}
nss_eogremgr_info("Tunnel: %d is destroyed\n", tunnel_id);
kfree(tunnels[tunnel_id]);
tunnels[tunnel_id] = NULL;
return NSS_EOGRE_SUCCESS;
}
EXPORT_SYMBOL(nss_eogremgr_tunnel_destroy);
/*
* nss_eogremgr_tunnel_ipv6_create()
* API to create EoGRE IPv6 tunnel and Push IPv6 rule to NSS.
*/
nss_eogremgr_status_t nss_eogremgr_tunnel_ipv6_create(struct nss_connmgr_gre_cfg *cfg,
struct nss_ipv6_rule_create_msg *nircm, uint32_t tunnel_id)
{
struct net_device *gre_dev;
struct nss_ctx_instance *nss_ctx;
enum nss_connmgr_gre_err_codes err_code_gre;
nss_tx_status_t status;
int gre_outer_ifnum;
/*
* Check if tunnel_id is in range.
*/
if (tunnel_id >= NSS_EOGREMGR_MAX_TUNNELS) {
nss_eogremgr_warn("Invalid tunnel_id: %d max: %d\n",
tunnel_id, NSS_EOGREMGR_MAX_TUNNELS);
return NSS_EOGRE_ERR_INVALID_TUNNEL_ID;
}
/*
* Check if tunnel_id is already in use.
*/
if (tunnels[tunnel_id]) {
nss_eogremgr_warn("Tunnel already exist for this tunnel_id = %d, netdev = %s\n",
tunnel_id, tunnels[tunnel_id]->gre_dev->name);
return NSS_EOGRE_ERR_TUNNEL_ID_EXIST;
}
/*
* GRE API to create GRE standard interface
*/
gre_dev = nss_connmgr_gre_create_interface(cfg, &err_code_gre);
if (!gre_dev) {
nss_eogremgr_warn("Could not create GRE interface, err=%d\n", err_code_gre);
return NSS_EOGRE_ERR_GRE_CREATE_FAILED;
}
nss_eogremgr_info("GRE interface %s created with tunnel_id: %u\n", gre_dev->name, tunnel_id);
gre_outer_ifnum = nss_cmn_get_interface_number_by_dev_and_type(
gre_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_OUTER);
if (gre_outer_ifnum < 0) {
nss_eogremgr_warn("No GRE outer interface registered for gre netdev: %s\n", gre_dev->name);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_INVALID_GRE_IFNUM;
}
nss_ctx = nss_ipv6_get_mgr();
if (!nss_ctx) {
nss_eogremgr_warn("Tunnel: %d could not get IPv6 ctx\n", tunnel_id);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_BAD_NSS_CTX;
}
/*
* Configure ifnum for GRE decap node before adding IPv6 rule.
*/
nircm->conn_rule.flow_interface_num = gre_outer_ifnum;
/*
* Fill the flow/return ident with 32 bit key, to create a 5-tuple match for GRE with key
*/
if (cfg->okey_valid) {
nircm->tuple.flow_ident = cfg->okey;
nircm->tuple.return_ident = cfg->okey;
}
/*
* Push IPv6 rule to NSS
*/
status = nss_eogremgr_tunnel_create_ipv6_rule(nss_ctx, nircm);
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("IPv6 rule create failed with status: %d\n", status);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_CREATE_IP_RULE_FAILED;
}
nss_eogremgr_info("EoGRE Tunnel create done tunnel_id:%d IPv6: %pI6h :%u, %pI6h :%u, p: %d\n",
tunnel_id, &nircm->tuple.flow_ip, nircm->tuple.flow_ident, &nircm->tuple.return_ip,
nircm->tuple.return_ident, nircm->tuple.protocol);
/*
* Add EoGRE tunnel entry
*/
tunnels[tunnel_id] = kzalloc(sizeof(struct nss_eogremgr_tunnel), GFP_ATOMIC);
if(!tunnels[tunnel_id]) {
nss_eogremgr_warn("Failed to allocate tunnel memory for tunnel_id : %d\n", tunnel_id);
nss_eogremgr_tunnel_destroy_ipv6_rule(nss_ctx, &nircm->tuple);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_ALLOC_TUNNEL_FAILED;
}
memcpy(&tunnels[tunnel_id]->ip_rule.v6, nircm, sizeof(struct nss_ipv6_rule_create_msg));
tunnels[tunnel_id]->is_ipv4 = false;
tunnels[tunnel_id]->gre_dev = gre_dev;
return NSS_EOGRE_SUCCESS;
}
EXPORT_SYMBOL(nss_eogremgr_tunnel_ipv6_create);
/*
* nss_eogremgr_tunnel_ipv4_create()
* API to create EoGRE IPv4 tunnel and push IPv4 rule to NSS.
*/
nss_eogremgr_status_t nss_eogremgr_tunnel_ipv4_create(struct nss_connmgr_gre_cfg *cfg,
struct nss_ipv4_rule_create_msg *nircm, uint32_t tunnel_id)
{
struct net_device *gre_dev;
struct nss_ctx_instance *nss_ctx;
int gre_outer_ifnum;
nss_tx_status_t status;
enum nss_connmgr_gre_err_codes err_code_gre;
/*
* Check if tunnel_id is in range.
*/
if (tunnel_id >= NSS_EOGREMGR_MAX_TUNNELS) {
nss_eogremgr_warn("Invalid param tunnel_id: %d max: %d\n", tunnel_id, NSS_EOGREMGR_MAX_TUNNELS);
return NSS_EOGRE_ERR_INVALID_TUNNEL_ID;
}
/*
* Check if tunnel_id is already in use.
*/
if (tunnels[tunnel_id]) {
nss_eogremgr_warn("Tunnel already exist for this tunnel_id = %d, netdev = %s\n",
tunnel_id, tunnels[tunnel_id]->gre_dev->name);
return NSS_EOGRE_ERR_TUNNEL_ID_EXIST;
}
/*
* Create GRE standard interface
*/
gre_dev = nss_connmgr_gre_create_interface(cfg, &err_code_gre);
if (!gre_dev) {
nss_eogremgr_warn("Could not create GRE interface, err=%d\n", err_code_gre);
return NSS_EOGRE_ERR_GRE_CREATE_FAILED;
}
nss_eogremgr_info("GRE interface %s created with tunnel_id: %u\n", gre_dev->name, tunnel_id);
gre_outer_ifnum = nss_cmn_get_interface_number_by_dev_and_type(
gre_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_OUTER);
if (gre_outer_ifnum < 0) {
nss_eogremgr_warn("No GRE outer interface registered for gre netdev: %s\n", gre_dev->name);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_INVALID_GRE_IFNUM;
}
nss_ctx = nss_ipv4_get_mgr();
if (!nss_ctx) {
nss_eogremgr_warn("EoGRE couldn't get IPv4 ctx\n");
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_BAD_NSS_CTX;
}
/*
* Configure ifnum for GRE decap node before adding IPv4 rule.
*/
nircm->conn_rule.flow_interface_num = gre_outer_ifnum;
/*
* Fill the flow/return ident with 32 bit key, to create a 5-tuple match for GRE with key
*/
if (cfg->okey_valid) {
nircm->tuple.flow_ident = cfg->okey;
nircm->tuple.return_ident = cfg->okey;
nircm->conn_rule.flow_ident_xlate = cfg->okey;
nircm->conn_rule.return_ident_xlate = cfg->okey;
}
/*
* Push IPv4 rule to NSS.
*/
status = nss_eogremgr_tunnel_create_ipv4_rule(nss_ctx, nircm);
if (status != NSS_TX_SUCCESS) {
nss_eogremgr_warn("IPv4 rule create failed with status: %d\n", status);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_CREATE_IP_RULE_FAILED;
}
nss_eogremgr_info("EoGRE Tunnel create done tunnel_id:%d IPv4: %pI4h :%u, %pI4h :%u, p: %d\n",
tunnel_id, &nircm->tuple.flow_ip, nircm->tuple.flow_ident, &nircm->tuple.return_ip,
nircm->tuple.return_ident, nircm->tuple.protocol);
/*
* Add EoGRE tunnel entry
*/
tunnels[tunnel_id] = kzalloc(sizeof(struct nss_eogremgr_tunnel), GFP_ATOMIC);
if (!tunnels[tunnel_id]) {
nss_eogremgr_warn("Failed to allocate tunnel memory for tunnel_id : %d\n", tunnel_id);
nss_eogremgr_tunnel_destroy_ipv4_rule(nss_ctx, &nircm->tuple);
nss_connmgr_gre_destroy_interface(gre_dev);
return NSS_EOGRE_ERR_ALLOC_TUNNEL_FAILED;
}
memcpy(&tunnels[tunnel_id]->ip_rule.v4, nircm, sizeof(struct nss_ipv4_rule_create_msg));
tunnels[tunnel_id]->is_ipv4 = true;
tunnels[tunnel_id]->gre_dev = gre_dev;
return NSS_EOGRE_SUCCESS;
}
EXPORT_SYMBOL(nss_eogremgr_tunnel_ipv4_create);
/*
* nss_eogremgr_destroy_tunnel_data()
* Destroy EoGRE global tunnel structure
*/
static int nss_eogremgr_destroy_tunnel_data(void)
{
int tunnel_id, status, fail_count = 0;
for (tunnel_id = 0; tunnel_id < NSS_EOGREMGR_MAX_TUNNELS; tunnel_id++) {
if (!tunnels[tunnel_id]) {
continue;
}
status = nss_eogremgr_tunnel_destroy(tunnel_id);
if (!status) {
nss_eogremgr_warn("Unable to delete GRE netdev and IPv4/IPv6 rule for tunnel_id: %u\n", tunnel_id);
fail_count++;
}
}
return fail_count;
}
/*
* nss_eogremgr_exit_module()
* Tunnel EoGRE module exit function
*/
static void __exit nss_eogremgr_exit_module(void)
{
int status;
/*
* Destroy tunnel data
*/
status = nss_eogremgr_destroy_tunnel_data();
if (status) {
nss_eogremgr_info("Error in module unloading, Unable to delete %d tunnels \n", status);
return;
}
nss_eogremgr_info("module unloaded\n");
}
/*
* nss_eogremgr_init_module()
* Tunnel EoGRE module init function
*/
static int __init nss_eogremgr_init_module(void)
{
#ifdef CONFIG_OF
/*
* If the node is not compatible, don't do anything.
*/
if (!of_find_node_by_name(NULL, "nss-common")) {
return 0;
}
#endif
nss_eogremgr_info("module %s loaded\n", NSS_CLIENT_BUILD_ID);
return 0;
}
module_init(nss_eogremgr_init_module);
module_exit(nss_eogremgr_exit_module);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("NSS EOGRE manager");