Merge tag 'nfs-for-3.17-5' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[pandora-kernel.git] / net / ipv4 / udp_tunnel.c
1 #include <linux/module.h>
2 #include <linux/errno.h>
3 #include <linux/socket.h>
4 #include <linux/udp.h>
5 #include <linux/types.h>
6 #include <linux/kernel.h>
7 #include <net/udp.h>
8 #include <net/udp_tunnel.h>
9 #include <net/net_namespace.h>
10
11 int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
12                     struct socket **sockp)
13 {
14         int err = -EINVAL;
15         struct socket *sock = NULL;
16
17 #if IS_ENABLED(CONFIG_IPV6)
18         if (cfg->family == AF_INET6) {
19                 struct sockaddr_in6 udp6_addr;
20
21                 err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
22                 if (err < 0)
23                         goto error;
24
25                 sk_change_net(sock->sk, net);
26
27                 udp6_addr.sin6_family = AF_INET6;
28                 memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
29                        sizeof(udp6_addr.sin6_addr));
30                 udp6_addr.sin6_port = cfg->local_udp_port;
31                 err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
32                                   sizeof(udp6_addr));
33                 if (err < 0)
34                         goto error;
35
36                 if (cfg->peer_udp_port) {
37                         udp6_addr.sin6_family = AF_INET6;
38                         memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
39                                sizeof(udp6_addr.sin6_addr));
40                         udp6_addr.sin6_port = cfg->peer_udp_port;
41                         err = kernel_connect(sock,
42                                              (struct sockaddr *)&udp6_addr,
43                                              sizeof(udp6_addr), 0);
44                 }
45                 if (err < 0)
46                         goto error;
47
48                 udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
49                 udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
50         } else
51 #endif
52         if (cfg->family == AF_INET) {
53                 struct sockaddr_in udp_addr;
54
55                 err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
56                 if (err < 0)
57                         goto error;
58
59                 sk_change_net(sock->sk, net);
60
61                 udp_addr.sin_family = AF_INET;
62                 udp_addr.sin_addr = cfg->local_ip;
63                 udp_addr.sin_port = cfg->local_udp_port;
64                 err = kernel_bind(sock, (struct sockaddr *)&udp_addr,
65                                   sizeof(udp_addr));
66                 if (err < 0)
67                         goto error;
68
69                 if (cfg->peer_udp_port) {
70                         udp_addr.sin_family = AF_INET;
71                         udp_addr.sin_addr = cfg->peer_ip;
72                         udp_addr.sin_port = cfg->peer_udp_port;
73                         err = kernel_connect(sock,
74                                              (struct sockaddr *)&udp_addr,
75                                              sizeof(udp_addr), 0);
76                         if (err < 0)
77                                 goto error;
78                 }
79
80                 sock->sk->sk_no_check_tx = !cfg->use_udp_checksums;
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(udp_sock_create);
99
100 MODULE_LICENSE("GPL");