Update support for Solaris.
Adds support for the latest TAP driver from http://www.whiteboard.ne.jp/~admin2/tuntap/, so tinc now also works in switch mode on Solaris 11.
This commit is contained in:
		
							parent
							
								
									06943e828c
								
							
						
					
					
						commit
						c9bdac68e1
					
				
					 1 changed files with 270 additions and 75 deletions
				
			
		|  | @ -1,6 +1,7 @@ | |||
| /*
 | ||||
|     device.c -- Interaction with Solaris tun device | ||||
|     Copyright (C) 2001-2005 Ivo Timmermans, | ||||
|                   2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> | ||||
|                   2001-2013 Guus Sliepen <guus@tinc-vpn.org> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|  | @ -23,95 +24,253 @@ | |||
| 
 | ||||
| #include <sys/stropts.h> | ||||
| #include <sys/sockio.h> | ||||
| #include <net/if_tun.h> | ||||
| 
 | ||||
| #include "../conf.h" | ||||
| #include "../device.h" | ||||
| #include "../logger.h" | ||||
| #include "../names.h" | ||||
| #include "../net.h" | ||||
| #include "../route.h" | ||||
| #include "../utils.h" | ||||
| #include "../xalloc.h" | ||||
| #include "../if_tun.h" | ||||
| 
 | ||||
| #define DEFAULT_DEVICE "/dev/tun" | ||||
| #define DEFAULT_TUN_DEVICE "/dev/tun" | ||||
| #define DEFAULT_TAP_DEVICE "/dev/tap" | ||||
| 
 | ||||
| static enum { | ||||
| 	DEVICE_TYPE_TUN, | ||||
| 	DEVICE_TYPE_TAP, | ||||
| } device_type = DEVICE_TYPE_TUN; | ||||
| 
 | ||||
| int device_fd = -1; | ||||
| static int ip_fd = -1, if_fd = -1; | ||||
| static int if_fd = -1; | ||||
| static int ip_fd = -1; | ||||
| static int arp_fd = -1; | ||||
| char *device = NULL; | ||||
| char *iface = NULL; | ||||
| static char *device_info = NULL; | ||||
| 
 | ||||
| static uint64_t device_total_in = 0; | ||||
| static uint64_t device_total_out = 0; | ||||
| uint64_t device_in_packets = 0; | ||||
| uint64_t device_in_bytes = 0; | ||||
| uint64_t device_out_packets = 0; | ||||
| uint64_t device_out_bytes = 0; | ||||
| 
 | ||||
| static bool setup_device(void) { | ||||
| 	int ppa; | ||||
| 	char *ptr; | ||||
| 	char *type; | ||||
| 
 | ||||
| 	if(!get_config_string(lookup_config(config_tree, "Device"), &device)) | ||||
| 		device = xstrdup(DEFAULT_DEVICE); | ||||
| 
 | ||||
| 	if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device, strerror(errno)); | ||||
| 		return false; | ||||
| 	if(!get_config_string(lookup_config(config_tree, "Device"), &device)) { | ||||
| 		if(routing_mode == RMODE_ROUTER) | ||||
| 			device = xstrdup(DEFAULT_TUN_DEVICE); | ||||
| 		else | ||||
| 			device = xstrdup(DEFAULT_TAP_DEVICE); | ||||
| 	} | ||||
| 
 | ||||
| #ifdef FD_CLOEXEC | ||||
| 	fcntl(device_fd, F_SETFD, FD_CLOEXEC); | ||||
| #endif | ||||
| 	if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) { | ||||
| 		if(!strcasecmp(type, "tun")) | ||||
| 			/* use default */; | ||||
| 		else if(!strcasecmp(type, "tap")) | ||||
| 			device_type = DEVICE_TYPE_TAP; | ||||
| 		else { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Unknown device type %s!", type); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if(strstr(device, "tap") || routing_mode != RMODE_ROUTER) | ||||
| 			device_type = DEVICE_TYPE_TAP; | ||||
| 	} | ||||
| 
 | ||||
| 	ppa = 0; | ||||
| 	if(device_type == DEVICE_TYPE_TUN) | ||||
| 		device_info = "Solaris tun device"; | ||||
| 	else | ||||
| 		device_info = "Solaris tap device"; | ||||
| 
 | ||||
