static void
wait_for_no_addresses (NMIP6Manager *self, NMIP6Device *device)
{
	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (self);
	guint64 now, end;
	gboolean has_addrs = TRUE;

	now = end = g_get_real_time ();
	end += (G_USEC_PER_SEC * 3);

	while (has_addrs && now < end) {
		struct rtnl_addr *rtnladdr;
		struct nl_addr *nladdr;

		nl_cache_refill (priv->nlh, priv->addr_cache);
		for (has_addrs = FALSE, rtnladdr = FIRST_ADDR (priv->addr_cache);
		     rtnladdr;
		     rtnladdr = NEXT_ADDR (rtnladdr)) {

			nladdr = rtnl_addr_get_local (rtnladdr);
			if (   rtnl_addr_get_ifindex (rtnladdr) == device->ifindex
			    && nladdr
			    && nl_addr_get_family (nladdr) == AF_INET6) {
				/* Still IPv6 addresses on the interface */
				has_addrs = TRUE;
				nm_log_dbg (LOGD_IP6, "(%s) waiting for cleared IPv6 addresses", device->iface);
				g_usleep (100);
				now = g_get_real_time ();
				break;
			}
		}
	}
}
char *
nm_netlink_index_to_iface (int idx)
{
	NMNetlinkMonitor *self;
	NMNetlinkMonitorPrivate *priv;
	char *buf = NULL;

	g_return_val_if_fail (idx >= 0, NULL);

	self = nm_netlink_monitor_get ();
	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);

	buf = g_malloc0 (MAX_IFACE_LEN);
	g_assert (buf);

	nl_cache_refill (priv->nlh_sync, priv->link_cache);
	if (!rtnl_link_i2name (priv->link_cache, idx, buf, MAX_IFACE_LEN - 1)) {
		nm_log_warn (LOGD_HW, "(%d) failed to find interface name for index", idx);
		g_free (buf);
		buf = NULL;
	}

	g_object_unref (self);
	return buf;
}
Пример #3
0
void netlink_wrapper::neigh_timer_expired() {
	m_cache_lock.lock();

	nl_logfunc("--->netlink_wrapper::neigh_timer_expired");
	nl_cache_refill(m_handle, m_cache_neigh);
	notify_cache_entries(m_cache_neigh);
	nl_logfunc("<---netlink_wrapper::neigh_timer_expired");

	m_cache_lock.unlock();
}
Пример #4
0
indigo_error_t
indigo_port_stats_get(
    of_port_stats_request_t *port_stats_request,
    of_port_stats_reply_t **port_stats_reply_ptr)
{
    of_port_no_t req_of_port_num;
    of_port_stats_reply_t *port_stats_reply;
    indigo_error_t err = INDIGO_ERROR_NONE;

    port_stats_reply = of_port_stats_reply_new(port_stats_request->version);
    if (port_stats_reply == NULL) {
        err = INDIGO_ERROR_RESOURCE;
        goto out;
    }

    of_list_port_stats_entry_t list;
    of_port_stats_reply_entries_bind(port_stats_reply, &list);

    of_port_stats_request_port_no_get(port_stats_request, &req_of_port_num);
    int dump_all = req_of_port_num == OF_PORT_DEST_NONE_BY_VERSION(port_stats_request->version);

    /* Refresh statistics */
    nl_cache_refill(route_cache_sock, link_cache);

    struct nl_msg *msg = ind_ovs_create_nlmsg(ovs_vport_family, OVS_VPORT_CMD_GET);
    if (dump_all) {
        nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
    } else {
        nla_put_u32(msg, OVS_VPORT_ATTR_PORT_NO, req_of_port_num);
    }

    /* Ask kernel to send us one or more OVS_VPORT_CMD_NEW messages */
    if (nl_send_auto(ind_ovs_socket, msg) < 0) {
        err = INDIGO_ERROR_UNKNOWN;
        goto out;
    }
    ind_ovs_nlmsg_freelist_free(msg);

    /* Handle OVS_VPORT_CMD_NEW messages */
    nl_cb_set(netlink_callbacks, NL_CB_VALID, NL_CB_CUSTOM,
              port_stats_iterator, &list);
    if (nl_recvmsgs(ind_ovs_socket, netlink_callbacks) < 0) {
        err = INDIGO_ERROR_UNKNOWN;
        goto out;
    }

out:
    if (err != INDIGO_ERROR_NONE) {
        of_port_stats_reply_delete(port_stats_reply);
        port_stats_reply = NULL;
    }

    *port_stats_reply_ptr = port_stats_reply;
    return err;
}
Пример #5
0
int netem_set_params(const char *iface, struct netem_params *params)
{
	struct rtnl_link *link;
	struct rtnl_qdisc *qdisc;
	int err;

	pthread_mutex_lock(&nl_sock_mutex);

	/* filter link by name */
	if ((link = rtnl_link_get_by_name(link_cache, iface)) == NULL) {
		fprintf(stderr, "unknown interface/link name.\n");
		pthread_mutex_unlock(&nl_sock_mutex);
		return -1;
	}

	if (!(qdisc = rtnl_qdisc_alloc())) {
		/* OOM error */
		fprintf(stderr, "couldn't alloc qdisc\n");
		pthread_mutex_unlock(&nl_sock_mutex);
		return -1;
	}

	rtnl_tc_set_link(TC_CAST(qdisc), link);
	rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
	rtnl_tc_set_kind(TC_CAST(qdisc), "netem");

	rtnl_netem_set_delay(qdisc,
	                     params->delay * 1000); /* expects microseconds */
	rtnl_netem_set_jitter(qdisc, params->jitter * 1000);
	/* params->loss is given in 10ths of a percent */
	rtnl_netem_set_loss(qdisc, (params->loss * (UINT_MAX / 1000)));

	/* Submit request to kernel and wait for response */
	err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE | NLM_F_REPLACE);

	/* Return the qdisc object to free memory resources */
	rtnl_qdisc_put(qdisc);

	if (err < 0) {
		fprintf(stderr, "Unable to add qdisc: %s\n", nl_geterror(err));
		pthread_mutex_unlock(&nl_sock_mutex);
		return err;
	}

	if ((err = nl_cache_refill(sock, link_cache)) < 0) {
		fprintf(stderr, "Unable to resync link cache: %s\n",
		        nl_geterror(err));
		pthread_mutex_unlock(&nl_sock_mutex);
		return -1;
	}

	pthread_mutex_unlock(&nl_sock_mutex);
	return 0;
}
Пример #6
0
int main(int argc, char *argv[])
{
	struct rtnl_link *link;
	struct nl_cache *link_cache;
	struct nl_sock *sk;
	int err;

	sk = nl_socket_alloc();
	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
		nl_perror(err, "Unable to connect socket");
		return err;
	}

	if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
		nl_perror(err, "Unable to allocate cache");
		return err;
	}

	if ((err = create_bridge(sk, link_cache, TEST_BRIDGE_NAME)) < 0) {
		nl_perror(err, "Unable to allocate testbridge");
		return err;
	}

	nl_cache_refill(sk, link_cache);

	link = rtnl_link_get_by_name(link_cache, TEST_BRIDGE_NAME);
	struct rtnl_link *ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME);
	if (!ltap) {
		fprintf(stderr, "You should create a tap interface before lunch this test (# tunctl -t %s)\n", TEST_INTERFACE_NAME);
		return -1;
	}

	if ((err = rtnl_link_enslave(sk, link, ltap)) < 0) {
		nl_perror(err, "Unable to enslave interface to his bridge\n");
		return err;
	}

	if(rtnl_link_is_bridge(link) == 0) {
		fprintf(stderr, "Link is not a bridge\n");
		return -2;
	}
	if(rtnl_link_get_master(ltap) <= 0) {
		fprintf(stderr, "Interface is not attached to a bridge\n");
		return -3;
	}

	rtnl_link_put(ltap);
	rtnl_link_put(link);

	nl_cache_free(link_cache);
	nl_socket_free(sk);

	return 0;
}
struct nl_cache *rtnl_addr_alloc_cache(struct nl_handle *handle)
{
	struct nl_cache *cache;
	
	cache = nl_cache_alloc(&rtnl_addr_ops);
	if (!cache)
		return NULL;

	if (handle && nl_cache_refill(handle, cache) < 0) {
		nl_cache_free(cache);
		return NULL;
	}

