From 79c720a8302aafcd471b535833e88dae0e653671 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Mon, 6 Jul 2009 15:37:19 -0700 Subject: [PATCH] Provide method to locate device by IP and add more nodev functions to netdev In some cases we need to be able to locate a device by its IPv4 address. There doesn't seem to be an easy way to do this on Linux, so we iterate through all devices until it's found. The caller can provide a hint as to the device, so subsequent checks can be quicker. This checkin also adds nodev versions of functions to lookup ARP entries and the IPv4 address of a device. --- lib/netdev.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++------ lib/netdev.h | 4 +++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/lib/netdev.c b/lib/netdev.c index 8c14dd30..6644f7e8 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -784,12 +784,14 @@ netdev_set_advertisements(struct netdev *netdev, uint32_t advertise) /* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if * 'in4' is 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 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,7 +799,7 @@ 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; @@ -805,6 +807,12 @@ netdev_get_in4(const struct netdev *netdev, struct in_addr *in4) return ip.s_addr != INADDR_ANY; } +bool +netdev_get_in4(const struct netdev *netdev, struct in_addr *in4) +{ + return netdev_nodev_get_in4(netdev->name, in4); +} + static void make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr) { @@ -970,13 +978,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 +994,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) { @@ -1275,6 +1292,45 @@ 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)) + && (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; +} + /* 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'. diff --git a/lib/netdev.h b/lib/netdev.h index d128f3f3..d8f1e096 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -105,11 +105,15 @@ int netdev_set_policing(struct netdev *, uint32_t kbits_rate, uint32_t kbits_burst); void netdev_enumerate(struct svec *); +bool netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name); int netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *); +bool netdev_nodev_get_in4(const char *netdev_name, struct in_addr *); int netdev_nodev_set_etheraddr(const char *name, const uint8_t mac[6]); int netdev_nodev_get_etheraddr(const char *netdev_name, uint8_t mac[6]); int netdev_nodev_set_policing(const char *netdev_name, uint32_t kbits_rate, uint32_t kbits_burst); +int netdev_nodev_arp_lookup(const char *netdev_name, uint32_t ip, + uint8_t mac[6]); int netdev_nodev_get_carrier(const char *netdev_name, bool *carrier); int netdev_get_vlan_vid(const char *netdev_name, int *vlan_vid); -- 2.30.2