xref: /linux/tools/testing/selftests/net/tuntap_helpers.h (revision ca220141fa8ebae09765a242076b2b77338106b0)
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 
29 unsigned int if_nametoindex(const char *ifname);
30 
31 static inline int ip_addr_len(int family)
32 {
33 	return (family == AF_INET) ? sizeof(struct in_addr) :
34 				     sizeof(struct in6_addr);
35 }
36 
37 static 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 
47 static 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);
70 err_req_alloc:
71 	ynl_sock_destroy(ys);
72 	return ret;
73 }
74 
75 static 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 
85 static 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);
109 err_req_alloc:
110 	ynl_sock_destroy(ys);
111 	return ret;
112 }
113 
114 static 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 
121 static inline int
122 ip_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);
153 err_rsp_get:
154 	rt_route_getroute_req_free(req);
155 err_req_alloc:
156 	ynl_sock_destroy(ys);
157 	return ret;
158 }
159 
160 static inline int
161 ip_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);
187 err_attr_fill:
188 	rt_link_newlink_req_free(req);
189 err_req_alloc:
190 	ynl_sock_destroy(ys);
191 	return ret;
192 }
193 
194 static 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);
213 err_req_alloc:
214 	ynl_sock_destroy(ys);
215 	return ret;
216 }
217 
218 static 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 
230 static 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 
246 static 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 
253 static 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 
259 static 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 
278 static 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 
288 static 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 
305 static 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 
315 static 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 
326 static 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 
343 static 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 
355 static 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 */
391