blob: 9a2556a03b1db7e4c5beda3d58b21fcb83891c64 [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_app.c
*/
#include <linux/etherdevice.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/ip6_checksum.h>
#include <linux/ipv6.h>
#include <linux/crypto.h>
#include <nss_api_if.h>
#include <nss_qvpn.h>
#include "nss_ovpnmgr.h"
#include "nss_ovpnmgr_crypto.h"
#include "nss_ovpnmgr_tun.h"
#include "nss_ovpnmgr_app.h"
#include "nss_ovpnmgr_debugfs.h"
#include "nss_ovpnmgr_priv.h"
#include "nss_ovpnmgr_route.h"
#include "nss_ovpn_sk_priv.h"
/*
* nss_ovpnmgr_app_find_dev()
* Returns net_device on which route tuple is routed
*/
static struct net_device *nss_ovpnmgr_app_find_dev(struct nss_ovpnmgr_route_tuple *rt)
{
struct net_device *dev;
struct rt6_info *rt6;
struct rtable *rt4;
if (rt->ip_version == IPVERSION) {
rt4 = ip_route_output(&init_net, rt->ip_addr[0], 0, 0, 0);
if (IS_ERR(rt4)) {
return NULL;
}
dev = rt4->dst.dev;
ip_rt_put(rt4);
return dev;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
rt6 = rt6_lookup(&init_net, (const struct in6_addr *)rt->ip_addr, NULL, 0, 0);
#else
rt6 = rt6_lookup(&init_net, (const struct in6_addr *)rt->ip_addr, NULL, 0, 0, 0);
#endif
if (!rt6) {
return NULL;
}
dev = rt6->dst.dev;
ip6_rt_put(rt6);
return dev;
}
/*
* nss_ovpnmgr_app_find_tun()
* Returns NSS ifnum.
*/
struct net_device *nss_ovpnmgr_app_find_tun(struct net_device *app_dev, struct nss_ovpnmgr_route_tuple *rt, uint32_t *ifnum)
{
struct nss_ovpnmgr_app *app;
struct nss_ovpnmgr_tun *tun;
struct net_device *dev = NULL;
uint32_t tun_ifnum = NSS_MAX_NET_INTERFACES;
ssize_t addr_size;
addr_size = (rt->ip_version == IPVERSION) ? sizeof(rt->ip_addr[0]) : sizeof(rt->ip_addr);
read_lock_bh(&ovpnmgr_ctx.lock);
app = nss_ovpnmgr_app_find(app_dev);
if (!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 NULL;
}
/*
* Check if application device is in UP state. If it is UP then only
* allow ECM to push flow rules. This logic will make sure that openvpn
* flows are not accelerated when tunnel device is down
*/
if (!(app->dev->flags & IFF_UP)) {
read_unlock_bh(&ovpnmgr_ctx.lock);
nss_ovpnmgr_warn("%px: Application device not up, app_dev=%s\n", app->dev, app->dev->name);
return NULL;
}
if (app->mode == NSS_OVPNMGR_APP_MODE_CLIENT) {
/*
* There are no routes in client mode.
*/
list_for_each_entry(tun, &app->tun_list, list) {
dev = nss_ovpnmgr_app_find_dev(rt);
/*
* If routed interface is tun/tap return inner interface.
*/
if (dev == app->dev) {
nss_ovpnmgr_info("%px: Found dev = %s\n", app, dev->name);
tun_ifnum = tun->inner.ifnum;
dev = tun->dev;
goto done;
}
/*
* Check if rt->ip_addr matches tunnel IP.
*/
if (!memcmp(rt->ip_addr, &tun->tun_hdr.src_ip, addr_size)) {
nss_ovpnmgr_info("%px: Encapsulated packet.\n", app);
tun_ifnum = tun->outer.ifnum;
dev = tun->dev;
goto done;
}
}
read_unlock_bh(&ovpnmgr_ctx.lock);
return NULL;
}
list_for_each_entry(tun, &app->tun_list, list) {
struct nss_ovpnmgr_route *route;
route = nss_ovpnmgr_route_find(&tun->route_list, rt);
if (route) {
tun_ifnum = tun->inner.ifnum;
dev = tun->dev;
goto done;
}
/*
* Check if rt->ip_addr matches tunnel IP.
*/
if (!memcmp(rt->ip_addr, &tun->tun_hdr.src_ip, addr_size)) {
tun_ifnum = tun->outer.ifnum;
dev = tun->dev;
goto done;
}
}
done:
read_unlock_bh(&ovpnmgr_ctx.lock);
*ifnum = tun_ifnum;
return dev;
}
EXPORT_SYMBOL(nss_ovpnmgr_app_find_tun);
/*
* nss_ovpnmgr_app_forward()
* Send packet to Host
*/
void nss_ovpnmgr_app_forward(struct nss_ovpnmgr_app *app, struct nss_ovpnmgr_tun *tun, struct sk_buff *skb)
{
struct nss_ovpnmgr_metadata *metadata = (struct nss_ovpnmgr_metadata *)skb->cb;
BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct nss_ovpnmgr_metadata));
metadata->flags = NSS_OVPNMGR_METADATA_FLAG_PKT_DECAP | NSS_OVPNMGR_METADATA_FLAG_PKT_TYPE_CTRL;
metadata->tunnel_id = tun->tunnel_id;
if (nss_ovpn_sk_send(skb, app->app_data)) {
nss_ovpnmgr_warn("%px: failed to send packet to OVPN application.\n", tun);
tun->inner.stats.host_pkt_drop++;
dev_kfree_skb_any(skb);
}
}
/*
* nss_ovpnmgr_app_find()
* Find OVPN application instance.
*/
struct nss_ovpnmgr_app *nss_ovpnmgr_app_find(struct net_device *dev)
{
struct nss_ovpnmgr_app *app;
list_for_each_entry(app, &ovpnmgr_ctx.app_list, list) {
if (app->dev == dev) {
return app;
}
}
return NULL;
}
/*
* nss_ovpnmgr_app_del()
* Delete OpenVPN application.
*/
int nss_ovpnmgr_app_del(struct net_device *app_dev)
{
struct nss_ovpnmgr_app *app;
struct nss_ovpnmgr_tun *tun, *n;
write_lock_bh(&ovpnmgr_ctx.lock);
app = nss_ovpnmgr_app_find(app_dev);
if (!app) {
write_unlock_bh(&ovpnmgr_ctx.lock);
nss_ovpnmgr_warn("%px: Application is not registered: app_dev = %s\n", app_dev, app_dev->name);
return -ENODEV;
}
list_del(&app->list);
write_unlock_bh(&ovpnmgr_ctx.lock);
nss_ovpnmgr_info("%px: Uninitializing application instance\n", app);
/*
* Application is removed from list.
* Check if there are any tunnels.
* Delete all the tunnels from NSS FW and free.
*/
list_for_each_entry_safe(tun, n, &app->tun_list, list) {
nss_ovpnmgr_tun_del(tun->tunnel_id);
}
dev_put(app_dev);
nss_ovpnmgr_debugfs_remove(app->dentry);
kfree(app);
return 0;
}
EXPORT_SYMBOL(nss_ovpnmgr_app_del);
/*
* nss_ovpnmgr_app_add()
* Add OpenVPN application.
*/
int nss_ovpnmgr_app_add(struct net_device *app_dev, enum nss_ovpnmgr_app_mode mode, void *app_data)
{
struct nss_ovpnmgr_app *app;
BUG_ON(!app_dev);
nss_ovpnmgr_info("Adding new APP :tun_dev=%s:", app_dev->name);
app = kzalloc(sizeof(*app), GFP_KERNEL);
if (!app) {
nss_ovpnmgr_warn("Failed to allocate memory for app\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&app->tun_list);
INIT_LIST_HEAD(&app->list);
/*
* Hold tun/tap netdev and release when deleting application.
*/
dev_hold(app_dev);
/*
* Initialize application instance.
*/
app->mode = mode;
app->dev = app_dev;
app->app_data = app_data;
nss_ovpnmgr_debugfs_create(app);
write_lock_bh(&ovpnmgr_ctx.lock);
/*
* Check if the application is already registered.
*/
if (nss_ovpnmgr_app_find(app_dev)) {
write_unlock_bh(&ovpnmgr_ctx.lock);
nss_ovpnmgr_info("%px: Application is already registered: app_dev=%s\n", app_dev, app_dev->name);
dev_put(app_dev);
nss_ovpnmgr_debugfs_remove(app->dentry);
kfree(app);
return -EEXIST;
}
list_add(&app->list, &ovpnmgr_ctx.app_list);
write_unlock_bh(&ovpnmgr_ctx.lock);
nss_ovpnmgr_info("%px: Application is registered successfully\n", app);
return 0;
}
EXPORT_SYMBOL(nss_ovpnmgr_app_add);