2 * Copyright (C) 2010 Piotr JaroszyĆski <p.jaroszynski@gmail.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <linux_api.h>
23 #include <ipxe/list.h>
24 #include <ipxe/linux.h>
25 #include <ipxe/malloc.h>
26 #include <ipxe/device.h>
27 #include <ipxe/netdevice.h>
28 #include <ipxe/iobuf.h>
29 #include <ipxe/ethernet.h>
30 #include <ipxe/settings.h>
31 #include <ipxe/socket.h>
33 /* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */
35 #include <linux/socket.h>
38 #include <linux/if_ether.h>
39 #include <linux/if_tun.h>
41 #define RX_BUF_SIZE 1536
47 * The TAP is a Virtual Ethernet network device.
51 /** Tap interface name */
53 /** File descriptor of the opened tap device */
57 /** Open the TAP device */
58 static int tap_open(struct net_device
* netdev
)
60 struct tap_nic
* nic
= netdev
->priv
;
64 nic
->fd
= linux_open("/dev/net/tun", O_RDWR
);
66 DBGC(nic
, "tap %p open('/dev/net/tun') = %d (%s)\n", nic
, nic
->fd
, linux_strerror(linux_errno
));
70 memset(&ifr
, 0, sizeof(ifr
));
71 /* IFF_NO_PI for no extra packet information */
72 ifr
.ifr_flags
= IFF_TAP
| IFF_NO_PI
;
73 strncpy(ifr
.ifr_name
, nic
->interface
, IFNAMSIZ
);
74 DBGC(nic
, "tap %p interface = '%s'\n", nic
, nic
->interface
);
76 ret
= linux_ioctl(nic
->fd
, TUNSETIFF
, &ifr
);
79 DBGC(nic
, "tap %p ioctl(%d, ...) = %d (%s)\n", nic
, nic
->fd
, ret
, linux_strerror(linux_errno
));
84 /* Set nonblocking mode to make tap_poll easier */
85 ret
= linux_fcntl(nic
->fd
, F_SETFL
, O_NONBLOCK
);
88 DBGC(nic
, "tap %p fcntl(%d, ...) = %d (%s)\n", nic
, nic
->fd
, ret
, linux_strerror(linux_errno
));
96 /** Close the TAP device */
97 static void tap_close(struct net_device
*netdev
)
99 struct tap_nic
* nic
= netdev
->priv
;
100 linux_close(nic
->fd
);
104 * Transmit an ethernet packet.
106 * The packet can be written to the TAP device and marked as complete immediately.
108 static int tap_transmit(struct net_device
*netdev
, struct io_buffer
*iobuf
)
110 struct tap_nic
* nic
= netdev
->priv
;
113 /* Pad and align packet */
114 iob_pad(iobuf
, ETH_ZLEN
);
116 rc
= linux_write(nic
->fd
, iobuf
->data
, iobuf
->tail
- iobuf
->data
);
117 DBGC2(nic
, "tap %p wrote %d bytes\n", nic
, rc
);
118 netdev_tx_complete(netdev
, iobuf
);
123 /** Poll for new packets */
124 static void tap_poll(struct net_device
*netdev
)
126 struct tap_nic
* nic
= netdev
->priv
;
128 struct io_buffer
* iobuf
;
133 if (linux_poll(&pfd
, 1, 0) == -1) {
134 DBGC(nic
, "tap %p poll failed (%s)\n", nic
, linux_strerror(linux_errno
));
137 if ((pfd
.revents
& POLLIN
) == 0)
140 /* At this point we know there is at least one new packet to be read */
142 iobuf
= alloc_iob(RX_BUF_SIZE
);
146 while ((r
= linux_read(nic
->fd
, iobuf
->data
, RX_BUF_SIZE
)) > 0) {
147 DBGC2(nic
, "tap %p read %d bytes\n", nic
, r
);
150 netdev_rx(netdev
, iobuf
);
152 iobuf
= alloc_iob(RX_BUF_SIZE
);
161 DBGC(nic
, "tap %p alloc_iob failed\n", nic
);
167 * Not used on linux, provide a dummy implementation.
169 static void tap_irq(struct net_device
*netdev
, int enable
)
171 struct tap_nic
*nic
= netdev
->priv
;
173 DBGC(nic
, "tap %p irq enable = %d\n", nic
, enable
);
176 /** Tap operations */
177 static struct net_device_operations tap_operations
= {
180 .transmit
= tap_transmit
,
185 /** Handle a device request for the tap driver */
186 static int tap_probe(struct linux_device
*device
, struct linux_device_request
*request
)
188 struct linux_setting
*if_setting
;
189 struct net_device
*netdev
;
193 netdev
= alloc_etherdev(sizeof(*nic
));
197 netdev_init(netdev
, &tap_operations
);
199 linux_set_drvdata(device
, netdev
);
200 netdev
->dev
= &device
->dev
;
201 memset(nic
, 0, sizeof(*nic
));
203 /* Look for the mandatory if setting */
204 if_setting
= linux_find_setting("if", &request
->settings
);
208 printf("tap missing a mandatory if setting\n");
213 nic
->interface
= if_setting
->value
;
214 snprintf ( device
->dev
.name
, sizeof ( device
->dev
.name
), "%s",
216 device
->dev
.desc
.bus_type
= BUS_TYPE_TAP
;
217 if_setting
->applied
= 1;
219 /* Apply rest of the settings */
220 linux_apply_settings(&request
->settings
, &netdev
->settings
.settings
);
222 /* Register network device */
223 if ((rc
= register_netdev(netdev
)) != 0)
226 netdev_link_up(netdev
);
231 unregister_netdev(netdev
);
233 netdev_nullify(netdev
);
238 /** Remove the device */
239 static void tap_remove(struct linux_device
*device
)
241 struct net_device
*netdev
= linux_get_drvdata(device
);
242 unregister_netdev(netdev
);
243 netdev_nullify(netdev
);
247 /** Tap linux_driver */
248 struct linux_driver tap_driver __linux_driver
= {
251 .remove
= tap_remove
,