blob: 3db9fec6dd9a99dd48b4029561eaa25ccdfd6a1b [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_ovpn_sk.c
* Socket implementation for OVPN.
*/
#include <linux/version.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <net/sock.h>
#include <net/udp_tunnel.h>
#include <linux/crypto.h>
#include <linux/inetdevice.h>
#include <nss_api_if.h>
#include <nss_qvpn.h>
#include "nss_ovpn_sk.h"
#include "nss_ovpnmgr.h"
#include "nss_ovpn_sk_priv.h"
static struct proto nss_ovpn_sk_proto __read_mostly = {
.name = "OVPN",
.owner = THIS_MODULE,
.obj_size = sizeof(struct nss_ovpn_sk_pinfo),
};
/*
* nss_ovpn_sk_route_info_copy()
* Copy route info passed from application.
*/
static inline void nss_ovpn_sk_route_info_copy(struct nss_ovpnmgr_route_tuple *route, struct nss_ovpn_sk_route_info *route_info)
{
route->ip_version = route_info->ip_version;
memcpy(route->ip_addr, route_info->ip_network, sizeof(route_info->ip_network));
}
/*
* nss_ovpn_sk_route_state_get()
* Get route status from OVPN manager.
*/
static int nss_ovpn_sk_route_state_get(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_route_info route_info;
struct nss_ovpnmgr_route_tuple route;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&route_info, (void *)argp, sizeof(route_info))) {
nss_ovpn_sk_warn("%px: Failed to copy route information\n", sock);
return -EFAULT;
}
nss_ovpn_sk_route_info_copy(&route, &route_info);
return nss_ovpnmgr_route_is_active(route_info.tunnel_id, &route);
}
/*
* nss_ovpn_sk_stats_get()
* Get tunnel statistics.
*/
static int nss_ovpn_sk_stats_get(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_tun_stats tun_stats;
struct nss_ovpnmgr_tun_stats stats;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
int ret;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&tun_stats, (void *)argp, sizeof(tun_stats))) {
nss_ovpn_sk_warn("%px: Failed to copy tunnel statistics\n", sock);
return -EFAULT;
}
ret = nss_ovpnmgr_tun_stats_get(tun_stats.tunnel_id, &stats);
if (ret) {
nss_ovpn_sk_warn("%px: Failed to get statistics for tunnel_id = %u\n", sock, tun_stats.tunnel_id);
return ret;
}
tun_stats.tun_read_bytes = stats.tun_read_bytes;
tun_stats.tun_write_bytes = stats.tun_write_bytes;
tun_stats.link_read_bytes = stats.link_read_bytes;
tun_stats.link_read_bytes_auth = stats.link_read_bytes_auth;
tun_stats.link_write_bytes = stats.link_write_bytes;
if (copy_to_user((void *)argp, &tun_stats, sizeof(tun_stats))) {
nss_ovpn_sk_warn("%px: Failed to copy statistics\n", sock);
return -EFAULT;
}
return 0;
}
/*
* nss_ovpn_sk_crypto_key_del()
* Delete crypto key.
*/
static int nss_ovpn_sk_crypto_key_del(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_crypto_session crypto_session;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&crypto_session, (void *)argp, sizeof(crypto_session))) {
nss_ovpn_sk_warn("%px: Failed to copy crypto session\n", sock);
return -EFAULT;
}
return nss_ovpnmgr_crypto_key_del(crypto_session.tunnel_id);
}
/*
* nss_ovpn_sk_crypto_key_add()
* Add crypto key.
*/
static int nss_ovpn_sk_crypto_key_add(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_crypto_session crypto_info;
struct nss_ovpnmgr_crypto_config crypto_cfg;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&crypto_info, (void *)argp, sizeof(crypto_info))) {
nss_ovpn_sk_warn("%px: Failed to copy crypto key\n", sock);
return -EFAULT;
}
crypto_cfg.algo = crypto_info.config.algo;
crypto_cfg.encrypt.cipher_keylen = crypto_info.config.cipher_key_size;
crypto_cfg.encrypt.hmac_keylen = crypto_info.config.hmac_key_size;
crypto_cfg.decrypt.cipher_keylen = crypto_info.config.cipher_key_size;
crypto_cfg.decrypt.hmac_keylen = crypto_info.config.hmac_key_size;
memcpy(&crypto_cfg.encrypt, &crypto_info.encrypt, sizeof(crypto_info.encrypt));
memcpy(&crypto_cfg.decrypt, &crypto_info.decrypt, sizeof(crypto_info.decrypt));
nss_ovpn_sk_info("%px: Crypto Add: tunnel_id = %u, key_id = %d\n", sock, crypto_info.tunnel_id, crypto_info.key_id);
return nss_ovpnmgr_crypto_key_add(crypto_info.tunnel_id, crypto_info.key_id, &crypto_cfg);
}
/*
* nss_ovpn_sk_route_del()
* Delete route from tunnel.
*/
static int nss_ovpn_sk_route_del(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_route_info route_info;
struct nss_ovpnmgr_route_tuple route;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&route_info, (void *)argp, sizeof(route_info))) {
nss_ovpn_sk_warn("%px: Failed to copy route information\n", sock);
return -EFAULT;
}
nss_ovpn_sk_route_info_copy(&route, &route_info);
return nss_ovpnmgr_route_del(route_info.tunnel_id, &route);
}
/*
* nss_ovpn_sk_route_add()
* Add route for the tunnel.
*/
static int nss_ovpn_sk_route_add(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_route_info route_info;
struct nss_ovpnmgr_route_tuple route;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&route_info, (void *)argp, sizeof(route_info))) {
nss_ovpn_sk_warn("%px: Failed to copy route information\n", sock);
return -EFAULT;
}
nss_ovpn_sk_route_info_copy(&route, &route_info);
return nss_ovpnmgr_route_add(route_info.tunnel_id, &route);
}
/*
* nss_ovpn_sk_update_ipv4_tuple()
* Update ipv4 tuple with hop_limit and source IP if needed.
*/
static int nss_ovpn_sk_update_ipv4_tuple(struct nss_ovpn_sk_pinfo *pinfo, struct nss_ovpn_sk_tunnel *tun_data)
{
struct rtable *rt;
rt = ip_route_output(dev_net(pinfo->dev), tun_data->tun_hdr.dst_ip[0], 0, 0, 0);
if (unlikely(IS_ERR(rt))) {
nss_ovpn_sk_warn("%px: Failed to find IPv4 route.\n", pinfo);
return -EINVAL;
}
if (!tun_data->tun_hdr.src_ip[0]) {
__be32 addr;
nss_ovpn_sk_info("%px: Source IP address is 0, find it\n", pinfo);
/*
* Get Source IP address for given dst_ip.
*/
addr = inet_select_addr(rt->dst.dev, tun_data->tun_hdr.dst_ip[0], RT_SCOPE_LINK);
if (!addr) {
nss_ovpn_sk_warn("%px: Failed to find source IPv4 address.\n", pinfo);
return -EINVAL;
}
tun_data->tun_hdr.src_ip[0] = addr;
}
/*
* Get TTL.
*/
tun_data->tun_hdr.hop_limit = ip4_dst_hoplimit(&rt->dst);
return 0;
}
/*
* nss_ovpn_sk_update_ipv6_tuple()
* Update ipv6 tuple with hop_limit and source IP if needed.
*/
static int nss_ovpn_sk_update_ipv6_tuple(struct nss_ovpn_sk_pinfo *pinfo, struct nss_ovpn_sk_tunnel *tun_data)
{
struct dst_entry *dst;
struct rt6_info *rt6;
int addr_type;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
rt6 = rt6_lookup(dev_net(pinfo->dev), (struct in6_addr *)tun_data->tun_hdr.dst_ip, NULL, 0, 0);
#else
rt6 = rt6_lookup(dev_net(pinfo->dev), (struct in6_addr *)tun_data->tun_hdr.dst_ip, NULL, 0, 0, 0);
#endif
if (!rt6) {
nss_ovpn_sk_warn("%px: Failed to find IPv6 route.\n", pinfo);
return -EINVAL;
}
dst = &rt6->dst;
/*
* Find Source IP address if application did not provide.
*/
addr_type = ipv6_addr_type((struct in6_addr *)&tun_data->tun_hdr.src_ip);
if (addr_type == IPV6_ADDR_ANY) {
int ret;
ret = ipv6_dev_get_saddr(dev_net(pinfo->dev), ip6_dst_idev(dst)->dev,
(struct in6_addr *)tun_data->tun_hdr.dst_ip, 0,
(struct in6_addr *)tun_data->tun_hdr.src_ip);
if (ret) {
nss_ovpn_sk_warn("%px: Failed to find source IPv6 address: %d\n", pinfo, ret);
return -EINVAL;
}
}
tun_data->tun_hdr.hop_limit = ip6_dst_hoplimit(dst);
return 0;
}
/*
* nss_ovpn_sk_tun_del()
* Delete tunnel.
*/
static int nss_ovpn_sk_tun_del(struct socket *sock, unsigned long argp)
{
uint32_t tunnel_id;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&tunnel_id, (void *)argp, sizeof(tunnel_id))) {
nss_ovpn_sk_warn("%px: Failed to copy tunnel id\n", sock);
return -EFAULT;
}
return nss_ovpnmgr_tun_del(tunnel_id);
}
/*
* nss_ovpn_sk_tun_add()
* Add tunnel.
*/
static int nss_ovpn_sk_tun_add(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
struct nss_ovpnmgr_crypto_config crypto_cfg;
struct nss_ovpnmgr_tun_config tun_cfg;
struct nss_ovpnmgr_tun_tuple tun_hdr;
struct nss_ovpn_sk_tunnel tun_data;
struct net_device *tun_dev;
uint32_t tunnel_id;
int err;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
if (copy_from_user(&tun_data, (void *)argp, sizeof(tun_data))) {
nss_ovpn_sk_warn("%px: Failed to copy application data\n", sock);
return -EFAULT;
}
tun_cfg.flags = tun_data.ovpn.flags;
tun_cfg.peer_id = tun_data.ovpn.peer_id;
/*
* Update TTL and if necessary source IP address.
*/
if (tun_data.ovpn.flags & NSS_OVPN_SK_OVPN_HDR_FLAG_IPv6) {
err = nss_ovpn_sk_update_ipv6_tuple(pinfo, &tun_data);
} else {
err = nss_ovpn_sk_update_ipv4_tuple(pinfo, &tun_data);
}
if (err)
return err;
memcpy(&tun_hdr.src_ip[0], &tun_data.tun_hdr.src_ip[0], sizeof(tun_data.tun_hdr.src_ip));
memcpy(&tun_hdr.dst_ip[0], &tun_data.tun_hdr.dst_ip[0], sizeof(tun_data.tun_hdr.dst_ip));
tun_hdr.src_port = tun_data.tun_hdr.src_port;
tun_hdr.dst_port = tun_data.tun_hdr.dst_port;
tun_hdr.hop_limit = tun_data.tun_hdr.hop_limit;
crypto_cfg.algo = tun_data.crypto.config.algo;
crypto_cfg.encrypt.cipher_keylen = tun_data.crypto.config.cipher_key_size;
crypto_cfg.encrypt.hmac_keylen = tun_data.crypto.config.hmac_key_size;
crypto_cfg.decrypt.cipher_keylen = tun_data.crypto.config.cipher_key_size;
crypto_cfg.decrypt.hmac_keylen = tun_data.crypto.config.hmac_key_size;
memcpy(&crypto_cfg.encrypt.cipher_key, &tun_data.crypto.encrypt.cipher_key, tun_data.crypto.config.cipher_key_size);
memcpy(&crypto_cfg.encrypt.hmac_key, &tun_data.crypto.encrypt.hmac_key, tun_data.crypto.config.hmac_key_size);
memcpy(&crypto_cfg.decrypt.cipher_key, &tun_data.crypto.decrypt.cipher_key, tun_data.crypto.config.cipher_key_size);
memcpy(&crypto_cfg.decrypt.hmac_key, &tun_data.crypto.decrypt.hmac_key, tun_data.crypto.config.hmac_key_size);
/*
* Add new tunnel.
*/
tunnel_id = nss_ovpnmgr_tun_add(pinfo->dev, &tun_hdr, &tun_cfg, &crypto_cfg);
if (!tunnel_id) {
nss_ovpn_sk_warn("%px: Failed to add tunnel for application:%u\n", sock, pinfo->pid);
return -EINVAL;
}
if (crypto_cfg.algo != NSS_OVPNMGR_ALGO_NULL_CIPHER_NULL_AUTH) {
/*
* Add crypto key.
*/
err = nss_ovpnmgr_crypto_key_add(tunnel_id, tun_data.crypto.key_id, &crypto_cfg);
if (err) {
nss_ovpn_sk_warn("%px: crypto key add failed for tunnel_id = %u\n", sock, tunnel_id);
nss_ovpnmgr_tun_del(tunnel_id);
return -EFAULT;
}
}
/*
* Bring up tunnel netdev.
*/
tun_dev = dev_get_by_index(&init_net, tunnel_id);
if (unlikely(!tun_dev)) {
nss_ovpn_sk_warn("%px: tun_dev is not found: tunnel_id = %u\n", sock, tunnel_id);
nss_ovpnmgr_tun_del(tunnel_id);
return -EFAULT;
}
dev_put(tun_dev);
/*
* Bring up tunnel device.
*/
rtnl_lock();
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
err = dev_open(tun_dev);
#else
err = dev_open(tun_dev, NULL);
#endif
rtnl_unlock();
if (err) {
nss_ovpn_sk_warn("%px: tun_dev is not found: tunnel_id = %u\n", sock, tunnel_id);
nss_ovpnmgr_tun_del(tunnel_id);
return -EFAULT;
}
/*
* Copy tunnel_id to application memory.
*/
tun_data.ovpn.tunnel_id = tunnel_id;
if (copy_to_user((void *)argp, &tun_data, sizeof(tun_data))) {
nss_ovpn_sk_warn("%px: Failed to copy tunnel information to application\n", sock);
nss_ovpnmgr_tun_del(tunnel_id);
return -EFAULT;
}
return 0;
}
/*
* nss_ovpn_sk_app_dereg()
* Deregister application.
*/
static int nss_ovpn_sk_app_dereg(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
int ret;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
nss_ovpn_sk_info("%px: Deregistering app with pid=%u\n", sock, pinfo->pid);
ret = nss_ovpnmgr_app_del(pinfo->dev);
if (!ret)
pinfo->pid = 0;
/*
* pinfo->dev was held during application registration.
* Since application is closing the socket. release the hold.
*/
if (pinfo->dev) {
dev_put(pinfo->dev);
pinfo->dev = NULL;
}
return ret;
}
/*
* nss_ovpn_sk_app_reg()
* Register application.
*/
static int nss_ovpn_sk_app_reg(struct socket *sock, unsigned long argp)
{
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
struct nss_ovpn_sk_app_inst app;
int ret;
if (copy_from_user(&app, (void *)argp, sizeof(app))) {
nss_ovpn_sk_warn("%px: Failed to copy application data\n", sock);
return -EFAULT;
}
nss_ovpn_sk_info("%px: pid = %u, tun_fd = %d, udp_fd = %d\n", sock, app.pid, app.tun_fd, app.udp_fd);
pinfo->dev = dev_get_by_name(&init_net, app.tun_dev);
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Couldn't find tun/tap dev=%s\n", sock, app.tun_dev);
return -EFAULT;
}
ret = nss_ovpnmgr_app_add(pinfo->dev, app.app_mode, (void *)sock);
if (ret) {
nss_ovpn_sk_warn("%px: Failed to register application, pid=%u\n", sock, app.pid);
dev_put(pinfo->dev);
return ret;
}
/*
* Initialize socket private data.
* this will be used in rest of the socket functionality
*/
pinfo->pid = app.pid;
pinfo->tun_fd = app.tun_fd;
pinfo->udp_fd = app.udp_fd;
/*
* Do not release pinfo->dev here. It will be used
* by socket layer as long as the application is registered.
*/
return 0;
}
/*
* nss_ovpn_sk_release()
* Release socket.
*/
int nss_ovpn_sk_release(struct socket *sock)
{
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
nss_ovpn_sk_info("%px: Releasing socket\n", sock);
if (pinfo->pid && nss_ovpnmgr_app_del(pinfo->dev)) {
nss_ovpn_sk_info("%px: Failed to delete App\n", sock);
}
/*
* pinfo->dev was held during application registration.
* Since application is closing the socket. release the hold.
*/
if (pinfo->dev) {
dev_put(pinfo->dev);
pinfo->dev = NULL;
}
if (sock->sk)
sock_put(sock->sk);
return 0;
}
/*
* nss_ovpn_sk_sendmsg()
* Receive packet from application.
*/
static int nss_ovpn_sk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
{
struct nss_ovpn_sk_pkt_info *pkt_info_data;
struct nss_ovpnmgr_metadata mdata;
struct nss_ovpn_sk_pinfo *pinfo = (struct nss_ovpn_sk_pinfo *)sock->sk;
struct sk_buff *skb = NULL;
struct cmsghdr *cmsg;
size_t total_len = iov_iter_count(&msg->msg_iter);
uint8_t *data;
int outer_hdr_offset = 0;
if (!pinfo->dev) {
nss_ovpn_sk_warn("%px: Application is not registered\n", sock);
return -EINVAL;
}
/*
* Packet received from application has two parts:
* 1. Control message provides details of the packet.
* 2. Packet content.
* Extract control message into pkt_info.
*/
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg) {
nss_ovpn_sk_warn("%px: Control message is invalid\n", sock);
return -EINVAL;
}
if (!CMSG_OK(msg, cmsg)) {
nss_ovpn_sk_warn("%px: Incorrect message format\n", sock);
return -EINVAL;
}
if (cmsg->cmsg_len < CMSG_LEN(sizeof(*pkt_info_data))) {
nss_ovpn_sk_warn("%px: Incorrect message length\n", sock);
return -EINVAL;
}
pkt_info_data = (struct nss_ovpn_sk_pkt_info *)CMSG_DATA(cmsg);
if (!pkt_info_data) {
nss_ovpn_sk_warn("%px: Cannot send this packet, there is no msg_control\n", sock);
return -EFAULT;
}
if (pkt_info_data->flags & NSS_OVPN_SK_PKT_INFO_FLAG_DIR_DECAP) {
if (pkt_info_data->flags & NSS_OVPN_SK_PKT_INFO_FLAG_PKT_TYPE_IPV6) {
outer_hdr_offset = sizeof(struct ipv6hdr) + sizeof(struct udphdr);
nss_ovpn_sk_info("%px: Decrypt: IPv6 + UDP\n", sock);
} else {
outer_hdr_offset = sizeof(struct iphdr) + sizeof(struct udphdr);
nss_ovpn_sk_info("%px: Decrypt: IPv4 + UDP\n", sock);
}
} else {
/*
* Encapsulation requires headroom + tailroom.
*/
outer_hdr_offset = NSS_OVPNMGR_TUN_HEADROOM + NSS_OVPNMGR_TUN_TAILROOM;
}
skb = dev_alloc_skb(total_len + outer_hdr_offset);
if (unlikely(!skb)) {
nss_ovpn_sk_warn("%px: Couldn't allocation skb\n", sock);
return -ENOBUFS;
}
if (pkt_info_data->flags & NSS_OVPN_SK_PKT_INFO_FLAG_DIR_DECAP) {
data = skb_put(skb, total_len + outer_hdr_offset);
data += outer_hdr_offset;
} else {
skb_reserve(skb, NSS_OVPNMGR_TUN_HEADROOM);
data = skb_put(skb, total_len);
}
if (copy_from_iter(data, total_len, &msg->msg_iter) != total_len) {
nss_ovpn_sk_info("%px: skb copy conversion failed\n", sock);
dev_kfree_skb_any(skb);
return -EFAULT;
}
mdata.tunnel_id = pkt_info_data->tunnel_id;
mdata.flags = pkt_info_data->flags;
if (nss_ovpnmgr_tun_tx(mdata.tunnel_id, &mdata, skb)) {
nss_ovpn_sk_info("%px: Packet offload failed.\n", sock);
dev_kfree_skb_any(skb);
return -EFAULT;
}
return 0;
}
/*
* nss_ovpn_sk_recvmsg()
* Send packet to application.
*/
static int nss_ovpn_sk_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags)
{
struct nss_ovpn_sk_pkt_info *pkt_info_data, pkt_data;
struct nss_ovpnmgr_metadata pkt_info;
int copied, ret;
struct sk_buff *skb;
struct cmsghdr *cmsg, k_cmsg;
struct sock *sk = sock->sk;
if (flags & ~(MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC | MSG_CMSG_COMPAT)) {
nss_ovpn_sk_info("%px: Cannot send this packet to app, there is no msg_control\n", sock);
return -EINVAL;
}
/*
* Packet received from application has two parts:
* 1. Control message provides details of the packet.
* 2. Packet content.
* Extract control message into pkt_info.
*/
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg) {
nss_ovpn_sk_warn("%px: Control message is invalid\n", sock);
return -EINVAL;
}
if (copy_from_user(&k_cmsg, cmsg, sizeof(struct cmsghdr))) {
nss_ovpn_sk_warn("Copy from user failed\n");
return -EINVAL;
}
cmsg = &k_cmsg;
if (!CMSG_OK(msg, cmsg)) {
nss_ovpn_sk_warn("%px: Incorrect message format\n", sock);
return -EINVAL;
}
if (cmsg->cmsg_len < CMSG_LEN(sizeof(*pkt_info_data))) {
nss_ovpn_sk_warn("%px: Incorrect message length\n", sock);
return -EINVAL;
}
pkt_info_data = (struct nss_ovpn_sk_pkt_info *)CMSG_DATA(cmsg);
if (!pkt_info_data) {
nss_ovpn_sk_warn("%px: Cannot send this packet to app, there is no msg_control.\n", sock);
return -EINVAL;
}
skb = skb_recv_datagram(sk, flags, MSG_DONTWAIT, &ret);
if (!skb) {
nss_ovpn_sk_warn("%px: There are no packets in the queue.\n", sock);
return -ENOBUFS;
}
/*
* Control informatoin of packet is copied in skb->cb by OVPN manager.
* Send control information to application.
*/
memcpy(&pkt_info, skb->cb, sizeof(pkt_info));
pkt_data.tunnel_id = pkt_info.tunnel_id;
pkt_data.flags = pkt_info.flags;
if (copy_to_user(pkt_info_data, &pkt_data, sizeof(pkt_data))) {
nss_ovpn_sk_warn("Copy from user failed\n");
return -EINVAL;
}
put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(*pkt_info_data), pkt_info_data);
copied = skb->len;
if (copied > size) {
msg->msg_flags |= MSG_TRUNC;
copied = size;
}
skb_reset_transport_header(skb);
ret = skb_copy_datagram_msg(skb, 0, msg, copied);
if (ret) {
nss_ovpn_sk_warn("%px: Packet copy to user failed\n", sock);
skb_free_datagram(sk, skb);
return ret;
}
ret = (flags & MSG_TRUNC) ? skb->len : copied;
nss_ovpn_sk_info("%px: Message copied with packet length=%d\n", sock, ret);
return ret;
}
/*
* nss_ovpn_sk_ioctl()
* Ioctl interface.
*/
static int nss_ovpn_sk_ioctl(struct socket *sock, unsigned int cmd, unsigned long argp)
{
switch (cmd) {
case NSS_OVPN_SK_SIOC_APP_REG:
return nss_ovpn_sk_app_reg(sock, argp);
case NSS_OVPN_SK_SIOC_APP_DEREG:
return nss_ovpn_sk_app_dereg(sock, argp);
case NSS_OVPN_SK_SIOC_TUN_ADD:
return nss_ovpn_sk_tun_add(sock, argp);
case NSS_OVPN_SK_SIOC_TUN_DEL:
return nss_ovpn_sk_tun_del(sock, argp);
case NSS_OVPN_SK_SIOC_ROUTE_ADD:
return nss_ovpn_sk_route_add(sock, argp);
case NSS_OVPN_SK_SIOC_ROUTE_DEL:
return nss_ovpn_sk_route_del(sock, argp);
case NSS_OVPN_SK_SIOC_ROUTE_STATE_GET:
return nss_ovpn_sk_route_state_get(sock, argp);
case NSS_OVPN_SK_SIOC_CRYPTO_KEY_ADD:
return nss_ovpn_sk_crypto_key_add(sock, argp);
case NSS_OVPN_SK_SIOC_CRYPTO_KEY_DEL:
return nss_ovpn_sk_crypto_key_del(sock, argp);
case NSS_OVPN_SK_SIOC_STATS_GET:
return nss_ovpn_sk_stats_get(sock, argp);
}
nss_ovpn_sk_warn("%px: Invalid ioctl command: %u\n", sock, cmd);
return -ENOIOCTLCMD;
}
static const struct proto_ops nss_ovpn_sk_proto_ops = {
.family = PF_OVPN,
.owner = THIS_MODULE,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.getname = sock_no_getname,
.ioctl = nss_ovpn_sk_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.getsockopt = sock_no_getsockopt,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
.sendmsg = nss_ovpn_sk_sendmsg,
.recvmsg = nss_ovpn_sk_recvmsg,
.poll = datagram_poll,
.bind = sock_no_bind,
.release = nss_ovpn_sk_release,
.setsockopt = sock_no_setsockopt,
.accept = sock_no_accept,
};
/*
* nss_ovpn_sk_create()
* Create OVPN socket.
*/
static int nss_ovpn_sk_create(struct net *net, struct socket *sock, int protocol, int kern)
{
struct nss_ovpn_sk_pinfo *pinfo;
struct sock *sk;
nss_ovpn_sk_info("%px: protocol = %d, sock->type = %d\n", sock, protocol, sock->type);
if (sock->type != SOCK_DGRAM) {
nss_ovpn_sk_warn("%px: Only SOCK_DGRAM socket is supported\n", sock);
return -ESOCKTNOSUPPORT;
}
/*
* For now we don't have any protocol defined.
* We could define different protocols and generalize
* this implementation.
*/
if (protocol) {
nss_ovpn_sk_warn("%px: Protocol specific socket is not implemented.\n", sock);
return -EPROTONOSUPPORT;
}
sk = sk_alloc(net, PF_OVPN, GFP_KERNEL, &nss_ovpn_sk_proto, kern);
if (!sk) {
nss_ovpn_sk_warn("%px: sock instance allocation failed.\n", sock);
return -ENOMEM;
}
pinfo = (struct nss_ovpn_sk_pinfo *)sk;
pinfo->pid = 0;
pinfo->tun_fd = 0;
pinfo->tun_sock = NULL;
pinfo->udp_fd = 0;
sock->ops = &nss_ovpn_sk_proto_ops;
sock_init_data(sock, sk);
sk->sk_family = PF_OVPN;
sk->sk_destruct = NULL;
nss_ovpn_sk_info("Socket created successfully\n");
return 0;
}
static const struct net_proto_family nss_ovpn_sk_family = {
.family = PF_OVPN,
.create = nss_ovpn_sk_create,
.owner = THIS_MODULE,
};
/*
* nss_ovpn_sk_send()
* Enqueue packet to socket queue.
*/
int nss_ovpn_sk_send(struct sk_buff *skb, void *app_data)
{
struct socket *sock = app_data;
struct sock *sk;
if (!app_data) {
nss_ovpn_sk_warn("%px: app_data is NULL\n", skb);
return -1;
}
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) {
return -1;
}
sk = sock->sk;
/*
* Orphan the skb - required as we might hang on to it
* for indefinite time.
*/
skb_orphan(skb);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
nf_reset(skb);
#else
nf_reset_ct(skb);
#endif
/* Enqueue packet */
if (sock_queue_rcv_skb(sk, skb) < 0) {
return -1;
}
nss_ovpn_sk_info("%px: Packet enqueued into socket queue\n", skb);
return 0;
}
/*
* nss_ovpn_sk_init()
* Register OVPN socket family.
*/
int nss_ovpn_sk_init(void)
{
int ret = proto_register(&nss_ovpn_sk_proto, 0);
if (ret) {
nss_ovpn_sk_warn("Failed to register OVPN socket protocol\n");
return ret;
}
ret = sock_register(&nss_ovpn_sk_family);
if (ret) {
nss_ovpn_sk_warn("Failed to register OVPN socket family\n");
proto_unregister(&nss_ovpn_sk_proto);
return ret;
}
return 0;
}
/*
* nss_ovpn_sk_cleanup()
* De-register OVPN socket family.
*/
void nss_ovpn_sk_cleanup(void)
{
sock_unregister(PF_OVPN);
proto_unregister(&nss_ovpn_sk_proto);
}
MODULE_ALIAS_NETPROTO(AF_OVPN);