	return cache;
}
int
nm_netlink_iface_to_index (const char *iface)
{
	NMNetlinkMonitor *self;
	NMNetlinkMonitorPrivate *priv;
	int idx;

	g_return_val_if_fail (iface != NULL, -1);

	self = nm_netlink_monitor_get ();
	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);

	nl_cache_refill (priv->nlh_sync, priv->link_cache);
	idx = rtnl_link_name2i (priv->link_cache, iface);
	g_object_unref (self);

	return idx;
}
Пример #9
0
Файл: rule.c Проект: DINKIN/tuo
/**
 * Build a rule cache including all rules of the specified family currently configured in the kernel.
 * @arg handle		netlink handle
 * @arg family		address family
 *
 * Allocates a new rule cache, initializes it properly and updates it
 * to include all rules of the specified address family currently
 * configured in the kernel.
 *
 * @note The caller is responsible for destroying and freeing the
 *       cache after using it. (nl_cache_destroy_and_free())
 * @return The new cache or NULL if an error occured.
 */
struct nl_cache * rtnl_rule_alloc_cache_by_family(struct nl_handle *handle,
						  int family)
{
	struct nl_cache * cache;

	cache = nl_cache_alloc(&rtnl_rule_ops);
	if (cache == NULL)
		return NULL;

	/* XXX RULE_CACHE_FAMILY(cache) = family; */

	if (handle && nl_cache_refill(handle, cache) < 0) {
		free(cache);
		return NULL;
	}

	return cache;
}
Пример #10
0
/**
 * Build a rule cache including all rules currently configured in the kernel.
 * @arg sk		Netlink socket.
 * @arg family		Address family or AF_UNSPEC.
 * @arg result		Pointer to store resulting cache.
 *
 * Allocates a new rule cache, initializes it properly and updates it
 * to include all rules currently configured in the kernel.
 *
 * @return 0 on success or a negative error code.
 */
int rtnl_rule_alloc_cache(struct nl_sock *sock, int family,
			  struct nl_cache **result)
{
	struct nl_cache * cache;
	int err;

	if (!(cache = nl_cache_alloc(&rtnl_rule_ops)))
		return -NLE_NOMEM;

	cache->c_iarg1 = family;

	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
		free(cache);
		return err;
	}

	*result = cache;
	return 0;
}
Пример #11
0
/**
 * Allocate a cache and fill it with all configured classifiers
 * @arg sk		Netlink socket
 * @arg ifindex		Interface index of the network device
 * @arg parent		Parent qdisc/traffic class class
 * @arg result		Pointer to store the created cache
 *
 * Allocates a new classifier cache and fills it with a list of all
 * configured classifier attached to the specified parent qdisc/traffic
 * class on the specified network device. Release the cache with
 * nl_cache_free().
 *
 * @return 0 on success or a negative error code.
 */
int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,			 struct nl_cache **result)
{
	struct nl_cache * cache;
	int err;
	
	if (!(cache = nl_cache_alloc(&rtnl_cls_ops)))
		return -NLE_NOMEM;

	cache->c_iarg1 = ifindex;
	cache->c_iarg2 = parent;
	
	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
		nl_cache_free(cache);
		return err;
	}

	*result = cache;
	return 0;
}
struct rtnl_link *
nm_netlink_index_to_rtnl_link (int idx)
{
	NMNetlinkMonitor *self;
	NMNetlinkMonitorPrivate *priv;
	struct rtnl_link *ret = NULL;

	if (idx <= 0)
		return NULL;

	self = nm_netlink_monitor_get ();
	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);

	nl_cache_refill (priv->nlh_sync, priv->link_cache);
	ret = rtnl_link_get (priv->link_cache, idx);
	g_object_unref (self);

	return ret;
}
Пример #13
0
/**
 * Build an inetdiag cache to hold socket state information.
 * @arg	sk      Netlink socket
 * @arg family  The address family to query
 * @arg states  Socket states to query
 * @arg result  Result pointer
 *
 * @note The caller is responsible for destroying and free the cache after using
 *  it.
 * @return 0 on success of a negative error code.
 */
int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states,
		struct nl_cache **result)
{
	struct nl_cache *cache = NULL;
	int err;

	if (!(cache = nl_cache_alloc(&idiagnl_msg_ops)))
		return -NLE_NOMEM;

	cache->c_iarg1 = family;
	cache->c_iarg2 = states;

	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
		free(cache);
		return err;
	}

	*result = cache;
	return 0;
}
static gboolean
deferred_emit_carrier_state (gpointer user_data)
{
	NMNetlinkMonitor *self = NM_NETLINK_MONITOR (user_data);
	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
	int err;

	priv->request_status_id = 0;

	/* Update the link cache with latest state, and if there are no errors
	 * emit the link states for all the interfaces in the cache.
	 */
	err = nl_cache_refill (priv->nlh_sync, priv->link_cache);
	if (err < 0)
		nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror (err));
	else
		nl_cache_foreach_filter (priv->link_cache, NULL, link_msg_handler, self);

	return FALSE;
}
Пример #15
0
TError TNlLink::WaitAddress(int timeout_s) {
    struct nl_cache *cache;
    int ret;

    L() << "Wait for autoconf at " << GetDesc() << std::endl;

    ret = rtnl_addr_alloc_cache(GetSock(), &cache);
    if (ret < 0)
        return Nl->Error(ret, "Cannot allocate addr cache");

    do {
        for (auto obj = nl_cache_get_first(cache); obj; obj = nl_cache_get_next(obj)) {
            auto addr = (struct rtnl_addr *)obj;

            if (!rtnl_addr_get_local(addr) ||
                    rtnl_addr_get_ifindex(addr) != GetIndex() ||
                    rtnl_addr_get_family(addr) != AF_INET6 ||
                    rtnl_addr_get_scope(addr) >= RT_SCOPE_LINK ||
                    (rtnl_addr_get_flags(addr) &
                     (IFA_F_TENTATIVE | IFA_F_DEPRECATED)))
                continue;

            L() << "Got " << TNlAddr(rtnl_addr_get_local(addr)).Format()
                << " at " << GetDesc() << std::endl;

            nl_cache_free(cache);
            return TError::Success();
        }

        usleep(1000000);
        ret = nl_cache_refill(GetSock(), cache);
        if (ret < 0)
            return Nl->Error(ret, "Cannot refill address cache");
    } while (--timeout_s > 0);

    nl_cache_free(cache);
    return TError(EError::Unknown, "Network autoconf timeout");
}
Пример #16
0
	void LinuxPlatformBackend::update (const QStringList& devices)
	{
		if (!LinkCache_)
			return;

		nl_cache_refill (Rtsock_, LinkCache_);

		for (const auto& devName : devices)
		{
			auto link = rtnl_link_get_by_name (LinkCache_, devName.toLocal8Bit ().constData ());
			if (!link)
			{
				qWarning () << Q_FUNC_INFO
						<< "no link for device"
						<< devName;
				continue;
			}

			auto& info = DevInfos_ [devName];

			info.Traffic_.Down_ = rtnl_link_get_stat (link, RTNL_LINK_RX_BYTES);
			info.Traffic_.Up_ = rtnl_link_get_stat (link, RTNL_LINK_TX_BYTES);
		}
	}
