datapath: Add support for 4.2 kernel.
[cascardo/ovs.git] / datapath / linux / compat / udp_tunnel.c
1 #include <linux/version.h>
2
3 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
4
5 #include <linux/module.h>
6 #include <linux/errno.h>
7 #include <linux/socket.h>
8 #include <linux/udp.h>
9 #include <linux/types.h>
10 #include <linux/kernel.h>
11 #include <net/ip_tunnels.h>
12 #include <net/udp.h>
13 #include <net/udp_tunnel.h>
14 #include <net/net_namespace.h>
15
16 int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
17                         struct socket **sockp)
18 {
19         int err;
20         struct socket *sock = NULL;
21
22 #if IS_ENABLED(CONFIG_IPV6)
23         if (cfg->family == AF_INET6) {
24                 struct sockaddr_in6 udp6_addr;
25
26                 err = sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock);
27                 if (err < 0)
28                         goto error;
29
30                 udp6_addr.sin6_family = AF_INET6;
31                 memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
32                        sizeof(udp6_addr.sin6_addr));
33                 udp6_addr.sin6_port = cfg->local_udp_port;
34                 err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
35                                   sizeof(udp6_addr));
36                 if (err < 0)
37                         goto error;
38
39                 if (cfg->peer_udp_port) {
40                         udp6_addr.sin6_family = AF_INET6;
41                         memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
42                                sizeof(udp6_addr.sin6_addr));
43                         udp6_addr.sin6_port = cfg->peer_udp_port;
44                         err = kernel_connect(sock,
45                                              (struct sockaddr *)&udp6_addr,
46                                              sizeof(udp6_addr), 0);
47                 }
48                 if (err < 0)
49                         goto error;
50         } else
51 #endif
52         if (cfg->family == AF_INET) {
53                 struct sockaddr_in udp_addr;
54
55                 err = sock_create_kern(net, AF_INET, SOCK_DGRAM, 0, &sock);
56                 if (err < 0)
57                         goto error;
58
59                 udp_addr.sin_family = AF_INET;
60                 udp_addr.sin_addr = cfg->local_ip;
61                 udp_addr.sin_port = cfg->local_udp_port;
62                 err = kernel_bind(sock, (struct sockaddr *)&udp_addr,
63                                   sizeof(udp_addr));
64                 if (err < 0)
65                         goto error;
66
67                 if (cfg->peer_udp_port) {
68                         udp_addr.sin_family = AF_INET;
69                         udp_addr.sin_addr = cfg->peer_ip;
70                         udp_addr.sin_port = cfg->peer_udp_port;
71                         err = kernel_connect(sock,
72                                              (struct sockaddr *)&udp_addr,
73                                              sizeof(udp_addr), 0);
74                         if (err < 0)
75                                 goto error;
76                 }
77         } else {
78                 return -EPFNOSUPPORT;
79         }
80
81
82         *sockp = sock;
83
84         return 0;
85
86 error:
87         if (sock) {
88                 kernel_sock_shutdown(sock, SHUT_RDWR);
89                 sock_release(sock);
90         }
91         *sockp = NULL;
92         return err;
93 }
94 EXPORT_SYMBOL_GPL(rpl_udp_sock_create);
95
96 void rpl_setup_udp_tunnel_sock(struct net *net, struct socket *sock,
97                                struct udp_tunnel_sock_cfg *cfg)
98 {
99         struct sock *sk = sock->sk;
100
101         /* Disable multicast loopback */
102         inet_sk(sk)->mc_loop = 0;
103
104         rcu_assign_sk_user_data(sk, cfg->sk_user_data);
105
106         udp_sk(sk)->encap_type = cfg->encap_type;
107         udp_sk(sk)->encap_rcv = cfg->encap_rcv;
108 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
109         udp_sk(sk)->encap_destroy = cfg->encap_destroy;
110 #endif
111
112         udp_tunnel_encap_enable(sock);
113 }
114 EXPORT_SYMBOL_GPL(rpl_setup_udp_tunnel_sock);
115
116 void ovs_udp_gso(struct sk_buff *skb)
117 {
118         int udp_offset = skb_transport_offset(skb);
119         struct udphdr *uh;
120
121         uh = udp_hdr(skb);
122         uh->len = htons(skb->len - udp_offset);
123 }
124 EXPORT_SYMBOL_GPL(ovs_udp_gso);
125
126 void ovs_udp_csum_gso(struct sk_buff *skb)
127 {
128         struct iphdr *iph = ip_hdr(skb);
129         int udp_offset = skb_transport_offset(skb);
130
131         ovs_udp_gso(skb);
132
133         /* csum segment if tunnel sets skb with csum. The cleanest way
134          * to do this just to set it up from scratch. */
135         skb->ip_summed = CHECKSUM_NONE;
136         udp_set_csum(true, skb, iph->saddr, iph->daddr,
137                      skb->len - udp_offset);
138 }
139 EXPORT_SYMBOL_GPL(ovs_udp_csum_gso);
140
141 int rpl_udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk,
142                             struct sk_buff *skb, __be32 src, __be32 dst,
143                             __u8 tos, __u8 ttl, __be16 df, __be16 src_port,
144                             __be16 dst_port, bool xnet, bool nocheck)
145 {
146         struct udphdr *uh;
147
148         __skb_push(skb, sizeof(*uh));
149         skb_reset_transport_header(skb);
150         uh = udp_hdr(skb);
151
152         uh->dest = dst_port;
153         uh->source = src_port;
154         uh->len = htons(skb->len);
155
156         udp_set_csum(nocheck, skb, src, dst, skb->len);
157
158         return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,
159                              tos, ttl, df, xnet);
160 }
161 EXPORT_SYMBOL_GPL(rpl_udp_tunnel_xmit_skb);
162
163 void rpl_udp_tunnel_sock_release(struct socket *sock)
164 {
165         rcu_assign_sk_user_data(sock->sk, NULL);
166         kernel_sock_shutdown(sock, SHUT_RDWR);
167         sock_release(sock);
168 }
169 EXPORT_SYMBOL_GPL(rpl_udp_tunnel_sock_release);
170
171 #endif /* Linux version < 4.0 */