Commit | Line | Data |
---|---|---|
8024e028 TH |
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"); |