summaryrefslogblamecommitdiffstats
path: root/src/networkmanager.cpp
blob: b0290efa1475b48ae4025658e5cc584fa0334d93 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                                             
  

                                                                      

   

 
























































































                                                                                   





































































































                                                                                             








































































































































































































































                                                                                                                                      












































































                                                                                           







































































































































































                                                                                                           
/**
 * @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"

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;
}



/**/
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() <<  _tag << "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())) {
		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;
}



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) {
			qDebug() <<  _tag << "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 ());
			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;
}