941 lines · 22.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) */ #include <stdbool.h>#include <stdio.h>#include <unistd.h>#include <stdarg.h>#include <errno.h>#include <stddef.h>#include <string.h>#include <sys/ioctl.h>#include <net/if.h>#include <linux/if_tun.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/un.h>#include <netinet/ip.h>#include <linux/if_ether.h>#include <linux/if_packet.h>#include <sys/wait.h>#include <sys/uio.h>#include <linux/virtio_net.h>#include <netdb.h>#include <stdlib.h>#include <os.h>#include <limits.h>#include <um_malloc.h>#include "vector_user.h" #define ID_GRE 0#define ID_L2TPV3 1#define ID_BESS 2#define ID_MAX 2 #define TOKEN_IFNAME "ifname"#define TOKEN_SCRIPT "ifup" #define TRANS_RAW "raw"#define TRANS_RAW_LEN strlen(TRANS_RAW) #define TRANS_FD "fd"#define TRANS_FD_LEN strlen(TRANS_FD) #define TRANS_VDE "vde"#define TRANS_VDE_LEN strlen(TRANS_VDE) #define VNET_HDR_FAIL "could not enable vnet headers on fd %d"#define TUN_GET_F_FAIL "tapraw: TUNGETFEATURES failed: %s"#define L2TPV3_BIND_FAIL "l2tpv3_open : could not bind socket err=%i"#define UNIX_BIND_FAIL "unix_open : could not bind socket err=%i"#define BPF_ATTACH_FAIL "Failed to attach filter size %d prog %px to %d, err %d\n"#define BPF_DETACH_FAIL "Failed to detach filter size %d prog %px to %d, err %d\n" #define MAX_UN_LEN 107 static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";static const char *template = "tapXXXXXX"; /* This is very ugly and brute force lookup, but it is done * only once at initialization so not worth doing hashes or * anything more intelligent */ char *uml_vector_fetch_arg(struct arglist *ifspec, char *token){	int i; 	for (i = 0; i < ifspec->numargs; i++) {		if (strcmp(ifspec->tokens[i], token) == 0)			return ifspec->values[i];	}	return NULL; } struct arglist *uml_parse_vector_ifspec(char *arg){	struct arglist *result;	int pos, len;	bool parsing_token = true, next_starts = true; 	if (arg == NULL)		return NULL;	result = uml_kmalloc(sizeof(struct arglist), UM_GFP_KERNEL);	if (result == NULL)		return NULL;	result->numargs = 0;	len = strlen(arg);	for (pos = 0; pos < len; pos++) {		if (next_starts) {			if (parsing_token) {				result->tokens[result->numargs] = arg + pos;			} else {				result->values[result->numargs] = arg + pos;				result->numargs++;			}			next_starts = false;		}		if (*(arg + pos) == '=') {			if (parsing_token)				parsing_token = false;			else				goto cleanup;			next_starts = true;			(*(arg + pos)) = '\0';		}		if (*(arg + pos) == ',') {			parsing_token = true;			next_starts = true;			(*(arg + pos)) = '\0';		}	}	return result;cleanup:	printk(UM_KERN_ERR "vector_setup - Couldn't parse '%s'\n", arg);	kfree(result);	return NULL;} /* * Socket/FD configuration functions. These return an structure * of rx and tx descriptors to cover cases where these are not * the same (f.e. read via raw socket and write via tap). */ #define PATH_NET_TUN "/dev/net/tun"  static int create_tap_fd(char *iface){	struct ifreq ifr;	int fd = -1;	int err = -ENOMEM, offload; 	fd = open(PATH_NET_TUN, O_RDWR);	if (fd < 0) {		printk(UM_KERN_ERR "uml_tap: failed to open tun device\n");		goto tap_fd_cleanup;	}	memset(&ifr, 0, sizeof(ifr));	ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;	strscpy(ifr.ifr_name, iface); 	err = ioctl(fd, TUNSETIFF, (void *) &ifr);	if (err != 0) {		printk(UM_KERN_ERR "uml_tap: failed to select tap interface\n");		goto tap_fd_cleanup;	} 	offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;	ioctl(fd, TUNSETOFFLOAD, offload);	return fd;tap_fd_cleanup:	if (fd >= 0)		os_close_file(fd);	return err;} static int create_raw_fd(char *iface, int flags, int proto){	struct ifreq ifr;	int fd = -1;	struct sockaddr_ll sock;	int err = -ENOMEM; 	fd = socket(AF_PACKET, SOCK_RAW, flags);	if (fd == -1) {		err = -errno;		goto raw_fd_cleanup;	}	memset(&ifr, 0, sizeof(ifr));	strscpy(ifr.ifr_name, iface);	if (ioctl(fd, SIOCGIFINDEX, (void *) &ifr) < 0) {		err = -errno;		goto raw_fd_cleanup;	} 	sock.sll_family = AF_PACKET;	sock.sll_protocol = htons(proto);	sock.sll_ifindex = ifr.ifr_ifindex; 	if (bind(fd,		(struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {		err = -errno;		goto raw_fd_cleanup;	}	return fd;raw_fd_cleanup:	printk(UM_KERN_ERR "user_init_raw: init failed, error %d", err);	if (fd >= 0)		os_close_file(fd);	return err;}  static struct vector_fds *user_init_tap_fds(struct arglist *ifspec){	int fd = -1, i;	char *iface;	struct vector_fds *result = NULL;	bool dynamic = false;	char dynamic_ifname[IFNAMSIZ];	char *argv[] = {NULL, NULL, NULL, NULL}; 	iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);	if (iface == NULL) {		dynamic = true;		iface = dynamic_ifname;		srand(getpid());	} 	result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);	if (result == NULL) {		printk(UM_KERN_ERR "uml_tap: failed to allocate file descriptors\n");		goto tap_cleanup;	}	result->rx_fd = -1;	result->tx_fd = -1;	result->remote_addr = NULL;	result->remote_addr_size = 0; 	/* TAP */	do {		if (dynamic) {			strcpy(iface, template);			for (i = 0; i < strlen(iface); i++) {				if (iface[i] == 'X') {					iface[i] = padchar[rand() % strlen(padchar)];				}			}		}		fd = create_tap_fd(iface);		if ((fd < 0) && (!dynamic)) {			printk(UM_KERN_ERR "uml_tap: failed to create tun interface\n");			goto tap_cleanup;		}		result->tx_fd = fd;		result->rx_fd = fd;	} while (fd < 0); 	argv[0] = uml_vector_fetch_arg(ifspec, TOKEN_SCRIPT);	if (argv[0]) {		argv[1] = iface;		run_helper(NULL, NULL, argv);	} 	return result;tap_cleanup:	printk(UM_KERN_ERR "user_init_tap: init failed, error %d", fd);	kfree(result);	return NULL;} static struct vector_fds *user_init_hybrid_fds(struct arglist *ifspec){	char *iface;	struct vector_fds *result = NULL;	char *argv[] = {NULL, NULL, NULL, NULL}; 	iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);	if (iface == NULL) {		printk(UM_KERN_ERR "uml_tap: failed to parse interface spec\n");		goto hybrid_cleanup;	} 	result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);	if (result == NULL) {		printk(UM_KERN_ERR "uml_tap: failed to allocate file descriptors\n");		goto hybrid_cleanup;	}	result->rx_fd = -1;	result->tx_fd = -1;	result->remote_addr = NULL;	result->remote_addr_size = 0; 	/* TAP */ 	result->tx_fd = create_tap_fd(iface);	if (result->tx_fd < 0) {		printk(UM_KERN_ERR "uml_tap: failed to create tun interface: %i\n", result->tx_fd);		goto hybrid_cleanup;	} 	/* RAW */ 	result->rx_fd = create_raw_fd(iface, ETH_P_ALL, ETH_P_ALL);	if (result->rx_fd == -1) {		printk(UM_KERN_ERR			"uml_tap: failed to create paired raw socket: %i\n", result->rx_fd);		goto hybrid_cleanup;	} 	argv[0] = uml_vector_fetch_arg(ifspec, TOKEN_SCRIPT);	if (argv[0]) {		argv[1] = iface;		run_helper(NULL, NULL, argv);	}	return result;hybrid_cleanup:	printk(UM_KERN_ERR "user_init_hybrid: init failed");	kfree(result);	return NULL;} static struct vector_fds *user_init_unix_fds(struct arglist *ifspec, int id){	int fd = -1;	int socktype;	char *src, *dst;	struct vector_fds *result = NULL;	struct sockaddr_un *local_addr = NULL, *remote_addr = NULL; 	src = uml_vector_fetch_arg(ifspec, "src");	dst = uml_vector_fetch_arg(ifspec, "dst");	result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);	if (result == NULL) {		printk(UM_KERN_ERR "unix open:cannot allocate remote addr");		goto unix_cleanup;	}	remote_addr = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL);	if (remote_addr == NULL) {		printk(UM_KERN_ERR "unix open:cannot allocate remote addr");		goto unix_cleanup;	} 	switch (id) {	case ID_BESS:		socktype = SOCK_SEQPACKET;		if ((src != NULL) && (strlen(src) <= MAX_UN_LEN)) {			local_addr = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL);			if (local_addr == NULL) {				printk(UM_KERN_ERR "bess open:cannot allocate local addr");				goto unix_cleanup;			}			local_addr->sun_family = AF_UNIX;			memcpy(local_addr->sun_path, src, strlen(src) + 1);		}		if ((dst == NULL) || (strlen(dst) > MAX_UN_LEN))			goto unix_cleanup;		remote_addr->sun_family = AF_UNIX;		memcpy(remote_addr->sun_path, dst, strlen(dst) + 1);		break;	default:		printk(KERN_ERR "Unsupported unix socket type\n");		return NULL;	} 	fd = socket(AF_UNIX, socktype, 0);	if (fd == -1) {		printk(UM_KERN_ERR			"unix open: could not open socket, error = %d",			-errno		);		goto unix_cleanup;	}	if (local_addr != NULL) {		if (bind(fd, (struct sockaddr *) local_addr, sizeof(struct sockaddr_un))) {			printk(UM_KERN_ERR UNIX_BIND_FAIL, errno);			goto unix_cleanup;		}	}	switch (id) {	case ID_BESS:		if (connect(fd, (const struct sockaddr *) remote_addr, sizeof(struct sockaddr_un)) < 0) {			printk(UM_KERN_ERR "bess open:cannot connect to %s %i", remote_addr->sun_path, -errno);			goto unix_cleanup;		}		break;	}	result->rx_fd = fd;	result->tx_fd = fd;	result->remote_addr_size = sizeof(struct sockaddr_un);	result->remote_addr = remote_addr;	return result;unix_cleanup:	if (fd >= 0)		os_close_file(fd);	kfree(remote_addr);	kfree(result);	return NULL;} static int strtofd(const char *nptr){	long fd;	char *endptr; 	if (nptr == NULL)		return -1; 	errno = 0;	fd = strtol(nptr, &endptr, 10);	if (nptr == endptr ||		errno != 0 ||		*endptr != '\0' ||		fd < 0 ||		fd > INT_MAX) {		return -1;	}	return fd;} static struct vector_fds *user_init_fd_fds(struct arglist *ifspec){	int fd = -1;	char *fdarg = NULL;	struct vector_fds *result = NULL; 	fdarg = uml_vector_fetch_arg(ifspec, "fd");	fd = strtofd(fdarg);	if (fd == -1) {		printk(UM_KERN_ERR "fd open: bad or missing fd argument");		goto fd_cleanup;	} 	result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);	if (result == NULL) {		printk(UM_KERN_ERR "fd open: allocation failed");		goto fd_cleanup;	} 	result->rx_fd = fd;	result->tx_fd = fd;	result->remote_addr_size = 0;	result->remote_addr = NULL;	return result; fd_cleanup:	if (fd >= 0)		os_close_file(fd);	kfree(result);	return NULL;} /* enough char to store an int type */#define ENOUGH(type) ((CHAR_BIT * sizeof(type) - 1) / 3 + 2)#define ENOUGH_OCTAL(type) ((CHAR_BIT * sizeof(type) + 2) / 3)/* vde_plug --descr xx --port2 xx --mod2 xx --group2 xx seqpacket://NN vnl (NULL) */#define VDE_MAX_ARGC 12#define VDE_SEQPACKET_HEAD "seqpacket://"#define VDE_SEQPACKET_HEAD_LEN (sizeof(VDE_SEQPACKET_HEAD) - 1)#define VDE_DEFAULT_DESCRIPTION "UML" static struct vector_fds *user_init_vde_fds(struct arglist *ifspec){	char seqpacketvnl[VDE_SEQPACKET_HEAD_LEN + ENOUGH(int) + 1];	char *argv[VDE_MAX_ARGC] = {"vde_plug"};	int argc = 1;	int rv;	int sv[2];	struct vector_fds *result = NULL; 	char *vnl = uml_vector_fetch_arg(ifspec,"vnl");	char *descr = uml_vector_fetch_arg(ifspec,"descr");	char *port = uml_vector_fetch_arg(ifspec,"port");	char *mode = uml_vector_fetch_arg(ifspec,"mode");	char *group = uml_vector_fetch_arg(ifspec,"group");	if (descr == NULL) descr = VDE_DEFAULT_DESCRIPTION; 	argv[argc++] = "--descr";	argv[argc++] = descr;	if (port != NULL) {		argv[argc++] = "--port2";		argv[argc++] = port;	}	if (mode != NULL) {		argv[argc++] = "--mod2";		argv[argc++] = mode;	}	if (group != NULL) {		argv[argc++] = "--group2";		argv[argc++] = group;	}	argv[argc++] = seqpacketvnl;	argv[argc++] = vnl;	argv[argc++] = NULL; 	rv = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv);	if (rv  < 0) {		printk(UM_KERN_ERR "vde: seqpacket socketpair err %d", -errno);		return NULL;	}	rv = os_set_exec_close(sv[0]);	if (rv  < 0) {		printk(UM_KERN_ERR "vde: seqpacket socketpair cloexec err %d", -errno);		goto vde_cleanup_sv;	}	snprintf(seqpacketvnl, sizeof(seqpacketvnl), VDE_SEQPACKET_HEAD "%d", sv[1]); 	run_helper(NULL, NULL, argv); 	close(sv[1]); 	result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);	if (result == NULL) {

Lines 1–500 of 941.