X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev.c;h=bed480f71159e3f734952b37fe7aec5da8d1e9ae;hb=f2d7fd66cf51b83acbe509f8ef6d4b34538d0646;hp=7fd070eb6041d786fc83a840562e398759e10efd;hpb=064af42167bf4fc9aaea2702d80ce08074b889c0;p=sliver-openvswitch.git diff --git a/lib/netdev.c b/lib/netdev.c index 7fd070eb6..bed480f71 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -1,17 +1,17 @@ /* * Copyright (c) 2008, 2009 Nicira Networks. * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #include @@ -572,7 +572,7 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) if (n_bytes < 0) { if (errno != EAGAIN) { VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", - strerror(errno), netdev->name); + netdev->name, strerror(errno)); } return errno; } else { @@ -781,15 +781,19 @@ netdev_set_advertisements(struct netdev *netdev, uint32_t advertise) return do_ethtool(netdev, &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET"); } -/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if - * 'in4' is non-null) and returns true. Otherwise, returns false. */ +/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address + * and '*mask' to the netmask (if they are non-null) and returns true. + * Otherwise, returns false. */ bool -netdev_get_in4(const struct netdev *netdev, struct in_addr *in4) +netdev_nodev_get_in4(const char *netdev_name, struct in_addr *in4, + struct in_addr *mask) { struct ifreq ifr; struct in_addr ip = { INADDR_ANY }; - strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); + init_netdev(); + + strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); ifr.ifr_addr.sa_family = AF_INET; COVERAGE_INC(netdev_get_in4); if (ioctl(af_inet_sock, SIOCGIFADDR, &ifr) == 0) { @@ -797,14 +801,32 @@ netdev_get_in4(const struct netdev *netdev, struct in_addr *in4) ip = sin->sin_addr; } else { VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFADDR) failed: %s", - netdev->name, strerror(errno)); + netdev_name, strerror(errno)); } if (in4) { *in4 = ip; } + + if (mask) { + if (ioctl(af_inet_sock, SIOCGIFNETMASK, &ifr) == 0) { + struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + *mask = sin->sin_addr; + } else { + VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFNETMASK) failed: %s", + netdev_name, strerror(errno)); + } + } + return ip.s_addr != INADDR_ANY; } +bool +netdev_get_in4(const struct netdev *netdev, struct in_addr *in4, struct + in_addr *mask) +{ + return netdev_nodev_get_in4(netdev->name, in4, mask); +} + static void make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr) { @@ -970,13 +992,15 @@ netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags, * returns 0. Otherwise, it returns a positive errno value; in particular, * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */ int -netdev_arp_lookup(const struct netdev *netdev, - uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) +netdev_nodev_arp_lookup(const char *netdev_name, uint32_t ip, + uint8_t mac[ETH_ADDR_LEN]) { struct arpreq r; struct sockaddr_in *pa; int retval; + init_netdev(); + memset(&r, 0, sizeof r); pa = (struct sockaddr_in *) &r.arp_pa; pa->sin_family = AF_INET; @@ -984,18 +1008,25 @@ netdev_arp_lookup(const struct netdev *netdev, pa->sin_port = 0; r.arp_ha.sa_family = ARPHRD_ETHER; r.arp_flags = 0; - strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev); + strncpy(r.arp_dev, netdev_name, sizeof r.arp_dev); COVERAGE_INC(netdev_arp_lookup); retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0; if (!retval) { memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s", - netdev->name, IP_ARGS(&ip), strerror(retval)); + netdev_name, IP_ARGS(&ip), strerror(retval)); } return retval; } +int +netdev_arp_lookup(const struct netdev *netdev, uint32_t ip, + uint8_t mac[ETH_ADDR_LEN]) +{ + return netdev_nodev_arp_lookup(netdev->name, ip, mac); +} + static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats) { @@ -1027,6 +1058,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; } @@ -1053,6 +1085,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; } @@ -1115,6 +1149,12 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats) int netdev_get_carrier(const struct netdev *netdev, bool *carrier) +{ + return netdev_nodev_get_carrier(netdev->name, carrier); +} + +int +netdev_nodev_get_carrier(const char *netdev_name, bool *carrier) { char line[8]; int retval; @@ -1124,7 +1164,7 @@ netdev_get_carrier(const struct netdev *netdev, bool *carrier) *carrier = false; - fn = xasprintf("/sys/class/net/%s/carrier", netdev->name); + fn = xasprintf("/sys/class/net/%s/carrier", netdev_name); fd = open(fn, O_RDONLY); if (fd < 0) { error = errno; @@ -1269,6 +1309,112 @@ netdev_enumerate(struct svec *svec) } } +/* Attempts to locate a device based on its IPv4 address. The caller + * may provide a hint as to the device by setting 'netdev_name' to a + * likely device name. This string must be malloc'd, since if it is + * not correct then it will be freed. If there is no hint, then + * 'netdev_name' must be the NULL pointer. + * + * If the device is found, the return value will be true and 'netdev_name' + * contains the device's name as a string, which the caller is responsible + * for freeing. If the device is not found, the return value is false. */ +bool +netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name) +{ + int i; + struct in_addr dev_in4; + struct svec dev_list; + + /* Check the hint first. */ + if (*netdev_name && (netdev_nodev_get_in4(*netdev_name, &dev_in4, NULL)) + && (dev_in4.s_addr == in4->s_addr)) { + return true; + } + + free(*netdev_name); + *netdev_name = NULL; + netdev_enumerate(&dev_list); + + for (i=0; is_addr)) { + *netdev_name = xstrdup(dev_list.names[i]); + svec_destroy(&dev_list); + return true; + } + } + + svec_destroy(&dev_list); + return false; +} + +/* Looks up the next hop for 'ip'. If the next hop can be found, the + * address is stored in 'next_hop'. If a gateway is not required to + * reach 'ip', zero is stored in 'next_hop'. In either case, zero is + * returned and a copy of the name of the device to reach 'ip' is stored + * in 'netdev_name', which the caller is responsible for freeing. If a + * route could not be determined, a positive errno is returned. */ +int +netdev_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, + char **netdev_name) +{ + static const char fn[] = "/proc/net/route"; + FILE *stream; + char line[256]; + int ln; + + *netdev_name = NULL; + stream = fopen(fn, "r"); + if (stream == NULL) { + VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno)); + return errno; + } + + ln = 0; + while (fgets(line, sizeof line, stream)) { + if (++ln >= 2) { + char iface[17]; + uint32_t dest, gateway, mask; + int refcnt, metric, mtu; + unsigned int flags, use, window, irtt; + + if (sscanf(line, + "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32 + " %d %u %u\n", + iface, &dest, &gateway, &flags, &refcnt, + &use, &metric, &mask, &mtu, &window, &irtt) != 11) { + + VLOG_WARN_RL(&rl, "%s: could not parse line %d: %s", + fn, ln, line); + continue; + } + if (!(flags & RTF_UP)) { + /* Skip routes that aren't up. */ + continue; + } + + /* The output of 'dest', 'mask', and 'gateway' were given in + * network byte order, so we don't need need any endian + * conversions here. */ + if ((dest & mask) == (host->s_addr & mask)) { + if (!gateway) { + /* The host is directly reachable. */ + next_hop->s_addr = 0; + } else { + /* To reach the host, we must go through a gateway. */ + next_hop->s_addr = gateway; + } + *netdev_name = xstrdup(iface); + fclose(stream); + return 0; + } + } + } + + fclose(stream); + return ENXIO; +} + /* Obtains the current flags for the network device named 'netdev_name' and * stores them into '*flagsp'. Returns 0 if successful, otherwise a positive * errno value. On error, stores 0 into '*flagsp'.