diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 67d0181d3..ef51e9a2d 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -125,6 +125,23 @@ struct wpa_scan_results { size_t num; }; +/** + * struct wpa_interface_info - Network interface information + * @next: Pointer to the next interface or NULL if this is the last one + * @ifname: Interface name that can be used with init() or init2() + * @desc: Human readable adapter description (e.g., vendor/model) or NULL if + * not available + * @drv_bame: struct wpa_driver_ops::name (note: unlike other strings, this one + * is not an allocated copy, i.e., get_interfaces() caller will not free + * this) + */ +struct wpa_interface_info { + struct wpa_interface_info *next; + char *ifname; + char *desc; + const char *drv_name; +}; + /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). @@ -991,6 +1008,15 @@ struct wpa_driver_ops { * uses global data. */ void * (*init2)(void *ctx, const char *ifname, void *global_priv); + + /** + * get_interfaces - Get information about available interfaces + * @global_priv: private driver global data from global_init() + * Returns: Allocated buffer of interface information (caller is + * responsible for freeing the data structure) on success, NULL on + * failure + */ + struct wpa_interface_info * (*get_interfaces)(void *global_priv); }; /* Function to check whether a driver is for wired connections */ diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 20425bbd4..078f20830 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -2887,5 +2887,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { NULL /* set_country */, NULL /* global_init */, NULL /* global_deinit */, - NULL /* init2 */ + NULL /* init2 */, + NULL /* get_interfaces */ }; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index 8aa05aab7..fb472ac15 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -778,7 +778,8 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { NULL /* set_country */, NULL /* global_init */, NULL /* global_deinit */, - NULL /* init2 */ + NULL /* init2 */, + NULL /* get_interfaces */ }; diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index 9bd4ff32b..92137da7f 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -1147,6 +1147,29 @@ static void wpa_driver_test_global_deinit(void *priv) } +static struct wpa_interface_info * +wpa_driver_test_get_interfaces(void *global_priv) +{ + /* struct wpa_driver_test_global *global = priv; */ + struct wpa_interface_info *iface; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return iface; + iface->ifname = os_strdup("sta0"); + iface->desc = os_strdup("test interface 0"); + iface->drv_name = "test"; + iface->next = os_zalloc(sizeof(*iface)); + if (iface->next) { + iface->next->ifname = os_strdup("sta1"); + iface->next->desc = os_strdup("test interface 1"); + iface->next->drv_name = "test"; + } + + return iface; +} + + const struct wpa_driver_ops wpa_driver_test_ops = { "test", "wpa_supplicant test driver", @@ -1200,5 +1223,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = { NULL /* set_country */, wpa_driver_test_global_init, wpa_driver_test_global_deinit, - wpa_driver_test_init2 + wpa_driver_test_init2, + wpa_driver_test_get_interfaces }; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 4154583d9..650368b42 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -30,7 +30,10 @@ #include "wps_supplicant.h" #include "wps/wps.h" +extern struct wpa_driver_ops *wpa_supplicant_drivers[]; +static int wpa_supplicant_global_iface_list(struct wpa_global *global, + char *buf, int len); static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, char *buf, int len); @@ -1624,6 +1627,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) reply_len = -1; + } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { + reply_len = wpa_supplicant_global_iface_list( + wpa_s->global, reply, reply_size); } else if (os_strcmp(buf, "INTERFACES") == 0) { reply_len = wpa_supplicant_global_iface_interfaces( wpa_s->global, reply, reply_size); @@ -1739,6 +1745,63 @@ static int wpa_supplicant_global_iface_remove(struct wpa_global *global, } +static void wpa_free_iface_info(struct wpa_interface_info *iface) +{ + struct wpa_interface_info *prev; + + while (iface) { + prev = iface; + iface = iface->next; + + os_free(prev->ifname); + os_free(prev->desc); + os_free(prev); + } +} + + +static int wpa_supplicant_global_iface_list(struct wpa_global *global, + char *buf, int len) +{ + int i, res; + struct wpa_interface_info *iface = NULL, *last = NULL, *tmp; + char *pos, *end; + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + struct wpa_driver_ops *drv = wpa_supplicant_drivers[i]; + if (drv->get_interfaces == NULL) + continue; + tmp = drv->get_interfaces(global->drv_priv); + if (tmp == NULL) + continue; + + if (last == NULL) + iface = last = tmp; + else + last->next = tmp; + while (last->next) + last = last->next; + } + + pos = buf; + end = buf + len; + for (tmp = iface; tmp; tmp = tmp->next) { + res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", + tmp->drv_name, tmp->ifname, + tmp->desc ? tmp->desc : ""); + if (res < 0 || res >= end - pos) { + *pos = '\0'; + break; + } + pos += res; + } + + wpa_free_iface_info(iface); + + return pos - buf; +} + + static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, char *buf, int len) { @@ -1791,6 +1854,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { if (wpa_supplicant_global_iface_remove(global, buf + 17)) reply_len = -1; + } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { + reply_len = wpa_supplicant_global_iface_list( + global, reply, reply_size); } else if (os_strcmp(buf, "INTERFACES") == 0) { reply_len = wpa_supplicant_global_iface_interfaces( global, reply, reply_size); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 51892f66e..bd1a5a6b5 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1120,6 +1120,13 @@ static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "INTERFACE_LIST"); +} + + struct wpa_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1166,6 +1173,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "terminate", wpa_cli_cmd_terminate }, { "interface_add", wpa_cli_cmd_interface_add }, { "interface_remove", wpa_cli_cmd_interface_remove }, + { "interface_list", wpa_cli_cmd_interface_list }, { "ap_scan", wpa_cli_cmd_ap_scan }, { "stkstart", wpa_cli_cmd_stkstart }, { "ft_ds", wpa_cli_cmd_ft_ds },