/* * 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); }
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; }
/* * 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); }
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); }
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; }
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; }
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; }
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); }
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; }
/* * 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; }
/* * 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; }
/* * 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; }
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; }