Exemple #1
0
/*
 * Extract interface index from object path.
 * Path names must be NI_OBJECTMODEL_OBJECT_PATH "/" <something> "/Interface/" <index>
 */
static ni_netdev_t *
ni_objectmodel_addrconf_path_to_device(const char *path)
{
	unsigned int ifindex;
	ni_netconfig_t *nc;
	char cc;

	if (strncmp(path, NI_OBJECTMODEL_OBJECT_PATH, strlen(NI_OBJECTMODEL_OBJECT_PATH)))
		return NULL;
	path += strlen(NI_OBJECTMODEL_OBJECT_PATH);

	if (*path++ != '/')
		return NULL;
	while ((cc = *path++) != '/') {
		if (cc == '\0')
			return NULL;
	}

	if (strncmp(path, "Interface/", 10))
		return NULL;
	path += 10;

	if (ni_parse_uint(path, &ifindex, 10) < 0)
		return NULL;

	nc = ni_global_state_handle(1);
	if (nc == NULL) {
		ni_error("%s: unable to refresh interfaces", __func__);
		return NULL;
	}

	return ni_netdev_by_index(nc, ifindex);
}
Exemple #2
0
static int
__ni_rtevent_deladdr(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct ifaddrmsg *ifa;
	ni_address_t tmp, *ap;
	ni_netdev_t *dev;

	if (!(ifa = ni_rtnl_ifaddrmsg(h, RTM_DELADDR)))
		return -1;

	dev = ni_netdev_by_index(nc, ifa->ifa_index);
	if (dev == NULL)
		return 0;

	if (__ni_rtnl_parse_newaddr(dev->link.ifflags, h, ifa, &tmp) < 0) {
		ni_error("Problem parsing RTM_DELADDR message for %s", dev->name);
		return -1;
	}

	if ((ap = ni_address_list_find(dev->addrs, &tmp.local_addr)) != NULL) {
		__ni_netdev_addr_event(dev, NI_EVENT_ADDRESS_DELETE, ap);

		__ni_address_list_remove(&dev->addrs, ap);
	}
	ni_string_free(&tmp.label);

	return 0;
}
Exemple #3
0
/*
 * Callback from wpa_supplicant client whenever the association state changes
 * in a significant way.
 */
void
ni_wireless_association_changed(unsigned int ifindex, ni_wireless_assoc_state_t new_state)
{
	ni_netconfig_t *nc = ni_global_state_handle(0);
	ni_netdev_t *dev;
	ni_wireless_t *wlan;

	if (!(dev = ni_netdev_by_index(nc, ifindex)))
		return;

	if (!(wlan = dev->wireless))
		return;

	if (new_state == wlan->assoc.state)
		return;

	wlan->assoc.state = new_state;
	if (new_state == NI_WIRELESS_ESTABLISHED)
		__ni_netdev_event(nc, dev, NI_EVENT_LINK_ASSOCIATED);

	/* We keep track of when we were last changing to or
	 * from fully authenticated state.
	 * We use this to decide when to give up and announce
	 * that we've lost the network - see the timer handling
	 * code above.
	 */
	ni_wireless_update_association_timer(dev);
}
Exemple #4
0
static ni_bool_t
__ni_dhcp4_address_on_link(ni_dhcp4_device_t *dev, struct in_addr ipv4)
{
	ni_netconfig_t *nc;
	ni_netdev_t *ifp;

	nc = ni_global_state_handle(0);
	if (!nc || !(ifp = ni_netdev_by_index(nc, dev->link.ifindex)))
		return FALSE;

	return __ni_dhcp4_address_on_device(ifp, ipv4);
}
Exemple #5
0
ni_netdev_t *
ni_netdev_ref_bind_ifname(ni_netdev_ref_t *ref, ni_netconfig_t *nc)
{
	ni_netdev_t *dev;

	if (!ref || (!nc && !(nc = ni_global_state_handle(0))))
		return NULL;

	dev = ni_netdev_by_index(nc, ref->index);
	if (dev == NULL)
		return NULL;

	if (!ni_string_eq(ref->name, dev->name))
		ni_string_dup(&ref->name, dev->name);
	return dev;
}
Exemple #6
0
ni_netdev_t *
ni_netdev_ref_resolve(ni_netdev_ref_t *ref, ni_netconfig_t *nc)
{
	ni_netdev_t *dev = NULL;

	if (!ref || (!nc && !(nc = ni_global_state_handle(0))))
		return NULL;

	if (ref->index && (dev = ni_netdev_by_index(nc, ref->index)))
		return dev;

	if (ref->name && (dev = ni_netdev_by_name(nc, ref->name)))
		return dev;

	return NULL;
}
Exemple #7
0
static int
__ni_rtevent_newroute(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct rtmsg *rtm;
	ni_route_t *rp, *r;
	ni_route_nexthop_t *nh;
	ni_netdev_t *dev = NULL;

	if (!(rtm = ni_rtnl_rtmsg(h, RTM_NEWROUTE)))
		return -1;

	/* filter unwanted / unsupported  msgs */
	if (ni_rtnl_route_filter_msg(rtm))
		return 1;

	rp = ni_route_new();
	if (ni_rtnl_route_parse_msg(h, rtm, rp) != 0) {
		ni_route_free(rp);
		return -1;
	}

	for (nh = &rp->nh; nh; nh = nh->next) {
		if (!(dev = ni_netdev_by_index(nc, nh->device.index)))
			continue;

		if (!(r = ni_route_tables_find_match(dev->routes, rp, ni_route_equal)))
			continue;

		rp->owner = r->owner;
		ni_netconfig_route_del(nc, r, dev);
		break;
	}
	if (ni_netconfig_route_add(nc, rp, dev) < 0) {
		ni_route_free(rp);
		return -1;
	}

	__ni_netinfo_route_event(nc, NI_EVENT_ROUTE_UPDATE, rp);
	ni_route_free(rp);
	return 0;
}
Exemple #8
0
static int
__ni_rtevent_nduseropt(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct nduseroptmsg *msg;
	struct nd_opt_hdr *opt;
	ni_netdev_t *dev;

	if (!(msg = ni_rtnl_nduseroptmsg(h, RTM_NEWNDUSEROPT)))
		return -1;

	dev = ni_netdev_by_index(nc, msg->nduseropt_ifindex);
	if (!dev) {
		ni_debug_events("ipv6 nd user option event for unknown device index: %u",
				msg->nduseropt_ifindex);
		return 0;
	}

	if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
	    msg->nduseropt_icmp_code != 0 ||
	    msg->nduseropt_family    != AF_INET6) {
		ni_debug_events("%s: unknown rtnetlink nd user option message"
				" type %d, code %d, family %d", dev->name,
				msg->nduseropt_icmp_type,
				msg->nduseropt_icmp_code,
				msg->nduseropt_family);
		return 0;
	}

	if (!nlmsg_valid_hdr(h, sizeof(struct nduseroptmsg) + msg->nduseropt_opts_len)) {
		ni_debug_events("%s: invalid rtnetlink nd user radv option length %d",
				dev->name, msg->nduseropt_opts_len);
		return -1;
	}

	opt = (struct nd_opt_hdr *)(msg + 1);

	return __ni_rtevent_process_nd_radv_opts(dev, opt, msg->nduseropt_opts_len);
}
Exemple #9
0
static int
__ni_rtevent_newaddr(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct ifaddrmsg *ifa;
	const ni_address_t *ap = NULL;
	ni_netdev_t *dev;

	if (!(ifa = ni_rtnl_ifaddrmsg(h, RTM_NEWADDR)))
		return -1;

	dev = ni_netdev_by_index(nc, ifa->ifa_index);
	if (dev == NULL)
		return 0;

	/*
	 * Here we just get a const pointer (=what we need)
	 * to the address stored in the list...
	 */
	if (__ni_netdev_process_newaddr_event(dev, h, ifa, &ap) < 0)
		return -1;

	__ni_netdev_addr_event(dev, NI_EVENT_ADDRESS_UPDATE, ap);
	return 0;
}
Exemple #10
0
/*
 * Process DELLINK event
 */
