1 /////////////////////////////////////////////////////////////////////////
2 // $Id: eth_tap.cc,v 1.32 2008/12/11 18:01:56 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2001 MandrakeSoft S.A.
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 // eth_tap.cc - TAP interface by Bryce Denney
29 // Here's how to get this working. On the host machine:
31 // # /sbin/insmod ethertap
32 // Using /lib/modules/2.2.14-5.0/net/ethertap.o
33 // # mknod /dev/tap0 c 36 16 # if not already there
34 // # /sbin/ifconfig tap0 10.0.0.1
35 // # /sbin/route add -host 10.0.0.2 gw 10.0.0.1
37 // Now you have a tap0 device which you can on the ifconfig output. The
38 // tap0 interface has the IP address of 10.0.0.1. The bochs machine will have
39 // the IP address 10.0.0.2.
41 // Compile a bochs version from March 8, 2002 or later with --enable-ne2000.
42 // Add this ne2k line to your .bochsrc to activate the tap device.
43 // ne2k: ioaddr=0x280, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
44 // Don't change the mac or ethmod!
46 // Boot up DLX Linux in Bochs. Log in as root and then type the following
47 // commands to set up networking:
48 // # ifconfig eth0 10.0.0.2
49 // # route add -net 10.0.0.0
50 // # route add default gw 10.0.0.1
51 // Now you should be able to ping from guest OS to your host machine, if
52 // you give its IP number. I'm still having trouble with pings from the
53 // host machine to the guest, so something is still not right. Symptoms: I
54 // ping from the host to the guest's IP address 10.0.0.2. With tcpdump I can
55 // see the ping going to Bochs, and then the ping reply coming from Bochs.
56 // But the ping program itself does not see the responses....well every
57 // once in a while it does, like 1 in 60 pings.
59 // host$ ping 10.0.0.2
60 // PING 10.0.0.2 (10.0.0.2) from 10.0.0.1 : 56(84) bytes of data.
63 // 20:29:59.018776 fe:fd:0:0:0:0 fe:fd:0:0:0:1 0800 98: 10.0.0.1 > 10.0.0.2: icmp: echo request
64 // 4500 0054 2800 0000 4001 3ea7 0a00 0001
65 // 0a00 0002 0800 09d3 a53e 0400 9765 893c
66 // 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213
68 // 20:29:59.023017 fe:fd:0:0:0:1 fe:fd:0:0:0:0 0800 98: 10.0.0.2 > 10.0.0.1: icmp: echo reply
69 // 4500 0054 004a 0000 4001 665d 0a00 0002
70 // 0a00 0001 0000 11d3 a53e 0400 9765 893c
71 // 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213
74 // I suspect it may be related to the fact that ping 10.0.0.1 from the
75 // host also doesn't work. Why wouldn't the host respond to its own IP
76 // address on the tap0 device?
78 // Theoretically, if you set up packet forwarding (with masquerading) on the
79 // host, you should be able to get Bochs talking to anyone on the internet.
82 // Pavel Dufek (PD), CZ, 2008 - quick & dirty hack for Solaris 10 sparc tap
85 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
86 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
87 // is used to know when we are exporting symbols and when we are importing.
90 #define NO_DEVICE_INCLUDES
93 #if BX_NETWORKING && defined(HAVE_ETHERTAP)
97 #define LOG_THIS bx_devices.pluginNE2kDevice->
100 #include <sys/param.h>
101 #include <sys/ioctl.h>
103 #include <sys/poll.h>
105 #include <sys/time.h>
106 #include <sys/resource.h>
107 #include <sys/socket.h>
109 #include <sys/wait.h>
110 #if defined(__linux__)
111 #include <asm/types.h>
112 #include <linux/netlink.h>
113 #include <linux/if.h>
114 #elif BX_HAVE_NET_IF_H
121 // Sun/Solaris specific defines/includes
122 #define TUNNEWPPA (('T'<<16) | 0x0001)
126 #define BX_ETH_TAP_LOGGING 0
129 // Define the class. This is private to this module
131 class bx_tap_pktmover_c
: public eth_pktmover_c
{
133 bx_tap_pktmover_c(const char *netif
, const char *macaddr
,
134 eth_rx_handler_t rxh
,
135 void *rxarg
, const char *script
);
136 void sendpkt(void *buf
, unsigned io_len
);
140 static void rx_timer_handler(void *);
142 Bit8u guest_macaddr
[6];
143 #if BX_ETH_TAP_LOGGING
144 FILE *txlog
, *txlog_txt
, *rxlog
, *rxlog_txt
;
150 // Define the static class that registers the derived pktmover class,
151 // and allocates one on request.
153 class bx_tap_locator_c
: public eth_locator_c
{
155 bx_tap_locator_c(void) : eth_locator_c("tap") {}
157 eth_pktmover_c
*allocate(const char *netif
, const char *macaddr
,
158 eth_rx_handler_t rxh
,
159 void *rxarg
, const char *script
) {
160 return (new bx_tap_pktmover_c(netif
, macaddr
, rxh
, rxarg
, script
));
166 // Define the methods for the bx_tap_pktmover derived class
170 bx_tap_pktmover_c::bx_tap_pktmover_c(const char *netif
,
172 eth_rx_handler_t rxh
,
177 char filename
[BX_PATHNAME_LEN
];
178 if (strncmp (netif
, "tap", 3) != 0) {
179 BX_PANIC (("eth_tap: interface name (%s) must be tap0..tap15", netif
));
182 strcpy(filename
,"/dev/tap"); /* PD - device on Solaris is always the same */
184 sprintf (filename
, "/dev/%s", netif
);
187 #if defined(__linux__)
188 // check if the TAP devices is running, and turn on ARP. This is based
189 // on code from the Mac-On-Linux project. http://http://www.maconlinux.org/
190 int sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
192 BX_PANIC (("socket creation: %s", strerror(errno
)));
196 memset(&ifr
, 0, sizeof(ifr
));
197 strncpy(ifr
.ifr_name
, netif
, sizeof(ifr
.ifr_name
));
198 if(ioctl(sock
, SIOCGIFFLAGS
, &ifr
) < 0) {
199 BX_PANIC (("SIOCGIFFLAGS on %s: %s", netif
, strerror(errno
)));
203 if (!(ifr
.ifr_flags
& IFF_RUNNING
)) {
204 BX_PANIC (("%s device is not running", netif
));
208 if ((ifr
.ifr_flags
& IFF_NOARP
)){
209 BX_INFO(("turn on ARP for %s device", netif
));
210 ifr
.ifr_flags
&= ~IFF_NOARP
;
211 if (ioctl(sock
, SIOCSIFFLAGS
, &ifr
) < 0) {
212 BX_PANIC (("SIOCSIFFLAGS: %s", strerror(errno
)));
220 fd
= open (filename
, O_RDWR
);
222 BX_PANIC(("open failed on TAP %s: %s", netif
, strerror(errno
)));
227 char *ptr
; /* PD - ppa allocation ala qemu */
228 char my_dev
[10]; /* enough ... */
230 struct strioctl strioc_ppa
;
233 strncpy(my_dev
,netif
,10); /* following ptr= does not work with const char* */
234 if( *my_dev
) { /* find the ppa number X from string tapX */
236 while( *ptr
&& !isdigit((int)*ptr
) ) ptr
++;
239 /* Assign a new PPA and get its unit number. */
240 strioc_ppa
.ic_cmd
= TUNNEWPPA
;
241 strioc_ppa
.ic_timout
= 0;
242 strioc_ppa
.ic_len
= sizeof(ppa
);
243 strioc_ppa
.ic_dp
= (char *)&ppa
;
244 if ((ppa
= ioctl (fd
, I_STR
, &strioc_ppa
)) < 0)
245 BX_PANIC(("Can't assign new interface tap%d !",ppa
));
248 /* set O_ASYNC flag so that we can poll with read() */
249 if ((flags
= fcntl(fd
, F_GETFL
)) < 0) {
250 BX_PANIC(("getflags on tap device: %s", strerror(errno
)));
253 if (fcntl(fd
, F_SETFL
, flags
) < 0) {
254 BX_PANIC(("set tap device flags: %s", strerror(errno
)));
257 BX_INFO(("eth_tap: opened %s device", netif
));
259 /* Execute the configuration script */
260 char intname
[IFNAMSIZ
];
261 strcpy(intname
,netif
);
262 if((script
!= NULL
) && (strcmp(script
, "") != 0) && (strcmp(script
, "none") != 0))
264 if (execute_script(script
, intname
) < 0)
265 BX_ERROR(("execute script '%s' on %s failed", script
, intname
));
269 this->rx_timer_index
=
270 bx_pc_system
.register_timer(this, this->rx_timer_handler
, 1000,
271 1, 1, "eth_tap"); // continuous, active
274 memcpy(&guest_macaddr
[0], macaddr
, 6);
275 #if BX_ETH_TAP_LOGGING
276 // eventually Bryce wants txlog to dump in pcap format so that
277 // tcpdump -r FILE can read it and interpret packets.
278 txlog
= fopen ("ne2k-tx.log", "wb");
279 if (!txlog
) BX_PANIC (("open ne2k-tx.log failed"));
280 txlog_txt
= fopen ("ne2k-txdump.txt", "wb");
281 if (!txlog_txt
) BX_PANIC (("open ne2k-txdump.txt failed"));
282 fprintf (txlog_txt
, "tap packetmover readable log file\n");
283 fprintf (txlog_txt
, "net IF = %s\n", netif
);
284 fprintf (txlog_txt
, "MAC address = ");
285 for (int i
=0; i
<6; i
++)
286 fprintf (txlog_txt
, "%02x%s", 0xff & macaddr
[i
], i
<5?":" : "");
287 fprintf (txlog_txt
, "\n--\n");
290 rxlog
= fopen ("ne2k-rx.log", "wb");
291 if (!rxlog
) BX_PANIC (("open ne2k-rx.log failed"));
292 rxlog_txt
= fopen ("ne2k-rxdump.txt", "wb");
293 if (!rxlog_txt
) BX_PANIC (("open ne2k-rxdump.txt failed"));
294 fprintf (rxlog_txt
, "tap packetmover readable log file\n");
295 fprintf (rxlog_txt
, "net IF = %s\n", netif
);
296 fprintf (rxlog_txt
, "MAC address = ");
297 for (int i
=0; i
<6; i
++)
298 fprintf (rxlog_txt
, "%02x%s", 0xff & macaddr
[i
], i
<5?":" : "");
299 fprintf (rxlog_txt
, "\n--\n");
305 void bx_tap_pktmover_c::sendpkt(void *buf
, unsigned io_len
)
307 Bit8u txbuf
[BX_PACKET_BUFSIZE
];
311 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
312 defined(__APPLE__) || defined(__OpenBSD__) || defined(__sun__) // Should be fixed for other *BSD
313 memcpy(txbuf
, buf
, io_len
);
314 /* PD - for Sun variant the retry cycle from qemu - mainly to be sure packet
319 ret
=write(fd
, txbuf
, io_len
);
320 if (ret
< 0 && (errno
== EINTR
|| errno
== EAGAIN
)) {
326 #else /* not defined __sun__ */
327 size
= write(fd
, txbuf
, io_len
);
328 #endif /* whole condition about defined __sun__ */
329 if (size
!= io_len
) {
330 #else /* not bsd/apple/sun style */
331 memcpy(txbuf
+2, buf
, io_len
);
332 size
= write(fd
, txbuf
, io_len
+2);
333 if (size
!= io_len
+2) {
335 BX_PANIC(("write on tap device: %s", strerror(errno
)));
337 BX_DEBUG(("wrote %d bytes + ev. 2 byte pad on tap", io_len
));
339 #if BX_ETH_TAP_LOGGING
340 BX_DEBUG(("sendpkt length %u", io_len
));
341 // dump raw bytes to a file, eventually dump in pcap format so that
342 // tcpdump -r FILE can interpret them for us.
343 int n
= fwrite(buf
, io_len
, 1, txlog
);
344 if (n
!= 1) BX_ERROR(("fwrite to txlog failed, io_len = %u", io_len
));
345 // dump packet in hex into an ascii log file
346 fprintf(txlog_txt
, "NE2K transmitting a packet, length %u\n", io_len
);
347 Bit8u
*charbuf
= (Bit8u
*)buf
;
348 for (n
=0; n
<(int)io_len
; n
++) {
349 if (((n
% 16) == 0) && n
>0)
350 fprintf(txlog_txt
, "\n");
351 fprintf(txlog_txt
, "%02x ", charbuf
[n
]);
353 fprintf(txlog_txt
, "\n--\n");
354 // flush log so that we see the packets as they arrive w/o buffering
360 void bx_tap_pktmover_c::rx_timer_handler(void *this_ptr
)
362 bx_tap_pktmover_c
*class_ptr
= (bx_tap_pktmover_c
*) this_ptr
;
363 class_ptr
->rx_timer();
366 void bx_tap_pktmover_c::rx_timer()
369 Bit8u buf
[BX_PACKET_BUFSIZE
];
375 sbuf
.maxlen
= sizeof(buf
);
376 sbuf
.buf
= (char *)buf
;
377 nbytes
= getmsg(fd
, NULL
, &sbuf
, &f
) >=0 ? sbuf
.len
: -1;
379 nbytes
= read (fd
, buf
, sizeof(buf
));
382 // hack: discard first two bytes
383 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__sun__) // Should be fixed for other *BSD
390 #if defined(__linux__)
391 // hack: TAP device likes to create an ethernet header which has
392 // the same source and destination address FE:FD:00:00:00:00.
393 // Change the dest address to FE:FD:00:00:00:01.
394 if (!memcmp(&rxbuf
[0], &rxbuf
[6], 6)) {
395 rxbuf
[5] = guest_macaddr
[5];
400 BX_DEBUG (("tap read returned %d bytes", nbytes
));
403 BX_ERROR (("tap read error: %s", strerror(errno
)));
406 #if BX_ETH_TAP_LOGGING
408 BX_DEBUG (("receive packet length %u", nbytes
));
409 // dump raw bytes to a file, eventually dump in pcap format so that
410 // tcpdump -r FILE can interpret them for us.
411 int n
= fwrite (rxbuf
, nbytes
, 1, rxlog
);
412 if (n
!= 1) BX_ERROR (("fwrite to rxlog failed, nbytes = %d", nbytes
));
413 // dump packet in hex into an ascii log file
414 fprintf (rxlog_txt
, "NE2K received a packet, length %u\n", nbytes
);
415 for (n
=0; n
<nbytes
; n
++) {
416 if (((n
% 16) == 0) && n
>0)
417 fprintf (rxlog_txt
, "\n");
418 fprintf (rxlog_txt
, "%02x ", rxbuf
[n
]);
420 fprintf (rxlog_txt
, "\n--\n");
421 // flush log so that we see the packets as they arrive w/o buffering
426 BX_DEBUG(("eth_tap: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes
, rxbuf
[0], rxbuf
[1], rxbuf
[2], rxbuf
[3], rxbuf
[4], rxbuf
[5], rxbuf
[6], rxbuf
[7], rxbuf
[8], rxbuf
[9], rxbuf
[10], rxbuf
[11]));
428 BX_INFO (("packet too short (%d), padding to 60", nbytes
));
431 (*rxh
)(rxarg
, rxbuf
, nbytes
);
434 #endif /* if BX_NETWORKING && defined HAVE_ETHERTAP */