/* Copyright (c) 2012, Matthias Schiffer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include "netif.h" #include "util.h" #include #include #include #include #include #include #include #include #include #define SYSFS_PATH_MAX 256 #define SYSFS_CLASS_NET "/sys/class/net" #define SYSFS_BATADV "/sys/kernel/debug/batman_adv" static bool netif_file_read(const char *ifname, const char *file, const char *format, ...) { char filename[SYSFS_PATH_MAX]; snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file); va_list ap; va_start(ap, format); bool ret = file_readv(filename, format, ap); va_end(ap); return ret; } static bool netif_file_exists(const char *ifname, const char *file) { char filename[SYSFS_PATH_MAX]; snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file); struct stat st; return (stat(filename, &st) == 0); } static bool batadv_file_read(const char *ifname, const char *file, const char *format, ...) { char filename[SYSFS_PATH_MAX]; snprintf(filename, SYSFS_PATH_MAX, SYSFS_BATADV"/%s/%s", ifname, file); va_list ap; va_start(ap, format); bool ret = file_readv(filename, format, ap); va_end(ap); return ret; } netif_type_t netif_get_type(const char *ifname) { int type; if (netif_file_read(ifname, "type", "%i", &type) && type == ARPHRD_ETHER) { if(netif_file_exists(ifname, "mesh")) return IF_MESH; else if(netif_file_exists(ifname, "bridge")) return IF_BRIDGE; else if(netif_file_exists(ifname, "tun_flags")) return IF_VIRTUAL; else if(netif_file_exists(ifname, "wireless")) return IF_WIRELESS; else return IF_WIRED; } return IF_UNKNOWN; } bool netif_is_mesh(const char *ifname) { return netif_file_exists(ifname, "mesh"); } char* netif_get_mesh(const char *ifname) { char *mesh = NULL; if (!netif_file_read(ifname, "batman_adv/mesh_iface", "%as", &mesh) || !strcmp(mesh, "none")) { free(mesh); return NULL; } return mesh; } char* netif_get_bridge(const char *ifname) { char filename[SYSFS_PATH_MAX], filename2[SYSFS_PATH_MAX] = {0}; snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/brport/bridge", ifname); if (readlink(filename, filename2, sizeof(filename2)) < 0) return NULL; return strdup(basename(filename2)); } eth_addr_t netif_get_eth_addr(const char *ifname) { eth_addr_t ret; uint8_t *a = ret.d; if (!netif_file_read(ifname, "address", "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) return ETH_ADDR_UNSPEC; return ret; } struct in6_addr netif_get_addr(unsigned ifindex, int scope) { struct msg { struct nlmsghdr nh; struct ifaddrmsg addr; uint8_t attrbuf[16384]; } msg; memset(&msg, 0, sizeof(msg)); int rtnetlink_sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); msg.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; msg.nh.nlmsg_type = RTM_GETADDR; msg.addr.ifa_family = AF_INET6; if(write(rtnetlink_sk, &msg, msg.nh.nlmsg_len) <= 0) { fprintf(stderr, "error: write: %m\n"); close(rtnetlink_sk); return in6addr_any; } int readlen = read(rtnetlink_sk, &msg, sizeof(msg)); if (readlen <= 0) { fprintf(stderr, "error: read: %m\n"); close(rtnetlink_sk); return in6addr_any; } close(rtnetlink_sk); struct msg *chunk; for (chunk = &msg; readlen > sizeof(struct nlmsghdr);) { int len = chunk->nh.nlmsg_len - sizeof(struct nlmsghdr); if (len < sizeof(struct ifaddrmsg) || readlen < len) return in6addr_any; if (!NLMSG_OK(&chunk->nh, readlen)) return in6addr_any; if (chunk->nh.nlmsg_type == RTM_NEWADDR && chunk->addr.ifa_scope == scope && chunk->addr.ifa_index == ifindex) { struct rtattr *rta = (struct rtattr *)IFA_RTA(&chunk->addr); int rtattrlen = IFA_PAYLOAD(&chunk->nh); for (; RTA_OK(rta, rtattrlen); rta = RTA_NEXT(rta, rtattrlen)) { if(rta->rta_type == IFA_ADDRESS && rta->rta_len == RTA_LENGTH(16)) { struct in6_addr ret; memcpy(ret.s6_addr, RTA_DATA(rta), 16); return ret; } } } readlen -= NLMSG_ALIGN(chunk->nh.nlmsg_len); chunk = (struct msg*)((uint8_t*)chunk + NLMSG_ALIGN(chunk->nh.nlmsg_len)); } return in6addr_any; } void netif_foreach(netif_cb cb, void *arg) { struct if_nameindex *ifaces = if_nameindex(); if (!ifaces) { fprintf(stderr, "error: if_nameindex: %m\n"); return; } int i; for (i = 0; ifaces[i].if_name; i++) cb(ifaces[i].if_name, ifaces[i].if_index, arg); if_freenameindex(ifaces); } eth_addr_t netif_mesh_get_primary_addr(const char *ifname) { eth_addr_t ret; uint8_t *a = ret.d; if (!batadv_file_read(ifname, "originators", "%*[^,], MainIF/MAC: %*[^/]/%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) return ETH_ADDR_UNSPEC; return ret; }