X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=lib%2Fnetdev-linux.c;h=18d327425a8afd46f6e12552f41c5f3cb40828e4;hb=36775dad3505929f8370166c33e8e0f04ca96c1c;hp=11d83e9716e559701de5e477a9259d1fbe9b7571;hpb=8398cf7efc3979cd6c8152915a584c13873ee322;p=openvswitch diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 11d83e97..18d32742 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +50,7 @@ #include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" +#include "openvswitch/gre.h" #include "packets.h" #include "poll-loop.h" #include "rtnetlink.h" @@ -55,6 +58,10 @@ #include "shash.h" #include "svec.h" +#ifndef GRE_IOCTL_ONLY +#include +#endif + #define THIS_MODULE VLM_netdev_linux #include "vlog.h" @@ -67,6 +74,19 @@ #define ADVERTISED_Asym_Pause (1 << 14) #endif +struct tap_state { + int tap_fd; /* File descriptor for TAP device. */ +}; + +/* Provider-specific netdev object. Netdev objects are devices that are + * created by the netdev library through a netdev_create() call. */ +struct netdev_obj_linux { + struct netdev_obj netdev_obj; + union { + struct tap_state tap; + } state; +}; + struct netdev_linux { struct netdev netdev; @@ -85,7 +105,8 @@ enum { VALID_IN4 = 1 << 2, VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, - VALID_CARRIER = 1 << 5 + VALID_CARRIER = 1 << 5, + VALID_IS_INTERNAL = 1 << 6 }; /* Cached network device information. */ @@ -100,6 +121,7 @@ struct netdev_linux_cache { struct in6_addr in6; int mtu; int carrier; + bool is_internal; }; static struct shash cache_map = SHASH_INITIALIZER(&cache_map); @@ -108,6 +130,25 @@ static struct rtnetlink_notifier netdev_linux_cache_notifier; /* An AF_INET socket (used for ioctl operations). */ static int af_inet_sock = -1; +struct gre_config { + uint32_t local_ip; + uint32_t remote_ip; + uint32_t in_key; + uint32_t out_key; + bool have_in_key; + bool have_out_key; + bool in_csum; + bool out_csum; +}; + +static struct { + union { + struct nl_sock *nl_sock; + int ioctl_fd; + }; + bool use_ioctl; +} gre_descriptors; + struct netdev_linux_notifier { struct netdev_notifier notifier; struct list node; @@ -140,6 +181,16 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family, static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats); static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats); +static struct netdev_obj_linux * +netdev_obj_linux_cast(const struct netdev_obj *netdev_obj) +{ + /* xxx Gross hack! Until we get the class 'type' issue resolved. */ +#if 0 + netdev_obj_assert_class(netdev_obj, &netdev_linux_class); +#endif + return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj); +} + static struct netdev_linux * netdev_linux_cast(const struct netdev *netdev) { @@ -192,20 +243,454 @@ netdev_linux_cache_cb(const struct rtnetlink_change *change, } } +/* The arguments are marked as unused to prevent warnings on platforms where + * the Netlink interface isn't supported. */ +static int +setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED, + bool create UNUSED) +{ +#ifdef GRE_IOCTL_ONLY + return EOPNOTSUPP; +#else + int error; + struct ofpbuf request, *reply; + unsigned int nl_flags; + struct ifinfomsg ifinfomsg; + struct nlattr *linkinfo_hdr; + struct nlattr *info_data_hdr; + uint16_t iflags = 0; + uint16_t oflags = 0; + uint8_t pmtudisc = 0; + + if (!gre_descriptors.nl_sock) { + error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, + &gre_descriptors.nl_sock); + if (error) { + VLOG_WARN("couldn't create netlink socket: %s\n", strerror(error)); + gre_descriptors.nl_sock = NULL; + goto error; + } + } + + ofpbuf_init(&request, 0); + + nl_flags = NLM_F_REQUEST; + if (create) { + nl_flags |= NLM_F_CREATE|NLM_F_EXCL; + } + + /* We over-reserve space, because we do some pointer arithmetic + * and don't want the buffer address shifting under us. */ + nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 2048, RTM_NEWLINK, + nl_flags); + + memset(&ifinfomsg, 0, sizeof ifinfomsg); + ifinfomsg.ifi_family = AF_UNSPEC; + nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); + + linkinfo_hdr = ofpbuf_tail(&request); + nl_msg_put_unspec(&request, IFLA_LINKINFO, NULL, 0); + + nl_msg_put_unspec(&request, IFLA_INFO_KIND, "gretap", 6); + + info_data_hdr = ofpbuf_tail(&request); + nl_msg_put_unspec(&request, IFLA_INFO_DATA, NULL, 0); + + /* Set flags */ + if (config->have_in_key) { + iflags |= GRE_KEY; + } + if (config->have_out_key) { + oflags |= GRE_KEY; + } + + if (config->in_csum) { + iflags |= GRE_CSUM; + } + if (config->out_csum) { + oflags |= GRE_CSUM; + } + + /* Add options */ + nl_msg_put_u32(&request, IFLA_GRE_IKEY, config->in_key); + nl_msg_put_u32(&request, IFLA_GRE_OKEY, config->out_key); + nl_msg_put_u16(&request, IFLA_GRE_IFLAGS, iflags); + nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags); + nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip); + nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip); + nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc); + nl_msg_put_u8(&request, IFLA_GRE_TTL, 0); + nl_msg_put_u8(&request, IFLA_GRE_TOS, 0); + + info_data_hdr->nla_len = (char *)ofpbuf_tail(&request) + - (char *)info_data_hdr; + linkinfo_hdr->nla_len = (char *)ofpbuf_tail(&request) + - (char *)linkinfo_hdr; + + nl_msg_put_string(&request, IFLA_IFNAME, name); + + error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); + ofpbuf_uninit(&request); + if (error) { + VLOG_WARN("couldn't transact netlink socket: %s\n", strerror(error)); + goto error; + } + ofpbuf_delete(reply); + +error: + return error; +#endif +} + +static int +setup_gre_ioctl(const char *name, struct gre_config *config, bool create) +{ + struct ip_tunnel_parm p; + struct ifreq ifr; + + memset(&p, 0, sizeof p); + + strncpy(p.name, name, IFNAMSIZ); + + p.iph.version = 4; + p.iph.ihl = 5; + p.iph.protocol = IPPROTO_GRE; + p.iph.saddr = config->local_ip; + p.iph.daddr = config->remote_ip; + + if (config->have_in_key) { + p.i_flags |= GRE_KEY; + p.i_key = config->in_key; + } + if (config->have_out_key) { + p.o_flags |= GRE_KEY; + p.o_key = config->out_key; + } + + if (config->in_csum) { + p.i_flags |= GRE_CSUM; + } + if (config->out_csum) { + p.o_flags |= GRE_CSUM; + } + + strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void *)&p; + + if (!gre_descriptors.ioctl_fd) { + gre_descriptors.ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (gre_descriptors.ioctl_fd < 0) { + VLOG_WARN("couldn't create gre ioctl socket: %s\n", strerror(errno)); + gre_descriptors.ioctl_fd = 0; + return errno; + } + } + + if (ioctl(gre_descriptors.ioctl_fd, create ? SIOCADDGRETAP : SIOCCHGGRETAP, + &ifr) < 0) { + VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno)); + return errno; + } + + return 0; +} + +static int +setup_gre(const char *name, const struct shash *args, bool create) +{ + int error; + struct in_addr in_addr; + struct shash_node *node; + struct gre_config config; + + memset(&config, 0, sizeof config); + config.in_csum = true; + config.out_csum = true; + + SHASH_FOR_EACH (node, args) { + if (!strcmp(node->name, "remote_ip")) { + if (lookup_ip(node->data, &in_addr)) { + VLOG_WARN("bad 'remote_ip' for gre device %s ", name); + } else { + config.remote_ip = in_addr.s_addr; + } + } else if (!strcmp(node->name, "local_ip")) { + if (lookup_ip(node->data, &in_addr)) { + VLOG_WARN("bad 'local_ip' for gre device %s ", name); + } else { + config.local_ip = in_addr.s_addr; + } + } else if (!strcmp(node->name, "key")) { + config.have_in_key = true; + config.have_out_key = true; + config.in_key = htonl(atoi(node->data)); + config.out_key = htonl(atoi(node->data)); + } else if (!strcmp(node->name, "in_key")) { + config.have_in_key = true; + config.in_key = htonl(atoi(node->data)); + } else if (!strcmp(node->name, "out_key")) { + config.have_out_key = true; + config.out_key = htonl(atoi(node->data)); + } else if (!strcmp(node->name, "csum")) { + if (!strcmp(node->data, "false")) { + config.in_csum = false; + config.out_csum = false; + } + } else { + VLOG_WARN("unknown gre argument '%s'", node->name); + } + } + + if (!config.remote_ip) { + VLOG_WARN("gre type requires valid 'remote_ip' argument"); + error = EINVAL; + goto error; + } + + if (!gre_descriptors.use_ioctl) { + error = setup_gre_netlink(name, &config, create); + if (error == EOPNOTSUPP) { + gre_descriptors.use_ioctl = true; + } + } + if (gre_descriptors.use_ioctl) { + error = setup_gre_ioctl(name, &config, create); + } + +error: + return error; +} + +static int +if_up(const char *name) +{ + struct ifreq ifr; + + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + ifr.ifr_flags = IFF_UP; + + if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) { + VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s", + name, strerror(errno)); + return errno; + } + + return 0; +} + +/* Creates the netdev object of 'type' with 'name'. */ +static int +netdev_linux_create_system(const char *name, const char *type UNUSED, + const struct shash *args, bool created) +{ + struct netdev_obj_linux *netdev_obj; + + if (!shash_is_empty(args)) { + VLOG_WARN("arguments for system devices should be empty"); + } + + netdev_obj = xcalloc(1, sizeof *netdev_obj); + netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class, + created); + + return 0; +} + +static int +netdev_linux_create_tap(const char *name, const char *type UNUSED, + const struct shash *args, bool created) +{ + struct netdev_obj_linux *netdev_obj; + struct tap_state *state; + static const char tap_dev[] = "/dev/net/tun"; + struct ifreq ifr; + int error; + + if (!shash_is_empty(args)) { + VLOG_WARN("arguments for TAP devices should be empty"); + } + + netdev_obj = xcalloc(1, sizeof *netdev_obj); + netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_tap_class, created); + + state = &netdev_obj->state.tap; + + /* Open tap device. */ + state->tap_fd = open(tap_dev, O_RDWR); + if (state->tap_fd < 0) { + error = errno; + VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error)); + goto error; + } + + /* Create tap device. */ + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + if (ioctl(state->tap_fd, TUNSETIFF, &ifr) == -1) { + VLOG_WARN("%s: creating tap device failed: %s", name, + strerror(errno)); + error = errno; + goto error; + } + + /* Make non-blocking. */ + error = set_nonblocking(state->tap_fd); + if (error) { + goto error; + } + + error = if_up(name); + if (error) { + goto error; + } + + return 0; + +error: + netdev_destroy(name); + return error; +} + +static int +netdev_linux_create_gre(const char *name, const char *type UNUSED, + const struct shash *args, bool created) +{ + struct netdev_obj_linux *netdev_obj; + int error; + + netdev_obj = xcalloc(1, sizeof *netdev_obj); + netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_gre_class, created); + + error = setup_gre(name, args, true); + if (error) { + goto error; + } + + error = if_up(name); + if (error) { + goto error; + } + + return 0; + +error: + netdev_destroy(name); + return error; +} + +static int +netdev_linux_reconfigure_gre(struct netdev_obj *netdev_obj_, + const struct shash *args) +{ + struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_); + const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj); + + return setup_gre(name, args, false); +} + +/* The arguments are marked as unused to prevent warnings on platforms where + * the Netlink interface isn't supported. */ +static int +destroy_gre_netlink(struct netdev_obj_linux *netdev_obj UNUSED) +{ +#ifdef GRE_IOCTL_ONLY + return EOPNOTSUPP; +#else + const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj); + int error; + struct ofpbuf request, *reply; + struct ifinfomsg ifinfomsg; + int ifindex; + + ofpbuf_init(&request, 0); + + nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 0, RTM_DELLINK, + NLM_F_REQUEST); + + memset(&ifinfomsg, 0, sizeof ifinfomsg); + ifinfomsg.ifi_family = AF_UNSPEC; + nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); + + ifindex = do_get_ifindex(name); + nl_msg_put_u32(&request, IFLA_LINK, ifindex); + + nl_msg_put_string(&request, IFLA_IFNAME, name); + + error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); + ofpbuf_uninit(&request); + if (error) { + VLOG_WARN("couldn't transact netlink socket: %s\n", strerror(error)); + goto error; + } + ofpbuf_delete(reply); + +error: + return 0; +#endif +} + +static int +destroy_gre_ioctl(struct netdev_obj_linux *netdev_obj) +{ + const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj); + struct ip_tunnel_parm p; + struct ifreq ifr; + + memset(&p, 0, sizeof p); + strncpy(p.name, name, IFNAMSIZ); + + strncpy(ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void *)&p; + + if (ioctl(gre_descriptors.ioctl_fd, SIOCDELGRETAP, &ifr) < 0) { + VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno)); + return errno; + } + + return 0; +} + +static void +destroy_tap(struct netdev_obj_linux *netdev_obj) +{ + struct tap_state *state = &netdev_obj->state.tap; + if (state->tap_fd >= 0) { + close(state->tap_fd); + } +} + +/* Destroys the netdev object 'netdev_obj_'. */ +static void +netdev_linux_destroy(struct netdev_obj *netdev_obj_) +{ + struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_); + const char *type = netdev_obj_get_type(netdev_obj_); + + if (!strcmp(type, "tap")) { + destroy_tap(netdev_obj); + } else if (!strcmp(type, "gre")) { + if (gre_descriptors.use_ioctl) { + destroy_gre_ioctl(netdev_obj); + } else { + destroy_gre_netlink(netdev_obj); + } + } + free(netdev_obj); +} + static int -netdev_linux_open(const char *name, char *suffix, int ethertype, - struct netdev **netdevp) +netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) { struct netdev_linux *netdev; enum netdev_flags flags; int error; /* Allocate network device. */ - netdev = xcalloc(1, sizeof *netdev); - netdev_init(&netdev->netdev, suffix, &netdev_linux_class); + netdev = xzalloc(sizeof *netdev); + netdev_init(&netdev->netdev, name, &netdev_linux_class); netdev->netdev_fd = -1; netdev->tap_fd = -1; - netdev->cache = shash_find_data(&cache_map, suffix); + netdev->cache = shash_find_data(&cache_map, name); if (!netdev->cache) { if (shash_is_empty(&cache_map)) { int error = rtnetlink_notifier_register( @@ -216,14 +701,14 @@ netdev_linux_open(const char *name, char *suffix, int ethertype, } } netdev->cache = xmalloc(sizeof *netdev->cache); - netdev->cache->shash_node = shash_add(&cache_map, suffix, + netdev->cache->shash_node = shash_add(&cache_map, name, netdev->cache); netdev->cache->valid = 0; netdev->cache->ref_cnt = 0; } netdev->cache->ref_cnt++; - if (!strncmp(name, "tap:", 4)) { + if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) { static const char tap_dev[] = "/dev/net/tun"; struct ifreq ifr; @@ -237,9 +722,11 @@ netdev_linux_open(const char *name, char *suffix, int ethertype, /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - error = netdev_linux_do_ioctl(&netdev->netdev, &ifr, - TUNSETIFF, "TUNSETIFF"); - if (error) { + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) { + VLOG_WARN("%s: creating tap device failed: %s", name, + strerror(errno)); + error = errno; goto error; } @@ -292,7 +779,7 @@ netdev_linux_open(const char *name, char *suffix, int ethertype, if (bind(netdev->netdev_fd, (struct sockaddr *) &sll, sizeof sll) < 0) { error = errno; - VLOG_ERR("bind to %s failed: %s", suffix, strerror(error)); + VLOG_ERR("bind to %s failed: %s", name, strerror(error)); goto error; } @@ -366,7 +853,7 @@ netdev_linux_recv(struct netdev *netdev_, void *data, size_t size) if (netdev->tap_fd < 0) { /* Device was opened with NETDEV_ETH_TYPE_NONE. */ - return EAGAIN; + return -EAGAIN; } for (;;) { @@ -378,7 +865,7 @@ netdev_linux_recv(struct netdev *netdev_, void *data, size_t size) VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", strerror(errno), netdev_get_name(netdev_)); } - return errno; + return -errno; } } } @@ -488,9 +975,17 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, const uint8_t mac[ETH_ADDR_LEN]) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - int error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); - if (!error) { - memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN); + int error; + + if (!(netdev->cache->valid & VALID_ETHERADDR) + || !eth_addr_equals(netdev->cache->etheraddr, mac)) { + error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); + if (!error) { + netdev->cache->valid |= VALID_ETHERADDR; + memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN); + } + } else { + error = 0; } return error; } @@ -536,6 +1031,17 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) return 0; } +/* Returns the ifindex of 'netdev', if successful, as a positive number. + * On failure, returns a negative errno value. */ +static int +netdev_linux_get_ifindex(const struct netdev *netdev) +{ + int ifindex, error; + + error = get_ifindex(netdev, &ifindex); + return error ? -error : ifindex; +} + static int netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) { @@ -625,33 +1131,90 @@ check_for_working_netlink_stats(void) * XXX All of the members of struct netdev_stats are 64 bits wide, but on * 32-bit architectures the Linux network stats are only 32 bits. */ static int -netdev_linux_get_stats(const struct netdev *netdev, struct netdev_stats *stats) +netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); static int use_netlink_stats = -1; int error; + struct netdev_stats raw_stats; + struct netdev_stats *collect_stats = stats; COVERAGE_INC(netdev_get_stats); + + if (!(netdev->cache->valid & VALID_IS_INTERNAL)) { + netdev->cache->is_internal = (netdev->tap_fd != -1); + + if (!netdev->cache->is_internal) { + struct ethtool_drvinfo drvinfo; + + memset(&drvinfo, 0, sizeof drvinfo); + error = netdev_linux_do_ethtool(&netdev->netdev, + (struct ethtool_cmd *)&drvinfo, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + + if (!error) { + netdev->cache->is_internal = !strcmp(drvinfo.driver, + "openvswitch"); + } + } + + netdev->cache->valid |= VALID_IS_INTERNAL; + } + + if (netdev->cache->is_internal) { + collect_stats = &raw_stats; + } + if (use_netlink_stats < 0) { use_netlink_stats = check_for_working_netlink_stats(); } if (use_netlink_stats) { int ifindex; - error = get_ifindex(netdev, &ifindex); + error = get_ifindex(&netdev->netdev, &ifindex); if (!error) { - error = get_stats_via_netlink(ifindex, stats); + error = get_stats_via_netlink(ifindex, collect_stats); } } else { - error = get_stats_via_proc(netdev->name, stats); + error = get_stats_via_proc(netdev->netdev.name, collect_stats); + } + + /* If this port is an internal port then the transmit and receive stats + * will appear to be swapped relative to the other ports since we are the + * one sending the data, not a remote computer. For consistency, we swap + * them back here. */ + if (netdev->cache->is_internal) { + stats->rx_packets = raw_stats.tx_packets; + stats->tx_packets = raw_stats.rx_packets; + stats->rx_bytes = raw_stats.tx_bytes; + stats->tx_bytes = raw_stats.rx_bytes; + stats->rx_errors = raw_stats.tx_errors; + stats->tx_errors = raw_stats.rx_errors; + stats->rx_dropped = raw_stats.tx_dropped; + stats->tx_dropped = raw_stats.rx_dropped; + stats->multicast = raw_stats.multicast; + stats->collisions = raw_stats.collisions; + stats->rx_length_errors = 0; + stats->rx_over_errors = 0; + stats->rx_crc_errors = 0; + stats->rx_frame_errors = 0; + stats->rx_fifo_errors = 0; + stats->rx_missed_errors = 0; + stats->tx_aborted_errors = 0; + stats->tx_carrier_errors = 0; + stats->tx_fifo_errors = 0; + stats->tx_heartbeat_errors = 0; + stats->tx_window_errors = 0; } + return error; } /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer' that are non-null. Each value is a * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if - * successful, otherwise a positive errno value. On failure, all of the - * passed-in values are set to 0. */ + * successful, otherwise a positive errno value. */ static int netdev_linux_get_features(struct netdev *netdev, uint32_t *current, uint32_t *advertised, @@ -1147,14 +1710,14 @@ netdev_linux_arp_lookup(const struct netdev *netdev, uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) { struct arpreq r; - struct sockaddr_in *pa; + struct sockaddr_in sin; int retval; memset(&r, 0, sizeof r); - pa = (struct sockaddr_in *) &r.arp_pa; - pa->sin_family = AF_INET; - pa->sin_addr.s_addr = ip; - pa->sin_port = 0; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ip; + sin.sin_port = 0; + memcpy(&r.arp_pa, &sin, sizeof sin); r.arp_ha.sa_family = ARPHRD_ETHER; r.arp_flags = 0; strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev); @@ -1297,13 +1860,16 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) } const struct netdev_class netdev_linux_class = { - "", /* prefix */ - "linux", /* name */ + "system", /* type */ netdev_linux_init, netdev_linux_run, netdev_linux_wait, + netdev_linux_create_system, + netdev_linux_destroy, + NULL, /* reconfigure */ + netdev_linux_open, netdev_linux_close, @@ -1319,6 +1885,7 @@ const struct netdev_class netdev_linux_class = { netdev_linux_set_etheraddr, netdev_linux_get_etheraddr, netdev_linux_get_mtu, + netdev_linux_get_ifindex, netdev_linux_get_carrier, netdev_linux_get_stats, @@ -1341,13 +1908,16 @@ const struct netdev_class netdev_linux_class = { }; const struct netdev_class netdev_tap_class = { - "tap", /* prefix */ - "tap", /* name */ + "tap", /* type */ netdev_linux_init, NULL, /* run */ NULL, /* wait */ + netdev_linux_create_tap, + netdev_linux_destroy, + NULL, /* reconfigure */ + netdev_linux_open, netdev_linux_close, @@ -1363,6 +1933,55 @@ const struct netdev_class netdev_tap_class = { netdev_linux_set_etheraddr, netdev_linux_get_etheraddr, netdev_linux_get_mtu, + netdev_linux_get_ifindex, + netdev_linux_get_carrier, + netdev_linux_get_stats, + + netdev_linux_get_features, + netdev_linux_set_advertisements, + netdev_linux_get_vlan_vid, + netdev_linux_set_policing, + + netdev_linux_get_in4, + netdev_linux_set_in4, + netdev_linux_get_in6, + netdev_linux_add_router, + netdev_linux_get_next_hop, + netdev_linux_arp_lookup, + + netdev_linux_update_flags, + + netdev_linux_poll_add, + netdev_linux_poll_remove, +}; + +const struct netdev_class netdev_gre_class = { + "gre", /* type */ + + netdev_linux_init, + NULL, /* run */ + NULL, /* wait */ + + netdev_linux_create_gre, + netdev_linux_destroy, + netdev_linux_reconfigure_gre, + + netdev_linux_open, + netdev_linux_close, + + netdev_linux_enumerate, + + netdev_linux_recv, + netdev_linux_recv_wait, + netdev_linux_drain, + + netdev_linux_send, + netdev_linux_send_wait, + + netdev_linux_set_etheraddr, + netdev_linux_get_etheraddr, + netdev_linux_get_mtu, + netdev_linux_get_ifindex, netdev_linux_get_carrier, netdev_linux_get_stats, @@ -1436,6 +2055,7 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats) if (!attrs[IFLA_STATS]) { VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats"); + ofpbuf_delete(reply); return EPROTO; } @@ -1462,6 +2082,8 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats) stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors; stats->tx_window_errors = rtnl_stats->tx_window_errors; + ofpbuf_delete(reply); + return 0; }