gboolean
nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self,
                                   guint32 ifindex,
                                   guint32 *ifflags,
                                   GError **error)
{
	NMNetlinkMonitorPrivate *priv;
	GetFlagsInfo info;
	struct rtnl_link *filter;
	int err;

	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
	g_return_val_if_fail (ifflags != NULL, FALSE);

	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);

	/* Update the link cache with the latest information */
	err = nl_cache_refill (priv->nlh_sync, priv->link_cache);
	if (err < 0) {
		g_set_error (error,
		             NM_NETLINK_MONITOR_ERROR,
		             NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE,
		             _("error updating link cache: %s"),
		             nl_geterror (err));
		return FALSE;
	}

	/* HACK: Apparently to get it working we have to refill the cache twice;
	 * otherwise some kernels (or maybe libnl?) only send a few of the
	 * interfaces in the refill request.
	 */
	if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) {
		g_set_error (error,
		             NM_NETLINK_MONITOR_ERROR,
		             NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE,
		             _("error updating link cache: %s"),
		             nl_geterror (err));
		return FALSE;
	}

	/* Set up the filter */
	filter = rtnl_link_alloc ();
	if (!filter) {
		g_set_error (error,
		             NM_NETLINK_MONITOR_ERROR,
		             NM_NETLINK_MONITOR_ERROR_BAD_ALLOC,
		             _("error processing netlink message: %s"),
		             nl_geterror (err));
		return FALSE;
	}
	rtnl_link_set_ifindex (filter, ifindex);

	memset (&info, 0, sizeof (info));
	info.self = self;
	info.filter = filter;
	info.error = NULL;
	nl_cache_foreach_filter (priv->link_cache, NULL, get_flags_sync_cb, &info);

	rtnl_link_put (filter);

	if (info.error) {
		if (error)
			*error = info.error;
		else
			g_error_free (info.error);
		return FALSE;
	} else
		*ifflags = info.flags;

	return TRUE; /* success */
}
Пример #18
0
NMIP6Config *
nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
{
	NMIP6ManagerPrivate *priv;
	NMIP6Device *device;
	NMIP6Config *config;
	struct rtnl_addr *rtnladdr;
	struct nl_addr *nladdr;
	struct in6_addr *addr;
	NMIP6Address *ip6addr;
	struct rtnl_route *rtnlroute;
	struct nl_addr *nldest, *nlgateway;
	struct in6_addr *dest, *gateway;
	gboolean defgw_set = FALSE;
	struct in6_addr defgw;
	uint32_t metric;
	NMIP6Route *ip6route;
	int i;

	g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
	g_return_val_if_fail (ifindex > 0, NULL);

	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);

	device = (NMIP6Device *) g_hash_table_lookup (priv->devices,
	                                              GINT_TO_POINTER (ifindex));
	if (!device) {
		nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex);
		return NULL;
	}

	config = nm_ip6_config_new ();
	if (!config) {
		nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.",
		            device->iface);
		return NULL;
	}

	/* Make sure we refill the route and address caches, otherwise we won't get
	 * up-to-date information here since the netlink route/addr change messages
	 * may be lagging a bit.
	 */
	nl_cache_refill (priv->nlh, priv->route_cache);
	nl_cache_refill (priv->nlh, priv->addr_cache);

	/* Add routes */
	for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) {
		/* Make sure it's an IPv6 route for this device */
		if (rtnl_route_get_oif (rtnlroute) != device->ifindex)
			continue;
		if (rtnl_route_get_family (rtnlroute) != AF_INET6)
			continue;

		nldest = rtnl_route_get_dst (rtnlroute);
		if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
			continue;
		dest = nl_addr_get_binary_addr (nldest);

		nlgateway = rtnl_route_get_gateway (rtnlroute);
		if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
			continue;
		gateway = nl_addr_get_binary_addr (nlgateway);

		if (rtnl_route_get_dst_len (rtnlroute) == 0) {
			/* Default gateway route; don't add to normal routes but to each address */
			if (!defgw_set) {
				memcpy (&defgw, gateway, sizeof (defgw));
				defgw_set = TRUE;
			}
			continue;
		}

		/* Also ignore link-local routes where the destination and gateway are
		 * the same, which apparently get added by the kernel but return -EINVAL
		 * when we try to add them via netlink.
		 */
		if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway))
			continue;

		ip6route = nm_ip6_route_new ();
		nm_ip6_route_set_dest (ip6route, dest);
		nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
		nm_ip6_route_set_next_hop (ip6route, gateway);
		rtnl_route_get_metric(rtnlroute, 1, &metric);
		if (metric != UINT_MAX)
			nm_ip6_route_set_metric (ip6route, metric);
		nm_ip6_config_take_route (config, ip6route);
	}

	/* Add addresses */
	for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) {
		if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
			continue;

		nladdr = rtnl_addr_get_local (rtnladdr);
		if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
			continue;

		addr = nl_addr_get_binary_addr (nladdr);
		ip6addr = nm_ip6_address_new ();
		nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
		nm_ip6_address_set_address (ip6addr, addr);
		nm_ip6_config_take_address (config, ip6addr);
		if (defgw_set)
			nm_ip6_address_set_gateway (ip6addr, &defgw);
	}

	/* Add DNS servers */
	if (device->rdnss_servers) {
		NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);

		for (i = 0; i < device->rdnss_servers->len; i++)
			nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
	}

	/* Add DNS domains */
	if (device->dnssl_domains) {
		NMIP6DNSSL *dnssl = (NMIP6DNSSL *)(device->dnssl_domains->data);

		for (i = 0; i < device->dnssl_domains->len; i++)
			nm_ip6_config_add_domain (config, dnssl[i].domain);
	}

	return config;
}
Пример #19
0
static int init_qdisc(libxl__checkpoint_devices_state *cds,
                      libxl__remus_device_nic *remus_nic)
{
    int rc, ret, ifindex;
    struct rtnl_link *ifb = NULL;
    struct rtnl_qdisc *qdisc = NULL;
    libxl__remus_state *rs = cds->concrete_data;

    STATE_AO_GC(cds->ao);

    /* Now that we have brought up REMUS_IFB device with plug qdisc for
     * this vif, so we need to refill the qdisc cache.
     */
    ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache);
    if (ret) {
        LOGD(ERROR, cds->domid,
             "cannot refill qdisc cache: %s", nl_geterror(ret));
        rc = ERROR_FAIL;
        goto out;
    }

    /* get a handle to the REMUS_IFB interface */
    ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb);
    if (ret) {
        LOGD(ERROR, cds->domid,
             "cannot obtain handle for %s: %s", remus_nic->ifb,
            nl_geterror(ret));
        rc = ERROR_FAIL;
        goto out;
    }

    ifindex = rtnl_link_get_ifindex(ifb);
    if (!ifindex) {
        LOGD(ERROR, cds->domid,
             "interface %s has no index", remus_nic->ifb);
        rc = ERROR_FAIL;
        goto out;
    }

    /* Get a reference to the root qdisc installed on the REMUS_IFB, by
     * querying the qdisc list we obtained earlier. The netbufscript
     * sets up the plug qdisc as the root qdisc, so we don't have to
     * search the entire qdisc tree on the REMUS_IFB dev.

     * There is no need to explicitly free this qdisc as its just a
     * reference from the qdisc cache we allocated earlier.
     */
    qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT);
    if (qdisc) {
        const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc));
        /* Sanity check: Ensure that the root qdisc is a plug qdisc. */
        if (!tc_kind || strcmp(tc_kind, "plug")) {
            LOGD(ERROR, cds->domid,
                 "plug qdisc is not installed on %s", remus_nic->ifb);
            rc = ERROR_FAIL;
            goto out;
        }
        remus_nic->qdisc = qdisc;
    } else {
        LOGD(ERROR, cds->domid,
             "Cannot get qdisc handle from ifb %s", remus_nic->ifb);
        rc = ERROR_FAIL;
        goto out;
    }

    rc = 0;

out:
    if (ifb)
        rtnl_link_put(ifb);

    if (rc && qdisc)
        nl_object_put((struct nl_object *)qdisc);

    return rc;
}
Пример #20
0
static struct nl_addr *process_get_neigh_mac(
		struct get_neigh_handler *neigh_handler)
{
	int err;
	struct nl_addr *ll_addr = get_neigh_mac(neigh_handler);
	struct rtnl_neigh *neigh_filter;
	fd_set fdset;
	int sock_fd;
	int fd;
	int nfds;
	int timer_fd;
	int ret;
	struct skt addr_dst;
	char buff[sizeof(SEND_PAYLOAD)] = SEND_PAYLOAD;
	int retries = 0;

	if (NULL != ll_addr)
		return ll_addr;

	err = nl_socket_add_membership(neigh_handler->sock,
				       RTNLGRP_NEIGH);
	if (err < 0)
		return NULL;

	neigh_filter = create_filter_neigh_for_dst(neigh_handler->dst,
						   neigh_handler->oif);
	if (neigh_filter == NULL)
		return NULL;

	set_neigh_filter(neigh_handler, neigh_filter);

	nl_socket_disable_seq_check(neigh_handler->sock);
	nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID, NL_CB_CUSTOM,
			    &get_neigh_cb, neigh_handler);

	fd = nl_socket_get_fd(neigh_handler->sock);

	err = create_socket(neigh_handler, &addr_dst, &sock_fd);

	if (err)
		return NULL;

	err = try_send_to(sock_fd, buff, sizeof(buff), &addr_dst);
	if (err)
		goto close_socket;

	timer_fd = create_timer(neigh_handler);
	if (timer_fd < 0)
		goto close_socket;

	nfds = MAX(fd, timer_fd) + 1;

	while (1) {
		FD_ZERO(&fdset);
		FD_SET(fd, &fdset);
		FD_SET(timer_fd, &fdset);

		/* wait for an incoming message on the netlink socket */
		ret = select(nfds, &fdset, NULL, NULL, NULL);
		if (ret == -1) {
			goto select_err;
		} else if (ret) {
			if (FD_ISSET(fd, &fdset)) {
				nl_recvmsgs_default(neigh_handler->sock);
				if (neigh_handler->found_ll_addr)
					break;
			} else {
				nl_cache_refill(neigh_handler->sock,
						neigh_handler->neigh_cache);
				ll_addr = get_neigh_mac(neigh_handler);
				if (NULL != ll_addr) {
					break;
				} else if (FD_ISSET(timer_fd, &fdset) &&
					   retries < NUM_OF_RETRIES) {
					try_send_to(sock_fd, buff, sizeof(buff),
						    &addr_dst);
				}
			}

			if (FD_ISSET(timer_fd, &fdset)) {
				uint64_t read_val;
				ssize_t rc;

				rc =
				    read(timer_fd, &read_val, sizeof(read_val));
				assert(rc == sizeof(read_val));
				if (++retries >=  NUM_OF_TRIES) {
					if (!errno)
						errno = EDESTADDRREQ;
					break;
				}
			}
		}
	}
