diff options
Diffstat (limited to 'net/ipv4/udp_tunnel.c')
-rw-r--r-- | net/ipv4/udp_tunnel.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c new file mode 100644 index 000000000000..61ec1a65207e --- /dev/null +++ b/net/ipv4/udp_tunnel.c | |||
@@ -0,0 +1,100 @@ | |||
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"); | ||