/* * 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 void __ni_wireless_association_timeout(void *ptr, const ni_timer_t *timer) { ni_netconfig_t *nc = ni_global_state_handle(0); ni_netdev_t *dev = ptr; ni_wireless_t *wlan = dev->wireless; if (wlan->assoc.timer != timer) return; ni_debug_wireless("%s: association timed out", dev->name); wlan->assoc.timer = NULL; __ni_netdev_event(nc, dev, NI_EVENT_LINK_DOWN); __ni_netdev_event(nc, dev, NI_EVENT_LINK_ASSOCIATION_LOST); ni_wireless_disconnect(dev); }
/* * 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; }
/* * Process device state change events */ void __ni_netdev_process_events(ni_netconfig_t *nc, ni_netdev_t *dev, unsigned int old_flags) { static struct flag_transition { unsigned int flag; unsigned int event_up; unsigned int event_down; } *edge, flag_transitions[] = { { NI_IFF_DEVICE_READY, NI_EVENT_DEVICE_READY, 0 }, { NI_IFF_DEVICE_UP, NI_EVENT_DEVICE_UP, NI_EVENT_DEVICE_DOWN }, { NI_IFF_LINK_UP, NI_EVENT_LINK_UP, NI_EVENT_LINK_DOWN }, { NI_IFF_NETWORK_UP, NI_EVENT_NETWORK_UP, NI_EVENT_NETWORK_DOWN }, }; size_t flags = sizeof(flag_transitions)/sizeof(flag_transitions[0]); unsigned int i, new_flags, flags_changed; ni_uint_array_t events = NI_UINT_ARRAY_INIT; new_flags = dev->link.ifflags; flags_changed = old_flags ^ new_flags; if (dev->created) { dev->created = 0; ni_uint_array_append(&events, NI_EVENT_DEVICE_CREATE); } /* transition up */ for (i = 0; i < flags; ++i) { edge = &flag_transitions[i]; if ((flags_changed & edge->flag) == 0) continue; if (new_flags & edge->flag) { ni_uint_array_append(&events, edge->event_up); } } /* transition down */ for (i = flags; i-- > 0; ) { edge = &flag_transitions[i]; if ((flags_changed & edge->flag) == 0) continue; if (old_flags & edge->flag) { if (dev->ipv6 && edge->event_down == NI_EVENT_DEVICE_DOWN) ni_ipv6_ra_info_flush(&dev->ipv6->radv); if (edge->event_down) ni_uint_array_append(&events, edge->event_down); } } if (dev->deleted) { dev->deleted = 0; ni_uint_array_append(&events, NI_EVENT_DEVICE_DELETE); } else if (events.count == 0) { __ni_netdev_event(nc, dev, NI_EVENT_DEVICE_CHANGE); } for (i = 0; i < events.count; ++i) { __ni_netdev_event(nc, dev, events.data[i]); } ni_uint_array_destroy(&events); }
/* * Initiate a network scan */ int __ni_wireless_do_scan(ni_netdev_t *dev) { ni_wpa_interface_t *wpa_dev; ni_wireless_t *wlan; ni_wireless_scan_t *scan; time_t now; wlan = dev->wireless; if ((scan = wlan->scan) == NULL) { ni_error("%s: no wireless scan handle?!", __func__); return -1; } /* (Re-)arm the scan timer */ __ni_wireless_scan_timer_arm(scan, dev, scan->interval); /* If the device is down, we cannot scan */ if (!ni_netdev_device_is_up(dev)) return 0; if (ni_rfkill_disabled(NI_RFKILL_TYPE_WIRELESS)) return -NI_ERROR_RADIO_DISABLED; if (!(wpa_dev = ni_wireless_bind_supplicant(dev))) return -1; wlan->capabilities = wpa_dev->capabilities; /* We currently don't have a reasonable way to call back * to a higher level from the depths of the wpa-supplicant * code. Thus we have to result to polling here :-( */ if (ni_wpa_interface_scan_in_progress(wpa_dev)) { __ni_wireless_scan_timer_arm(scan, dev, 1); return 0; } /* Retrieve whatever is there. */ if (ni_wpa_interface_retrieve_scan(wpa_dev, scan)) { ni_netconfig_t *nc = ni_global_state_handle(0); ni_debug_wireless("%s: list of networks changed", dev->name); __ni_netdev_event(nc, dev, NI_EVENT_LINK_SCAN_UPDATED); } /* If we haven't seen a scan in a long time, request one. */ now = time(NULL); if (scan->timestamp + scan->interval < now) { /* We can do this only if the device is up */ if (dev->link.ifflags & NI_IFF_DEVICE_UP) { if (scan->timestamp) ni_debug_wireless("%s: requesting wireless scan (last scan was %u seconds ago)", dev->name, (unsigned int) (now - scan->timestamp)); else ni_debug_wireless("%s: requesting wireless scan", dev->name); ni_wpa_interface_request_scan(wpa_dev, scan); } } return 0; }