select_err:
	close(timer_fd);
close_socket:
	close(sock_fd);
	return ll_addr ? ll_addr : neigh_handler->found_ll_addr;
}
Пример #21
0
static int route_request_update(struct nl_cache *c, struct nl_sock *h)
{
	struct rtmsg rhdr = {
		.rtm_family = c->c_iarg1,
	};

	if (c->c_iarg2 & ROUTE_CACHE_CONTENT)
		rhdr.rtm_flags |= RTM_F_CLONED;

	return nl_send_simple(h, RTM_GETROUTE, NLM_F_DUMP, &rhdr, sizeof(rhdr));
}

/**
 * @name Cache Management
 * @{
 */

/**
 * Build a route cache holding all routes currently configured in the kernel
 * @arg sk		Netlink socket.
 * @arg family		Address family of routes to cover or AF_UNSPEC
 * @arg flags		Flags
 *
 * Allocates a new cache, initializes it properly and updates it to
 * contain all routes currently configured in the kernel.
 *
 * @note The caller is responsible for destroying and freeing the
 *       cache after using it.
 * @return The cache or NULL if an error has occured.
 */
int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags,
			   struct nl_cache **result)
{
	struct nl_cache *cache;
	int err;

	if (!(cache = nl_cache_alloc(&rtnl_route_ops)))
		return -NLE_NOMEM;

	cache->c_iarg1 = family;
	cache->c_iarg2 = flags;

	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
		free(cache);
		return err;
	}

	*result = cache;
	return 0;
}

/** @} */

/**
 * @name Route Addition
 * @{
 */

static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags,
			   struct nl_msg **result)
{
	struct nl_msg *msg;
	int err;

	if (!(msg = nlmsg_alloc_simple(cmd, flags)))
		return -NLE_NOMEM;

	if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) {
		nlmsg_free(msg);
		return err;
	}

	*result = msg;
	return 0;
}

int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags,
				 struct nl_msg **result)
{
	return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags,
			       result);
}

int rtnl_route_add(struct nl_sock *sk, struct rtnl_route *route, int flags)
{
	struct nl_msg *msg;
	int err;

	if ((err = rtnl_route_build_add_request(route, flags, &msg)) < 0)
		return err;

	err = nl_send_auto_complete(sk, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return wait_for_ack(sk);
}

int rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags,
				 struct nl_msg **result)
{
	return build_route_msg(tmpl, RTM_DELROUTE, flags, result);
}

