From: Cedric A. <ad...@in...> - 2000-03-13 15:40:24
|
Jeff Dike writes: > > How would it be if you used ethertap on the host and uml? Could the > > uml kernel connect the output of one tap to the input of the other and > > vice-versa? > > Where can I find something to read about ethertap? Rusty also > mentioned this > a while back, as a possible way to get rid of the um_ifconfig kludge. > /usr/src/linux/Documentation/networking/ethertap.txt :-) I used it in an experimental program which I called 'usertunnel' which (which is not freely available), but which was working along those lines: User space tunnel ----------------- usertunnel.py is a simple tunnel at the ethernet frame level. Modified, it could offer proof of concept bridging. It uses two abilities from the Linux kernel: - /dev/tap : pseudo ethernet device in 2.1.x kernels. It is seen by the kernel exactly as an ethernet device. The difference is that instead of sending (/receiving) physically the packet, it is sent (/received) to a process that has opened /dev/tap. (see /usr/src/linux/Documentation/networking/ethertap.txt) - socket(..., SOCK_PACKET) : a type of socket similar to SOCK_RAW: the difference is that the socket will receive all the packets of a given _ethernet type_ (coming from all the interfaces). The usertunnel operate in two ways: - tunnel using TCP/IP connection: actually IP over IP tunneling. The tunnel is first started as a server on a machine ; then a client is started on another machine, that makes a TCP/IP connection to the server. Once the connection established, client and server exchange (symetrically) the frames they receive on each of their /dev/tap, via TCP/IP. Via TCP/IP the /dev/tap frame is sent as is, prefixed by two bytes indicating the size of the frame. - tunnel using Ethernet: each frame received on /dev/tap is encapsulated and sent on the Ethernet interface (eth0) with a special type (BridgeType=0xcda8) : | Ethernet dest. | Ethernet src. | type=0xcda8 | /dev/tap frame | It is sent to the destination whose ethernet address was specified on the command line. Note: - Because Python+/dev/tap0 doesn't give easily the frame boundary, only IP trafic is forwarded: IP headers are used to determine what is the actual frame length. --------------------------------------------------------------------------- Frame formats: - /dev/tap0 gives the frame with the following format (they are also written to /dev/tap0 with the same format): ² <-------------- ethernet frame -------------------...-> 0 1| 2 3 4 5 6 7| 8 9 10 11 12 13|14 15|16 17 ... 00 00| Ethernet dest. | Ethernet src. |type | data ... (pad) \----------------- tap frame ---------------------------.../ the Ethernet address is actually FE:FD:00:00:00:00 (arbitrary set by the Linux kernel) - with socket(..., SOCK_PACKET) the frames are received and sent with the same format without the padding (so it is a raw ethernet frame): 0 1 2 3 4 5| 6 7 8 9 10 11|12 13|14 15 16 17 .... Ethernet dest. | Ethernet src. |type | data ... \----------------- ethernet frame -----------------------.../ --------------------------------------------------------------------------- Data flow in ================================================================ USER SPACE PROCESS +------+ |telnet| +------+ +------------+ (3) | | usertunnel |<---------------------------------\ | +------------+ | | | | ================================================================ | | | V(1) V(4) | +-----------------------------------------+ | | Networking upper layers (socket/TCP/IP) | | +-----------------------------------------+ | | | | V(2) V(5) | +--------------------------------+ +-----------------+ | |Ethernet pseudo-device /dev/tap0| | Ethernet device | | +--------------------------------+ +-----------------+ | | | | \-------------------------------------------------------/ | LINUX KERNEL | ================================================================ | V(6) Physical ethernet link (0): +----+ +----+ | m1 | | m2 | +----+ ip1 +----+ ip2 ^ ethaddr1 ^ ethaddr2 | | +-----------------------------------+ Ethernet Two machines m1, m2 are used, connected by an Ethernet link. Let ip1 and ip2 be the IP addresses of m1 and m2 on Ethernet interfaces Let tapip1 and tapip2 be the IP addresses of m1 and m2 on tap0 interfaces Let ethaddr1 and ethaddr2 be the ethernet addresses on the m1 has /dev/tap0 configured with: ifconfig tap0 <tapip1> and m2 with: ifconfig tap0 <tapip2> The usertunnel is started with: on m1: usertunnel eth ethaddr2 on m2: usertunnel eth ethaddr1 The figure assumes telnet is started on m1 to tapip2 (telnetd responds on m2 to tapip1) and that a key is pressed. The data flow is then: (1) "telnet" sends a packet that contains the key (1 byte) it uses the system call: send(socket, <data:key>, <size of data>, ...) (2) the upper layers of the kernel processes this data as part of the TCP/IP connection: a TCP/IP packet is to be sent: [(pad)|pseudo eth|pseudo eth|ethType IP]???[ IP header | TCP header | data ] and is passed to the ethernet pseudo-device tap0 (3) The ethernet frame is passed from tap0 to the usertunnel process usertunnel had open /dev/tap0 and was blocked in a recvfrom(...) call (or in a select(...), then followed by recvfrom(...)): [ 0000 | pseudo eth | eth | EthType IP=0x800| IP header | TCP header | data ] (4) usertunnel receives the frame as above, and encapsulate it: [ ethaddr2 | ethaddr1 | EthType special=0xcda08 | <tap0 frame> ] where <tap0 frame> is exactly the frame received via tap0 (above) This new frame is send to the kernel as a raw packet, using the socket created with a socket(...,SOCK_PACKET) and a sendto indicating the name of the device ("eth0"). (5) The kernel dispatch it to the ethernet device driver of "eth0" without doing any TCP/IP (or upper layer) processing on it. (6) The ethernet driver receive the packet and transmit it physically on the link. At the reception of the packet it follows _exactly_ the reverse path on the m2 machine, so the corresponding figure and description is not included. -- Cedric |