diff --git a/src/netdev.c b/src/netdev.c index de30e728..9f008e4e 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -82,11 +82,14 @@ struct netdev { netdev_connect_cb_t connect_cb; netdev_disconnect_cb_t disconnect_cb; netdev_neighbor_report_cb_t neighbor_report_cb; + netdev_adhoc_cb_t adhoc_cb; void *user_data; struct eapol_sm *sm; struct handshake_state *handshake; uint32_t connect_cmd_id; uint32_t disconnect_cmd_id; + uint32_t join_adhoc_cmd_id; + uint32_t leave_adhoc_cmd_id; enum netdev_result result; struct l_timeout *neighbor_report_timeout; struct l_timeout *sa_query_timeout; @@ -588,6 +591,16 @@ static void netdev_free(void *data) netdev->user_data = NULL; } + if (netdev->join_adhoc_cmd_id) { + l_genl_family_cancel(nl80211, netdev->join_adhoc_cmd_id); + netdev->join_adhoc_cmd_id = 0; + } + + if (netdev->leave_adhoc_cmd_id) { + l_genl_family_cancel(nl80211, netdev->leave_adhoc_cmd_id); + netdev->leave_adhoc_cmd_id = 0; + } + device_remove(netdev->device); watchlist_destroy(&netdev->event_watches); watchlist_destroy(&netdev->frame_watches); @@ -2482,6 +2495,113 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss, return err; } +static void netdev_join_adhoc_cb(struct l_genl_msg *msg, void *user_data) +{ + struct netdev *netdev = user_data; + + netdev->join_adhoc_cmd_id = 0; + + if (netdev->adhoc_cb) + netdev->adhoc_cb(netdev, l_genl_msg_get_error(msg), + netdev->user_data); +} + +int netdev_join_adhoc(struct netdev *netdev, const char *ssid, + struct iovec *extra_ie, size_t extra_ie_elems, + bool control_port, netdev_adhoc_cb_t cb, + void *user_data) +{ + struct l_genl_msg *cmd; + uint32_t ifindex = device_get_ifindex(netdev->device); + uint32_t ch_freq = scan_channel_to_freq(6, SCAN_BAND_2_4_GHZ); + uint32_t ch_type = NL80211_CHAN_HT20; + + if (netdev->type != NL80211_IFTYPE_ADHOC) { + l_error("iftype is invalid for adhoc: %u", + netdev_get_iftype(netdev)); + return -ENOTSUP; + } + + if (netdev->join_adhoc_cmd_id || netdev->leave_adhoc_cmd_id) + return -EBUSY; + + netdev->adhoc_cb = cb; + netdev->user_data = user_data; + + cmd = l_genl_msg_new_sized(NL80211_CMD_JOIN_IBSS, 128); + + l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &ifindex); + l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, strlen(ssid), ssid); + l_genl_msg_append_attr(cmd, NL80211_ATTR_WIPHY_FREQ, 4, &ch_freq); + l_genl_msg_append_attr(cmd, NL80211_ATTR_WIPHY_CHANNEL_TYPE, 4, + &ch_type); + l_genl_msg_append_attrv(cmd, NL80211_ATTR_IE, extra_ie, extra_ie_elems); + l_genl_msg_append_attr(cmd, NL80211_ATTR_SOCKET_OWNER, 0, NULL); + + if (control_port) + l_genl_msg_append_attr(cmd, NL80211_ATTR_CONTROL_PORT, 0, NULL); + + if (netdev->pae_over_nl80211) + l_genl_msg_append_attr(cmd, + NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + 0, NULL); + + netdev->join_adhoc_cmd_id = l_genl_family_send(nl80211, cmd, + netdev_join_adhoc_cb, netdev, NULL); + + if (!netdev->join_adhoc_cmd_id) { + netdev->adhoc_cb = NULL; + netdev->user_data = NULL; + return -EIO; + } + + return 0; +} + +static void netdev_leave_adhoc_cb(struct l_genl_msg *msg, void *user_data) +{ + struct netdev *netdev = user_data; + + netdev->leave_adhoc_cmd_id = 0; + + if (netdev->adhoc_cb) + netdev->adhoc_cb(netdev, l_genl_msg_get_error(msg), + netdev->user_data); + + netdev->adhoc_cb = NULL; +} + +int netdev_leave_adhoc(struct netdev *netdev, netdev_adhoc_cb_t cb, + void *user_data) +{ + struct l_genl_msg *cmd; + + if (netdev->type != NL80211_IFTYPE_ADHOC) { + l_error("iftype is invalid for adhoc: %u", + netdev_get_iftype(netdev)); + return -ENOTSUP; + } + + if (netdev->join_adhoc_cmd_id || netdev->leave_adhoc_cmd_id) + return -EBUSY; + + netdev->adhoc_cb = cb; + netdev->user_data = user_data; + + cmd = l_genl_msg_new_sized(NL80211_CMD_LEAVE_IBSS, 64); + + l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &netdev->index); + + netdev->leave_adhoc_cmd_id = l_genl_family_send(nl80211, cmd, + netdev_leave_adhoc_cb, netdev, + NULL); + + if (!netdev->leave_adhoc_cmd_id) + return -EIO; + + return 0; +} + /* * Build an FT Authentication Request frame according to 12.5.2 / 12.5.4: * RSN or non-RSN Over-the-air FT Protocol, with the IE contents diff --git a/src/netdev.h b/src/netdev.h index 3017307b..97e842df 100644 --- a/src/netdev.h +++ b/src/netdev.h @@ -70,6 +70,8 @@ typedef void (*netdev_event_func_t)(struct netdev *netdev, void *user_data); typedef void (*netdev_disconnect_cb_t)(struct netdev *netdev, bool result, void *user_data); +typedef void (*netdev_adhoc_cb_t)(struct netdev *netdev, int result, + void *user_data); typedef void (*netdev_watch_func_t)(struct netdev *netdev, enum netdev_watch_event event, void *user_data); @@ -135,6 +137,13 @@ int netdev_preauthenticate(struct netdev *netdev, struct scan_bss *target_bss, int netdev_del_station(struct netdev *netdev, const uint8_t *sta, uint16_t reason_code, bool disassociate); +int netdev_join_adhoc(struct netdev *netdev, const char *ssid, + struct iovec *extra_ie, size_t extra_ie_elems, + bool control_port, netdev_adhoc_cb_t cb, + void *user_data); +int netdev_leave_adhoc(struct netdev *netdev, netdev_adhoc_cb_t cb, + void *user_data); + int netdev_set_powered(struct netdev *netdev, bool powered, netdev_set_powered_cb_t cb, void *user_data, netdev_destroy_func_t destroy);