int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags)
{
	struct nl_msg *msg;
	int err;

	if ((err = rtnl_route_build_del_request(route, flags, &msg)) < 0)
		return err;

	err = nl_send_auto_complete(sk, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return wait_for_ack(sk);
}

/** @} */

static struct nl_af_group route_groups[] = {
	{ AF_INET,	RTNLGRP_IPV4_ROUTE },
	{ AF_INET6,	RTNLGRP_IPV6_ROUTE },
	{ AF_DECnet,	RTNLGRP_DECnet_ROUTE },
	{ END_OF_GROUP_LIST },
};

static struct nl_cache_ops rtnl_route_ops = {
	.co_name		= "route/route",
	.co_hdrsize		= sizeof(struct rtmsg),
	.co_msgtypes		= {
					{ RTM_NEWROUTE, NL_ACT_NEW, "new" },
					{ RTM_DELROUTE, NL_ACT_DEL, "del" },
					{ RTM_GETROUTE, NL_ACT_GET, "get" },
					END_OF_MSGTYPES_LIST,
				  },
	.co_protocol		= NETLINK_ROUTE,
	.co_groups		= route_groups,
	.co_request_update	= route_request_update,
	.co_msg_parser		= route_msg_parser,
	.co_obj_ops		= &route_obj_ops,
};

static void __init route_init(void)
{
	nl_cache_mngt_register(&rtnl_route_ops);
}

static void __exit route_exit(void)
{
	nl_cache_mngt_unregister(&rtnl_route_ops);
}
Пример #22
0
int main(int argc, char *argv[])
{
    char *unikernel;
    enum {
        QEMU,
        KVM,
        UKVM,
        UNIX
    } hypervisor;

    if (argc < 3) {
        fprintf(stderr, "usage: runner HYPERVISOR UNIKERNEL [ ARGS... ]\n");
        fprintf(stderr, "HYPERVISOR: qemu | kvm | ukvm | unix\n");
        return 1;
    }
    if (strcmp(argv[1], "qemu") == 0)
        hypervisor = QEMU;
    else if (strcmp(argv[1], "kvm") == 0)
        hypervisor = KVM;
    else if (strcmp(argv[1], "ukvm") == 0)
        hypervisor = UKVM;
    else if (strcmp(argv[1], "unix") == 0)
        hypervisor = UNIX;
    else {
        warnx("error: Invalid hypervisor: %s", argv[1]);
        return 1;
    }
    unikernel = argv[2];
    /*
     * Remaining arguments are to be passed on to the unikernel.
     */
    argv += 3;
    argc -= 3;

    /*
     * Check we have CAP_NET_ADMIN.
     */
    if (capng_get_caps_process() != 0) {
        warnx("error: capng_get_caps_process() failed");
        return 1;
    }
    if (!capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_ADMIN)) {
        warnx("error: CAP_NET_ADMIN is required");
        return 1;
    }

    /*
     * Connect to netlink, load link cache from kernel.
     */
    struct nl_sock *sk;
    struct nl_cache *link_cache;
    int err;
 
    sk = nl_socket_alloc();
    assert(sk);
    err = nl_connect(sk, NETLINK_ROUTE);
    if (err < 0) {
        warnx("nl_connect() failed: %s", nl_geterror(err));
        return 1;
    }
    err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
    if (err < 0) {
        warnx("rtnl_link_alloc_cache() failed: %s", nl_geterror(err));
        return 1;
    }
   
    /*
     * Retrieve container network configuration -- IP address and
     * default gateway.
     */
    struct rtnl_link *l_veth;
    l_veth = rtnl_link_get_by_name(link_cache, VETH_LINK_NAME);
    if (l_veth == NULL) {
        warnx("error: Could not get link information for %s", VETH_LINK_NAME);
        return 1;
    }
    struct nl_addr *veth_addr;
    err = get_link_inet_addr(sk, l_veth, &veth_addr);
    if (err) {
        warnx("error: Unable to determine IP address of %s",
                VETH_LINK_NAME);
        return 1;
    }
    struct nl_addr *gw_addr;
    err = get_default_gw_inet_addr(sk, &gw_addr);
    if (err) {
        warnx("error: get_deGfault_gw_inet_addr() failed");
        return 1;
    }
    if (gw_addr == NULL) {
        warnx("error: No default gateway found. This is currently "
                "not supported");
        return 1;
    }

    /*
     * Create bridge and tap interface, enslave veth and tap interfaces to
     * bridge.
     */
    err = create_bridge_link(sk, BRIDGE_LINK_NAME);
    if (err < 0) {
        warnx("create_bridge_link(%s) failed: %s", BRIDGE_LINK_NAME,
                nl_geterror(err));
        return 1;
    }
    int tap_fd;

    if (hypervisor == UKVM)
        err = create_tap_link(TAP_LINK_NAME, &tap_fd);
    else
        err = create_tap_link(TAP_LINK_NAME, NULL);
    if (err != 0) {
        warnx("create_tap_link(%s) failed: %s", TAP_LINK_NAME, strerror(err));
        return 1;
    }

    /* Refill link cache with newly-created interfaces */
    nl_cache_refill(sk, link_cache);

    struct rtnl_link *l_bridge;
    l_bridge = rtnl_link_get_by_name(link_cache, BRIDGE_LINK_NAME);
    if (l_bridge == NULL) {
        warnx("error: Could not get link information for %s", BRIDGE_LINK_NAME);
        return 1;
    }
    struct rtnl_link *l_tap;
    l_tap = rtnl_link_get_by_name(link_cache, TAP_LINK_NAME);
    if (l_tap == NULL) {
        warnx("error: Could not get link information for %s", TAP_LINK_NAME);
        return 1;
    }
    err = rtnl_link_enslave(sk, l_bridge, l_veth);
    if (err < 0) {
        warnx("error: Unable to enslave %s to %s: %s", VETH_LINK_NAME,
                BRIDGE_LINK_NAME, nl_geterror(err));
        return 1;
    }
    err = rtnl_link_enslave(sk, l_bridge, l_tap);
    if (err < 0) {
        warnx("error: Unable to enslave %s to %s: %s", TAP_LINK_NAME,
                BRIDGE_LINK_NAME, nl_geterror(err));
        return 1;
    }

    /*
     * Flush all IPv4 addresses from the veth interface. This is now safe
     * as we are good to commit and have retrieved the existing configuration.
     */
    struct rtnl_addr *flush_addr;
    flush_addr = rtnl_addr_alloc();
    assert(flush_addr);
    rtnl_addr_set_ifindex(flush_addr, rtnl_link_get_ifindex(l_veth));
    rtnl_addr_set_family(flush_addr, AF_INET);
    rtnl_addr_set_local(flush_addr, veth_addr);
    err = rtnl_addr_delete(sk, flush_addr, 0);
    if (err < 0) {
        warnx("error: Could not flush addresses on %s: %s", VETH_LINK_NAME,
                nl_geterror(err));
        return 1;
    }
    rtnl_addr_put(flush_addr);

    /* 
     * Bring up the tap and bridge interfaces.
     */
    struct rtnl_link *l_up;
    l_up = rtnl_link_alloc();
    assert(l_up);
    /* You'd think set_operstate was the thing to do here. It's not. */
    rtnl_link_set_flags(l_up, IFF_UP);
    err = rtnl_link_change(sk, l_tap, l_up, 0);
    if (err < 0) {
        warnx("error: rtnl_link_change(%s, UP) failed: %s", TAP_LINK_NAME,
                nl_geterror(err));
        return 1;
    }
    err = rtnl_link_change(sk, l_bridge, l_up, 0);
    if (err < 0) {
        warnx("error: rtnl_link_change(%s, UP) failed: %s", BRIDGE_LINK_NAME,
                nl_geterror(err));
        return 1;
    }
    rtnl_link_put(l_up);

    /*
     * Collect network configuration data.
     */
    char ip[AF_INET_BUFSIZE];
    if (inet_ntop(AF_INET, nl_addr_get_binary_addr(veth_addr), ip,
            sizeof ip) == NULL) {
        perror("inet_ntop()");
        return 1;
    }
    char uarg_ip[AF_INET_BUFSIZE];
    unsigned int prefixlen = nl_addr_get_prefixlen(veth_addr);
    snprintf(uarg_ip, sizeof uarg_ip, "%s/%u", ip, prefixlen);

    char uarg_gw[AF_INET_BUFSIZE];
    if (inet_ntop(AF_INET, nl_addr_get_binary_addr(gw_addr), uarg_gw,
            sizeof uarg_gw) == NULL) {
        perror("inet_ntop()");
        return 1;
    }

    /*
     * Build unikernel and hypervisor arguments.
     */
    ptrvec* uargpv = pvnew();
    char *uarg_buf;
    /*
     * QEMU/KVM:
     * /usr/bin/qemu-system-x86_64 <qemu args> -kernel <unikernel> -append "<unikernel args>"
     */
    if (hypervisor == QEMU || hypervisor == KVM) {
        pvadd(uargpv, "/usr/bin/qemu-system-x86_64");
        pvadd(uargpv, "-nodefaults");
        pvadd(uargpv, "-no-acpi");
        pvadd(uargpv, "-display");
        pvadd(uargpv, "none");
        pvadd(uargpv, "-serial");
        pvadd(uargpv, "stdio");
        pvadd(uargpv, "-m");
        pvadd(uargpv, "512");
        if (hypervisor == KVM) {
            pvadd(uargpv, "-enable-kvm");
            pvadd(uargpv, "-cpu");
            pvadd(uargpv, "host");
        }
        else {
            /*
             * Required for AESNI use in Mirage.
             */
            pvadd(uargpv, "-cpu");
            pvadd(uargpv, "Westmere");
        }
        pvadd(uargpv, "-device");
        char *guest_mac = generate_mac();
        assert(guest_mac);
        err = asprintf(&uarg_buf, "virtio-net-pci,netdev=n0,mac=%s", guest_mac);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
        pvadd(uargpv, "-netdev");
        err = asprintf(&uarg_buf, "tap,id=n0,ifname=%s,script=no,downscript=no",
            TAP_LINK_NAME);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
        pvadd(uargpv, "-kernel");
        pvadd(uargpv, unikernel);
        pvadd(uargpv, "-append");
        /*
         * TODO: Replace any occurences of ',' with ',,' in -append, because
         * QEMU arguments are insane.
         */
        char cmdline[1024];
        char *cmdline_p = cmdline;
        size_t cmdline_free = sizeof cmdline;
        for (; *argv; argc--, argv++) {
            size_t alen = snprintf(cmdline_p, cmdline_free, "%s%s", *argv,
                    (argc > 1) ? " " : "");
            if (alen >= cmdline_free) {
                warnx("error: Command line too long");
                return 1;
            }
            cmdline_free -= alen;
            cmdline_p += alen;
        }
        size_t alen = snprintf(cmdline_p, cmdline_free,
                "--ipv4=%s --ipv4-gateway=%s", uarg_ip, uarg_gw);
        if (alen >= cmdline_free) {
            warnx("error: Command line too long");
            return 1;
        }
        pvadd(uargpv, cmdline);
    }
    /*
     * UKVM:
     * /unikernel/ukvm <ukvm args> <unikernel> -- <unikernel args>
     */
    else if (hypervisor == UKVM) {
        pvadd(uargpv, "/unikernel/ukvm");
        err = asprintf(&uarg_buf, "--net=@%d", tap_fd);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
        pvadd(uargpv, "--");
        pvadd(uargpv, unikernel);
        for (; *argv; argc--, argv++) {
            pvadd(uargpv, *argv);
        }
        err = asprintf(&uarg_buf, "--ipv4=%s", uarg_ip);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
        err = asprintf(&uarg_buf, "--ipv4-gateway=%s", uarg_gw);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
    }
    /*
     * UNIX:
     * <unikernel> <unikernel args>
     */
    else if (hypervisor == UNIX) {
        pvadd(uargpv, unikernel);
        err = asprintf(&uarg_buf, "--interface=%s", TAP_LINK_NAME);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
        for (; *argv; argc--, argv++) {
            pvadd(uargpv, *argv);
        }
        err = asprintf(&uarg_buf, "--ipv4=%s", uarg_ip);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
        err = asprintf(&uarg_buf, "--ipv4-gateway=%s", uarg_gw);
        assert(err != -1);
        pvadd(uargpv, uarg_buf);
    }
    char **uargv = (char **)pvfinal(uargpv);

    /*
     * Done with netlink, free all resources and close socket.
     */
    rtnl_link_put(l_veth);
    rtnl_link_put(l_bridge);
    rtnl_link_put(l_tap);
    nl_addr_put(veth_addr);
    nl_addr_put(gw_addr);

    nl_cache_free(link_cache);
    nl_close(sk);
    nl_socket_free(sk);

    /*
     * Drop all capabilities except CAP_NET_BIND_SERVICE.
     */
    capng_clear(CAPNG_SELECT_BOTH);
    capng_update(CAPNG_ADD,
            CAPNG_EFFECTIVE | CAPNG_PERMITTED | CAPNG_INHERITABLE,
            CAP_NET_BIND_SERVICE);
    if (capng_apply(CAPNG_SELECT_BOTH) != 0) {
        warnx("error: Could not drop capabilities");
        return 1;
    }

    /*
     * Run the unikernel.
     */
    err = execv(uargv[0], uargv);
    warn("error: execv() of %s failed", uargv[0]);
    return 1;
}
Пример #23
0
/**
 * Add cache responsibility to cache manager
 * @arg mngr		Cache manager.
 * @arg name		Name of cache to keep track of
 * @arg cb		Function to be called upon changes.
 * @arg data		Argument passed on to change callback
 * @arg result		Pointer to store added cache.
 *
 * Allocates a new cache of the specified type and adds it to the manager.
 * The operation will trigger a full dump request from the kernel to
 * initially fill the contents of the cache. The manager will subscribe
 * to the notification group of the cache to keep track of any further
 * changes.
 *
 * @return 0 on success or a negative error code.
 */
int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
		      change_func_t cb, void *data, struct nl_cache **result)
{
	struct nl_cache_ops *ops;
	struct nl_cache *cache;
	struct nl_af_group *grp;
	int err, i;

	ops = nl_cache_ops_lookup(name);
	if (!ops)
		return -NLE_NOCACHE;

	if (ops->co_protocol != mngr->cm_protocol)
		return -NLE_PROTO_MISMATCH;

	if (ops->co_groups == NULL)
		return -NLE_OPNOTSUPP;

	for (i = 0; i < mngr->cm_nassocs; i++)
		if (mngr->cm_assocs[i].ca_cache &&
		    mngr->cm_assocs[i].ca_cache->c_ops == ops)
			return -NLE_EXIST;

retry:
	for (i = 0; i < mngr->cm_nassocs; i++)
		if (!mngr->cm_assocs[i].ca_cache)
			break;

	if (i >= mngr->cm_nassocs) {
		mngr->cm_nassocs += 16;
		mngr->cm_assocs = realloc(mngr->cm_assocs,
					  mngr->cm_nassocs *
					  sizeof(struct nl_cache_assoc));
		if (mngr->cm_assocs == NULL)
			return -NLE_NOMEM;
		else {
			NL_DBG(1, "Increased capacity of cache manager %p " \
				  "to %d\n", mngr, mngr->cm_nassocs);
			goto retry;
		}
	}

	cache = nl_cache_alloc(ops);
	if (!cache)
		return -NLE_NOMEM;

	for (grp = ops->co_groups; grp->ag_group; grp++) {
		err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
		if (err < 0)
			goto errout_free_cache;
	}

	err = nl_cache_refill(mngr->cm_handle, cache);
	if (err < 0)
		goto errout_drop_membership;

	mngr->cm_assocs[i].ca_cache = cache;
	mngr->cm_assocs[i].ca_change = cb;
	mngr->cm_assocs[i].ca_change_data = data;

	if (mngr->cm_flags & NL_AUTO_PROVIDE)
		nl_cache_mngt_provide(cache);

	NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
	       cache, nl_cache_name(cache), mngr);

	*result = cache;
	return 0;

errout_drop_membership:
	for (grp = ops->co_groups; grp->ag_group; grp++)
		nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
errout_free_cache:
	nl_cache_free(cache);

	return err;
}
Пример #24
0
/**
 * Add cache to cache manager
 * @arg mngr		Cache manager.
 * @arg cache		Cache to be added to cache manager
 * @arg cb		Function to be called upon changes.
 * @arg data		Argument passed on to change callback
 *
 * Adds cache to the manager. The operation will trigger a full
 * dump request from the kernel to initially fill the contents
 * of the cache. The manager will subscribe to the notification group
 * of the cache and keep track of any further changes.
 *
 * The user is responsible for calling nl_cache_mngr_poll() or monitor
 * the socket and call nl_cache_mngr_data_ready() to allow the library
 * to process netlink notification events.
 *
 * @see nl_cache_mngr_poll()
 * @see nl_cache_mngr_data_ready()
 *
 * @return 0 on success or a negative error code.
 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
 * 			       cache type
 * @return -NLE_OPNOTSUPP Cache type does not support updates
 * @return -NLE_EXIST Cache of this type already being managed
 */
int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache,
		      change_func_t cb, void *data)
{
	struct nl_cache_ops *ops;
	struct nl_af_group *grp;
	int err, i;

	ops = cache->c_ops;
	if (!ops)
		return -NLE_INVAL;

	if (ops->co_protocol != mngr->cm_protocol)
		return -NLE_PROTO_MISMATCH;

	if (ops->co_groups == NULL)
		return -NLE_OPNOTSUPP;

	for (i = 0; i < mngr->cm_nassocs; i++)
		if (mngr->cm_assocs[i].ca_cache &&
		    mngr->cm_assocs[i].ca_cache->c_ops == ops)
			return -NLE_EXIST;

retry:
	for (i = 0; i < mngr->cm_nassocs; i++)
		if (!mngr->cm_assocs[i].ca_cache)
			break;

	if (i >= mngr->cm_nassocs) {
		mngr->cm_nassocs += NASSOC_EXPAND;
		mngr->cm_assocs = realloc(mngr->cm_assocs,
					  mngr->cm_nassocs *
					  sizeof(struct nl_cache_assoc));
		if (mngr->cm_assocs == NULL)
			return -NLE_NOMEM;

		memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0,
		       NASSOC_EXPAND * sizeof(struct nl_cache_assoc));

		NL_DBG(1, "Increased capacity of cache manager %p " \
			  "to %d\n", mngr, mngr->cm_nassocs);
		goto retry;
	}

	for (grp = ops->co_groups; grp->ag_group; grp++) {
		err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group);
		if (err < 0)
			return err;
	}

	err = nl_cache_refill(mngr->cm_sync_sock, cache);
	if (err < 0)
		goto errout_drop_membership;

	mngr->cm_assocs[i].ca_cache = cache;
	mngr->cm_assocs[i].ca_change = cb;
	mngr->cm_assocs[i].ca_change_data = data;

	if (mngr->cm_flags & NL_AUTO_PROVIDE)
		nl_cache_mngt_provide(cache);

	NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
	       cache, nl_cache_name(cache), mngr);

	return 0;

errout_drop_membership:
	for (grp = ops->co_groups; grp->ag_group; grp++)
		nl_socket_drop_membership(mngr->cm_sock, grp->ag_group);

	return err;
}
static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci,
				      struct rtnl_route *route)
{
	struct rtnl_rtcacheinfo nci = {
		.rtci_clntref  = ci->rta_clntref,
		.rtci_last_use = ci->rta_lastuse,
		.rtci_expires  = ci->rta_expires,
		.rtci_error    = ci->rta_error,
		.rtci_used     = ci->rta_used,
		.rtci_id       = ci->rta_id,
		.rtci_ts       = ci->rta_ts,
		.rtci_tsage    = ci->rta_tsage,
	};

	rtnl_route_set_cacheinfo(route, &nci);
}

static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
			    struct nlmsghdr *nlh, struct nl_parser_param *pp)
{
	struct rtmsg *rtm;
	struct rtnl_route *route;
	struct nlattr *tb[RTA_MAX + 1];
	struct nl_addr *src = NULL, *dst = NULL, *addr;
	int err;

	route = rtnl_route_alloc();
	if (!route) {
		err = nl_errno(ENOMEM);
		goto errout;
	}

	route->ce_msgtype = nlh->nlmsg_type;

	err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
			  route_policy);
	if (err < 0)
		goto errout;

	rtm = nlmsg_data(nlh);
	rtnl_route_set_family(route, rtm->rtm_family);
	rtnl_route_set_tos(route, rtm->rtm_tos);
	rtnl_route_set_table(route, rtm->rtm_table);
	rtnl_route_set_type(route, rtm->rtm_type);
	rtnl_route_set_scope(route, rtm->rtm_scope);
	rtnl_route_set_protocol(route, rtm->rtm_protocol);
	rtnl_route_set_flags(route, rtm->rtm_flags);

	if (tb[RTA_DST]) {
		dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family);
		if (dst == NULL)
			goto errout_errno;
	} else {
		dst = nl_addr_alloc(0);
		nl_addr_set_family(dst, rtm->rtm_family);
	}

	nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
	err = rtnl_route_set_dst(route, dst);
	if (err < 0)
		goto errout;

	nl_addr_put(dst);

	if (tb[RTA_SRC]) {
		src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family);
		if (src == NULL)
			goto errout_errno;
	} else if (rtm->rtm_src_len)
		src = nl_addr_alloc(0);

	if (src) {
		nl_addr_set_prefixlen(src, rtm->rtm_src_len);
		rtnl_route_set_src(route, src);
		nl_addr_put(src);
	}

	if (tb[RTA_IIF])
		rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF]));

	if (tb[RTA_OIF])
		rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF]));

	if (tb[RTA_GATEWAY]) {
		addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family);
		if (addr == NULL)
			goto errout_errno;
		rtnl_route_set_gateway(route, addr);
		nl_addr_put(addr);
	}

	if (tb[RTA_PRIORITY])
		rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY]));

	if (tb[RTA_PREFSRC]) {
		addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family);
		if (addr == NULL)
			goto errout_errno;
		rtnl_route_set_pref_src(route, addr);
		nl_addr_put(addr);
	}

	if (tb[RTA_METRICS]) {
		struct nlattr *mtb[RTAX_MAX + 1];
		int i;

		err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
		if (err < 0)
			goto errout;

		for (i = 1; i <= RTAX_MAX; i++) {
			if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
				uint32_t m = nla_get_u32(mtb[i]);
				if (rtnl_route_set_metric(route, i, m) < 0)
					goto errout_errno;
			}
		}
	}

	if (tb[RTA_MULTIPATH]) {
		struct rtnl_nexthop *nh;
		struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]);
		size_t tlen = nla_len(tb[RTA_MULTIPATH]);

		while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
			nh = rtnl_route_nh_alloc();
			if (!nh)
				goto errout;

			rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
			rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
			rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);

			if (rtnh->rtnh_len > sizeof(*rtnh)) {
				struct nlattr *ntb[RTA_MAX + 1];
				nla_parse(ntb, RTA_MAX, (struct nlattr *)
					  RTNH_DATA(rtnh),
					  rtnh->rtnh_len - sizeof(*rtnh),
					  route_policy);

				if (ntb[RTA_GATEWAY]) {
					nh->rtnh_gateway = nla_get_addr(
							ntb[RTA_GATEWAY],
							route->rt_family);
					nh->rtnh_mask = NEXTHOP_HAS_GATEWAY;
				}
			}

			rtnl_route_add_nexthop(route, nh);
			tlen -= RTNH_ALIGN(rtnh->rtnh_len);
			rtnh = RTNH_NEXT(rtnh);
		}
	}

	if (tb[RTA_FLOW])
		rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW]));

	if (tb[RTA_CACHEINFO])
		copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route);

	if (tb[RTA_MP_ALGO])
		rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO]));

	err = pp->pp_cb((struct nl_object *) route, pp);
	if (err < 0)
		goto errout;

	err = P_ACCEPT;

