// Copyright (c) 2014, Brad Parker // This file is licensed under the BSD 2-clause license. extern "C" { #include #include #include #include #include #include #include #include #include } #include "interface.h" #include "utils.h" #include #include #include #include #include #include #define BUFLEN 65535 #define ADDRBUFLEN BUFLEN /*! \class Interface \brief The Interface class is used to manage IP addresses associated with an interface. */ /*! Constructs an Interface object. All operations will be limited to the specified \a interface. */ Interface::Interface(QString interface) : QObject(0), m_interface(interface), m_index(-1) { m_index = Utils::interfaceIndex(interface); } Interface::~Interface() { } /*! Set the interface's administrative link state to up. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error. */ int Interface::setLinkUp() { struct rtnl_link *link, *link_orig = NULL; struct nl_sock *sock = NULL; QByteArray interfaceArray = m_interface.toLatin1(); const char *interface = interfaceArray.constData(); if((sock = Utils::connect())) { if(rtnl_link_get_kernel(sock, 0, interface, &link_orig) < 0) { std::cerr << "error looking up interface" << std::endl; nl_socket_free(sock); }else{ if(!link_orig) { std::cerr << "can't get link" << std::endl; nl_socket_free(sock); return 1; } link = rtnl_link_alloc(); if(!link) { std::cerr << "can't alloc link" << std::endl; rtnl_link_put(link_orig); nl_socket_free(sock); return 1; } int err = 0; rtnl_link_set_flags(link, IFF_UP); if((err = rtnl_link_change(sock, link_orig, link, 0)) < 0) { //error = nl_geterror(err); rtnl_link_put(link); rtnl_link_put(link_orig); nl_socket_free(sock); return 1; } rtnl_link_put(link); rtnl_link_put(link_orig); nl_socket_free(sock); return 1; } } return 0; } /*! Set the interface's administrative link state to down. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error. */ int Interface::setLinkDown() { struct rtnl_link *link, *link_orig = NULL; struct nl_sock *sock = NULL; QByteArray interfaceArray = m_interface.toLatin1(); const char *interface = interfaceArray.constData(); if((sock = Utils::connect())) { if(rtnl_link_get_kernel(sock, 0, interface, &link_orig) < 0) { std::cerr << "error looking up interface" << std::endl; nl_socket_free(sock); }else{ if(!link_orig) { std::cerr << "can't get link" << std::endl; nl_socket_free(sock); return 1; } link = rtnl_link_alloc(); if(!link) { std::cerr << "can't alloc link" << std::endl; rtnl_link_put(link_orig); nl_socket_free(sock); return 1; } int err = 0; rtnl_link_unset_flags(link, IFF_UP); if((err = rtnl_link_change(sock, link_orig, link, 0)) < 0) { //error = nl_geterror(err); rtnl_link_put(link); rtnl_link_put(link_orig); nl_socket_free(sock); return 1; } rtnl_link_put(link); rtnl_link_put(link_orig); nl_socket_free(sock); return 1; } } return 0; } /*! Add an IP address to the interface using the CIDR notation, e.g. 192.168.1.1/24. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error. */ int Interface::addAddress(QString addressStr) const { struct nl_sock *sock = NULL; QByteArray addressArrayNoMask = addressStr.left(addressStr.indexOf('/')).toLatin1(); QByteArray addressArray = addressStr.toLatin1(); QString bitmask = addressStr.right(2); const char *addressDataNoMask = addressArrayNoMask.constData(); const char *addressData = addressArray.constData(); in_addr_t brdInt = inet_addr(addressDataNoMask); struct in_addr brd = { .s_addr = brdInt }; int bitlen = bitmask.toInt(); if(bitlen <= 30) { for(int i = 31; i >= bitlen; i--) { brd.s_addr |= htonl(1 << (31 - i)); } } if(!(sock = Utils::connect())) { return 1; } struct rtnl_addr *addr = rtnl_addr_alloc(); if(!addr) { nl_socket_free(sock); return 1; } struct nl_addr *address = NULL; struct nl_addr *bcast = NULL; struct nl_msg *result = NULL; int parseResult = nl_addr_parse(addressData, AF_UNSPEC, &address); if(parseResult != 0) { std::cerr << "could not parse address" << std::endl; rtnl_addr_put(addr); nl_socket_free(sock); return 1; } int parseBroadcastResult = nl_addr_parse(inet_ntoa(brd), AF_INET, &bcast); if(parseBroadcastResult != 0) { std::cerr << "could not parse broadcast address" << std::endl; rtnl_addr_put(addr); nl_addr_put(address); nl_socket_free(sock); return 1; } rtnl_addr_set_ifindex(addr, m_index); rtnl_addr_set_local(addr, address); rtnl_addr_set_broadcast(addr, bcast); int ret = rtnl_addr_build_add_request(addr, 0, &result); if(ret != 0) { std::cerr << "could not build add request" << std::endl; rtnl_addr_put(addr); nl_addr_put(address); nl_addr_put(bcast); nl_socket_free(sock); return 1; } if(rtnl_addr_add(sock, addr, 0) != 0) { std::cerr << "could not add address" << std::endl; rtnl_addr_put(addr); nl_addr_put(address); nl_addr_put(bcast); nlmsg_free(result); nl_socket_free(sock); return 1; } rtnl_addr_put(addr); nl_addr_put(address); nl_addr_put(bcast); nlmsg_free(result); nl_socket_free(sock); return 0; } /*! Delete an IP address from the interface using the CIDR notation, e.g. 192.168.1.1/24. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error. */ int Interface::deleteAddress(QString addressStr) const { struct nl_sock *sock = NULL; QByteArray addressArrayNoMask = addressStr.left(addressStr.indexOf('/')).toLatin1(); QByteArray addressArray = addressStr.toLatin1(); QString bitmask = addressStr.right(2); const char *addressDataNoMask = addressArrayNoMask.constData(); const char *addressData = addressArray.constData(); in_addr_t brdInt = inet_addr(addressDataNoMask); struct in_addr brd = { .s_addr = brdInt }; int bitlen = bitmask.toInt(); if(bitlen <= 30) { for(int i = 31; i >= bitlen; i--) { brd.s_addr |= htonl(1 << (31 - i)); } } if(!(sock = Utils::connect())) { return 1; } struct rtnl_addr *addr = rtnl_addr_alloc(); struct nl_addr *address = NULL; struct nl_addr *bcast = NULL; struct nl_msg *result = NULL; if(!addr) { std::cerr << "could not allocate address" << std::endl; nl_socket_free(sock); return 1; } int parseResult = nl_addr_parse(addressData, AF_UNSPEC, &address); if(parseResult != 0) { std::cerr << "could not parse address" << std::endl; rtnl_addr_put(addr); nl_socket_free(sock); return 1; } int parseBroadcastResult = nl_addr_parse(inet_ntoa(brd), AF_INET, &bcast); if(parseBroadcastResult != 0) { std::cerr << "could not parse broadcast address" << std::endl; rtnl_addr_put(addr); nl_addr_put(address); nl_socket_free(sock); return 1; } rtnl_addr_set_ifindex(addr, m_index); rtnl_addr_set_local(addr, address); rtnl_addr_set_broadcast(addr, bcast); int ret = rtnl_addr_build_delete_request(addr, 0, &result); if(ret != 0) { std::cerr << "could not build add request" << std::endl; rtnl_addr_put(addr); nl_addr_put(address); nl_addr_put(bcast); nl_socket_free(sock); return 1; } if(rtnl_addr_delete(sock, addr, 0) != 0) { std::cerr << "could not delete address" << std::endl; rtnl_addr_put(addr); nlmsg_free(result); nl_addr_put(address); nl_addr_put(bcast); nl_socket_free(sock); return 1; } rtnl_addr_put(addr); nl_addr_put(address); nl_addr_put(bcast); nlmsg_free(result); nl_socket_free(sock); return 0; } /*! Returns true if the interface is administratively up, otherwise false if it is administratively down or if there was an error retrieving the status information. If there was an error, *ok will be set to false, otherwise it will be true. */ bool Interface::hasAdminLink(bool *ok) { struct rtnl_link *link = NULL; struct nl_sock *sock = NULL; QByteArray interfaceArray = m_interface.toLatin1(); const char *interface = interfaceArray.constData(); bool carrier = false; if((sock = Utils::connect())) { if(rtnl_link_get_kernel(sock, 0, interface, &link) < 0) { std::cerr << "error looking up interface" << std::endl; nl_socket_free(sock); }else{ unsigned int flags = rtnl_link_get_flags(link); carrier = (flags & IFF_UP) == IFF_UP; rtnl_link_put(link); nl_socket_free(sock); if(ok != NULL) { *ok = true; } return carrier; } } if(ok != NULL) { *ok = false; } return false; } /*! Returns true if the interface is up and running (link-layer is up), and false if the link/interface is down, or if there was an error retrieving the status information. If there was an error, *ok will be set to false, otherwise it will be true. */ bool Interface::hasCarrier(bool *ok) { struct rtnl_link *link = NULL; struct nl_sock *sock = NULL; QByteArray interfaceArray = m_interface.toLatin1(); const char *interface = interfaceArray.constData(); bool carrier = false; if((sock = Utils::connect())) { if(rtnl_link_get_kernel(sock, 0, interface, &link) < 0) { std::cerr << "error looking up interface" << std::endl; nl_socket_free(sock); }else{ unsigned int flags = rtnl_link_get_flags(link); carrier = flags & IFF_RUNNING; rtnl_link_put(link); nl_socket_free(sock); if(ok != NULL) { *ok = true; } return carrier; } } if(ok != NULL) { *ok = false; } return false; } /*! Returns a list of assigned IP addresses on the interface. All addresses include the subnet mask as CIDR notation (e.g. 192.168.1.1/24). */ QStringList Interface::addresses() const { struct nl_sock *sock = NULL; struct rtnl_addr *addr = NULL; struct nl_cache *addr_cache, *link_cache = NULL; struct nl_dump_params params; char buf[BUFLEN]; char addrs[ADDRBUFLEN]; QByteArray interfaceArray = m_interface.toLatin1(); const char *interface = interfaceArray.constData(); char *ipInterface = strndup(interface, strlen(interface)); memset(¶ms, 0, sizeof(struct nl_dump_params)); params.dp_type = NL_DUMP_LINE; params.dp_fd = NULL; params.dp_buf = buf; params.dp_buflen = BUFLEN; if(!(sock = Utils::connect())) { free(ipInterface); return QStringList(); } if(Utils::alloc_addr_cache(sock, &addr_cache)) { free(ipInterface); nl_socket_free(sock); return QStringList(); } if(Utils::alloc_link_cache(sock, &link_cache)) { free(ipInterface); nl_cache_free(addr_cache); nl_socket_free(sock); return QStringList(); } if(!(addr = rtnl_addr_alloc())) { std::cerr << "could not allocate address" << std::endl; free(ipInterface); nl_cache_free(addr_cache); nl_cache_free(link_cache); nl_socket_free(sock); return QStringList(); } rtnl_addr_set_family(addr, AF_INET); int ival = 0; if(!(ival = rtnl_link_name2i(link_cache, ipInterface))) { std::cerr << "Link " << ipInterface << " does not exist" << std::endl; free(ipInterface); rtnl_addr_put(addr); nl_cache_free(addr_cache); nl_cache_free(link_cache); nl_socket_free(sock); return QStringList(); } rtnl_addr_set_ifindex(addr, ival); memset(buf, 0, BUFLEN); memset(addrs, 0, ADDRBUFLEN); nl_cache_dump_filter(addr_cache, ¶ms, OBJ_CAST(addr)); buf[BUFLEN - 1] = '\0'; QString addrsString = buf; QStringList addrsLines = addrsString.split('\n'); QStringList addrsList; foreach(QString addrString, addrsLines) { QStringList fields = addrString.split(' '); if(fields.count() > 0) { QString address = fields.at(0); if(!address.isEmpty()) { addrsList << fields.at(0); } } } free(ipInterface); rtnl_addr_put(addr); nl_cache_free(addr_cache); nl_cache_free(link_cache); nl_socket_free(sock); return addrsList; } /*! Returns a list of all interfaces on the system. */ QStringList Interface::list() { struct nl_sock *sock = NULL; struct nl_cache *link_cache = NULL; QStringList linkList; if(!(sock = Utils::connect())) { return QStringList(); } if(Utils::alloc_link_cache(sock, &link_cache)) { nl_socket_free(sock); return QStringList(); } int index = 1; forever { struct rtnl_link *link = rtnl_link_get(link_cache, index); if(link != NULL) { char *name = rtnl_link_get_name(link); linkList << name; rtnl_link_put(link); }else{ break; } ++index; } nl_cache_free(link_cache); nl_socket_free(sock); // workaround for libnl issue: https://github.com/tgraf/libnl/issues/49 // find any interfaces libnl doesn't know about (e.g. USB wireless adapters after they've been unplugged and replugged) QDir dir("/sys/class/net"); QStringList interfaces = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); foreach(QString interface, interfaces) { if(!linkList.contains(interface)) { linkList << interface; } } return linkList; } /*! Returns the name of the interface used when the Interface object was constructed. */ const QString& Interface::name() const { return m_interface; }