+/* 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; i<dev_list.n; i++) {
+ if ((netdev_nodev_get_in4(dev_list.names[i], &dev_in4, NULL))
+ && (dev_in4.s_addr == in4->s_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;
+}
+