diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 95aa55339..f94f57a53 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1244,7 +1244,12 @@ enum wpa_driver_if_type { * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the * abstracted P2P Device function in the driver */ - WPA_IF_P2P_DEVICE + WPA_IF_P2P_DEVICE, + + /* + * WPA_IF_MESH - Mesh interface + */ + WPA_IF_MESH, }; struct wpa_init_params { diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 13f5759ac..a7a536d9f 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -5594,12 +5594,14 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_P2P_GO; case WPA_IF_P2P_DEVICE: return NL80211_IFTYPE_P2P_DEVICE; + case WPA_IF_MESH: + return NL80211_IFTYPE_MESH_POINT; } return -1; } -#ifdef CONFIG_P2P +#if defined(CONFIG_P2P) || defined(CONFIG_MESH) static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) { @@ -5613,8 +5615,7 @@ static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) } -static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, - u8 *new_addr) +static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr) { unsigned int idx; @@ -5631,13 +5632,13 @@ static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, if (idx == 64) return -1; - wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " + wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address " MACSTR, MAC2STR(new_addr)); return 0; } -#endif /* CONFIG_P2P */ +#endif /* CONFIG_P2P || CONFIG_MESH */ struct wdev_info { @@ -5724,10 +5725,10 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } } -#ifdef CONFIG_P2P +#if defined(CONFIG_P2P) || defined(CONFIG_MESH) if (!addr && (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || - type == WPA_IF_P2P_GO)) { + type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) { /* Enforce unique P2P Interface Address */ u8 new_addr[ETH_ALEN]; @@ -5739,8 +5740,9 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " - "for P2P group interface"); - if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { + "for %s interface", type == WPA_IF_MESH ? + "mesh" : "P2P group"); + if (nl80211_vif_addr(drv, new_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); return -1; @@ -5754,7 +5756,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } os_memcpy(if_addr, new_addr, ETH_ALEN); } -#endif /* CONFIG_P2P */ +#endif /* CONFIG_P2P || CONFIG_MESH */ if (type == WPA_IF_AP_BSS) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 34cbe74ed..c9eb56d9a 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2423,6 +2423,27 @@ static int wpa_supplicant_ctrl_iface_scan_results( #ifdef CONFIG_MESH +static int wpa_supplicant_ctrl_iface_mesh_interface_add( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + char *pos, ifname[IFNAMSIZ + 1]; + + ifname[0] = '\0'; + + pos = os_strstr(cmd, "ifname="); + if (pos) { + pos += 7; + os_strlcpy(ifname, pos, sizeof(ifname)); + } + + if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0) + return -1; + + os_strlcpy(reply, ifname, max_len); + return os_strlen(ifname); +} + + static int wpa_supplicant_ctrl_iface_mesh_group_add( struct wpa_supplicant *wpa_s, char *cmd) { @@ -2463,17 +2484,32 @@ static int wpa_supplicant_ctrl_iface_mesh_group_add( static int wpa_supplicant_ctrl_iface_mesh_group_remove( struct wpa_supplicant *wpa_s, char *cmd) { - /* - * TODO: Support a multiple mesh and other iface type combinations - */ - if (os_strcmp(cmd, wpa_s->ifname) != 0) { - wpa_printf(MSG_DEBUG, - "CTRL_IFACE: MESH_GROUP_REMOVE unknown interface name: %s", + struct wpa_supplicant *orig; + struct wpa_global *global; + int found = 0; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd); + + global = wpa_s->global; + orig = wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, cmd) == 0) { + found = 1; + break; + } + } + if (!found) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found", cmd); return -1; } - - wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd); + if (wpa_s->mesh_if_created && wpa_s == orig) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself"); + return -1; + } wpa_s->reassociate = 0; wpa_s->disconnected = 1; @@ -2486,6 +2522,9 @@ static int wpa_supplicant_ctrl_iface_mesh_group_remove( */ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + if (wpa_s->mesh_if_created) + wpa_supplicant_remove_iface(global, wpa_s, 0); + return 0; } @@ -7175,6 +7214,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_MESH + } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) { + reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( + wpa_s, buf + 19, reply, reply_size); + } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) { + reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( + wpa_s, "", reply, reply_size); } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) { if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15)) reply_len = -1; diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index 3f262fb8c..68187e0ae 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -474,3 +474,62 @@ int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, { return mesh_attr_text(ies, ies_len, buf, end); } + + +static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname, + size_t len) +{ + char *ifname_ptr = wpa_s->ifname; + int res; + + res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr, + wpa_s->mesh_if_idx); + if (os_snprintf_error(len, res) || + (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ)) { + /* Try to avoid going over the IFNAMSIZ length limit */ + res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx); + if (os_snprintf_error(len, res)) + return -1; + } + wpa_s->mesh_if_idx++; + return 0; +} + + +int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, + size_t len) +{ + struct wpa_interface iface; + struct wpa_supplicant *mesh_wpa_s; + u8 addr[ETH_ALEN]; + + if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0) + return -1; + + if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr, + NULL) < 0) { + wpa_printf(MSG_ERROR, + "mesh: Failed to create new mesh interface"); + return -1; + } + wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr " + MACSTR, ifname, MAC2STR(addr)); + + os_memset(&iface, 0, sizeof(iface)); + iface.ifname = ifname; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; + iface.ctrl_interface = wpa_s->conf->ctrl_interface; + + mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (!mesh_wpa_s) { + wpa_printf(MSG_ERROR, + "mesh: Failed to create new wpa_supplicant interface"); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + return -1; + } + mesh_wpa_s->mesh_if_created = 1; + mesh_wpa_s->parent = wpa_s; + return 0; +} diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h index 3c3ea1f88..3cb7f1b13 100644 --- a/wpa_supplicant/mesh.h +++ b/wpa_supplicant/mesh.h @@ -16,6 +16,8 @@ void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh); int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); +int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, + size_t len); #ifdef CONFIG_MESH diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 94e95e8e2..87071fa65 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1754,6 +1754,13 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) #ifdef CONFIG_MESH +static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv); +} + + static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2834,6 +2841,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_none, " = roam to the specified BSS" }, #ifdef CONFIG_MESH + { "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL, + cli_cmd_flag_none, + "[ifname] = Create a new mesh interface" }, { "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL, cli_cmd_flag_none, " = join a mesh network (disable others)" }, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 5e3b35bfb..8ea023ac2 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -4085,6 +4085,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, int terminate) { struct wpa_supplicant *prev; +#ifdef CONFIG_MESH + unsigned int mesh_if_created = wpa_s->mesh_if_created; + char *ifname = NULL; +#endif /* CONFIG_MESH */ /* Remove interface from the global list of interfaces */ prev = global->ifaces; @@ -4100,12 +4104,30 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname); +#ifdef CONFIG_MESH + if (mesh_if_created) { + ifname = os_strdup(wpa_s->ifname); + if (ifname == NULL) { + wpa_dbg(wpa_s, MSG_ERROR, + "mesh: Failed to malloc ifname"); + return -1; + } + } +#endif /* CONFIG_MESH */ + if (global->p2p_group_formation == wpa_s) global->p2p_group_formation = NULL; if (global->p2p_invite_group == wpa_s) global->p2p_invite_group = NULL; wpa_supplicant_deinit_iface(wpa_s, 1, terminate); +#ifdef CONFIG_MESH + if (mesh_if_created) { + wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname); + os_free(ifname); + } +#endif /* CONFIG_MESH */ + return 0; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index e0c7cd6cf..b0f386635 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -697,6 +697,8 @@ struct wpa_supplicant { struct hostapd_iface *ifmsh; #ifdef CONFIG_MESH struct mesh_rsn *mesh_rsn; + int mesh_if_idx; + unsigned int mesh_if_created:1; #endif /* CONFIG_MESH */ unsigned int off_channel_freq;