| /* |
| ************************************************************************** |
| * 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); |