errout:
	rtnl_route_put(route);
	return err;

errout_errno:
	err = nl_get_errno();
	goto errout;
}

static int route_request_update(struct nl_cache *c, struct nl_handle *h)
{
	return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP);
}

/**
 * @name Cache Management
 * @{
 */

/**
 * Build a route cache holding all routes currently configured in the kernel
 * @arg handle		netlink handle
 *
 * Allocates a new cache, initializes it properly and updates it to
 * contain all routes currently configured in the kernel.
 *
 * @note The caller is responsible for destroying and freeing the
 *       cache after using it.
 * @return The cache or NULL if an error has occured.
 */
struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle)
{
	struct nl_cache *cache;

	cache = nl_cache_alloc(&rtnl_route_ops);
	if (!cache)
		return NULL;

	if (handle && nl_cache_refill(handle, cache) < 0) {
		free(cache);
		return NULL;
	}

	return cache;
}

/** @} */

/**
 * @name Route Addition
 * @{
 */

static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd,
				      int flags)
{
	struct nl_msg *msg;
	struct nl_addr *addr;
	int scope, i, oif, nmetrics = 0;
	struct nlattr *metrics;
	struct rtmsg rtmsg = {
		.rtm_family = rtnl_route_get_family(tmpl),
		.rtm_dst_len = rtnl_route_get_dst_len(tmpl),
		.rtm_src_len = rtnl_route_get_src_len(tmpl),
		.rtm_tos = rtnl_route_get_tos(tmpl),
		.rtm_table = rtnl_route_get_table(tmpl),
		.rtm_type = rtnl_route_get_type(tmpl),
		.rtm_protocol = rtnl_route_get_protocol(tmpl),
		.rtm_flags = rtnl_route_get_flags(tmpl),
	};

	if (rtmsg.rtm_family == AF_UNSPEC) {
		nl_error(EINVAL, "Cannot build route message, address " \
				 "family is unknown.");
		return NULL;
	}

	scope = rtnl_route_get_scope(tmpl);
	if (scope == RT_SCOPE_NOWHERE) {
		if (rtmsg.rtm_type == RTN_LOCAL)
			scope = RT_SCOPE_HOST;
		else {
			/* XXX Change to UNIVERSE if gw || nexthops */
			scope = RT_SCOPE_LINK;
		}
	}

	rtmsg.rtm_scope = scope;

	msg = nlmsg_alloc_simple(cmd, flags);
	if (msg == NULL)
		return NULL;

	if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
		goto nla_put_failure;

	addr = rtnl_route_get_dst(tmpl);
	if (addr)
		NLA_PUT_ADDR(msg, RTA_DST, addr);

	addr = rtnl_route_get_src(tmpl);
	if (addr)
		NLA_PUT_ADDR(msg, RTA_SRC, addr);

	addr = rtnl_route_get_gateway(tmpl);
	if (addr)
		NLA_PUT_ADDR(msg, RTA_GATEWAY, addr);

	addr = rtnl_route_get_pref_src(tmpl);
	if (addr)
		NLA_PUT_ADDR(msg, RTA_PREFSRC, addr);

	NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl));

	oif = rtnl_route_get_oif(tmpl);
	if (oif != RTNL_LINK_NOT_FOUND)
		NLA_PUT_U32(msg, RTA_OIF, oif);

	for (i = 1; i <= RTAX_MAX; i++)
		if (rtnl_route_get_metric(tmpl, i) != UINT_MAX)
			nmetrics++;

	if (nmetrics > 0) {
		unsigned int val;

		metrics = nla_nest_start(msg, RTA_METRICS);
		if (metrics == NULL)
			goto nla_put_failure;

		for (i = 1; i <= RTAX_MAX; i++) {
			val = rtnl_route_get_metric(tmpl, i);
			if (val != UINT_MAX)
				NLA_PUT_U32(msg, i, val);
		}

		nla_nest_end(msg, metrics);
	}

#if 0
	RTA_IIF,
	RTA_MULTIPATH,
	RTA_PROTOINFO,
	RTA_FLOW,
	RTA_CACHEINFO,
	RTA_SESSION,
	RTA_MP_ALGO,
#endif

	return msg;

nla_put_failure:
	nlmsg_free(msg);
	return NULL;
}

struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags)
{
	return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags);
}

int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route,
		   int flags)
{
	struct nl_msg *msg;
	int err;

	msg = rtnl_route_build_add_request(route, flags);
	if (!msg)
		return nl_get_errno();

	err = nl_send_auto_complete(handle, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return nl_wait_for_ack(handle);
}

struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags)
{
	return build_route_msg(tmpl, RTM_DELROUTE, flags);
}

int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route,
		   int flags)
{
	struct nl_msg *msg;
	int err;

	msg = rtnl_route_build_del_request(route, flags);
	if (!msg)
		return nl_get_errno();

	err = nl_send_auto_complete(handle, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return nl_wait_for_ack(handle);
}

/** @} */

static struct nl_af_group route_groups[] = {
	{ AF_INET,	RTNLGRP_IPV4_ROUTE },
	{ AF_INET6,	RTNLGRP_IPV6_ROUTE },
	{ AF_DECnet,	RTNLGRP_DECnet_ROUTE },
	{ END_OF_GROUP_LIST },
};

static struct nl_cache_ops rtnl_route_ops = {
	.co_name		= "route/route",
	.co_hdrsize		= sizeof(struct rtmsg),
	.co_msgtypes		= {
					{ RTM_NEWROUTE, NL_ACT_NEW, "new" },
					{ RTM_DELROUTE, NL_ACT_DEL, "del" },
					{ RTM_GETROUTE, NL_ACT_GET, "get" },
					END_OF_MSGTYPES_LIST,
				  },
	.co_protocol		= NETLINK_ROUTE,
	.co_groups		= route_groups,
	.co_request_update	= route_request_update,
	.co_msg_parser		= route_msg_parser,
	.co_obj_ops		= &route_obj_ops,
};

static void __init route_init(void)
{
	nl_cache_mngt_register(&rtnl_route_ops);
}

static void __exit route_exit(void)
{
	nl_cache_mngt_unregister(&rtnl_route_ops);
}
Пример #26
0
static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
{
	struct tcmsg tchdr = {
		.tcm_family = AF_UNSPEC,
		.tcm_ifindex = cache->c_iarg1,
		.tcm_parent = cache->c_iarg2,
	};

	return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
			      sizeof(tchdr));
}


static int cls_build(struct rtnl_cls *cls, int type, int flags,
		     struct nl_msg **result)
{
	struct rtnl_cls_ops *cops;
	int err, prio, proto;
	struct tcmsg *tchdr;

	err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result);
	if (err < 0)
		return err;

	tchdr = nlmsg_data(nlmsg_hdr(*result));
	prio = rtnl_cls_get_prio(cls);
	proto = rtnl_cls_get_protocol(cls);
	tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));

	cops = rtnl_cls_lookup_ops(cls);
	if (cops && cops->co_get_opts) {
		struct nl_msg *opts;

		if (!(opts = nlmsg_alloc())) {
			err = -NLE_NOMEM;
			goto errout;
		}

		if (!(err = cops->co_get_opts(cls, opts)))
			err = nla_put_nested(*result, TCA_OPTIONS, opts);

		nlmsg_free(opts);
		if (err < 0)
			goto errout;
	}

	return 0;
errout:
	nlmsg_free(*result);
	return err;
}

