/*
* networkmanager.cpp
*
* Created on: Sep 5, 2011
* Author: niklas
*/
#include "networkmanager.h"
NetworkManager::NetworkManager() {
// TODO Auto-generated constructor stub
_tag = "[nd:NetworkManager]";
}
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();
qDebug() << _tag << "---doRoute() gwaddr" << gwaddr;
if (!(gw = nl_addr_parse(gwaddr, af))) {
qDebug() << _tag << "Invalid router address given:" << gwaddr;
return -1;
}
rtsock = nl_handle_alloc();
nl_connect(rtsock, NETLINK_ROUTE);
if ((cache = rtnl_link_alloc_cache(rtsock)) == NULL) {
qDebug() << _tag << "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);
qDebug() << _tag << "return value:" << retval << ":" << strerror(-retval);
rtnl_route_put(route);
nl_addr_put(gw);
nl_handle_destroy(rtsock);
return retval;
}
/**
* 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())) {
qDebug() << _tag << "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) {
qDebug() << _tag << "error with link cache alloc ";
}
old = rtnl_link_get_by_name(cache, ifn);
if (old) {
qDebug() << _tag << "change link";
retval = rtnl_link_change(rtsock, old, request, 0);
} else {
qDebug() << _tag << "change failed";
retval = -1;
qDebug() << _tag << "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) {
qDebug() << _tag << "error with link cache alloc";
return -1;
}
iface_idx = rtnl_link_name2i(cache, ifn);
if (!(addr = rtnl_addr_alloc())) {
qDebug() << _tag << "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) {
qDebug() << _tag << "error with set local addr";
}
prefixLength = ip4_netmaskToPrefix(ipAddress,netmask);
qDebug() << _tag << "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) {
qDebug() << _tag << "error with set broadcast addr";
}
rtnl_addr_set_ifindex(addr, iface_idx);
retval = sync_address(ifn, iface_idx, af, addr);
if(retval < 0) {
qDebug() << _tag << "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 == "") {
qDebug() << _tag << "error: netmask is empty";
return retval;
}
nae.setIp(QHostAddress(ipAddr));
nae.setNetmask(QHostAddress(netmask.trimmed()));
retval = nae.prefixLength();
return retval;
}
/**
* 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 ());
qDebug() << _tag << "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));
qDebug() << _tag << "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 ());
qDebug() << _tag << "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)) {
qDebug() << _tag << "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;
}