/** * @class NetworkManager * * @brief Manages the network configurations like setting new default routes. * * Manages the network configurations like setting new default routes. * It provides methods for ipv4 and some method for ipv6. * */ #include "networkmanager.h" #include #include "qlog4cxx.h" using namespace log4cxx; using namespace log4cxx::helpers; LoggerPtr ndnmLogger(Logger::getLogger("fbgui.nd.nm")); NetworkManager::NetworkManager() { // TODO Auto-generated constructor stub } NetworkManager::~NetworkManager() { // TODO Auto-generated destructor stub } /** * This method adds /replaces the default route. * This method adds /replaces the default route. * To keep it modular, it is possible * to specify an ip address family. * * @param ifName * the interface name * * @param gateway * the gateway address (e.g: 192.168.0.254) * @param mss * the mss. * @param af * specify the family type of the ip address. * possible values are: AF_INET for an IPv4 address * AF_INET6 for an IPv6 address * * @return * return -1 if an error happened. * return 0 if everything was ok. */ int NetworkManager::replaceDefaultRoute(QString ifname, QString gateway, int mss, int af) { struct nl_cache *cache; struct nl_handle* rtsock; struct nl_addr * gw; struct rtnl_route * route; int retval, iface_idx; QByteArray ba_ifn = ifname.toAscii(); char * ifn = ba_ifn.data(); QByteArray ba_gw = gateway.toAscii(); char * gwaddr = ba_gw.data(); LOG4CXX_DEBUG(ndnmLogger, "---doRoute() gwaddr" << gwaddr); //qDebug() << "---doRoute() gwaddr" << gwaddr; if (!(gw = nl_addr_parse(gwaddr, af))) { LOG4CXX_DEBUG(ndnmLogger, "Invalid router address given:" << gwaddr); return -1; } rtsock = nl_handle_alloc(); nl_connect(rtsock, NETLINK_ROUTE); if ((cache = rtnl_link_alloc_cache(rtsock)) == NULL) { LOG4CXX_DEBUG(ndnmLogger, "error with link cache alloc \n"); } iface_idx = rtnl_link_name2i(cache, ifn); route = rtnl_route_alloc(); rtnl_route_set_scope(route, RT_SCOPE_UNIVERSE); rtnl_route_set_gateway(route, gw); rtnl_route_set_oif(route, iface_idx); if (mss > 0) { rtnl_route_set_metric(route, RTAX_ADVMSS, mss); } retval = rtnl_route_add(rtsock, route, NLM_F_REPLACE); LOG4CXX_DEBUG(ndnmLogger, "return value:" << retval << ":" << strerror(-retval)); rtnl_route_put(route); nl_addr_put(gw); nl_handle_destroy(rtsock); return retval; } /**/ int NetworkManager::ip6_addRoute(const char *iface, const struct in6_addr *ip6_dest, int ip6_prefix, const struct in6_addr *ip6_gateway, int metric, int mss) { struct nl_cache *cache; struct nl_handle *nlh; struct rtnl_route *route; struct nl_addr *dest_addr; struct nl_addr *gw_addr = NULL; int err, iface_idx; nlh = nl_handle_alloc(); nl_connect(nlh, NETLINK_ROUTE); if ((cache = rtnl_link_alloc_cache(nlh)) == NULL) { //qDebug() << "error with link cache alloc \n"; printf("error with link cache alloc \n"); } iface_idx = rtnl_link_name2i(cache, iface); route = rtnl_route_alloc(); /* Destination */ dest_addr = nl_addr_build(AF_INET6, (struct in6_addr *) ip6_dest, sizeof(*ip6_dest)); nl_addr_set_prefixlen(dest_addr, (int) ip6_prefix); rtnl_route_set_dst(route, dest_addr); nl_addr_put(dest_addr); /* Gateway */ if (ip6_gateway && !IN6_IS_ADDR_UNSPECIFIED (ip6_gateway)) { gw_addr = nl_addr_build(AF_INET6, (struct in6_addr *) ip6_gateway, sizeof(*ip6_gateway)); if (gw_addr) { rtnl_route_set_gateway(route, gw_addr); rtnl_route_set_scope(route, RT_SCOPE_UNIVERSE); } else { rtnl_route_put(route); return -1; } } /* Metric */ if (metric) rtnl_route_set_prio(route, metric); /* Add the route */ err = rtnl_route_add(nlh, route, 0); if (err == -ESRCH && ip6_gateway) { /* Gateway might be over a bridge; try adding a route to gateway first */ struct rtnl_route *route2; route2 = create_route(iface_idx, mss); if (route2) { /* Add route to gateway over bridge */ rtnl_route_set_dst(route2, gw_addr); err = rtnl_route_add(nlh, route2, 0); if (!err) { /* Try adding the route again */ err = rtnl_route_add(nlh, route, 0); if (err) rtnl_route_del(nlh, route2, 0); } rtnl_route_put(route2); } } if (gw_addr) nl_addr_put(gw_addr); if (err) { //nm_warning ("Failed to set IPv6 route on '%s': %s", iface, nl_geterror ()); rtnl_route_put(route); route = NULL; } return 0; } struct rtnl_route * NetworkManager::create_route(int iface_idx, int mss) { struct rtnl_route *route; route = rtnl_route_alloc(); if (route) { rtnl_route_set_oif(route, iface_idx); if (mss && rtnl_route_set_metric(route, RTAX_ADVMSS, mss) < 0) { //nm_warning ("Could not set mss"); } } else //nm_warning ("Could not allocate route"); return route; } /** * The method brings an interface up. * * @param ifname * the name of the interface * * @return * 0 -> success * -1 -> error */ int NetworkManager::bringInterfaceUP(QString ifname) { return bringInterfaceUpDown(ifname, true); } /** * The method brings an interface down. * * @param ifname * the name of the interface * * @return * 0 -> success * -1 -> error */ int NetworkManager::bringInterfaceDown(QString ifname) { return bringInterfaceUpDown(ifname, false); } /** * This method brings an interface up or down. * * @param ifname * is a string which contains the interface name which is going down or up. * * @param up * is a bool. true means. we bring the interface up. * false meand. we bring the interface down. * @return * 0 if everything is ok * else an error */ int NetworkManager::bringInterfaceUpDown(QString ifname, bool up) { struct nl_cache *cache; struct nl_handle* rtsock; struct rtnl_link* request = NULL; struct rtnl_link* old = NULL; int retval; QByteArray ba_ifn = ifname.toAscii(); char * ifn = ba_ifn.data(); if (!(request = rtnl_link_alloc())) { LOG4CXX_DEBUG(ndnmLogger, "error. couldn't allocate a rtnl link"); return -1; } rtsock = nl_handle_alloc(); nl_connect(rtsock, NETLINK_ROUTE); if (up) { rtnl_link_set_flags(request, IFF_UP); } else { rtnl_link_unset_flags(request, IFF_UP); } if ((cache = rtnl_link_alloc_cache(rtsock)) == NULL) { LOG4CXX_DEBUG(ndnmLogger, "error with link cache alloc "); } old = rtnl_link_get_by_name(cache, ifn); if (old) { LOG4CXX_DEBUG(ndnmLogger, "change link"); retval = rtnl_link_change(rtsock, old, request, 0); } else { LOG4CXX_DEBUG(ndnmLogger, "change failed"); retval = -1; LOG4CXX_DEBUG(ndnmLogger, "return value:" << retval << ":" << strerror(-retval)); } rtnl_link_put(old); rtnl_link_put(request); nl_handle_destroy(rtsock); return retval; } /** * This method is used when the manual configuration is needed. * * This method is used when the manual configuration is needed. * First we bring up the interface. Than we configure the interface with * our manual entered configuration dates. * After that we replace the old default route with the new and * write a resolv.conf. * * @param ifname * name of the interface which we are about to configure. * * @param ipAddress * the new IP-Address. * * @param netmask * the netmask of the IP-Address. * * @param broadcast * the broadcast address. * @param gateway * the gateway address. * @param metric * do not exactly know why we need this. in most cases this should be 0. * @param af * the address type. Either AF_INET for IPv4 or AF_INET6 for IPv6. * @param pathToResolvConf * the path to the resolf.conf file. in most cases "/etc/". * @param nameServer * the name server addresses. */ int NetworkManager::ip4_setManualConfiguration(QString ifname, QString ipAddress, QString netmask, QString broadcast, QString gateway, int metric, int af, QString pathToResolvConf, QList nameServer) { //bring the interface up bringInterfaceUP(ifname); //set configuration ip4_configureInterface(ifname, ipAddress, broadcast, netmask, af); //set default route replaceDefaultRoute(ifname, gateway, metric, af); //write resolv.conf writeResolvConf(pathToResolvConf, ifname, nameServer); return 0; } int NetworkManager::ip4_configureInterface(QString ifname, QString ipAddress, QString broadcast, QString netmask, int af) { struct nl_cache *cache; struct nl_handle* rtsock; struct nl_addr * local; struct rtnl_addr * addr = NULL; int retval = 0; int iface_idx, err, prefixLength; QByteArray ba_ifn = ifname.trimmed().toAscii(); char * ifn = ba_ifn.data(); QByteArray ba_ip = ipAddress.trimmed().toAscii(); char * ipaddr = ba_ip.data(); QByteArray ba_bc = broadcast.trimmed().toAscii(); char * bcaddr = ba_bc.data(); rtsock = nl_handle_alloc(); nl_connect(rtsock, NETLINK_ROUTE); if ((cache = rtnl_link_alloc_cache(rtsock)) == NULL) { LOG4CXX_DEBUG(ndnmLogger, "error with link cache alloc"); return -1; } iface_idx = rtnl_link_name2i(cache, ifn); if (!(addr = rtnl_addr_alloc())) { LOG4CXX_DEBUG(ndnmLogger, "error with addr alloc"); return -1; } local = nl_addr_parse(ipaddr, af); err = rtnl_addr_set_local(addr, local); nl_addr_put(local); if (err != 0) { LOG4CXX_DEBUG(ndnmLogger, "error with set local addr"); } prefixLength = ip4_netmaskToPrefix(ipAddress, netmask); LOG4CXX_DEBUG(ndnmLogger, "prefix length:" << prefixLength); rtnl_addr_set_prefixlen(addr, prefixLength); local = nl_addr_parse(bcaddr, af); err = rtnl_addr_set_broadcast(addr, local); nl_addr_put(local); if (err != 0) { LOG4CXX_DEBUG(ndnmLogger, "error with set broadcast addr"); } rtnl_addr_set_ifindex(addr, iface_idx); retval = sync_address(ifn, iface_idx, af, addr); if (retval < 0) { LOG4CXX_DEBUG(ndnmLogger, "error in sync_address"); } rtnl_addr_put(addr); return retval; } /** * This Method returns the length of the IP-Address netmask prefix. * * @param ipAddr * the IP-address * * @param netmask * the netmask of the IP-address. * @return * the length of the IP-Address netmask prefix */ int NetworkManager::ip4_netmaskToPrefix(QString ipAddr, QString netmask) { int retval = -1; QNetworkAddressEntry nae; if (netmask == "") { LOG4CXX_DEBUG(ndnmLogger, "error: netmask is empty"); return retval; } nae.setIp(QHostAddress(ipAddr)); nae.setNetmask(QHostAddress(netmask.trimmed())); retval = nae.prefixLength(); return retval; } int NetworkManager::ip6_addAddress(struct ip6_addr* ip6Addr, const char *iface) { int num_addrs, i, iface_idx; struct rtnl_addr* addr = NULL; struct nl_cache *cache; struct nl_handle* rtsock; rtsock = nl_handle_alloc(); nl_connect(rtsock, NETLINK_ROUTE); if ((cache = rtnl_link_alloc_cache(rtsock)) == NULL) { LOG4CXX_DEBUG(ndnmLogger, "error with link cache alloc"); return -1; } iface_idx = rtnl_link_name2i(cache, iface); addr = ip6AddrToRtnlAddr(ip6Addr); if (!addr) { //nm_warning("couldn't create rtnl address!\n"); return -1; } rtnl_addr_set_ifindex(addr, iface_idx); return sync_address(iface, iface_idx, AF_INET6, addr); } /**/ struct rtnl_addr* NetworkManager::ip6AddrToRtnlAddr(struct ip6_addr* ip6Addr) { struct rtnl_addr *addr; bool success = true; if (!(addr = rtnl_addr_alloc())) return NULL; success = (nlAddrToRtnlAddr(ip6Addr, addr) >= 0); if (!success) { rtnl_addr_put(addr); addr = NULL; } return addr; } /**/ struct nl_addr* NetworkManager::ip6AddrToNlAddr(const struct ip6_addr *ip6Addr) { struct nl_addr * nla = NULL; if (!(nla = nl_addr_alloc(sizeof(struct in6_addr)))) return NULL; nl_addr_set_family(nla, AF_INET6); nl_addr_set_binary_addr(nla, (struct in6_addr *) ip6Addr, sizeof(struct in6_addr)); return nla; } /**/ int NetworkManager::nlAddrToRtnlAddr(const struct ip6_addr* ip6Addr, struct rtnl_addr* addr) { struct nl_addr * local = NULL; int err = 0; local = ip6AddrToNlAddr(ip6Addr); err = rtnl_addr_set_local(addr, local); nl_addr_put(local); return -err; } /** * delete all addresses of this interface but not the one we just set * * @return * -1 if something went wrong. else 0 */ int NetworkManager::sync_address(const char *iface, int ifindex, int family, struct rtnl_addr *addr) { struct nl_handle *nlh; struct nl_cache *addr_cache; struct rtnl_addr *filter_addr, *match_addr; struct nl_object *match; struct nl_addr *nladdr; int err; char buf[INET6_ADDRSTRLEN + 1]; nlh = nl_handle_alloc(); nl_connect(nlh, NETLINK_ROUTE); if (!nlh) return -1; addr_cache = rtnl_addr_alloc_cache(nlh); if (!addr_cache) return -1; filter_addr = rtnl_addr_alloc(); if (!filter_addr) { nl_cache_free(addr_cache); return -1; } rtnl_addr_set_ifindex(filter_addr, ifindex); if (family) rtnl_addr_set_family(filter_addr, family); //nm_log_dbg (log_domain, "(%s): syncing addresses (family %d)", iface, family); /* Walk through the cache, comparing the addresses already on * the interface to the addresses in addrs. */ for (match = nl_cache_get_first(addr_cache); match; match = nl_cache_get_next(match)) { int buf_valid = -1; match_addr = (struct rtnl_addr *) match; /* Skip addresses not on our interface */ if (!nl_object_match_filter(match, (struct nl_object *) filter_addr)) continue; if (addr) { if (addr && nl_object_identical(match, (struct nl_object *) addr)) { continue; } } nladdr = rtnl_addr_get_local(match_addr); /* Don't delete IPv6 link-local addresses; they don't belong to NM */ if (rtnl_addr_get_family(match_addr) == AF_INET6) { struct in6_addr *tmp; if (rtnl_addr_get_scope(match_addr) == RT_SCOPE_LINK) { //nm_log_dbg (log_domain, "(%s): ignoring IPv6 link-local address", iface); continue; } tmp = (in6_addr*) nl_addr_get_binary_addr(nladdr); if (inet_ntop(AF_INET6, tmp, buf, sizeof(buf))) buf_valid = 0; } else if (rtnl_addr_get_family(match_addr) == AF_INET) { struct in_addr *tmp; tmp = (in_addr *) nl_addr_get_binary_addr(nladdr); if (inet_ntop(AF_INET, tmp, buf, sizeof(buf))) buf_valid = 0; } if (buf_valid == 0) { //nm_log_dbg (log_domain, "(%s): removing address '%s/%d'", // iface, buf, nl_addr_get_prefixlen (nladdr)); } /* Otherwise, match_addr should be removed from the interface. */ err = rtnl_addr_delete(nlh, match_addr, 0); if (err < 0) { //nm_log_err (log_domain, "(%s): error %d returned from rtnl_addr_delete(): %s", // iface, err, nl_geterror ()); LOG4CXX_DEBUG(ndnmLogger, "error with delete addr"); } } rtnl_addr_put(filter_addr); nl_cache_free(addr_cache); /* Now add the remaining new addresses */ if (!addr) return -1; struct in6_addr *in6tmp; struct in_addr *in4tmp; int buf_valid = -1; nladdr = rtnl_addr_get_local(addr); if (rtnl_addr_get_family(addr) == AF_INET6) { in6tmp = (in6_addr*) nl_addr_get_binary_addr(nladdr); if (inet_ntop(AF_INET6, in6tmp, buf, sizeof(buf))) buf_valid = 0; } else if (rtnl_addr_get_family(addr) == AF_INET) { in4tmp = (in_addr*) nl_addr_get_binary_addr(nladdr); if (inet_ntop(AF_INET, in4tmp, buf, sizeof(buf))) buf_valid = 0; } if (buf_valid == 0) { //nm_log_dbg (log_domain, "(%s): adding address '%s/%d'", //iface, buf, nl_addr_get_prefixlen (nladdr)); LOG4CXX_DEBUG(ndnmLogger, "buf valid adding addr"); } err = rtnl_addr_add(nlh, addr, 0); if (err < 0) { //nm_log_err (log_domain, // "(%s): error %d returned from rtnl_addr_add():\n%s", // iface, err, nl_geterror ()); LOG4CXX_DEBUG(ndnmLogger, "error with add addr" << strerror(-err)); } rtnl_addr_put(addr); return err; } /** * This method writes a resolv.conf file. * * @param path * path to the resolv.conf file. (in most cases: /etc/) * @param ifname * name of the interface * @param * addresses of the nameserver * * @return * return 0 if success * else -1 */ int NetworkManager::writeResolvConf(QString path, QString ifname, QList nameServer) { QFile file(path + "resolv.conf"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { LOG4CXX_DEBUG(ndnmLogger, "error couldn't open file:" << path); return -1; } QTextStream out(&file); out << "# Generated by networkdiscovery manual configuration for interface " + ifname + "\n"; foreach(QString ns, nameServer ) { out << "nameserver " + ns + "\n"; } file.close(); return 0; }