| 	ptr = device; | ||||
| 	while(*ptr && !isdigit((int) *ptr)) | ||||
| 		ptr++; | ||||
| 	ppa = atoi(ptr); | ||||
| 	/* The following is black magic copied from OpenVPN. */ | ||||
| 
 | ||||
| 	if((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not open /dev/ip: %s", strerror(errno)); | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", "/dev/ip", strerror(errno)); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef FD_CLOEXEC | ||||
| 	fcntl(ip_fd, F_SETFD, FD_CLOEXEC); | ||||
| #endif | ||||
| 	if((device_fd = open(device, O_RDWR, 0)) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", device, strerror(errno)); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Get unit number. */ | ||||
| 
 | ||||
| 	char *ptr = device; | ||||
| 	get_config_string(lookup_config(config_tree, "Interface"), &ptr); | ||||
| 
 | ||||
| 	while(*ptr && !isdigit(*ptr)) | ||||
| 		ptr++; | ||||
| 	int ppa = atoi(ptr); | ||||
| 
 | ||||
| 	/* Assign a new PPA and get its unit number. */ | ||||
| 	if((ppa = ioctl(device_fd, TUNNEWPPA, ppa)) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Can't assign new interface: %s", strerror(errno)); | ||||
| 		return false; | ||||
| 
 | ||||
| 	struct strioctl strioc_ppa = { | ||||
| 		.ic_cmd = TUNNEWPPA, | ||||
| 		.ic_len = sizeof ppa, | ||||
| 		.ic_dp = (char *)&ppa, | ||||
| 	}; | ||||
| 
 | ||||
| 	if(!*ptr) { /* no number given, try dynamic */ | ||||
| 		bool found = false; | ||||
| 		while(!found && ppa < 64) { | ||||
| 			int new_ppa = ioctl(device_fd, I_STR, &strioc_ppa); | ||||
| 			if(new_ppa >= 0) { | ||||
| 				ppa = new_ppa; | ||||
| 				found = true; | ||||
| 				break; | ||||
| 			} | ||||
| 			ppa++; | ||||
| 		} | ||||
| 		if(!found) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not find free PPA for %s %s!", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} else { /* try this particular one */ | ||||
| 		if((ppa = ioctl(device_fd, I_STR, &strioc_ppa)) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not assign PPA %d for %s %s!", ppa, device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if((if_fd = open(device, O_RDWR, 0)) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s twice: %s", device, | ||||
| 			   strerror(errno)); | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", device, strerror(errno)); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef FD_CLOEXEC | ||||
| 	fcntl(if_fd, F_SETFD, FD_CLOEXEC); | ||||
| #endif | ||||
| 
 | ||||
| 	if(ioctl(if_fd, I_PUSH, "ip") < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Can't push IP module: %s", strerror(errno)); | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not push IP module onto %s %s!", device_info, device); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Assign ppa according to the unit number returned by tun device */ | ||||
| 	if(ioctl(if_fd, IF_UNITSEL, (char *) &ppa) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Can't set PPA %d: %s", ppa, strerror(errno)); | ||||
| 	xasprintf(&iface, "%s%d", device_type == DEVICE_TYPE_TUN ? "tun" : "tap", ppa); | ||||
| 
 | ||||
| 	{ | ||||
| 		/* Remove muxes just in case they are left over from a crashed tincd */ | ||||
| 		struct lifreq ifr = {}; | ||||
| 		strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name); | ||||
| 		if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) { | ||||
| 			int muxid = ifr.lifr_arp_muxid; | ||||
| 			ioctl(ip_fd, I_PUNLINK, muxid); | ||||
| 			muxid = ifr.lifr_ip_muxid; | ||||
| 			ioctl(ip_fd, I_PUNLINK, muxid); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(device_type == DEVICE_TYPE_TUN) { | ||||
| 		/* Assign ppa according to the unit number returned by tun device */ | ||||
| 		if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not set PPA %d on %s %s!", ppa, device_info, device); | ||||
| 			return false; | ||||
| 		}	 | ||||
| 	} | ||||
| 
 | ||||
| 	int arp_fd = -1; | ||||
| 
 | ||||
| 	if(device_type == DEVICE_TYPE_TAP) { | ||||
| 		struct lifreq ifr = {}; | ||||
| 
 | ||||
| 		if(ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not set flags on %s %s!", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name)); | ||||
| 		ifr.lifr_ppa = ppa; | ||||
| 
 | ||||
| 		/* Assign ppa according to the unit number returned by tun device */ | ||||
| 		if(ioctl(if_fd, SIOCSLIFNAME, &ifr) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not set PPA %d on %s %s!", ppa, device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 		if(ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not set flags on %s %s!", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Push arp module to if_fd */ | ||||
| 		if(ioctl(if_fd, I_PUSH, "arp") < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s %s!", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Pop any modules on the stream */ | ||||
| 		while(true) { | ||||
| 			if(ioctl(ip_fd, I_POP, NULL) < 0) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Push arp module to ip_fd */ | ||||
| 		if(ioctl(ip_fd, I_PUSH, "arp") < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s!", "/dev/ip"); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Open arp_fd */ | ||||
| 		if((arp_fd = open(device, O_RDWR, 0)) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", device, strerror(errno)); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Push arp module to arp_fd */ | ||||
| 		if(ioctl(arp_fd, I_PUSH, "arp") < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s %s!", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Set ifname to arp */ | ||||
| 		struct strioctl strioc_if = { | ||||
| 			.ic_cmd = SIOCSLIFNAME, | ||||
| 			.ic_len = sizeof ifr, | ||||
| 			.ic_dp = (char *)&ifr, | ||||
| 		}; | ||||
| 
 | ||||
| 		if(ioctl(arp_fd, I_STR, &strioc_if) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not set ifname to %s %s", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	int ip_muxid, arp_muxid; | ||||
| 
 | ||||
| 	if((ip_muxid = ioctl(ip_fd, I_PLINK, if_fd)) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not link %s %s to IP", device_info, device); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if(ioctl(ip_fd, I_LINK, if_fd) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Can't link TUN device to IP: %s", strerror(errno)); | ||||
| 	if(device_type == DEVICE_TYPE_TAP) { | ||||
| 		if((arp_muxid = ioctl(ip_fd, I_PLINK, arp_fd)) < 0) { | ||||
| 			logger(DEBUG_ALWAYS, LOG_ERR, "Could not link %s %s to ARP", device_info, device); | ||||
| 			return false; | ||||
| 		} | ||||
| 		close(arp_fd); | ||||
| 	} | ||||
| 
 | ||||
| 	struct lifreq ifr = {}; | ||||
| 	strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name)); | ||||
| 	ifr.lifr_ip_muxid = ip_muxid; | ||||
| 	if(device_type == DEVICE_TYPE_TAP) { | ||||
| 		ifr.lifr_arp_muxid = arp_muxid; | ||||
| 	} | ||||
| 
 | ||||
| 	if(ioctl(ip_fd, SIOCSLIFMUXID, &ifr) < 0) { | ||||
| 		if(device_type == DEVICE_TYPE_TAP) { | ||||
| 			ioctl(ip_fd, I_PUNLINK, arp_muxid); | ||||
| 		} | ||||
| 		ioctl(ip_fd, I_PUNLINK, ip_muxid); | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Could not set multiplexor id for %s %s", device_info, device); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) | ||||
| 		xasprintf(&iface, "tun%d", ppa); | ||||
| 	close(if_fd); | ||||
| 
 | ||||
| 	device_info = "Solaris tun device"; | ||||
| #ifdef FD_CLOEXEC | ||||
| 	fcntl(device_fd, F_SETFD, FD_CLOEXEC); | ||||
| 	fcntl(ip_fd, F_SETFD, FD_CLOEXEC); | ||||
| #endif | ||||
| 
 | ||||
| 	logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info); | ||||
| 
 | ||||
|  | @ -119,7 +278,17 @@ static bool setup_device(void) { | |||
| } | ||||
| 
 | ||||
| static void close_device(void) { | ||||
| 	close(if_fd); | ||||
| 	if(iface) { | ||||
| 		struct lifreq ifr = {}; | ||||
| 		strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name); | ||||
| 		if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) { | ||||
| 			int muxid = ifr.lifr_arp_muxid; | ||||
| 			ioctl(ip_fd, I_PUNLINK, muxid); | ||||
| 			muxid = ifr.lifr_ip_muxid; | ||||
| 			ioctl(ip_fd, I_PUNLINK, muxid); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	close(ip_fd); | ||||
| 	close(device_fd); | ||||
| 
 | ||||
|  | @ -130,58 +299,84 @@ static void close_device(void) { | |||
| static bool read_packet(vpn_packet_t *packet) { | ||||
| 	int inlen; | ||||
| 
 | ||||
| 	if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, | ||||
| 			   device, strerror(errno)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	switch(device_type) { | ||||
| 		case DEVICE_TYPE_TUN: | ||||
| 			if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { | ||||
| 				logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); | ||||
| 				return false; | ||||
| 			} | ||||
| 
 | ||||
| 	switch(packet->data[14] >> 4) { | ||||
| 		case 4: | ||||
| 			packet->data[12] = 0x08; | ||||
| 			packet->data[13] = 0x00; | ||||
| 			switch(packet->data[14] >> 4) { | ||||
| 				case 4: | ||||
| 					packet->data[12] = 0x08; | ||||
| 					packet->data[13] = 0x00; | ||||
| 					break; | ||||
| 				case 6: | ||||
| 					packet->data[12] = 0x86; | ||||
| 					packet->data[13] = 0xDD; | ||||
| 					break; | ||||
| 				default: | ||||
| 					logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version %d while reading packet from %s %s", packet->data[14] >> 4, device_info, device); | ||||
| 					return false; | ||||
| 			} | ||||
| 
 | ||||
| 			memset(packet->data, 0, 12); | ||||
| 			packet->len = inlen + 14; | ||||
| 			break; | ||||
| 		case 6: | ||||
| 			packet->data[12] = 0x86; | ||||
| 			packet->data[13] = 0xDD; | ||||
| 
 | ||||
| 		case DEVICE_TYPE_TAP: | ||||
| 			if((inlen = read(device_fd, packet->data, MTU)) <= 0) { | ||||
| 				logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); | ||||
| 				return false; | ||||
| 			} | ||||
| 
 | ||||
| 			packet->len = inlen + 14; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			logger(DEBUG_TRAFFIC, LOG_ERR, | ||||
| 					   "Unknown IP version %d while reading packet from %s %s", | ||||
| 					   packet->data[14] >> 4, device_info, device); | ||||
| 			return false; | ||||
| 			abort(); | ||||
| 	} | ||||
| 
 | ||||
| 	memset(packet->data, 0, 12); | ||||
| 	packet->len = inlen + 14; | ||||
| 	device_in_packets++; | ||||
| 	device_in_bytes += packet->len; | ||||
| 
 | ||||
| 	device_total_in += packet->len; | ||||
| 
 | ||||
| 	logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len, | ||||
| 			   device_info); | ||||
| 	logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len, device_info); | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static bool write_packet(vpn_packet_t *packet) { | ||||
| 	logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s", | ||||
| 			   packet->len, device_info); | ||||
| 	logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s", packet->len, device_info); | ||||
| 
 | ||||
| 	if(write(device_fd, packet->data + 14, packet->len - 14) < 0) { | ||||
| 		logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, | ||||
| 			   device, strerror(errno)); | ||||
| 		return false; | ||||
| 	switch(device_type) { | ||||
| 		case DEVICE_TYPE_TUN: | ||||
| 			if(write(device_fd, packet->data + 14, packet->len - 14) < 0) { | ||||
| 				logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); | ||||
| 				return false; | ||||
| 			} | ||||
| 			break; | ||||
| 
 | ||||
| 		case DEVICE_TYPE_TAP: | ||||
| 			if(write(device_fd, packet->data, packet->len) < 0) { | ||||
| 				logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); | ||||
| 				return false; | ||||
| 			} | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			abort(); | ||||
| 	} | ||||
| 
 | ||||
| 	device_total_out += packet->len; | ||||
| 	device_out_packets++; | ||||
| 	device_out_bytes += packet->len; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static void dump_device_stats(void) { | ||||
| 	logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device); | ||||
| 	logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in); | ||||
| 	logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out); | ||||
| 	logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in:  %10"PRIu64, device_in_bytes); | ||||
| 	logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes); | ||||
| } | ||||
| 
 | ||||
| const devops_t os_devops = { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue