/* Change state and parameters on port. */ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) { struct ifinfomsg *ifm; struct nlattr *protinfo; struct nlattr *afspec; struct net_bridge_port *p; struct nlattr *tb[IFLA_BRPORT_MAX + 1]; int err; ifm = nlmsg_data(nlh); protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC); if (!protinfo && !afspec) return 0; p = br_port_get_rtnl(dev); /* We want to accept dev as bridge itself if the AF_SPEC * is set to see if someone is setting vlan info on the brigde */ if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec)) return -EINVAL; if (p && protinfo) { if (protinfo->nla_type & NLA_F_NESTED) { err = nla_parse_nested(tb, IFLA_BRPORT_MAX, protinfo, ifla_brport_policy); if (err) return err; spin_lock_bh(&p->br->lock); err = br_setport(p, tb); spin_unlock_bh(&p->br->lock); } else { /* Binary compatability with old RSTP */ if (nla_len(protinfo) < sizeof(u8)) return -EINVAL; spin_lock_bh(&p->br->lock); err = br_set_port_state(p, nla_get_u8(protinfo)); spin_unlock_bh(&p->br->lock); } if (err) goto out; } if (afspec) { err = br_afspec((struct net_bridge *)netdev_priv(dev), p, afspec, RTM_SETLINK); } if (err == 0) br_ifinfo_notify(RTM_NEWLINK, p); out: return err; }
int handle_immigration_request(struct nl_msg *req_msg) { struct nl_msg *msg = NULL; struct nlattr *nla; int ret = 0; int seq; struct internal_state* state = get_current_state(); // In params int uid; int slot_index; char* name; // Out params int accept = 1; seq = nlmsg_hdr(req_msg)->nlmsg_seq; nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_UID); if (nla == NULL) return -EBADMSG; uid = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_INDEX); if (nla == NULL) return -EBADMSG; slot_index = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_NAME); if (nla == NULL) return -EBADMSG; //name = nl_data_get(nla_get_data(nla)); name = nla_data(nla); //printf("NPM CALLED FOR NAME: %s\n", name); if ( immigration_request_callback ) immigration_request_callback(uid, slot_index, name, &accept); if ( (ret=prepare_response_message(state->handle, DIRECTOR_IMMIGRATION_REQUEST_RESPONSE, state->gnl_fid, seq, &msg) ) != 0 ) { goto done; } ret = nla_put_u32(msg, DIRECTOR_A_DECISION, accept); if (ret != 0) goto error_del_resp; ret = send_request_message(state->handle, msg, 0); goto done; error_del_resp: nlmsg_free(msg); done: return ret; }
int handle_task_exitted(struct nl_msg *req_msg) { struct nl_msg *msg = NULL; struct nlattr *nla; int ret = 0; int seq; struct internal_state* state = get_current_state(); struct rusage *rusage; // In params pid_t pid; int exit_code; seq = nlmsg_hdr(req_msg)->nlmsg_seq; nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_PID); if (nla == NULL) return -EBADMSG; pid = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_EXIT_CODE); if (nla == NULL) return -EBADMSG; exit_code = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_RUSAGE); if (nla == NULL) return -EBADMSG; //rusage = nl_data_get(nla_get_data(nla)); rusage = nla_data(nla); if ( task_exitted_callback ) task_exitted_callback(pid, exit_code, rusage); if ( (ret=prepare_response_message(state->sk, DIRECTOR_ACK, state->gnl_fid, seq, &msg) ) != 0 ) { goto done; } if (ret != 0) goto error_del_resp; ret = send_request_message(state->sk, msg, 0); goto done; error_del_resp: nlmsg_free(msg); done: return ret; }
static void ind_ovs_handle_packet_miss(struct ind_ovs_upcall_thread *thread, struct ind_ovs_port *port, struct nl_msg *msg, struct nlattr **attrs) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct genlmsghdr *gnlh = (void *)(nlh + 1); struct nlattr *key = attrs[OVS_PACKET_ATTR_KEY]; struct nlattr *packet = attrs[OVS_PACKET_ATTR_PACKET]; assert(key && packet); struct ind_ovs_parsed_key pkey; ind_ovs_parse_key(key, &pkey); /* Lookup the flow in the userspace flowtable. */ struct ind_ovs_flow *flow; if (ind_ovs_lookup_flow(&pkey, &flow) != 0) { ind_ovs_upcall_request_pktin(pkey.in_port, port, packet, key, OF_PACKET_IN_REASON_NO_MATCH); return; } /* Reuse the incoming message for the packet execute */ gnlh->cmd = OVS_PACKET_CMD_EXECUTE; ind_ovs_translate_actions(&pkey, flow->of_list_action, msg, OVS_PACKET_ATTR_ACTIONS); __sync_fetch_and_add(&flow->packets, 1); __sync_fetch_and_add(&flow->bytes, nla_len(packet)); /* Reuse the translated actions for adding the kflow. */ struct nlattr *actions = nlmsg_find_attr(nlmsg_hdr(msg), sizeof(struct genlmsghdr) + sizeof(struct ovs_header), OVS_PACKET_ATTR_ACTIONS); /* Don't send the packet back out if it would be dropped. */ if (nla_len(actions) > 0) { nlh->nlmsg_pid = 0; nlh->nlmsg_seq = 0; nlh->nlmsg_flags = NLM_F_REQUEST; struct iovec *iovec = &thread->tx_queue[thread->tx_queue_len++]; iovec->iov_base = nlh; iovec->iov_len = nlh->nlmsg_len; if (thread->log_upcalls) { LOG_VERBOSE("Sending upcall reply:"); ind_ovs_dump_msg(nlh); } } /* See the comment for ind_ovs_upcall_seen_key. */ if (ind_ovs_upcall_seen_key(thread, key)) { /* Create a kflow with the given key and actions. */ ind_ovs_bh_request_kflow(key, actions); } }
static int npm_check_read_response(struct genl_info* info, void* params) { int ret = 0; struct nlattr* attr; struct npm_check_params* check_params = params; attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_DECISION); if ( attr == NULL ) { ret = -EBADMSG; goto done; } check_params->decision = nla_get_u32(attr); attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_DECISION_VALUE); if ( attr != NULL ) { check_params->decision_value = nla_get_u32(attr); } else { check_params->decision_value = 0; } done: return ret; }
ssize_t wimax_msg_len(struct sk_buff *msg) { struct nlmsghdr *nlh = (void *) msg->head; struct nlattr *nla; nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr), WIMAX_GNL_MSG_DATA); if (nla == NULL) { printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n"); return -EINVAL; } return nla_len(nla); }
/* * Change state of port (ie from forwarding to blocking etc) * Used by spanning tree in user space. */ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; struct nlattr *protinfo; struct net_device *dev; struct net_bridge_port *p; u8 new_state; if (nlmsg_len(nlh) < sizeof(*ifm)) return -EINVAL; ifm = nlmsg_data(nlh); if (ifm->ifi_family != AF_BRIDGE) return -EPFNOSUPPORT; protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); if (!protinfo || nla_len(protinfo) < sizeof(u8)) return -EINVAL; new_state = nla_get_u8(protinfo); if (new_state > BR_STATE_BLOCKING) return -EINVAL; dev = __dev_get_by_index(net, ifm->ifi_index); if (!dev) return -ENODEV; p = br_port_get_rtnl(dev); if (!p) return -EINVAL; /* if kernel STP is running, don't allow changes */ if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; if (!netif_running(dev) || (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = new_state; br_log_state(p); spin_lock_bh(&p->br->lock); br_port_state_selection(p->br); spin_unlock_bh(&p->br->lock); br_ifinfo_notify(RTM_NEWLINK, p); return 0; }
int handle_send_generic_user_message(struct sk_buff *skb, struct genl_info *info) { struct nlattr* attr; char* user_data; int user_data_length, target_slot_index, target_slot_type; // No handler registered? if ( !generic_user_message_handler ) { return -EINVAL; } attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_SLOT_INDEX); if ( attr == NULL ) { return -1; } target_slot_index = nla_get_u32(attr); attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_SLOT_TYPE); if ( attr == NULL ) { return -1; } target_slot_type = nla_get_u32(attr); attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_LENGTH); if ( attr == NULL ) { return -1; } user_data_length = nla_get_u32(attr); attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_USER_DATA); if ( attr == NULL ) { return -1; } user_data = nla_data(attr); return generic_user_message_handler(target_slot_type, target_slot_index, user_data_length, user_data); };
static int node_connected_read_response(struct genl_info* info, void* params) { int ret = 0; struct nlattr* attr; struct node_connected_params* node_connected_params = params; attr = nlmsg_find_attr(info->nlhdr, sizeof(struct genlmsghdr), DIRECTOR_A_DECISION); if ( attr == NULL ) { ret = -EBADMSG; goto done; } node_connected_params->accepted = nla_get_u32(attr); done: return ret; }
/* * Change state of port (ie from forwarding to blocking etc) * Used by spanning tree in user space. */ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct ifinfomsg *ifm; struct nlattr *protinfo; struct net_device *dev; struct net_bridge_port *p; u8 new_state; if (nlmsg_len(nlh) < sizeof(*ifm)) return -EINVAL; ifm = nlmsg_data(nlh); if (ifm->ifi_family != AF_BRIDGE) return -EPFNOSUPPORT; protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); if (!protinfo || nla_len(protinfo) < sizeof(u8)) return -EINVAL; new_state = nla_get_u8(protinfo); if (new_state > BR_STATE_BLOCKING) return -EINVAL; dev = __if_dev_get_by_index(ifm->ifi_index); if (!dev) return -ENODEV; p = dev->br_port; if (!p) return -EINVAL; /* if kernel STP is running, don't allow changes */ if (p->br->stp_enabled) return -EBUSY; if (!if_dev_admin_up(dev) || (!if_dev_running(dev) && new_state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = new_state; br_log_state(p); return 0; }
/* Change state and parameters on port. */ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) { struct ifinfomsg *ifm; struct nlattr *protinfo; struct net_bridge_port *p; struct nlattr *tb[IFLA_BRPORT_MAX + 1]; int err; ifm = nlmsg_data(nlh); protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); if (!protinfo) return 0; p = br_port_get_rtnl(dev); if (!p) return -EINVAL; if (protinfo->nla_type & NLA_F_NESTED) { err = nla_parse_nested(tb, IFLA_BRPORT_MAX, protinfo, ifla_brport_policy); if (err) return err; spin_lock_bh(&p->br->lock); err = br_setport(p, tb); spin_unlock_bh(&p->br->lock); } else { /* Binary compatability with old RSTP */ if (nla_len(protinfo) < sizeof(u8)) return -EINVAL; spin_lock_bh(&p->br->lock); err = br_set_port_state(p, nla_get_u8(protinfo)); spin_unlock_bh(&p->br->lock); } if (err == 0) br_ifinfo_notify(RTM_NEWLINK, p); return err; }
/* * See the OF_PORT_DEST_USE_TABLE comment in ind_ovs_translate_actions. * This is mostly the same as ind_ovs_handle_packet_miss but does * not reuse the original message for the execute command since * the incoming message has a userdata attribute. * It also doesn't install a kflow or update flow stats. */ static void ind_ovs_handle_packet_table(struct ind_ovs_upcall_thread *thread, struct ind_ovs_port *port, struct nl_msg *msg, struct nlattr **attrs) { struct nlattr *key = attrs[OVS_PACKET_ATTR_KEY]; struct nlattr *packet = attrs[OVS_PACKET_ATTR_PACKET]; assert(key && packet); struct ind_ovs_parsed_key pkey; ind_ovs_parse_key(key, &pkey); /* Lookup the flow in the userspace flowtable. */ struct ind_ovs_flow *flow; if (ind_ovs_lookup_flow(&pkey, &flow) != 0) { ind_ovs_upcall_request_pktin(pkey.in_port, port, packet, key, OF_PACKET_IN_REASON_NO_MATCH); return; } /* Send OVS_PACKET_CMD_EXECUTE. */ /* TODO ensure the message is large enough */ struct nl_msg *reply = ind_ovs_create_nlmsg(ovs_packet_family, OVS_PACKET_CMD_EXECUTE); ind_ovs_translate_actions(&pkey, flow->of_list_action, reply, OVS_PACKET_ATTR_ACTIONS); nla_put(reply, OVS_PACKET_ATTR_KEY, nla_len(key), nla_data(key)); nla_put(reply, OVS_PACKET_ATTR_PACKET, nla_len(packet), nla_data(packet)); struct nlattr *actions = nlmsg_find_attr(nlmsg_hdr(reply), sizeof(struct genlmsghdr) + sizeof(struct ovs_header), OVS_PACKET_ATTR_ACTIONS); /* Don't send the packet back out if it would be dropped. */ if (nla_len(actions) > 0) { nl_send_auto(port->notify_socket, reply); } ind_ovs_nlmsg_freelist_free(reply); }
/* Delete port information */ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh) { struct ifinfomsg *ifm; struct nlattr *afspec; struct net_bridge_port *p; int err; ifm = nlmsg_data(nlh); afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC); if (!afspec) return 0; p = br_port_get_rtnl(dev); /* We want to accept dev as bridge itself as well */ if (!p && !(dev->priv_flags & IFF_EBRIDGE)) return -EINVAL; err = br_afspec((struct net_bridge *)netdev_priv(dev), p, afspec, RTM_DELLINK); return err; }
/* * 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; }
/** * Setups generic netlink connection with the kernel module and retrieves assocaited family id * * @return 0 on success */ static int initialize_netlink_family(void) { struct nl_handle* hndl; struct nl_msg* msg = NULL; struct nl_msg *ans_msg = NULL; struct nlmsghdr *nl_hdr; struct genlmsghdr* genl_hdr; struct nlattr *nla; int ret_val = 0; hndl = nl_handle_alloc(); nl_set_buffer_size(hndl, 15000000, 15000000); //nl_handle_set_peer_pid(hndl, 0); //nl_set_passcred(hndl, 1); nl_disable_sequence_check(hndl); if ( (ret_val=nl_connect(hndl, NETLINK_GENERIC)) ) goto init_return; nl_set_buffer_size(hndl, 15000000, 15000000); if ( (ret_val=prepare_request_message(hndl, CTRL_CMD_GETFAMILY, GENL_ID_CTRL, &msg) ) != 0 ) { goto init_return; } ret_val = nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, "DIRECTORCHNL"); if (ret_val != 0) goto init_return; if ( (ret_val = send_request_message(hndl, msg, 0) ) != 0 ) goto init_return; if ( (ret_val = read_message(hndl, &ans_msg) ) != 0 ) goto init_return; genl_hdr = nl_msg_genlhdr(ans_msg); if (genl_hdr == NULL || genl_hdr->cmd != CTRL_CMD_NEWFAMILY) { ret_val = -EBADMSG; goto init_return; } nla = nlmsg_find_attr(nlmsg_hdr(ans_msg), sizeof(struct genlmsghdr), CTRL_ATTR_FAMILY_ID); if (nla == NULL) { ret_val = -EBADMSG; goto init_return; } state.gnl_fid = nla_get_u16(nla); if (state.gnl_fid == 0) { ret_val = -EBADMSG; goto init_return; } state.handle = hndl; return 0; init_return: nlmsg_free(ans_msg); if ( state.handle == NULL ) { nl_close(hndl); nl_handle_destroy(hndl); } return -EINVAL; }
int handle_immigration_confirmed(struct nl_msg *req_msg) { struct nl_msg *msg = NULL; struct nlattr *nla; int ret = 0; int seq; struct internal_state* state = get_current_state(); // In params int uid; int slot_index; char* name; pid_t local_pid; pid_t remote_pid; unsigned long jiffies; seq = nlmsg_hdr(req_msg)->nlmsg_seq; nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_UID); if (nla == NULL) return -EBADMSG; uid = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_INDEX); if (nla == NULL) return -EBADMSG; slot_index = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_NAME); if (nla == NULL) return -EBADMSG; //name = nl_data_get(nla_get_data(nla)); name = nla_data(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_JIFFIES); if (nla == NULL) return -EBADMSG; jiffies = nla_get_u64(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_PID); if (nla == NULL) return -EBADMSG; local_pid = nla_get_u32(nla); nla = nlmsg_find_attr(nlmsg_hdr(req_msg), sizeof(struct genlmsghdr), DIRECTOR_A_REMOTE_PID); if (nla == NULL) return -EBADMSG; remote_pid = nla_get_u32(nla); //printf("NPM CALLED FOR NAME: %s\n", name); if ( immigration_confirmed_callback ) immigration_confirmed_callback(uid, slot_index, name, jiffies, local_pid, remote_pid); if ( (ret=prepare_response_message(state->sk, DIRECTOR_ACK, state->gnl_fid, seq, &msg) ) != 0 ) { goto done; } ret = send_request_message(state->sk, msg, 0); goto done; error_del_resp: nlmsg_free(msg); done: return ret; }
/* * 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; }