/**
* @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 <log4cxx/logger.h>
#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<QString> 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<QString> 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;
}