1 | |
2 | // SPDX-License-Identifier: GPL-2.0-only |
3 | #include <linux/module.h> |
4 | #include <linux/errno.h> |
5 | #include <linux/socket.h> |
6 | #include <linux/udp.h> |
7 | #include <linux/types.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/in6.h> |
10 | #include <net/udp.h> |
11 | #include <net/udp_tunnel.h> |
12 | #include <net/net_namespace.h> |
13 | #include <net/netns/generic.h> |
14 | #include <net/ip6_tunnel.h> |
15 | #include <net/ip6_checksum.h> |
16 | |
17 | int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg, |
18 | struct socket **sockp) |
19 | { |
20 | struct sockaddr_in6 udp6_addr = {}; |
21 | int err; |
22 | struct socket *sock = NULL; |
23 | |
24 | err = sock_create_kern(net, AF_INET6, type: SOCK_DGRAM, proto: 0, res: &sock); |
25 | if (err < 0) |
26 | goto error; |
27 | |
28 | if (cfg->ipv6_v6only) { |
29 | err = ip6_sock_set_v6only(sk: sock->sk); |
30 | if (err < 0) |
31 | goto error; |
32 | } |
33 | if (cfg->bind_ifindex) { |
34 | err = sock_bindtoindex(sk: sock->sk, ifindex: cfg->bind_ifindex, lock_sk: true); |
35 | if (err < 0) |
36 | goto error; |
37 | } |
38 | |
39 | udp6_addr.sin6_family = AF_INET6; |
40 | memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6, |
41 | sizeof(udp6_addr.sin6_addr)); |
42 | udp6_addr.sin6_port = cfg->local_udp_port; |
43 | err = kernel_bind(sock, addr: (struct sockaddr *)&udp6_addr, |
44 | addrlen: sizeof(udp6_addr)); |
45 | if (err < 0) |
46 | goto error; |
47 | |
48 | if (cfg->peer_udp_port) { |
49 | memset(&udp6_addr, 0, sizeof(udp6_addr)); |
50 | udp6_addr.sin6_family = AF_INET6; |
51 | memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6, |
52 | sizeof(udp6_addr.sin6_addr)); |
53 | udp6_addr.sin6_port = cfg->peer_udp_port; |
54 | err = kernel_connect(sock, |
55 | addr: (struct sockaddr *)&udp6_addr, |
56 | addrlen: sizeof(udp6_addr), flags: 0); |
57 | } |
58 | if (err < 0) |
59 | goto error; |
60 | |
61 | udp_set_no_check6_tx(sk: sock->sk, val: !cfg->use_udp6_tx_checksums); |
62 | udp_set_no_check6_rx(sk: sock->sk, val: !cfg->use_udp6_rx_checksums); |
63 | |
64 | *sockp = sock; |
65 | return 0; |
66 | |
67 | error: |
68 | if (sock) { |
69 | kernel_sock_shutdown(sock, how: SHUT_RDWR); |
70 | sock_release(sock); |
71 | } |
72 | *sockp = NULL; |
73 | return err; |
74 | } |
75 | EXPORT_SYMBOL_GPL(udp_sock_create6); |
76 | |
77 | int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, |
78 | struct sk_buff *skb, |
79 | struct net_device *dev, |
80 | const struct in6_addr *saddr, |
81 | const struct in6_addr *daddr, |
82 | __u8 prio, __u8 ttl, __be32 label, |
83 | __be16 src_port, __be16 dst_port, bool nocheck) |
84 | { |
85 | struct udphdr *uh; |
86 | struct ipv6hdr *ip6h; |
87 | |
88 | __skb_push(skb, len: sizeof(*uh)); |
89 | skb_reset_transport_header(skb); |
90 | uh = udp_hdr(skb); |
91 | |
92 | uh->dest = dst_port; |
93 | uh->source = src_port; |
94 | |
95 | uh->len = htons(skb->len); |
96 | |
97 | skb_dst_set(skb, dst); |
98 | |
99 | udp6_set_csum(nocheck, skb, saddr, daddr, len: skb->len); |
100 | |
101 | __skb_push(skb, len: sizeof(*ip6h)); |
102 | skb_reset_network_header(skb); |
103 | ip6h = ipv6_hdr(skb); |
104 | ip6_flow_hdr(hdr: ip6h, tclass: prio, flowlabel: label); |
105 | ip6h->payload_len = htons(skb->len); |
106 | ip6h->nexthdr = IPPROTO_UDP; |
107 | ip6h->hop_limit = ttl; |
108 | ip6h->daddr = *daddr; |
109 | ip6h->saddr = *saddr; |
110 | |
111 | ip6tunnel_xmit(sk, skb, dev); |
112 | return 0; |
113 | } |
114 | EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb); |
115 | |
116 | /** |
117 | * udp_tunnel6_dst_lookup - perform route lookup on UDP tunnel |
118 | * @skb: Packet for which lookup is done |
119 | * @dev: Tunnel device |
120 | * @net: Network namespace of tunnel device |
121 | * @sock: Socket which provides route info |
122 | * @oif: Index of the output interface |
123 | * @saddr: Memory to store the src ip address |
124 | * @key: Tunnel information |
125 | * @sport: UDP source port |
126 | * @dport: UDP destination port |
127 | * @dsfield: The traffic class field |
128 | * @dst_cache: The dst cache to use for lookup |
129 | * This function performs a route lookup on a UDP tunnel |
130 | * |
131 | * It returns a valid dst pointer and stores src address to be used in |
132 | * tunnel in param saddr on success, else a pointer encoded error code. |
133 | */ |
134 | |
135 | struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb, |
136 | struct net_device *dev, |
137 | struct net *net, |
138 | struct socket *sock, |
139 | int oif, |
140 | struct in6_addr *saddr, |
141 | const struct ip_tunnel_key *key, |
142 | __be16 sport, __be16 dport, u8 dsfield, |
143 | struct dst_cache *dst_cache) |
144 | { |
145 | struct dst_entry *dst = NULL; |
146 | struct flowi6 fl6; |
147 | |
148 | #ifdef CONFIG_DST_CACHE |
149 | if (dst_cache) { |
150 | dst = dst_cache_get_ip6(dst_cache, saddr); |
151 | if (dst) |
152 | return dst; |
153 | } |
154 | #endif |
155 | memset(&fl6, 0, sizeof(fl6)); |
156 | fl6.flowi6_mark = skb->mark; |
157 | fl6.flowi6_proto = IPPROTO_UDP; |
158 | fl6.flowi6_oif = oif; |
159 | fl6.daddr = key->u.ipv6.dst; |
160 | fl6.saddr = key->u.ipv6.src; |
161 | fl6.fl6_sport = sport; |
162 | fl6.fl6_dport = dport; |
163 | fl6.flowlabel = ip6_make_flowinfo(tclass: dsfield, flowlabel: key->label); |
164 | |
165 | dst = ipv6_stub->ipv6_dst_lookup_flow(net, sock->sk, &fl6, |
166 | NULL); |
167 | if (IS_ERR(ptr: dst)) { |
168 | netdev_dbg(dev, "no route to %pI6\n" , &fl6.daddr); |
169 | return ERR_PTR(error: -ENETUNREACH); |
170 | } |
171 | if (dst->dev == dev) { /* is this necessary? */ |
172 | netdev_dbg(dev, "circular route to %pI6\n" , &fl6.daddr); |
173 | dst_release(dst); |
174 | return ERR_PTR(error: -ELOOP); |
175 | } |
176 | #ifdef CONFIG_DST_CACHE |
177 | if (dst_cache) |
178 | dst_cache_set_ip6(dst_cache, dst, saddr: &fl6.saddr); |
179 | #endif |
180 | *saddr = fl6.saddr; |
181 | return dst; |
182 | } |
183 | EXPORT_SYMBOL_GPL(udp_tunnel6_dst_lookup); |
184 | |
185 | MODULE_LICENSE("GPL" ); |
186 | |