/**
 * @name Classifier Addition/Modification/Deletion
 * @{
 */

/**
 * Build a netlink message to add a new classifier
 * @arg cls		classifier to add
 * @arg flags		additional netlink message flags
 * @arg result		Pointer to store resulting message.
 *
 * Builds a new netlink message requesting an addition of a classifier
 * The netlink message header isn't fully equipped with all relevant
 * fields and must be sent out via nl_send_auto_complete() or
 * supplemented as needed. \a classifier must contain the attributes of
 * the new classifier set via \c rtnl_cls_set_* functions. \a opts
 * may point to the clsasifier specific options.
 *
 * @return 0 on success or a negative error code.
 */
int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags,
			       struct nl_msg **result)
{
	return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result);
}

/**
 * Add a new classifier
 * @arg sk		Netlink socket.
 * @arg cls 		classifier to add
 * @arg flags		additional netlink message flags
 *
 * Builds a netlink message by calling rtnl_cls_build_add_request(),
 * sends the request to the kernel and waits for the next ACK to be
 * received and thus blocks until the request has been processed.
 *
 * @return 0 on sucess or a negative error if an error occured.
 */
int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
{
	struct nl_msg *msg;
	int err;
	
	if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0)
		return err;
	
	err = nl_send_auto_complete(sk, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return nl_wait_for_ack(sk);
}

/**
 * Build a netlink message to change classifier attributes
 * @arg cls		classifier to change
 * @arg flags		additional netlink message flags
 * @arg result		Pointer to store resulting message.
 *
 * Builds a new netlink message requesting a change of a neigh
 * attributes. The netlink message header isn't fully equipped with
 * all relevant fields and must thus be sent out via nl_send_auto_complete()
 * or supplemented as needed.
 *
 * @return 0 on success or a negative error code.
 */
int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags,
				  struct nl_msg **result)
{
	return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result);
}

/**
 * Change a classifier
 * @arg sk		Netlink socket.
 * @arg cls		classifier to change
 * @arg flags		additional netlink message flags
 *
 * Builds a netlink message by calling rtnl_cls_build_change_request(),
 * sends the request to the kernel and waits for the next ACK to be
 * received and thus blocks until the request has been processed.
 *
 * @return 0 on sucess or a negative error if an error occured.
 */
int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
{
	struct nl_msg *msg;
	int err;
	
	if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0)
		return err;
	
	err = nl_send_auto_complete(sk, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return nl_wait_for_ack(sk);
}

/**
 * Build a netlink request message to delete a classifier
 * @arg cls		classifier to delete
 * @arg flags		additional netlink message flags
 * @arg result		Pointer to store resulting message.
 *
 * Builds a new netlink message requesting a deletion of a classifier.
 * The netlink message header isn't fully equipped with all relevant
 * fields and must thus be sent out via nl_send_auto_complete()
 * or supplemented as needed.
 *
 * @return 0 on success or a negative error code.
 */
int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags,
				  struct nl_msg **result)
{
	return cls_build(cls, RTM_DELTFILTER, flags, result);
}


/**
 * Delete a classifier
 * @arg sk		Netlink socket.
 * @arg cls		classifier to delete
 * @arg flags		additional netlink message flags
 *
 * Builds a netlink message by calling rtnl_cls_build_delete_request(),
 * sends the request to the kernel and waits for the next ACK to be
 * received and thus blocks until the request has been processed.
 *
 * @return 0 on sucess or a negative error if an error occured.
 */
int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
{
	struct nl_msg *msg;
	int err;
	
	if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0)
		return err;
	
	err = nl_send_auto_complete(sk, msg);
	nlmsg_free(msg);
	if (err < 0)
		return err;

	return nl_wait_for_ack(sk);
}

/** @} */

/**
 * @name Cache Management
 * @{
 */

/**
 * Build a classifier cache including all classifiers attached to the
 * specified class/qdisc on eht specified interface.
 * @arg sk		Netlink socket.
 * @arg ifindex		interface index of the link the classes are
 *                      attached to.
 * @arg parent          parent qdisc/class
 * @arg result		Pointer to store resulting cache.
 *
 * Allocates a new cache, initializes it properly and updates it to
 * include all classes attached to the specified interface.
 *
 * @note The caller is responsible for destroying and freeing the
 *       cache after using it.
 * @return 0 on success or a negative error code.
 */
int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,			 struct nl_cache **result)
{
	struct nl_cache * cache;
	int err;
	
	if (!(cache = nl_cache_alloc(&rtnl_cls_ops)))
		return -NLE_NOMEM;

	cache->c_iarg1 = ifindex;
	cache->c_iarg2 = parent;
	
	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
		nl_cache_free(cache);
		return err;
	}

	*result = cache;
	return 0;
}

/** @} */

static struct nl_cache_ops rtnl_cls_ops = {
	.co_name		= "route/cls",
	.co_hdrsize		= sizeof(struct tcmsg),
	.co_msgtypes		= {
					{ RTM_NEWTFILTER, NL_ACT_NEW, "new" },
					{ RTM_DELTFILTER, NL_ACT_DEL, "del" },
					{ RTM_GETTFILTER, NL_ACT_GET, "get" },
					END_OF_MSGTYPES_LIST,
				  },
	.co_protocol		= NETLINK_ROUTE,
	.co_request_update	= cls_request_update,
	.co_msg_parser		= cls_msg_parser,
	.co_obj_ops		= &cls_obj_ops,
};

static void __init cls_init(void)
{
	nl_cache_mngt_register(&rtnl_cls_ops);
}

static void __exit cls_exit(void)
{
	nl_cache_mngt_unregister(&rtnl_cls_ops);
}
Пример #27
0
int netem_get_params(char *iface, struct netem_params *params)
{
	struct rtnl_link *link;
	struct rtnl_qdisc *filter_qdisc;
	struct rtnl_qdisc *found_qdisc = NULL;
	int err;
	int delay, jitter, loss;

	pthread_mutex_lock(&nl_sock_mutex);
	if ((err = nl_cache_refill(sock, link_cache)) < 0) {
		fprintf(stderr, "Unable to resync link cache: %s\n",
		        nl_geterror(err));
		goto cleanup;
	}

	if ((err = nl_cache_refill(sock, qdisc_cache)) < 0) {
		fprintf(stderr, "Unable to resync link cache: %s\n",
		        nl_geterror(err));
		goto cleanup;
	}

	/* filter link by name */
	if ((link = rtnl_link_get_by_name(link_cache, iface)) == NULL) {
		fprintf(stderr, "unknown interface/link name.\n");
		goto cleanup;
	}

	if (!(filter_qdisc = rtnl_qdisc_alloc())) {
		/* OOM error */
		fprintf(stderr, "couldn't alloc qdisc\n");
		goto cleanup_link;
	}

	rtnl_tc_set_link(TC_CAST(filter_qdisc), link);
	rtnl_tc_set_parent(TC_CAST(filter_qdisc), TC_H_ROOT);
	rtnl_tc_set_kind(TC_CAST(filter_qdisc), "netem");

	found_qdisc = (struct rtnl_qdisc *)nl_cache_find(
	    qdisc_cache, OBJ_CAST(filter_qdisc));
	if (!found_qdisc) {
		/* The iface probably doesn't have a netem qdisc at startup. */
		goto cleanup_filter_qdisc;
	}

	if (0 > (delay = rtnl_netem_get_delay(found_qdisc))) {
		fprintf(stderr, "couldn't get delay for iface: %s\n", iface);
		goto cleanup_qdisc;
	}
	params->delay = (double)delay / 1000;

	if (0 > (jitter = rtnl_netem_get_jitter(found_qdisc))) {
		fprintf(stderr, "couldn't get jitter for iface: %s\n", iface);
		goto cleanup_qdisc;
	}
	params->jitter = (double)jitter / 1000;

	if (0 > (loss = rtnl_netem_get_loss(found_qdisc))) {
		fprintf(stderr, "couldn't get loss for iface: %s\n", iface);
		goto cleanup_qdisc;
	}
	/* loss is specified in 10ths of a percent, ie. 1 ==> 0.1% */
	params->loss = (int)(loss / (UINT_MAX / 1000));

	rtnl_qdisc_put(found_qdisc);
	rtnl_qdisc_put(filter_qdisc);
	rtnl_link_put(link);
	pthread_mutex_unlock(&nl_sock_mutex);
	return 0;

cleanup_qdisc:
	rtnl_qdisc_put(found_qdisc);
cleanup_filter_qdisc:
	rtnl_qdisc_put(filter_qdisc);
cleanup_link:
	rtnl_link_put(link);
cleanup:
	pthread_mutex_unlock(&nl_sock_mutex);
	return -1;
}