blob: 4642f8edf2cf95a01a75c1b529aca81325742237 [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/if.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/of.h>
#include <linux/types.h>
#include <linux/version.h>
#include <net/genetlink.h>
#include <nss_api_if.h>
#include <nss_capwap.h>
#include <nss_capwapmgr.h>
#include <nss_cmn.h>
#include <nss_ipsecmgr.h>
#include <nss_nl_if.h>
#include "nss_nl.h"
#include "nss_nlcapwap.h"
#include "nss_nlcapwap_if.h"
#include "nss_nlcmn_if.h"
#include "nss_nldtls.h"
#include "nss_nldtls_if.h"
#include "nss_nlgre_redir_if.h"
#include "nss_nlgre_redir_family.h"
#include "nss_nlipsec.h"
#include "nss_nlipsec_if.h"
#include "nss_nlipv4.h"
#include "nss_nlipv4_if.h"
#include "nss_nlipv6.h"
#include "nss_nlipv6_if.h"
#include "nss_nloam.h"
#include "nss_nloam_if.h"
#include "nss_nlethrx.h"
#include "nss_nlethrx_if.h"
#include "nss_nledma.h"
#include "nss_nledma_if.h"
#include "nss_nlcapwap.h"
#include "nss_nlcapwap_if.h"
#include "nss_nldynamic_interface.h"
#include "nss_nldynamic_interface_if.h"
#include "nss_nln2h.h"
#include "nss_nln2h_if.h"
#include "nss_nlc2c_tx.h"
#include "nss_nlc2c_tx_if.h"
#include "nss_nlc2c_rx.h"
#include "nss_nlc2c_rx_if.h"
#include "nss_nlipv4_reasm.h"
#include "nss_nlipv4_reasm_if.h"
#include "nss_nlipv6_reasm.h"
#include "nss_nlipv6_reasm_if.h"
#include "nss_nlwifili.h"
#include "nss_nlwifili_if.h"
#include "nss_nllso_rx.h"
#include "nss_nllso_rx_if.h"
#include "nss_nlmap_t.h"
#include "nss_nlmap_t_if.h"
#include "nss_nlpppoe.h"
#include "nss_nlpppoe_if.h"
#include "nss_nll2tpv2.h"
#include "nss_nll2tpv2_if.h"
#include "nss_nlpptp.h"
#include "nss_nlpptp_if.h"
#include "nss_nludp_st.h"
#include "nss_nludp_st_if.h"
/*
* nss_nl.c
* NSS Netlink manager
*/
/*
* NSS NL family definition
*/
struct nss_nl_family {
uint8_t *name; /* name of the family */
nss_nl_fn_t entry; /* entry function of the family */
nss_nl_fn_t exit; /* exit function of the family */
bool valid; /* valid or invalid */
};
/*
* Family handler table
*/
static struct nss_nl_family family_handlers[] = {
{
/*
* NSS_NLIPV4
*/
.name = NSS_NLIPV4_FAMILY, /* ipv4 */
.entry = NSS_NLIPV4_INIT, /* init */
.exit = NSS_NLIPV4_EXIT, /* exit */
.valid = CONFIG_NSS_NLIPV4 /* 1 or 0 */
},
{
/*
* NSS_NLIPSEC
*/
.name = NSS_NLIPSEC_FAMILY, /* ipsec */
.entry = NSS_NLIPSEC_INIT, /* init */
.exit = NSS_NLIPSEC_EXIT, /* exit */
.valid = CONFIG_NSS_NLIPSEC /* 1 or 0 */
},
{
/*
* NSS_NLOAM
*/
.name = NSS_NLOAM_FAMILY, /* oam */
.entry = NSS_NLOAM_INIT, /* init */
.exit = NSS_NLOAM_EXIT, /* exit */
.valid = CONFIG_NSS_NLOAM /* 1 or 0 */
},
{
/*
* NSS_NLIPV6
*/
.name = NSS_NLIPV6_FAMILY, /* ipv6 */
.entry = NSS_NLIPV6_INIT, /* init */
.exit = NSS_NLIPV6_EXIT, /* exit */
.valid = CONFIG_NSS_NLIPV6 /* 1 or 0 */
},
{
/*
* NSS_NLGRE_REDIR
*/
.name = NSS_NLGRE_REDIR_FAMILY, /* gre_redir */
.entry = NSS_NLGRE_REDIR_FAMILY_INIT, /* init */
.exit = NSS_NLGRE_REDIR_FAMILY_EXIT, /* exit */
.valid = CONFIG_NSS_NLGRE_REDIR_FAMILY /* 1 or 0 */
},
{
/*
* NSS_NLCAPWAP
*/
.name = NSS_NLCAPWAP_FAMILY, /* capwap */
.entry = NSS_NLCAPWAP_INIT, /* init */
.exit = NSS_NLCAPWAP_EXIT, /* exit */
.valid = CONFIG_NSS_NLCAPWAP /* 1 or 0 */
},
{
/*
* NSS_NLDTLS
*/
.name = NSS_NLDTLS_FAMILY, /* dtls */
.entry = NSS_NLDTLS_INIT, /* init */
.exit = NSS_NLDTLS_EXIT, /* exit */
.valid = CONFIG_NSS_NLDTLS /* 1 or 0 */
},
{
/*
* NSS_NLETHRX
*/
.name = NSS_NLETHRX_FAMILY, /* ethrx */
.entry = NSS_NLETHRX_INIT, /* init */
.exit = NSS_NLETHRX_EXIT, /* exit */
.valid = CONFIG_NSS_NLETHRX /* 1 or 0 */
},
{
/*
* NSS_NLEDMA
*/
.name = NSS_NLEDMA_FAMILY, /* edma */
.entry = NSS_NLEDMA_INIT, /* init */
.exit = NSS_NLEDMA_EXIT, /* exit */
.valid = CONFIG_NSS_NLEDMA /* 1 or 0 */
},
{
/*
* NSS_NLDYNAMIC_INTERFACE
*/
.name = NSS_NLDYNAMIC_INTERFACE_FAMILY, /* dynamic interface */
.entry = NSS_NLDYNAMIC_INTERFACE_INIT, /* init */
.exit = NSS_NLDYNAMIC_INTERFACE_EXIT, /* exit */
.valid = CONFIG_NSS_NLDYNAMIC_INTERFACE /* 1 or 0 */
},
{
/*
* NSS_NLN2H
*/
.name = NSS_NLN2H_FAMILY, /* n2h */
.entry = NSS_NLN2H_INIT, /* init */
.exit = NSS_NLN2H_EXIT, /* exit */
.valid = CONFIG_NSS_NLN2H /* 1 or 0 */
},
{
/*
* NSS_NLC2C_TX
*/
.name = NSS_NLC2C_TX_FAMILY, /* c2c_tx */
.entry = NSS_NLC2C_TX_INIT, /* init */
.exit = NSS_NLC2C_TX_EXIT, /* exit */
.valid = CONFIG_NSS_NLC2C_TX /* 1 or 0 */
},
{
/*
* NSS_NLC2C_RX
*/
.name = NSS_NLC2C_RX_FAMILY, /* c2c_rx */
.entry = NSS_NLC2C_RX_INIT, /* init */
.exit = NSS_NLC2C_RX_EXIT, /* exit */
.valid = CONFIG_NSS_NLC2C_RX /* 1 or 0 */
},
{
/*
* NSS_NLIPV4_REASM
*/
.name = NSS_NLIPV4_REASM_FAMILY, /* ipv4_reasm */
.entry = NSS_NLIPV4_REASM_INIT, /* init */
.exit = NSS_NLIPV4_REASM_EXIT, /* exit */
.valid = CONFIG_NSS_NLIPV4_REASM /* 1 or 0 */
},
{
/*
* NSS_NLIPV6_REASM
*/
.name = NSS_NLIPV6_REASM_FAMILY, /* ipv6_reasm */
.entry = NSS_NLIPV6_REASM_INIT, /* init */
.exit = NSS_NLIPV6_REASM_EXIT, /* exit */
.valid = CONFIG_NSS_NLIPV6_REASM /* 1 or 0 */
},
{
/*
* NSS_NLWIFILI
*/
.name = NSS_NLWIFILI_FAMILY, /* wifili */
.entry = NSS_NLWIFILI_INIT, /* init */
.exit = NSS_NLWIFILI_EXIT, /* exit */
.valid = CONFIG_NSS_NLWIFILI /* 1 or 0 */
},
{
/*
* NSS_NLLSO_RX
*/
.name = NSS_NLLSO_RX_FAMILY, /* lso_rx */
.entry = NSS_NLLSO_RX_INIT, /* init */
.exit = NSS_NLLSO_RX_EXIT, /* exit */
.valid = CONFIG_NSS_NLLSO_RX /* 1 or 0 */
},
{
/*
* NSS_NLMAP_T
*/
.name = NSS_NLMAP_T_FAMILY, /* map_t */
.entry = NSS_NLMAP_T_INIT, /* init */
.exit = NSS_NLMAP_T_EXIT, /* exit */
.valid = CONFIG_NSS_NLMAP_T /* 1 or 0 */
},
{
/*
* NSS_NLPPPOE
*/
.name = NSS_NLPPPOE_FAMILY, /* pppoe */
.entry = NSS_NLPPPOE_INIT, /* init */
.exit = NSS_NLPPPOE_EXIT, /* exit */
.valid = CONFIG_NSS_NLPPPOE /* 1 or 0 */
},
{
/*
* NSS_NLL2TPV2
*/
.name = NSS_NLL2TPV2_FAMILY, /* l2tpv2 */
.entry = NSS_NLL2TPV2_INIT, /* init */
.exit = NSS_NLL2TPV2_EXIT, /* exit */
.valid = CONFIG_NSS_NLL2TPV2 /* 1 or 0 */
},
{
/*
* NSS_NLPPTP
*/
.name = NSS_NLPPTP_FAMILY, /* pptp */
.entry = NSS_NLPPTP_INIT, /* init */
.exit = NSS_NLPPTP_EXIT, /* exit */
.valid = CONFIG_NSS_NLPPTP /* 1 or 0 */
},
{
/*
* NSS_NLUDP_ST
*/
.name = NSS_NLUDP_ST_FAMILY, /* udp_st */
.entry = NSS_NLUDP_ST_INIT, /* init */
.exit = NSS_NLUDP_ST_EXIT, /* exit */
.valid = CONFIG_NSS_NLUDP_ST /* 1 or 0 */
},
};
#define NSS_NL_FAMILY_HANDLER_SZ ARRAY_SIZE(family_handlers)
/*
* nss_nl_alloc_msg()
* allocate NETLINK message
*
* NOTE: this returns the SKB/message
*/
struct sk_buff *nss_nl_new_msg(struct genl_family *family, uint8_t cmd)
{
struct sk_buff *skb;
struct nss_nlcmn *cm;
int len;
/*
* aligned length
*/
len = nla_total_size(family->hdrsize);
/*
* allocate NL message
*/
skb = genlmsg_new(len, GFP_ATOMIC);
if (!skb) {
nss_nl_error("%s: unable to allocate notifier SKB\n", family->name);
return NULL;
}
/*
* append the generic message header
*/
cm = genlmsg_put(skb, 0 /*pid*/, 0 /*seq*/, family, 0 /*flags*/, cmd);
if (!cm) {
nss_nl_error("%s: no space to put generic header\n", family->name);
nlmsg_free(skb);
return NULL;
}
/*
* Kernel PID=0
*/
cm->pid = 0;
return skb;
}
/*
* nss_nl_copy_msg()
* copy a existing NETLINK message into a new one
*
* NOTE: this returns the new SKB/message
*/
struct sk_buff *nss_nl_copy_msg(struct sk_buff *orig)
{
struct sk_buff *copy;
struct nss_nlcmn *cm;
cm = nss_nl_get_data(orig);
copy = skb_copy(orig, GFP_KERNEL);
if (!copy) {
nss_nl_error("%d:unable to copy incoming message of len(%d)\n", cm->pid, orig->len);
return NULL;
}
return copy;
}
/*
* nss_nl_get_data()
* Returns start of payload data
*/
void *nss_nl_get_data(struct sk_buff *skb)
{
return genlmsg_data(NLMSG_DATA(skb->data));
}
/*
* nss_nl_mcast_event()
* mcast the event to the user listening on the MCAST group ID
*
* Note: It will free the message buffer if there is no space left to end
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
int nss_nl_mcast_event(struct genl_family *family, struct sk_buff *skb)
{
struct nss_nlcmn *cm;
cm = genlmsg_data(NLMSG_DATA(skb->data));
/*
* End the message as no more updates are left to happen.
* After this, the message is assunmed to be read-only
*/
genlmsg_end(skb, cm);
return genlmsg_multicast(family, skb, cm->pid, 0, GFP_ATOMIC);
}
#else
int nss_nl_mcast_event(struct genl_multicast_group *grp, struct sk_buff *skb)
{
struct nss_nlcmn *cm;
cm = genlmsg_data(NLMSG_DATA(skb->data));
/*
* End the message as no more updates are left to happen.
* After this, the message is assunmed to be read-only
*/
genlmsg_end(skb, cm);
return genlmsg_multicast(skb, cm->pid, grp->id, GFP_ATOMIC);
}
#endif
/*
* nss_nl_ucast_resp()
* send the response to the user (PID)
*
* NOTE: this assumes the socket to be available for reception
*/
int nss_nl_ucast_resp(struct sk_buff *skb)
{
struct nss_nlcmn *cm;
struct net *net;
cm = genlmsg_data(NLMSG_DATA(skb->data));
net = (struct net *)cm->sock_data;
cm->sock_data = 0;
/*
* End the message as no more updates are left to happen
* After this message is assumed to be read-only
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
genlmsg_end(skb, cm);
#else
if (genlmsg_end(skb, cm) < 0) {
nss_nl_error("%d: unable to close generic ucast message\n", cm->pid);
nlmsg_free(skb);
return -ENOMEM;
}
#endif
return genlmsg_unicast(net, skb, cm->pid);
}
/*
* nss_nl_get_msg()
* verifies and returns the message pointer
*/
struct nss_nlcmn *nss_nl_get_msg(struct genl_family *family, struct genl_info *info, uint16_t cmd)
{
struct nss_nlcmn *cm;
uint32_t pid;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
pid = info->snd_pid;
#else
pid = info->snd_portid;
#endif
/*
* validate the common message header version & magic
*/
cm = info->userhdr;
if (nss_nlcmn_chk_ver(cm, family->version) == false) {
nss_nl_error("%d, %s: version mismatch (%d)\n", pid, family->name, cm->version);
return NULL;
}
/*
* check if the message len arrived matches with expected len
*/
if (nss_nlcmn_get_len(cm) != family->hdrsize) {
nss_nl_error("%d, %s: invalid command len (%d)\n", pid, family->name, nss_nlcmn_get_len(cm));
return NULL;
}
cm->pid = pid;
cm->sock_data = (nss_ptr_t)genl_info_net(info);
return cm;
}
/*
* nss_nl_init()
* init module
*/
static int __init nss_nl_init(void)
{
struct nss_nl_family *family = NULL;
int i = 0;
#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_nl_info_always("NSS Netlink manager loaded: %s\n", NSS_CLIENT_BUILD_ID);
/*
* initialize the handler families, the intention to init the
* families that are marked active
*/
family = &family_handlers[0];
for (i = 0; i < NSS_NL_FAMILY_HANDLER_SZ; i++, family++) {
/*
* Check if the family exists
*/
if (!family->valid || !family->entry) {
nss_nl_info_always("skipping family:%s\n", family->name);
nss_nl_info_always("valid = %d, entry = %d\n", family->valid, !!family->entry);
continue;
}
nss_nl_info_always("attaching family:%s\n", family->name);
family->entry();
}
return 0;
}
/*
* nss_nl_exit()
* deinit module
*/
static void __exit nss_nl_exit(void)
{
struct nss_nl_family *family = NULL;
int i = 0;
#ifdef CONFIG_OF
/*
* If the node is not compatible, don't do anything.
*/
if (!of_find_node_by_name(NULL, "nss-common")) {
return;
}
#endif
nss_nl_info_always("NSS Netlink manager unloaded\n");
/*
* initialize the handler families
*/
family = &family_handlers[0];
for (i = 0; i < NSS_NL_FAMILY_HANDLER_SZ; i++, family++) {
/*
* Check if the family exists
*/
if (!family->valid || !family->exit) {
nss_nl_info_always("skipping family:%s\n", family->name);
nss_nl_info_always("valid = %d, exit = %d\n", family->valid, !!family->exit);
continue;
}
nss_nl_info_always("detaching family:%s\n", family->name);
family->exit();
}
}
module_init(nss_nl_init);
module_exit(nss_nl_exit);
MODULE_DESCRIPTION("NSS NETLINK");
MODULE_LICENSE("Dual BSD/GPL");