mirror of
				https://github.com/xcat2/xNBA.git
				synced 2025-10-25 00:15:52 +00:00 
			
		
		
		
	[linux] Add the tap driver
Add the tap driver that can be used like: $ ./ipxe.linux --net tap,if=tap0,mac=00:0c:29:c5:39:a1 The if setting is mandatory. Signed-off-by: Piotr Jaroszyński <p.jaroszynski@gmail.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
		
				
					committed by
					
						 Michael Brown
						Michael Brown
					
				
			
			
				
	
			
			
			
						parent
						
							ddef2e1bc1
						
					
				
				
					commit
					d60cbe43b7
				
			| @@ -263,3 +263,8 @@ REQUIRE_OBJECT ( gdbstub_cmd ); | ||||
|  */ | ||||
| REQUIRE_OBJECT ( device ); | ||||
| REQUIRE_OBJECT ( embedded ); | ||||
|  | ||||
| /* linux drivers aren't picked up by the parserom utility so drag them in here */ | ||||
| #ifdef DRIVERS_LINUX | ||||
| REQUIRE_OBJECT ( tap ); | ||||
| #endif | ||||
|   | ||||
| @@ -14,6 +14,8 @@ | ||||
| #define NAP_LINUX | ||||
| #define SMBIOS_LINUX | ||||
|  | ||||
| #define DRIVERS_LINUX | ||||
|  | ||||
| #define IMAGE_SCRIPT | ||||
|  | ||||
| #endif /* CONFIG_DEFAULTS_LINUX_H */ | ||||
|   | ||||
							
								
								
									
										249
									
								
								src/drivers/linux/tap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/drivers/linux/tap.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| /* | ||||
|  * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski@gmail.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <linux_api.h> | ||||
| #include <ipxe/list.h> | ||||
| #include <ipxe/linux.h> | ||||
| #include <ipxe/malloc.h> | ||||
| #include <ipxe/device.h> | ||||
| #include <ipxe/netdevice.h> | ||||
| #include <ipxe/iobuf.h> | ||||
| #include <ipxe/ethernet.h> | ||||
| #include <ipxe/settings.h> | ||||
| #include <ipxe/socket.h> | ||||
|  | ||||
| /* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ | ||||
| #define __GLIBC__ 2 | ||||
| #include <linux/socket.h> | ||||
| #undef __GLIBC__ | ||||
| #include <linux/if.h> | ||||
| #include <linux/if_ether.h> | ||||
| #include <linux/if_tun.h> | ||||
|  | ||||
| #define RX_BUF_SIZE 1536 | ||||
|  | ||||
| /** @file | ||||
|  * | ||||
|  * The TAP driver. | ||||
|  * | ||||
|  * The TAP is a Virtual Ethernet network device. | ||||
|  */ | ||||
|  | ||||
| struct tap_nic { | ||||
| 	/** Tap interface name */ | ||||
| 	char * interface; | ||||
| 	/** File descriptor of the opened tap device */ | ||||
| 	int fd; | ||||
| }; | ||||
|  | ||||
| /** Open the TAP device */ | ||||
| static int tap_open(struct net_device * netdev) | ||||
| { | ||||
| 	struct tap_nic * nic = netdev->priv; | ||||
| 	struct ifreq ifr; | ||||
| 	int ret; | ||||
|  | ||||
| 	nic->fd = linux_open("/dev/net/tun", O_RDWR); | ||||
| 	if (nic->fd < 0) { | ||||
| 		DBGC(nic, "tap %p open('/dev/net/tun') = %d (%s)\n", nic, nic->fd, linux_strerror(linux_errno)); | ||||
| 		return nic->fd; | ||||
| 	} | ||||
|  | ||||
| 	memset(&ifr, 0, sizeof(ifr)); | ||||
| 	/* IFF_NO_PI for no extra packet information */ | ||||
| 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI; | ||||
| 	strncpy(ifr.ifr_name, nic->interface, IFNAMSIZ); | ||||
| 	DBGC(nic, "tap %p interface = '%s'\n", nic, nic->interface); | ||||
|  | ||||
| 	ret = linux_ioctl(nic->fd, TUNSETIFF, &ifr); | ||||
|  | ||||
| 	if (ret != 0) { | ||||
| 		DBGC(nic, "tap %p ioctl(%d, ...) = %d (%s)\n", nic, nic->fd, ret, linux_strerror(linux_errno)); | ||||
| 		linux_close(nic->fd); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	/* Set nonblocking mode to make tap_poll easier */ | ||||
| 	ret = linux_fcntl(nic->fd, F_SETFL, O_NONBLOCK); | ||||
|  | ||||
| 	if (ret != 0) { | ||||
| 		DBGC(nic, "tap %p fcntl(%d, ...) = %d (%s)\n", nic, nic->fd, ret, linux_strerror(linux_errno)); | ||||
| 		linux_close(nic->fd); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /** Close the TAP device */ | ||||
| static void tap_close(struct net_device *netdev) | ||||
| { | ||||
| 	struct tap_nic * nic = netdev->priv; | ||||
| 	linux_close(nic->fd); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Transmit an ethernet packet. | ||||
|  * | ||||
|  * The packet can be written to the TAP device and marked as complete immediately. | ||||
|  */ | ||||
| static int tap_transmit(struct net_device *netdev, struct io_buffer *iobuf) | ||||
| { | ||||
| 	struct tap_nic * nic = netdev->priv; | ||||
| 	int rc; | ||||
|  | ||||
| 	/* Pad and align packet */ | ||||
| 	iob_pad(iobuf, ETH_ZLEN); | ||||
|  | ||||
| 	rc = linux_write(nic->fd, iobuf->data, iobuf->tail - iobuf->data); | ||||
| 	DBGC2(nic, "tap %p wrote %d bytes\n", nic, rc); | ||||
| 	netdev_tx_complete(netdev, iobuf); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /** Poll for new packets */ | ||||
| static void tap_poll(struct net_device *netdev) | ||||
| { | ||||
| 	struct tap_nic * nic = netdev->priv; | ||||
| 	struct pollfd pfd; | ||||
| 	struct io_buffer * iobuf; | ||||
| 	int r; | ||||
|  | ||||
| 	pfd.fd = nic->fd; | ||||
| 	pfd.events = POLLIN; | ||||
| 	if (linux_poll(&pfd, 1, 0) == -1) { | ||||
| 		DBGC(nic, "tap %p poll failed (%s)\n", nic, linux_strerror(linux_errno)); | ||||
| 		return; | ||||
| 	} | ||||
| 	if ((pfd.revents & POLLIN) == 0) | ||||
| 		return; | ||||
|  | ||||
| 	/* At this point we know there is at least one new packet to be read */ | ||||
|  | ||||
| 	iobuf = alloc_iob(RX_BUF_SIZE); | ||||
| 	if (! iobuf) | ||||
| 		goto allocfail; | ||||
|  | ||||
| 	while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) { | ||||
| 		DBGC2(nic, "tap %p read %d bytes\n", nic, r); | ||||
|  | ||||
| 		iob_put(iobuf, r); | ||||
| 		netdev_rx(netdev, iobuf); | ||||
|  | ||||
| 		iobuf = alloc_iob(RX_BUF_SIZE); | ||||
| 		if (! iobuf) | ||||
| 			goto allocfail; | ||||
| 	} | ||||
|  | ||||
| 	free_iob(iobuf); | ||||
| 	return; | ||||
|  | ||||
| allocfail: | ||||
| 	DBGC(nic, "tap %p alloc_iob failed\n", nic); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Set irq. | ||||
|  * | ||||
|  * Not used on linux, provide a dummy implementation. | ||||
|  */ | ||||
| static void tap_irq(struct net_device *netdev, int enable) | ||||
| { | ||||
| 	struct tap_nic *nic = netdev->priv; | ||||
|  | ||||
| 	DBGC(nic, "tap %p irq enable = %d\n", nic, enable); | ||||
| } | ||||
|  | ||||
| /** Tap operations */ | ||||
| static struct net_device_operations tap_operations = { | ||||
| 	.open		= tap_open, | ||||
| 	.close		= tap_close, | ||||
| 	.transmit	= tap_transmit, | ||||
| 	.poll		= tap_poll, | ||||
| 	.irq		= tap_irq, | ||||
| }; | ||||
|  | ||||
| /** Handle a device request for the tap driver */ | ||||
| static int tap_probe(struct linux_device *device, struct linux_device_request *request) | ||||
| { | ||||
| 	struct linux_setting *if_setting; | ||||
| 	struct net_device *netdev; | ||||
| 	struct tap_nic *nic; | ||||
| 	int rc; | ||||
|  | ||||
| 	netdev = alloc_etherdev(sizeof(*nic)); | ||||
| 	if (! netdev) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	netdev_init(netdev, &tap_operations); | ||||
| 	nic = netdev->priv; | ||||
| 	linux_set_drvdata(device, netdev); | ||||
| 	netdev->dev = &device->dev; | ||||
| 	memset(nic, 0, sizeof(*nic)); | ||||
|  | ||||
| 	netdev_link_up(netdev); | ||||
|  | ||||
| 	if ((rc = register_netdev(netdev)) != 0) | ||||
| 		goto err_register; | ||||
|  | ||||
| 	/* Look for the mandatory if setting */ | ||||
| 	if_setting = linux_find_setting("if", &request->settings); | ||||
|  | ||||
| 	/* No if setting */ | ||||
| 	if (! if_setting) { | ||||
| 		printf("tap missing a mandatory if setting\n"); | ||||
| 		rc = -EINVAL; | ||||
| 		goto err_settings; | ||||
| 	} | ||||
|  | ||||
| 	nic->interface = if_setting->value; | ||||
| 	if_setting->applied = 1; | ||||
|  | ||||
| 	/* Apply rest of the settings */ | ||||
| 	linux_apply_settings(&request->settings, &netdev->settings.settings); | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| err_settings: | ||||
| 	unregister_netdev(netdev); | ||||
| err_register: | ||||
| 	netdev_nullify(netdev); | ||||
| 	netdev_put(netdev); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| /** Remove the device */ | ||||
| static void tap_remove(struct linux_device *device) | ||||
| { | ||||
| 	struct net_device *netdev = linux_get_drvdata(device); | ||||
| 	unregister_netdev(netdev); | ||||
| 	netdev_nullify(netdev); | ||||
| 	netdev_put(netdev); | ||||
| } | ||||
|  | ||||
| /** Tap linux_driver */ | ||||
| struct linux_driver tap_driver __linux_driver = { | ||||
| 	.name = "tap", | ||||
| 	.probe = tap_probe, | ||||
| 	.remove = tap_remove, | ||||
| 	.can_probe = 1, | ||||
| }; | ||||
| @@ -128,6 +128,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); | ||||
| #define ERRFILE_snponly		     ( ERRFILE_DRIVER | 0x005a0000 ) | ||||
| #define ERRFILE_jme		     ( ERRFILE_DRIVER | 0x005b0000 ) | ||||
| #define ERRFILE_virtio_net	     ( ERRFILE_DRIVER | 0x005c0000 ) | ||||
| #define ERRFILE_tap		     ( ERRFILE_DRIVER | 0x005d0000 ) | ||||
|  | ||||
| #define ERRFILE_scsi		     ( ERRFILE_DRIVER | 0x00700000 ) | ||||
| #define ERRFILE_arbel		     ( ERRFILE_DRIVER | 0x00710000 ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user