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