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