Kernel Module Snippets – Part 4 – UDP in the kernel

Kernel Module Snippets – Part 4 – UDP in the kernel

Intro

There are many ways to implement the UDP handling. Since I’m primarily interested in using UDP to tunnel traffic from a client to a server, both of which are known and controlled by me, I’m going to implement a udp_tunnel. This is different to say, how a DNS server would work, listening on a UDP port and responding to requests from anywhere.

We’re going to extend our code that deals with virtual network devices. The virtual network device deals with the capturing of user traffic, and transmitting it through the tunnel. This is the other end of the tunnel where we receive the packet, remove any extra headers we may have added, and then send it back onto the network stack to be transmitted further.

The Code

   
  
static int my_udp_recv(struct sock *sk, struct sk_buff *skb)
{
struct net *net = sock_net(sk);
struct my *my = rcu_dereference_sk_user_data(sk);
...
/* This is where your magic goes. */
...
err = netif_receive_skb(skb);
return 0;
drop:
kfree_skb(skb);
return 0;
}

static struct sk_buff *my_gro_receive(struct sock *sk,
struct list_head *head,
struct sk_buff *skb)
{
u8 proto = my_from_sock(sk)->protocol;
const struct net_offload **offloads;
const struct net_offload *ops;
struct sk_buff *pp = NULL;

NAPI_GRO_CB(skb)->encap_mark = 0;

/* Flag this frame as already having an outer encap header */
NAPI_GRO_CB(skb)->is_fou = 1;

rcu_read_lock();
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
if (!ops || !ops->callbacks.gro_receive)
goto out_unlock;
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);

out_unlock:
rcu_read_unlock();
return pp;
}

static int my_gro_complete(struct sock *sk, struct sk_buff *skb,
int nhoff)
{
const struct net_offload *ops;
u8 proto = my_from_sock(sk)->protocol;
int err = -ENOSYS;
const struct net_offload **offloads;

rcu_read_lock();
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock;

err = ops->callbacks.gro_complete(skb, nhoff);
skb_set_inner_mac_header(skb, nhoff);

out_unlock:
rcu_read_unlock();
return err;
}

static int my_err_lookup(struct sock *sk, struct sk_buff *skb)
{
return 0;
}

static int my_create(struct net *net, struct my_cfg *cfg, struct socket **sockp)
{
struct socket *sock = NULL;
struct my *my;
struct sock *sk;
struct udp_tunnel_sock_cfg tunnel_cfg;
int err;

/* Open UDP socket */
err = udp_sock_create(net, &cfg->udp_config, &sock);
if (err < 0) {
goto error;
}

/* Allocate MY port structure */
my = kzalloc(sizeof(*my), GFP_KERNEL);
if (!my) {
err = -ENOMEM;
goto error;
}

memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
tunnel_cfg.encap_type = 1;
tunnel_cfg.sk_user_data = my;
tunnel_cfg.encap_destroy = NULL;
tunnel_cfg.encap_rcv = my_udp_recv;
tunnel_cfg.encap_err_lookup = my_err_lookup;
tunnel_cfg.gro_receive = my_gro_receive;
tunnel_cfg.gro_complete = my_gro_complete;

setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
...
}

static void my_release(struct my *my)
{
struct socket *sock = my->sock;

list_del(&my->list);
if (sock)
udp_tunnel_sock_release(sock);
}

MODULE_AUTHOR("Rob Hartzenberg ");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("UDP Tunnel");

  

Categories: Development, Training

By Rob Hartzenberg

September 30, 2020

Rob Hartzenberg
Author: Rob Hartzenberg

Linux Engineer

PREVIOUS

Kernel Module Snippets – Part 2 – Hello World Continued

NEXT

Kernel Module Snippets - Part 5 - Encryption