int
__ni_rtevent_dellink(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct ifinfomsg *ifi;
	ni_netdev_t *dev;
	struct nlattr *nla;
	const char *ifname = NULL;

	if (!(ifi = ni_rtnl_ifinfomsg(h, RTM_DELLINK)))
		return -1;

	if ((nla = nlmsg_find_attr(h, sizeof(*ifi), IFLA_IFNAME)) != NULL) {
		ifname = (char *) nla_data(nla);
	}
	if (ifi->ifi_family == AF_BRIDGE) {
		ni_debug_events("%s: ignoring bridge DELLINK event", ifname);
		return 0;
	}

	/* Open code interface removal. */
	if ((dev = ni_netdev_by_index(nc, ifi->ifi_index)) == NULL) {
		ni_debug_events("RTM_DELLINK message for unknown interface %s index %d",
				ifname, ifi->ifi_index);
		return -1;
	} else {
		unsigned int old_flags = dev->link.ifflags;

		dev->link.ifflags = __ni_netdev_translate_ifflags(ifi->ifi_flags, old_flags);
		dev->deleted = 1;
		__ni_netdev_process_events(nc, dev, old_flags);
		ni_client_state_drop(dev->link.ifindex);
		ni_netconfig_device_remove(nc, dev);
	}

	return 0;
}
Exemple #11
0
/*
 * Process NEWPREFIX event. This essentially maps 1:1 to IPv6 router advertisements received
 * by the kernel.
 */
int
__ni_rtevent_newprefix(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	struct prefixmsg *pfx;
	ni_ipv6_devinfo_t *ipv6;
	ni_ipv6_ra_pinfo_t *pi, *old = NULL;
	ni_netdev_t *dev;

	if (!(pfx = ni_rtnl_prefixmsg(h, RTM_NEWPREFIX)))
		return -1;

	dev = ni_netdev_by_index(nc, pfx->prefix_ifindex);
	if (!dev) {
		ni_debug_events("ipv6 prefix info event for unknown device index: %u",
				pfx->prefix_ifindex);
		return 0;
	}

	ipv6 = ni_netdev_get_ipv6(dev);
	if (!ipv6) {
		ni_error("%s: unable to allocate device ipv6 structure: %m",
				dev->name);
		return -1;
	}

	pi = calloc(1, sizeof(*pi));
	if (!pi) {
		ni_error("%s: unable to allocate ipv6 prefix info structure: %m",
				dev->name);
		return -1;
	}

	ni_timer_get_time(&pi->acquired);

	if (__ni_rtnl_parse_newprefix(dev->name, h, pfx, pi) < 0) {
		ni_error("%s: unable to parse ipv6 prefix info event data",
				dev->name);
		ni_ipv6_ra_pinfo_free(pi);
		return -1;
	}

	if ((old = ni_ipv6_ra_pinfo_list_remove(&ipv6->radv.pinfo, pi)) != NULL) {
		if (pi->valid_lft != NI_LIFETIME_EXPIRED) {
			/* Replace with updated prefix info - most recent in front */
			ni_ipv6_ra_pinfo_list_prepend(&ipv6->radv.pinfo, pi);
			__ni_netdev_prefix_event(dev, NI_EVENT_PREFIX_UPDATE, pi);
		} else {
			/* A lifetime of 0 means the router requests a prefix remove;
			 * at least 3.0.x kernel set valid lft to 0 and keep pref. */
			ni_ipv6_ra_pinfo_free(pi);
			__ni_netdev_prefix_event(dev, NI_EVENT_PREFIX_DELETE, old);
		}
		free(old);
	} else if (pi->valid_lft != NI_LIFETIME_EXPIRED) {
		/* Add prefix info - most recent in front */
		ni_ipv6_ra_pinfo_list_prepend(&ipv6->radv.pinfo, pi);
		__ni_netdev_prefix_event(dev, NI_EVENT_PREFIX_UPDATE, pi);
	} else {
		/* Request to remove unhandled prefix (missed event?), ignore it. */
		ni_ipv6_ra_pinfo_free(pi);
	}

	return 0;
}
Exemple #12
0
/*
 * Process NEWLINK event
 */
