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.
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");