-/* Copyright (C) 2008 Board of Trustees, Leland Stanford Jr. University.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
*/
#include "netdev.h"
#include "buffer.h"
#include "openflow.h"
#include "packets.h"
+#include "poll-loop.h"
#define THIS_MODULE VLM_netdev
#include "vlog.h"
int fd;
uint8_t etheraddr[ETH_ADDR_LEN];
int speed;
+ int mtu;
uint32_t features;
int save_flags;
};
close(sock);
}
+/* Check whether device NAME has an IPv6 address assigned to it and, if so, log
+ * an error. */
static void
check_ipv6_address(const char *name)
{
}
}
+/* Opens the network device named 'name' (e.g. "eth0") and returns zero if
+ * successful, otherwise a positive errno value. On success, sets '*netdev'
+ * to the new network device, otherwise to null. */
int
netdev_open(const char *name, struct netdev **netdev_)
{
socklen_t rcvbuf_len;
size_t rcvbuf;
uint8_t etheraddr[ETH_ADDR_LEN];
+ int mtu;
int error;
struct netdev *netdev;
rcvbuf -= n_bytes;
}
- /* Get ethernet device index and hardware address. */
+ /* Get ethernet device index. */
strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
VLOG_ERR("ioctl(SIOCGIFINDEX) on %s device failed: %s",
goto error;
}
ifindex = ifr.ifr_ifindex;
+
+ /* Get MAC address. */
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s",
name, strerror(errno));
}
memcpy(etheraddr, ifr.ifr_hwaddr.sa_data, sizeof etheraddr);
+ /* Get MTU. */
+ if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
+ VLOG_ERR("ioctl(SIOCGIFMTU) on %s device failed: %s",
+ name, strerror(errno));
+ goto error;
+ }
+ mtu = ifr.ifr_mtu;
+
/* Allocate network device. */
netdev = xmalloc(sizeof *netdev);
netdev->name = xstrdup(name);
netdev->fd = fd;
memcpy(netdev->etheraddr, etheraddr, sizeof etheraddr);
+ netdev->mtu = mtu;
/* Get speed, features. */
do_ethtool(netdev);
return error;
}
- /* Report IP addresses to administrator. */
+ /* Complain to administrator if any IP addresses are assigned to the
+ * interface. We warn about this because packets received for that IP
+ * address will be processed both by the kernel TCP/IP stack and by us as a
+ * switch, which produces poor results. */
check_ipv4_address(name);
check_ipv6_address(name);
return error;
}
+/* Closes and destroys 'netdev'. */
void
netdev_close(struct netdev *netdev)
{
}
}
+/* Pads 'buffer' out with zero-bytes to the minimum valid length of an
+ * Ethernet packet, if necessary. */
static void
pad_to_minimum_length(struct buffer *buffer)
{
}
}
+/* Attempts to receive a packet from 'netdev' into 'buffer', which the caller
+ * must have initialized with sufficient room for the packet. The space
+ * required to receive any packet is ETH_HEADER_LEN bytes, plus VLAN_HEADER_LEN
+ * bytes, plus the device's MTU (which may be retrieved via netdev_get_mtu()).
+ * (Some devices do not allow for a VLAN header, in which case VLAN_HEADER_LEN
+ * need not be included.)
+ *
+ * If a packet is successfully retrieved, returns 0. In this case 'buffer' is
+ * guaranteed to contain at least ETH_TOTAL_MIN bytes. Otherwise, returns a
+ * positive errno value. Returns EAGAIN immediately if no packet is ready to
+ * be returned.
+ */
int
-netdev_recv(struct netdev *netdev, struct buffer *buffer, bool block)
+netdev_recv(struct netdev *netdev, struct buffer *buffer)
{
ssize_t n_bytes;
do {
n_bytes = recv(netdev->fd,
buffer_tail(buffer), buffer_tailroom(buffer),
- block ? 0 : MSG_DONTWAIT);
+ MSG_DONTWAIT);
} while (n_bytes < 0 && errno == EINTR);
if (n_bytes < 0) {
if (errno != EAGAIN) {
}
}
+/* Registers with the poll loop to wake up from the next call to poll_block()
+ * when a packet is ready to be received with netdev_recv() on 'netdev'. */
+void
+netdev_recv_wait(struct netdev *netdev)
+{
+ poll_fd_wait(netdev->fd, POLLIN, NULL);
+}
+
+/* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive
+ * errno value. Returns EAGAIN without blocking if the packet cannot be queued
+ * immediately. Returns EMSGSIZE if a partial packet was transmitted or if
+ * the packet is too big to transmit on the device.
+ *
+ * The kernel maintains a packet transmission queue, so the caller is not
+ * expected to do additional queuing of packets. */
int
-netdev_send(struct netdev *netdev, struct buffer *buffer, bool block)
+netdev_send(struct netdev *netdev, struct buffer *buffer)
{
ssize_t n_bytes;
const struct eth_header *eh;
/* Ensure packet is long enough. (Although all incoming packets are at
* least ETH_TOTAL_MIN bytes long, we could have trimmed some data off a
- * minimum-size packet, e.g. by dropping a vlan header.) */
+ * minimum-size packet, e.g. by dropping a vlan header.)
+ *
+ * The kernel does not require this, but it ensures that we always access
+ * valid memory in grabbing the sockaddr below. */
pad_to_minimum_length(buffer);
/* Construct packet sockaddr, which SOCK_PACKET requires. */
spkt.spkt_protocol = eh->eth_type;
do {
- n_bytes = sendto(netdev->fd, buffer->data, buffer->size,
- block ? 0 : MSG_DONTWAIT,
+ n_bytes = sendto(netdev->fd, buffer->data, buffer->size, 0,
(const struct sockaddr *) &spkt, sizeof spkt);
} while (n_bytes < 0 && errno == EINTR);
+
if (n_bytes < 0) {
- if (errno != EAGAIN) {
+ /* The Linux AF_PACKET implementation never blocks waiting for room
+ * for packets, instead returning ENOBUFS. Translate this into EAGAIN
+ * for the caller. */
+ if (errno == ENOBUFS) {
+ return EAGAIN;
+ } else if (errno != EAGAIN) {
VLOG_WARN("error sending Ethernet packet on %s: %s",
- netdev->name, strerror(errno));
+ netdev->name, strerror(errno));
}
return errno;
} else if (n_bytes != buffer->size) {
}
}
-const uint8_t *
-netdev_get_etheraddr(const struct netdev *netdev)
+/* Registers with the poll loop to wake up from the next call to poll_block()
+ * when the packet transmission queue has sufficient room to transmit a packet
+ * with netdev_send().
+ *
+ * The kernel maintains a packet transmission queue, so the client is not
+ * expected to do additional queuing of packets. Thus, this function is
+ * unlikely to ever be used. It is included for completeness. */
+void
+netdev_send_wait(struct netdev *netdev)
{
- return netdev->etheraddr;
+ poll_fd_wait(netdev->fd, POLLOUT, NULL);
}
-int
-netdev_get_fd(const struct netdev *netdev)
+/* Returns a pointer to 'netdev''s MAC address. The caller must not modify or
+ * free the returned buffer. */
+const uint8_t *
+netdev_get_etheraddr(const struct netdev *netdev)
{
- return netdev->fd;
+ return netdev->etheraddr;
}
+/* Returns the name of the network device that 'netdev' represents,
+ * e.g. "eth0". The caller must not modify or free the returned string. */
const char *
netdev_get_name(const struct netdev *netdev)
{
return netdev->name;
}
+/* Returns the maximum size of transmitted (and received) packets on 'netdev',
+ * in bytes, not including the hardware header; thus, this is typically 1500
+ * bytes for Ethernet devices. */
+int
+netdev_get_mtu(const struct netdev *netdev)
+{
+ return netdev->mtu;
+}
+
+/* Returns the current speed of the network device that 'netdev' represents, in
+ * megabits per second, or 0 if the speed is unknown. */
int
netdev_get_speed(const struct netdev *netdev)
{
return netdev->speed;
}
+/* Returns the features supported by 'netdev', as a bitmap of bits from enum
+ * ofp_phy_port, in host byte order. */
uint32_t
netdev_get_features(const struct netdev *netdev)
{
\f
static void restore_all_flags(void *aux);
+/* Set up a signal hook to restore network device flags on program
+ * termination. */
static void
init_netdev(void)
{
}
}
+/* Restore the network device flags on 'netdev' to those that were active
+ * before we changed them. Returns 0 if successful, otherwise a positive
+ * errno value.
+ *
+ * To avoid reentry, the caller must ensure that fatal signals are blocked. */
static int
restore_flags(struct netdev *netdev)
{
return 0;
}
+/* Retores all the flags on all network devices that we modified. Called from
+ * a signal handler, so it does not attempt to report error conditions. */
static void
restore_all_flags(void *aux UNUSED)
{