988 lines · 28.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE #include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <linux/if_tun.h>#include <sys/ioctl.h>#include <sys/socket.h> #include "kselftest_harness.h"#include "tuntap_helpers.h" static const char param_dev_geneve_name[] = "geneve1";static unsigned char param_hwaddr_outer_dst[] = { 0x00, 0xfe, 0x98,						  0x14, 0x22, 0x42 };static unsigned char param_hwaddr_outer_src[] = { 0x00, 0xfe, 0x98,						  0x94, 0xd2, 0x43 };static unsigned char param_hwaddr_inner_dst[] = { 0x00, 0xfe, 0x98,						  0x94, 0x22, 0xcc };static unsigned char param_hwaddr_inner_src[] = { 0x00, 0xfe, 0x98,						  0x94, 0xd2, 0xdd }; static struct in_addr param_ipaddr4_outer_dst = {	__constant_htonl(0xac100001),}; static struct in_addr param_ipaddr4_outer_src = {	__constant_htonl(0xac100002),}; static struct in_addr param_ipaddr4_inner_dst = {	__constant_htonl(0xac100101),}; static struct in_addr param_ipaddr4_inner_src = {	__constant_htonl(0xac100102),}; static struct in6_addr param_ipaddr6_outer_dst = {	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },}; static struct in6_addr param_ipaddr6_outer_src = {	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },}; static struct in6_addr param_ipaddr6_inner_dst = {	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },}; static struct in6_addr param_ipaddr6_inner_src = {	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },}; #ifndef BIT#define BIT(nr) (1UL << (nr))#endif #define VN_ID 1#define VN_PORT 4789#define UDP_SRC_PORT 22#define UDP_DST_PORT 48878#define IPPREFIX_LEN 24#define IP6PREFIX_LEN 64#define TIMEOUT_SEC 10#define TIMEOUT_USEC 100000#define MAX_RETRIES 20 #define UDP_TUNNEL_GENEVE_4IN4 0x01#define UDP_TUNNEL_GENEVE_6IN4 0x02#define UDP_TUNNEL_GENEVE_4IN6 0x04#define UDP_TUNNEL_GENEVE_6IN6 0x08 #define UDP_TUNNEL_MAX_SEGMENTS BIT(7) #define UDP_TUNNEL_OUTER_IPV4 (UDP_TUNNEL_GENEVE_4IN4 | UDP_TUNNEL_GENEVE_6IN4)#define UDP_TUNNEL_INNER_IPV4 (UDP_TUNNEL_GENEVE_4IN4 | UDP_TUNNEL_GENEVE_4IN6) #define UDP_TUNNEL_GENEVE_4IN4_HDRLEN                        \	(ETH_HLEN + 2 * sizeof(struct iphdr) + GENEVE_HLEN + \	 2 * sizeof(struct udphdr))#define UDP_TUNNEL_GENEVE_6IN6_HDRLEN                          \	(ETH_HLEN + 2 * sizeof(struct ipv6hdr) + GENEVE_HLEN + \	 2 * sizeof(struct udphdr))#define UDP_TUNNEL_GENEVE_4IN6_HDRLEN                               \	(ETH_HLEN + sizeof(struct iphdr) + sizeof(struct ipv6hdr) + \	 GENEVE_HLEN + 2 * sizeof(struct udphdr))#define UDP_TUNNEL_GENEVE_6IN4_HDRLEN                               \	(ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct iphdr) + \	 GENEVE_HLEN + 2 * sizeof(struct udphdr)) #define UDP_TUNNEL_HDRLEN(type)                                             \	((type) == UDP_TUNNEL_GENEVE_4IN4 ? UDP_TUNNEL_GENEVE_4IN4_HDRLEN : \	 (type) == UDP_TUNNEL_GENEVE_6IN6 ? UDP_TUNNEL_GENEVE_6IN6_HDRLEN : \	 (type) == UDP_TUNNEL_GENEVE_4IN6 ? UDP_TUNNEL_GENEVE_4IN6_HDRLEN : \	 (type) == UDP_TUNNEL_GENEVE_6IN4 ? UDP_TUNNEL_GENEVE_6IN4_HDRLEN : \					    0) #define UDP_TUNNEL_MSS(type) (ETH_DATA_LEN - UDP_TUNNEL_HDRLEN(type))#define UDP_TUNNEL_MAX(type, is_tap) \	(ETH_MAX_MTU - UDP_TUNNEL_HDRLEN(type) - ((is_tap) ? ETH_HLEN : 0)) #define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel)#define MAX_VNET_TUNNEL_PACKET_SZ                                       \	(TUN_VNET_TNL_SIZE + ETH_HLEN + UDP_TUNNEL_GENEVE_6IN6_HDRLEN + \	 ETH_MAX_MTU) struct geneve_setup_config {	int family;	union {		struct in_addr r4;		struct in6_addr r6;	} remote;	__be32 vnid;	__be16 vnport;	unsigned char hwaddr[6];	uint8_t csum;}; static int tun_attach(int fd, char *dev){	struct ifreq ifr; 	memset(&ifr, 0, sizeof(ifr));	strcpy(ifr.ifr_name, dev);	ifr.ifr_flags = IFF_ATTACH_QUEUE; 	return ioctl(fd, TUNSETQUEUE, (void *)&ifr);} static int tun_detach(int fd, char *dev){	struct ifreq ifr; 	memset(&ifr, 0, sizeof(ifr));	strcpy(ifr.ifr_name, dev);	ifr.ifr_flags = IFF_DETACH_QUEUE; 	return ioctl(fd, TUNSETQUEUE, (void *)&ifr);} static int tun_alloc(char *dev){	struct ifreq ifr;	int fd, err; 	fd = open("/dev/net/tun", O_RDWR);	if (fd < 0) {		fprintf(stderr, "can't open tun: %s\n", strerror(errno));		return fd;	} 	memset(&ifr, 0, sizeof(ifr));	strcpy(ifr.ifr_name, dev);	ifr.ifr_flags = IFF_TAP | IFF_NAPI | IFF_MULTI_QUEUE; 	err = ioctl(fd, TUNSETIFF, (void *)&ifr);	if (err < 0) {		fprintf(stderr, "can't TUNSETIFF: %s\n", strerror(errno));		close(fd);		return err;	}	strcpy(dev, ifr.ifr_name);	return fd;} static int tun_delete(char *dev){	return ip_link_del(dev);} static int tun_open(char *dev, const int flags, const int hdrlen,		    const int features, const unsigned char *mac_addr){	struct ifreq ifr = { 0 };	int fd, sk = -1; 	fd = open("/dev/net/tun", O_RDWR);	if (fd < 0) {		perror("open");		return -1;	} 	ifr.ifr_flags = flags;	if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {		perror("ioctl(TUNSETIFF)");		goto err;	}	strcpy(dev, ifr.ifr_name); 	if (hdrlen > 0) {		if (ioctl(fd, TUNSETVNETHDRSZ, &hdrlen) < 0) {			perror("ioctl(TUNSETVNETHDRSZ)");			goto err;		}	} 	if (features) {		if (ioctl(fd, TUNSETOFFLOAD, features) < 0) {			perror("ioctl(TUNSETOFFLOAD)");			goto err;		}	} 	sk = socket(PF_INET, SOCK_DGRAM, 0);	if (sk < 0) {		perror("socket");		goto err;	} 	if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {		perror("ioctl(SIOCGIFFLAGS)");		goto err;	} 	ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);	if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {		perror("ioctl(SIOCSIFFLAGS)");		goto err;	} 	if (mac_addr && flags & IFF_TAP) {		ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;		memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETH_ALEN); 		if (ioctl(sk, SIOCSIFHWADDR, &ifr) < 0) {			perror("ioctl(SIOCSIFHWADDR)");			goto err;		}	} out:	if (sk >= 0)		close(sk);	return fd; err:	close(fd);	fd = -1;	goto out;} static size_t sockaddr_len(int family){	return (family == AF_INET) ? sizeof(struct sockaddr_in) :				     sizeof(struct sockaddr_in6);} static int geneve_fill_newlink(struct rt_link_newlink_req *req, void *data){	struct geneve_setup_config *cfg = data; #define SET_GENEVE_REMOTE rt_link_newlink_req_set_linkinfo_data_geneve_remote#define SET_GENEVE_REMOTE6 rt_link_newlink_req_set_linkinfo_data_geneve_remote6 	rt_link_newlink_req_set_address(req, cfg->hwaddr, ETH_ALEN);	rt_link_newlink_req_set_linkinfo_data_geneve_id(req, cfg->vnid);	rt_link_newlink_req_set_linkinfo_data_geneve_port(req, cfg->vnport);	rt_link_newlink_req_set_linkinfo_data_geneve_udp_csum(req, cfg->csum); 	if (cfg->family == AF_INET)		SET_GENEVE_REMOTE(req, cfg->remote.r4.s_addr);	else		SET_GENEVE_REMOTE6(req, &cfg->remote.r6,				   sizeof(cfg->remote.r6)); 	return 0;} static int geneve_create(const char *dev, int family, void *remote,			 void *hwaddr){	struct geneve_setup_config geneve; 	memset(&geneve, 0, sizeof(geneve));	geneve.vnid = VN_ID;	geneve.vnport = htons(VN_PORT);	geneve.csum = 1;	geneve.family = family;	if (family == AF_INET)		memcpy(&geneve.remote.r4, remote, sizeof(struct in_addr));	else		memcpy(&geneve.remote.r6, remote, sizeof(struct in6_addr));	memcpy(geneve.hwaddr, hwaddr, ETH_ALEN); 	return ip_link_add(dev, "geneve", geneve_fill_newlink, (void *)&geneve);} static int set_pmtu_discover(int fd, bool is_ipv4){	int level, name, val; 	if (is_ipv4) {		level = SOL_IP;		name = IP_MTU_DISCOVER;		val = IP_PMTUDISC_DO;	} else {		level = SOL_IPV6;		name = IPV6_MTU_DISCOVER;		val = IPV6_PMTUDISC_DO;	} 	return setsockopt(fd, level, name, &val, sizeof(val));} static int udp_socket_open(struct sockaddr_storage *ssa, bool do_frag,			   bool do_connect, struct sockaddr_storage *dsa){	struct timeval to = { .tv_sec = TIMEOUT_SEC };	int fd, family = ssa->ss_family;	int salen = sockaddr_len(family); 	fd = socket(family, SOCK_DGRAM, 0);	if (fd < 0)		return -1; 	if (bind(fd, (struct sockaddr *)ssa, salen) < 0) {		perror("bind");		goto err;	} 	if (do_connect && connect(fd, (struct sockaddr *)dsa, salen) < 0) {		perror("connect");		goto err;	} 	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to)) < 0) {		perror("setsockopt(SO_RCVTIMEO)");		goto err;	} 	if (!do_frag && set_pmtu_discover(fd, family == AF_INET) < 0) {		perror("set_pmtu_discover");		goto err;	}	return fd; err:	close(fd);	return -1;} static void parse_route_rsp(struct rt_route_getroute_rsp *rsp, void *rtm_type){	*(uint8_t *)rtm_type = rsp->_hdr.rtm_type;} static int ip_route_check(const char *intf, int family, void *addr){	uint8_t rtm_type, table = RT_TABLE_LOCAL;	int retries = MAX_RETRIES; 	while (retries-- > 0) {		if (ip_route_get(intf, family, table, addr, parse_route_rsp,				 &rtm_type) == 0 &&		    rtm_type == RTN_LOCAL)			break; 		usleep(TIMEOUT_USEC);	} 	if (retries < 0)		return -1; 	return 0;} static int send_gso_udp_msg(int socket, struct sockaddr_storage *addr,			    uint8_t *send_buf, int send_len, int gso_size){	char control[CMSG_SPACE(sizeof(uint16_t))] = { 0 };	int alen = sockaddr_len(addr->ss_family);	struct msghdr msg = { 0 };	struct iovec iov = { 0 };	int ret; 	iov.iov_base = send_buf;	iov.iov_len = send_len; 	msg.msg_iov = &iov;	msg.msg_iovlen = 1;	msg.msg_name = addr;	msg.msg_namelen = alen; 	if (gso_size > 0) {		struct cmsghdr *cmsg; 		msg.msg_control = control;		msg.msg_controllen = sizeof(control); 		cmsg = CMSG_FIRSTHDR(&msg);		cmsg->cmsg_level = SOL_UDP;		cmsg->cmsg_type = UDP_SEGMENT;		cmsg->cmsg_len = CMSG_LEN(sizeof(uint16_t));		*(uint16_t *)CMSG_DATA(cmsg) = gso_size;	} 	ret = sendmsg(socket, &msg, 0);	if (ret < 0)		perror("sendmsg"); 	return ret;} static int validate_hdrlen(uint8_t **cur, int *len, int x){	if (*len < x)		return -1;	*cur += x;	*len -= x;	return 0;} static int parse_udp_tunnel_vnet_packet(uint8_t *buf, int len, int tunnel_type,					bool is_tap){	struct ipv6hdr *iph6;	struct udphdr *udph;	struct iphdr *iph4;	uint8_t *cur = buf; 	if (validate_hdrlen(&cur, &len, TUN_VNET_TNL_SIZE))		return -1; 	if (is_tap) {		if (validate_hdrlen(&cur, &len, ETH_HLEN))			return -1;	} 	if (tunnel_type & UDP_TUNNEL_OUTER_IPV4) {		iph4 = (struct iphdr *)cur;		if (validate_hdrlen(&cur, &len, sizeof(struct iphdr)))			return -1;		if (iph4->version != 4 || iph4->protocol != IPPROTO_UDP)			return -1;	} else {		iph6 = (struct ipv6hdr *)cur;		if (validate_hdrlen(&cur, &len, sizeof(struct ipv6hdr)))			return -1;		if (iph6->version != 6 || iph6->nexthdr != IPPROTO_UDP)			return -1;	} 	udph = (struct udphdr *)cur;	if (validate_hdrlen(&cur, &len, sizeof(struct udphdr)))		return -1;	if (ntohs(udph->dest) != VN_PORT)		return -1; 	if (validate_hdrlen(&cur, &len, GENEVE_HLEN))		return -1;	if (validate_hdrlen(&cur, &len, ETH_HLEN))		return -1; 	if (tunnel_type & UDP_TUNNEL_INNER_IPV4) {		iph4 = (struct iphdr *)cur;		if (validate_hdrlen(&cur, &len, sizeof(struct iphdr)))			return -1;		if (iph4->version != 4 || iph4->protocol != IPPROTO_UDP)			return -1;	} else {		iph6 = (struct ipv6hdr *)cur;		if (validate_hdrlen(&cur, &len, sizeof(struct ipv6hdr)))			return -1;		if (iph6->version != 6 || iph6->nexthdr != IPPROTO_UDP)			return -1;	} 	udph = (struct udphdr *)cur;	if (validate_hdrlen(&cur, &len, sizeof(struct udphdr)))		return -1;	if (ntohs(udph->dest) != UDP_DST_PORT)		return -1; 	return len;} FIXTURE(tun){	char ifname[IFNAMSIZ];	int fd, fd2;}; FIXTURE_SETUP(tun){	memset(self->ifname, 0, sizeof(self->ifname)); 	self->fd = tun_alloc(self->ifname);	ASSERT_GE(self->fd, 0); 	self->fd2 = tun_alloc(self->ifname);	ASSERT_GE(self->fd2, 0);} FIXTURE_TEARDOWN(tun)

Lines 1–500 of 988.