int
__ni_rtevent_newlink(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h)
{
	char namebuf[IF_NAMESIZE+1] = {'\0'};
	ni_netdev_t *dev, *old;
	struct ifinfomsg *ifi;
	struct nlattr *nla;
	char *ifname = NULL;
	int old_flags = 0;

	if (!(ifi = ni_rtnl_ifinfomsg(h, RTM_NEWLINK)))
		return -1;

	if (ifi->ifi_family == AF_BRIDGE)
		return 0;

	old = ni_netdev_by_index(nc, ifi->ifi_index);
	ifname = if_indextoname(ifi->ifi_index, namebuf);
	if (!ifname) {
		/*
		 * device (index) does not exists any more;
		 * process deletion/cleanup of the device.
		 */
		if (old) {
			old_flags = old->link.ifflags;
			old->link.ifflags = 0;
			old->deleted = 1;

			__ni_netdev_process_events(nc, old, old_flags);
			ni_client_state_drop(old->link.ifindex);
			ni_netconfig_device_remove(nc, old);
		}
		return 0;
	}

	if (old) {
		if (!ni_string_eq(old->name, ifname)) {
			ni_debug_events("%s[%u]: device renamed to %s",
					old->name, old->link.ifindex, ifname);
			ni_string_dup(&old->name, ifname);
			__ni_netdev_event(nc, old, NI_EVENT_DEVICE_RENAME);
		}
		dev = old;
		old_flags = old->link.ifflags;
	} else {
		if (!(dev = ni_netdev_new(ifname, ifi->ifi_index))) {
			ni_warn("%s[%u]: unable to allocate memory for device",
					ifname, ifi->ifi_index);
			return -1;
		}
		dev->created = 1;
		ni_netconfig_device_append(nc, dev);
	}

	if (__ni_netdev_process_newlink(dev, h, ifi, nc) < 0) {
		ni_error("Problem parsing RTM_NEWLINK message for %s", ifname);
		return -1;
	}

	if ((ifname = dev->name)) {
		ni_netdev_t *conflict;

		conflict = ni_netdev_by_name(nc, ifname);
		if (conflict && conflict->link.ifindex != (unsigned int)ifi->ifi_index) {
			/*
			 * As the events often provide an already obsolete name [2 events,
			 * we process 1st with next in read buffer], we are reading the
			 * current dev->name in advance (above).
			 *
			 * On a rename like eth0->rename1->eth1, eth1->rename2->eth0, the
			 * current dev->name is already eth1 at processing time of eth0
			 * to rename1 event. This sometimes causes that we find eth1 in
			 * our device list [eth1 -> rename2 event in the read buffer].
			 *
			 * Just update the name of the conflicting device in advance too
			 * and when the interface does not exist any more, emit events.
			 */
			char *current = if_indextoname(conflict->link.ifindex, namebuf);
			if (current) {
				ni_string_dup(&conflict->name, current);
				__ni_netdev_event(nc, conflict, NI_EVENT_DEVICE_RENAME);
			} else {
				unsigned int ifflags = conflict->link.ifflags;
				conflict->link.ifflags = 0;
				conflict->deleted = 1;

				__ni_netdev_process_events(nc, conflict, ifflags);
				ni_client_state_drop(conflict->link.ifindex);
				ni_netconfig_device_remove(nc, conflict);
			}
		}
	}

	__ni_netdev_process_events(nc, dev, old_flags);

	if ((nla = nlmsg_find_attr(h, sizeof(*ifi), IFLA_WIRELESS)) != NULL)
		__ni_wireless_link_event(nc, dev, nla_data(nla), nla_len(nla));

	return 0;
}
Exemple #13
0
int
ni_dhcp4_tester_run(ni_dhcp4_tester_t *opts)
{
	ni_netconfig_t *nc;
	ni_netdev_t *ifp = NULL;
	ni_dhcp4_device_t *dev = NULL;
	ni_dhcp4_request_t *req = NULL;
	unsigned int link_timeout = 20;
	int rv;

	if (opts->timeout && opts->timeout != -1U) {
		link_timeout = (opts->timeout * 2) / 3;
		opts->timeout -= link_timeout;
	}

	if (!opts || ni_string_empty(opts->ifname))
		ni_fatal("Invalid start parameters!");

	dhcp4_tester_opts   = *opts;
	dhcp4_tester_status = NI_WICKED_RC_ERROR;

	if (!(nc = ni_global_state_handle(1)))
		ni_fatal("Cannot refresh interface list!");

	if (!(ifp = ni_netdev_by_name(nc, opts->ifname)))
		ni_fatal("Cannot find interface with name '%s'", opts->ifname);

	if (!ni_dhcp4_supported(ifp))
		ni_fatal("DHCPv4 not supported on '%s'", opts->ifname);

	if (!(dev = ni_dhcp4_device_new(ifp->name, &ifp->link)))
		ni_fatal("Cannot allocate dhcp4 client for '%s'", opts->ifname);

	ni_dhcp4_set_event_handler(ni_dhcp4_tester_protocol_event);

	if (!(req = ni_dhcp4_request_new())) {
		ni_error("Cannot allocate dhcp4 request");
		goto failure;
	}

	if (!ni_dhcp4_tester_req_init(req, opts->request))
		goto failure;

	if (!ni_netdev_link_is_up(ifp)) {
		ni_netdev_req_t *ifreq;
		ni_debug_dhcp("%s: Link is not up, trying to bring it up",
				ifp->name);

		ifreq = ni_netdev_req_new();
		ifreq->ifflags = NI_IFF_LINK_UP | NI_IFF_NETWORK_UP;
		if ((rv = ni_system_interface_link_change(ifp, ifreq)) < 0) {
			ni_error("%s: Unable to set up link", ifp->name);
			ni_netdev_req_free(ifreq);
			goto failure;
		}
		ni_netdev_req_free(ifreq);

		do {
			sleep(1);

			if (!(nc = ni_global_state_handle(1)))
				goto failure;

			if (!(ifp = ni_netdev_by_index(nc, dev->link.ifindex)))
				break;

			if (ni_netdev_link_is_up(ifp))
				break;

			ni_debug_dhcp("%s: Link is not (yet) up", ifp->name);
		} while (link_timeout-- > 1);

		if (!ifp || !ni_netdev_link_is_up(ifp) || !link_timeout) {
			ni_error("%s: Unable to bring link up",
				ifp && ifp->name ? ifp->name : dev->ifname);
			goto failure;
		}

		/* Do not try to send too early, even link is reported up now */
		sleep(1);
	}

	if (opts->timeout && opts->timeout != -1U)
		req->acquire_timeout = opts->timeout;

	req->broadcast = opts->broadcast;

	if ((rv = ni_dhcp4_acquire(dev, req)) < 0) {
		ni_error("%s: DHCP4v6 acquire request %s failed: %s",
				dev->ifname, ni_uuid_print(&req->uuid),
				ni_strerror(rv));
		goto failure;
	}

	dhcp4_tester_status = NI_WICKED_RC_IN_PROGRESS;
	while (!ni_caught_terminal_signal()) {
		long timeout;

		timeout = ni_timer_next_timeout();

		if (ni_socket_wait(timeout) != 0)
			break;
	}
	ni_server_deactivate_interface_events();
	ni_socket_deactivate_all();

failure:
	if (dev)
		ni_dhcp4_device_put(dev);
	if (req)
		ni_dhcp4_request_free(req);
	return dhcp4_tester_status;
}