Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/* SPDX-License-Identifier: GPL-2.0-only */
2
3#ifndef _TUNTAP_HELPERS_H
4#define _TUNTAP_HELPERS_H
5
6#include <errno.h>
7#include <linux/if_packet.h>
8#include <linux/ipv6.h>
9#include <linux/virtio_net.h>
10#include <netinet/in.h>
11#include <netinet/if_ether.h>
12#include <netinet/udp.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <ynl.h>
18
19#include "rt-route-user.h"
20#include "rt-addr-user.h"
21#include "rt-neigh-user.h"
22#include "rt-link-user.h"
23
24#define GENEVE_HLEN 8
25#define PKT_DATA 0xCB
26#define TUNTAP_DEFAULT_TTL 8
27#define TUNTAP_DEFAULT_IPID 1337
28
29unsigned int if_nametoindex(const char *ifname);
30
31static inline int ip_addr_len(int family)
32{
33 return (family == AF_INET) ? sizeof(struct in_addr) :
34 sizeof(struct in6_addr);
35}
36
37static inline void fill_ifaddr_msg(struct ifaddrmsg *ifam, int family,
38 int prefix, int flags, const char *dev)
39{
40 ifam->ifa_family = family;
41 ifam->ifa_prefixlen = prefix;
42 ifam->ifa_index = if_nametoindex(dev);
43 ifam->ifa_flags = flags;
44 ifam->ifa_scope = RT_SCOPE_UNIVERSE;
45}
46
47static inline int ip_addr_add(const char *dev, int family, void *addr,
48 uint8_t prefix)
49{
50 int nl_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
51 int ifa_flags = IFA_F_PERMANENT | IFA_F_NODAD;
52 int ret = -1, ipalen = ip_addr_len(family);
53 struct rt_addr_newaddr_req *req;
54 struct ynl_sock *ys;
55
56 ys = ynl_sock_create(&ynl_rt_addr_family, NULL);
57 if (!ys)
58 return -1;
59
60 req = rt_addr_newaddr_req_alloc();
61 if (!req)
62 goto err_req_alloc;
63
64 fill_ifaddr_msg(&req->_hdr, family, prefix, ifa_flags, dev);
65 rt_addr_newaddr_req_set_nlflags(req, nl_flags);
66 rt_addr_newaddr_req_set_local(req, addr, ipalen);
67
68 ret = rt_addr_newaddr(ys, req);
69 rt_addr_newaddr_req_free(req);
70err_req_alloc:
71 ynl_sock_destroy(ys);
72 return ret;
73}
74
75static inline void fill_neigh_req_header(struct ndmsg *ndm, int family,
76 int state, const char *dev)
77{
78 ndm->ndm_family = family;
79 ndm->ndm_ifindex = if_nametoindex(dev);
80 ndm->ndm_state = state;
81 ndm->ndm_flags = 0;
82 ndm->ndm_type = RTN_UNICAST;
83}
84
85static inline int ip_neigh_add(const char *dev, int family, void *addr,
86 unsigned char *lladdr)
87{
88 int nl_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
89 int ret = -1, ipalen = ip_addr_len(family);
90 struct rt_neigh_newneigh_req *req;
91 struct ynl_sock *ys;
92
93 ys = ynl_sock_create(&ynl_rt_neigh_family, NULL);
94 if (!ys)
95 return -1;
96
97 req = rt_neigh_newneigh_req_alloc();
98 if (!req)
99 goto err_req_alloc;
100
101 fill_neigh_req_header(&req->_hdr, family, NUD_PERMANENT, dev);
102 rt_neigh_newneigh_req_set_nlflags(req, nl_flags);
103 rt_neigh_newneigh_req_set_dst(req, addr, ipalen);
104 rt_neigh_newneigh_req_set_lladdr(req, lladdr, ETH_ALEN);
105 rt_neigh_newneigh_req_set_ifindex(req, if_nametoindex(dev));
106
107 ret = rt_neigh_newneigh(ys, req);
108 rt_neigh_newneigh_req_free(req);
109err_req_alloc:
110 ynl_sock_destroy(ys);
111 return ret;
112}
113
114static inline void fill_route_req_header(struct rtmsg *rtm, int family,
115 int table)
116{
117 rtm->rtm_family = family;
118 rtm->rtm_table = table;
119}
120
121static inline int
122ip_route_get(const char *dev, int family, int table, void *dst,
123 void (*parse_rsp)(struct rt_route_getroute_rsp *rsp, void *out),
124 void *out)
125{
126 int ret = -1, ipalen = ip_addr_len(family);
127 struct rt_route_getroute_req *req;
128 struct rt_route_getroute_rsp *rsp;
129 struct ynl_sock *ys;
130
131 ys = ynl_sock_create(&ynl_rt_route_family, NULL);
132 if (!ys)
133 return -1;
134
135 req = rt_route_getroute_req_alloc();
136 if (!req)
137 goto err_req_alloc;
138
139 fill_route_req_header(&req->_hdr, family, table);
140 rt_route_getroute_req_set_nlflags(req, NLM_F_REQUEST);
141 rt_route_getroute_req_set_dst(req, dst, ipalen);
142 rt_route_getroute_req_set_oif(req, if_nametoindex(dev));
143
144 rsp = rt_route_getroute(ys, req);
145 if (!rsp)
146 goto err_rsp_get;
147
148 ret = 0;
149 if (parse_rsp)
150 parse_rsp(rsp, out);
151
152 rt_route_getroute_rsp_free(rsp);
153err_rsp_get:
154 rt_route_getroute_req_free(req);
155err_req_alloc:
156 ynl_sock_destroy(ys);
157 return ret;
158}
159
160static inline int
161ip_link_add(const char *dev, char *link_type,
162 int (*fill_link_attr)(struct rt_link_newlink_req *req, void *data),
163 void *data)
164{
165 int nl_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
166 struct rt_link_newlink_req *req;
167 struct ynl_sock *ys;
168 int ret = -1;
169
170 ys = ynl_sock_create(&ynl_rt_link_family, NULL);
171 if (!ys)
172 return -1;
173
174 req = rt_link_newlink_req_alloc();
175 if (!req)
176 goto err_req_alloc;
177
178 req->_hdr.ifi_flags = IFF_UP;
179 rt_link_newlink_req_set_nlflags(req, nl_flags);
180 rt_link_newlink_req_set_ifname(req, dev);
181 rt_link_newlink_req_set_linkinfo_kind(req, link_type);
182
183 if (fill_link_attr && fill_link_attr(req, data) < 0)
184 goto err_attr_fill;
185
186 ret = rt_link_newlink(ys, req);
187err_attr_fill:
188 rt_link_newlink_req_free(req);
189err_req_alloc:
190 ynl_sock_destroy(ys);
191 return ret;
192}
193
194static inline int ip_link_del(const char *dev)
195{
196 struct rt_link_dellink_req *req;
197 struct ynl_sock *ys;
198 int ret = -1;
199
200 ys = ynl_sock_create(&ynl_rt_link_family, NULL);
201 if (!ys)
202 return -1;
203
204 req = rt_link_dellink_req_alloc();
205 if (!req)
206 goto err_req_alloc;
207
208 rt_link_dellink_req_set_nlflags(req, NLM_F_REQUEST);
209 rt_link_dellink_req_set_ifname(req, dev);
210
211 ret = rt_link_dellink(ys, req);
212 rt_link_dellink_req_free(req);
213err_req_alloc:
214 ynl_sock_destroy(ys);
215 return ret;
216}
217
218static inline size_t build_eth(uint8_t *buf, uint16_t proto, unsigned char *src,
219 unsigned char *dest)
220{
221 struct ethhdr *eth = (struct ethhdr *)buf;
222
223 eth->h_proto = htons(proto);
224 memcpy(eth->h_source, src, ETH_ALEN);
225 memcpy(eth->h_dest, dest, ETH_ALEN);
226
227 return ETH_HLEN;
228}
229
230static inline uint32_t add_csum(const uint8_t *buf, int len)
231{
232 uint16_t *sbuf = (uint16_t *)buf;
233 uint32_t sum = 0;
234
235 while (len > 1) {
236 sum += *sbuf++;
237 len -= 2;
238 }
239
240 if (len)
241 sum += *(uint8_t *)sbuf;
242
243 return sum;
244}
245
246static inline uint16_t finish_ip_csum(uint32_t sum)
247{
248 while (sum >> 16)
249 sum = (sum & 0xffff) + (sum >> 16);
250 return ~((uint16_t)sum);
251}
252
253static inline uint16_t build_ip_csum(const uint8_t *buf, int len, uint32_t sum)
254{
255 sum += add_csum(buf, len);
256 return finish_ip_csum(sum);
257}
258
259static inline int build_ipv4_header(uint8_t *buf, uint8_t proto,
260 int payload_len, struct in_addr *src,
261 struct in_addr *dst)
262{
263 struct iphdr *iph = (struct iphdr *)buf;
264
265 iph->ihl = 5;
266 iph->version = 4;
267 iph->ttl = TUNTAP_DEFAULT_TTL;
268 iph->tot_len = htons(sizeof(*iph) + payload_len);
269 iph->id = htons(TUNTAP_DEFAULT_IPID);
270 iph->protocol = proto;
271 iph->saddr = src->s_addr;
272 iph->daddr = dst->s_addr;
273 iph->check = build_ip_csum(buf, iph->ihl << 2, 0);
274
275 return iph->ihl << 2;
276}
277
278static inline void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
279{
280 uint16_t val, *ptr = (uint16_t *)ip6h;
281
282 val = ntohs(*ptr);
283 val &= 0xF00F;
284 val |= ((uint16_t)dsfield) << 4;
285 *ptr = htons(val);
286}
287
288static inline int build_ipv6_header(uint8_t *buf, uint8_t proto,
289 uint8_t dsfield, int payload_len,
290 struct in6_addr *src, struct in6_addr *dst)
291{
292 struct ipv6hdr *ip6h = (struct ipv6hdr *)buf;
293
294 ip6h->version = 6;
295 ip6h->payload_len = htons(payload_len);
296 ip6h->nexthdr = proto;
297 ip6h->hop_limit = TUNTAP_DEFAULT_TTL;
298 ipv6_set_dsfield(ip6h, dsfield);
299 memcpy(&ip6h->saddr, src, sizeof(ip6h->saddr));
300 memcpy(&ip6h->daddr, dst, sizeof(ip6h->daddr));
301
302 return sizeof(struct ipv6hdr);
303}
304
305static inline int build_geneve_header(uint8_t *buf, uint32_t vni)
306{
307 uint16_t protocol = htons(ETH_P_TEB);
308 uint32_t geneve_vni = htonl((vni << 8) & 0xffffff00);
309
310 memcpy(buf + 2, &protocol, 2);
311 memcpy(buf + 4, &geneve_vni, 4);
312 return GENEVE_HLEN;
313}
314
315static inline int build_udp_header(uint8_t *buf, uint16_t sport, uint16_t dport,
316 int payload_len)
317{
318 struct udphdr *udph = (struct udphdr *)buf;
319
320 udph->source = htons(sport);
321 udph->dest = htons(dport);
322 udph->len = htons(sizeof(*udph) + payload_len);
323 return sizeof(*udph);
324}
325
326static inline void build_udp_packet_csum(uint8_t *buf, int family,
327 bool csum_off)
328{
329 struct udphdr *udph = (struct udphdr *)buf;
330 size_t ipalen = ip_addr_len(family);
331 uint32_t sum;
332
333 /* No extension IPv4 and IPv6 headers addresses are the last fields */
334 sum = add_csum(buf - 2 * ipalen, 2 * ipalen);
335 sum += htons(IPPROTO_UDP) + udph->len;
336
337 if (!csum_off)
338 sum += add_csum(buf, udph->len);
339
340 udph->check = finish_ip_csum(sum);
341}
342
343static inline int build_udp_packet(uint8_t *buf, uint16_t sport, uint16_t dport,
344 int payload_len, int family, bool csum_off)
345{
346 struct udphdr *udph = (struct udphdr *)buf;
347
348 build_udp_header(buf, sport, dport, payload_len);
349 memset(buf + sizeof(*udph), PKT_DATA, payload_len);
350 build_udp_packet_csum(buf, family, csum_off);
351
352 return sizeof(*udph) + payload_len;
353}
354
355static inline int build_virtio_net_hdr_v1_hash_tunnel(uint8_t *buf, bool is_tap,
356 int hdr_len, int gso_size,
357 int outer_family,
358 int inner_family)
359{
360 struct virtio_net_hdr_v1_hash_tunnel *vh_tunnel = (void *)buf;
361 struct virtio_net_hdr_v1 *vh = &vh_tunnel->hash_hdr.hdr;
362 int outer_iphlen, inner_iphlen, eth_hlen, gso_type;
363
364 eth_hlen = is_tap ? ETH_HLEN : 0;
365 outer_iphlen = (outer_family == AF_INET) ? sizeof(struct iphdr) :
366 sizeof(struct ipv6hdr);
367 inner_iphlen = (inner_family == AF_INET) ? sizeof(struct iphdr) :
368 sizeof(struct ipv6hdr);
369
370 vh_tunnel->outer_th_offset = eth_hlen + outer_iphlen;
371 vh_tunnel->inner_nh_offset = vh_tunnel->outer_th_offset + ETH_HLEN +
372 GENEVE_HLEN + sizeof(struct udphdr);
373
374 vh->csum_start = vh_tunnel->inner_nh_offset + inner_iphlen;
375 vh->csum_offset = __builtin_offsetof(struct udphdr, check);
376 vh->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
377 vh->hdr_len = hdr_len;
378 vh->gso_size = gso_size;
379
380 if (gso_size) {
381 gso_type = outer_family == AF_INET ?
382 VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 :
383 VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6;
384 vh->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4 | gso_type;
385 }
386
387 return sizeof(struct virtio_net_hdr_v1_hash_tunnel);
388}
389
390#endif /* _TUNTAP_HELPERS_H */