/* * iface.c - Network interface control functions * * Copyright (C) 2006, 2007, 2008 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Author(s): David Cantrell */ #include #include #include #include #include #include #include #include "iface.h" #include "str.h" /* * Return an NETLINK_ROUTE cache. */ struct nl_cache *iface_get_link_cache(struct nl_handle **handle) { struct nl_cache *cache = NULL; if ((*handle = nl_handle_alloc()) == NULL) { perror("nl_handle_alloc() failure in iface_get_link_cache()"); return NULL; } if (nl_connect(*handle, NETLINK_ROUTE)) { perror("nl_connect() failure in iface_get_link_cache()"); nl_handle_destroy(*handle); return NULL; } if ((cache = rtnl_link_alloc_cache(*handle)) == NULL) { perror("rtnl_link_alloc_cache() failure in iface_get_link_cache()"); nl_close(*handle); nl_handle_destroy(*handle); return NULL; } return cache; } /* * Given an interface name (e.g., eth0), return the IP address in human * readable format (i.e., the output from inet_ntop()). Return NULL for * no match. NOTE: This function will check for IPv6 and IPv4 * addresses. In the case where the interface has both, the IPv4 address * is returned. The only way you will get an IPv6 address from this function * is if that's the only address configured for the interface. */ char *iface_ip2str(char *ifname) { int ifindex = -1, buflen = 0, family = 0; char *buf = NULL, *bufv4 = NULL, *bufv6 = NULL, *pos = NULL; struct nl_handle *handle = NULL; struct nl_cache *cache = NULL; struct nl_object *obj = NULL; struct rtnl_addr *raddr = NULL; struct nl_addr *addr = NULL; if (ifname == NULL) { perror("Missing ifname in iface_ip2str()"); return NULL; } if ((cache = iface_get_link_cache(&handle)) == NULL) { perror("iface_get_link_cache() failure in iface_ip2str()"); return NULL; } ifindex = rtnl_link_name2i(cache, ifname); if ((cache = rtnl_addr_alloc_cache(handle)) == NULL) { perror("rtnl_addr_alloc_cache() failure in iface_ip2str()"); goto ip2str_error; } /* find the IPv4 and IPv6 addresses for this interface */ if ((obj = nl_cache_get_first(cache)) == NULL) { perror("nl_cache_get_first() failure in iface_ip2str()"); goto ip2str_error; } do { raddr = (struct rtnl_addr *) obj; if (rtnl_addr_get_ifindex(raddr) == ifindex) { family = rtnl_addr_get_family(raddr); if (family == AF_INET || family == AF_INET6) { /* skip if we have already saved an address */ /* FIXME: we should handle multiple addresses for the same * family per interface */ if (family == AF_INET && bufv4 != NULL) { continue; } if (family == AF_INET6 && bufv6 != NULL) { continue; } /* get the address */ addr = rtnl_addr_get_local(raddr); /* convert to human readable format */ if (family == AF_INET) { buflen = INET_ADDRSTRLEN; } else if (family == AF_INET6) { buflen = INET6_ADDRSTRLEN; } buflen += 1; if ((buf = malloc(buflen)) == NULL) { perror("malloc() failure on buf in iface_ip2str()"); nl_addr_destroy(addr); goto ip2str_error; } buf = nl_addr2str(addr, buf, buflen); nl_addr_destroy(addr); /* trim the prefix notation */ if ((pos = index(buf, '/')) != NULL) { *pos = '\0'; if ((buf = realloc(buf, strlen(buf) + 1)) == NULL) { perror("realloc() failure on buf in iface_ip2str()"); nl_addr_destroy(addr); goto ip2str_error; } } /* save the IP address in the right buffer */ if (family == AF_INET) { bufv4 = strdup(buf); } else if (family == AF_INET6) { bufv6 = strdup(buf); } /* empty the main conversion buffer */ if (buf) { free(buf); buf = NULL; } } } } while ((obj = nl_cache_get_next(obj)) != NULL); ip2str_error: nl_close(handle); nl_handle_destroy(handle); /* return IPv4 address if we have both families * return IPv6 address if we only have IPv6 family * return NULL otherwise */ if ((bufv4 && bufv6) || (bufv4 && !bufv6)) { return bufv4; } else if (!bufv4 && bufv6) { return bufv6; } else { return NULL; } } /** * Given an interface name (e.g., eth0), return the MAC address in human * readable format (e.g., 00:11:52:12:D9:A0). Return NULL for no match. */ char *iface_mac2str(char *ifname) { int buflen = 20; char *buf = NULL; struct nl_handle *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *link = NULL; struct nl_addr *addr = NULL; if (ifname == NULL) { perror("Missing ifname in iface_mac2str()"); return NULL; } if ((cache = iface_get_link_cache(&handle)) == NULL) { perror("iface_get_link_cache() failure in iface_mac2str()"); return NULL; } if ((link = rtnl_link_get_by_name(cache, ifname)) == NULL) { perror("rtnl_link_get_by_name() failure in iface_mac2str()"); goto mac2str_error2; } if ((addr = rtnl_link_get_addr(link)) == NULL) { perror("rtnl_link_get_addr() failure in iface_mac2str()"); goto mac2str_error3; } if ((buf = malloc(buflen)) == NULL) { perror("malloc() failure on buf in iface_mac2str()"); goto mac2str_error4; } if ((buf = nl_addr2str(addr, buf, buflen)) != NULL) { buf = str2upper(buf); } mac2str_error4: nl_addr_destroy(addr); mac2str_error3: rtnl_link_put(link); mac2str_error2: nl_close(handle); nl_handle_destroy(handle); return buf; }