blob: 17b8f03fc994a469aeee6d3f19408b7251f8d89e [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_ovpnmgr.c
*/
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/if_tun.h>
#include <nss_api_if.h>
#include <nss_qvpn.h>
#include "nss_ovpnmgr.h"
#include "nss_ovpnmgr_crypto.h"
#include "nss_ovpnmgr_app.h"
#include "nss_ovpnmgr_tun.h"
#include "nss_ovpnmgr_debugfs.h"
#include "nss_ovpnmgr_priv.h"
#include "nss_ovpnmgr_route.h"
#include "nss_ovpn_sk_priv.h"
/*
* OVPN manager context structure
*/
struct nss_ovpnmgr_context ovpnmgr_ctx;
/*
* nss_ovpnmgr_netdevice_event()
* OpenVPN manager netdevice notifications.
*/
static int nss_ovpnmgr_netdevice_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct netdev_notifier_info *info = ptr;
struct net_device *app_dev = netdev_notifier_info_to_dev(info);
struct nss_ovpnmgr_tun *tun, *n;
struct nss_ovpnmgr_app *app;
struct net_device *nss_dev;
/*
* Do not process any event other than UP and DOWN
*/
if ((event != NETDEV_UP) && (event != NETDEV_DOWN)) {
return NOTIFY_DONE;
}
/*
* We should process notification only for TUN/TAP device
*/
if (!(app_dev->priv_flags_ext & IFF_EXT_TUN_TAP)) {
return NOTIFY_DONE;
}
/*
* Find the application for the net_device triggering notification
*/
read_lock_bh(&ovpnmgr_ctx.lock);
app = nss_ovpnmgr_app_find(app_dev);
if (unlikely(!app)) {
read_unlock_bh(&ovpnmgr_ctx.lock);
nss_ovpnmgr_warn("%px: Application is not registered: app_dev = %s\n", app_dev, app_dev->name);
return NOTIFY_DONE;
}
/*
* Listen to application netdevice UP and DOWN event. If UP event is triggered
* then bring all NSS device attached to application device UP. If down event is triggered
* then bring down all NSS device attached to application device DOWN. This will make ECM
* flush(DOWN)/add(UP) flow rules in the system.
*
* Iterate through all NSS device registered and change their state to UP
*/
list_for_each_entry_safe(tun, n, &app->tun_list, list) {
nss_dev = __dev_get_by_index(&init_net, tun->tunnel_id);
if (unlikely(!nss_dev)) {
nss_ovpnmgr_warn("%px: Couldn't find tunnel: tunnel_id = %u\n\n", tun, tun->tunnel_id);
continue;
}
/*
* dev_open and dev_close can sleep; hence calling it in non atomic context.
*/
read_unlock_bh(&ovpnmgr_ctx.lock);
if (event == NETDEV_UP) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
dev_open(nss_dev);
#else
dev_open(nss_dev, NULL);
#endif
} else {
dev_close(nss_dev);
}
read_lock_bh(&ovpnmgr_ctx.lock);
}
read_unlock_bh(&ovpnmgr_ctx.lock);
/*
* Notify done for all the events we don't care
*/
return NOTIFY_DONE;
}
/*
* nss_ovpnmgr_netdevice_nb
* OpenVPN Netdevice notification callback
*/
static struct notifier_block nss_ovpnmgr_netdevice_nb __read_mostly = {
.notifier_call = nss_ovpnmgr_netdevice_event,
};
/*
* nss_ovpnmgr_init()
* Initialize NSS OVPN Manager
*/
int __init nss_ovpnmgr_init(void)
{
if (!nss_cmn_get_nss_enabled()) {
nss_ovpnmgr_warn("OVPN Manager is not compatible with this platform\n");
return -1;
}
rwlock_init(&ovpnmgr_ctx.lock);
INIT_LIST_HEAD(&ovpnmgr_ctx.app_list);
if (nss_ovpn_sk_init()) {
nss_ovpnmgr_warn("Failed to initialize socket interface\n");
return -1;
}
if (nss_ovpnmgr_debugfs_init()) {
nss_ovpnmgr_warn("Debugfs Initialization failed\n");
nss_ovpn_sk_cleanup();
return -1;
}
tun_register_offload_stats_callback(nss_ovpnmgr_tun_get_stats);
register_netdevice_notifier(&nss_ovpnmgr_netdevice_nb);
nss_ovpnmgr_info("OVPN Init successful\n");
return 0;
}
/*
* nss_ovpnmgr_exit()
* Cleanup NSS OVPN Manager and exit
*/
void __exit nss_ovpnmgr_exit(void)
{
unregister_netdevice_notifier(&nss_ovpnmgr_netdevice_nb);
nss_ovpnmgr_debugfs_cleanup();
nss_ovpn_sk_cleanup();
tun_unregister_offload_stats_callback();
nss_ovpnmgr_info("OVPN Manager Removed\n");
}
module_init(nss_ovpnmgr_init);
module_exit(nss_ovpnmgr_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("NSS OVPN Manager");