Initial step in adding simple AP mode support into wpa_supplicant
[wpasupplicant] / src / drivers / driver_nl80211.c
index e056ec0..a2f0fc7 100644 (file)
@@ -86,12 +86,15 @@ struct wpa_driver_nl80211_data {
 
        u8 bssid[ETH_ALEN];
        int associated;
+       u8 ssid[32];
+       size_t ssid_len;
 };
 
 
 static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
                                            void *timeout_ctx);
-static int wpa_driver_nl80211_set_mode(void *priv, int mode);
+static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+                                      int mode);
 #ifdef WEXT_COMPAT
 static int wpa_driver_nl80211_flush_pmkid(void *priv);
 #endif /* WEXT_COMPAT */
@@ -359,31 +362,41 @@ static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid)
 static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
 {
        struct wpa_driver_nl80211_data *drv = priv;
-       struct iwreq iwr;
-       int ret = 0;
+#ifdef WEXT_COMPAT
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+               struct iwreq iwr;
+               int ret = 0;
 
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       iwr.u.essid.pointer = (caddr_t) ssid;
-       iwr.u.essid.length = 32;
+               os_memset(&iwr, 0, sizeof(iwr));
+               os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+               iwr.u.essid.pointer = (caddr_t) ssid;
+               iwr.u.essid.length = 32;
 
-       if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
-               perror("ioctl[SIOCGIWESSID]");
-               ret = -1;
-       } else {
-               ret = iwr.u.essid.length;
-               if (ret > 32)
-                       ret = 32;
-               /* Some drivers include nul termination in the SSID, so let's
-                * remove it here before further processing. WE-21 changes this
-                * to explicitly require the length _not_ to include nul
-                * termination. */
-               if (ret > 0 && ssid[ret - 1] == '\0' &&
-                   drv->we_version_compiled < 21)
-                       ret--;
-       }
+               if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+                       perror("ioctl[SIOCGIWESSID]");
+                       ret = -1;
+               } else {
+                       ret = iwr.u.essid.length;
+                       if (ret > 32)
+                               ret = 32;
+                       /*
+                        * Some drivers include nul termination in the SSID, so
+                        * let's remove it here before further processing.
+                        * WE-21 changes this to explicitly require the length
+                        * _not_ to include nul termination.
+                        */
+                       if (ret > 0 && ssid[ret - 1] == '\0' &&
+                           drv->we_version_compiled < 21)
+                               ret--;
+               }
 
-       return ret;
+               return ret;
+       }
+#endif /* WEXT_COMPAT */
+       if (!drv->associated)
+               return -1;
+       os_memcpy(ssid, drv->ssid, drv->ssid_len);
+       return drv->ssid_len;
 }
 
 
@@ -1261,6 +1274,7 @@ nla_put_failure:
 
 struct wiphy_info_data {
        int max_scan_ssids;
+       int ap_supported;
 };
 
 
@@ -1277,6 +1291,18 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                info->max_scan_ssids =
                        nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
 
+       if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
+               struct nlattr *nl_mode;
+               int i;
+               nla_for_each_nested(nl_mode,
+                                   tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
+                       if (nl_mode->nla_type == NL80211_IFTYPE_AP) {
+                               info->ap_supported = 1;
+                               break;
+                       }
+               }
+       }
+
        return NL_SKIP;
 }
 
@@ -1312,6 +1338,8 @@ static void wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                return;
        drv->has_capability = 1;
        drv->capa.max_scan_ssids = info.max_scan_ssids;
+       if (info.ap_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
 }
 
 
@@ -1456,6 +1484,13 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
 {
        int flags;
 
+       drv->ifindex = if_nametoindex(drv->ifname);
+
+       if (wpa_driver_nl80211_set_mode(drv, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
+                          "use managed mode");
+       }
+
        if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) {
                wpa_printf(MSG_ERROR, "Could not get interface '%s' flags",
                           drv->ifname);
@@ -1476,15 +1511,8 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
        wpa_driver_nl80211_flush_pmkid(drv);
 #endif /* WEXT_COMPAT */
 
-       if (wpa_driver_nl80211_set_mode(drv, 0) < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
-                          "use managed mode");
-       }
-
        wpa_driver_nl80211_get_range(drv);
 
-       drv->ifindex = if_nametoindex(drv->ifname);
-
        wpa_driver_nl80211_capa(drv);
 
        wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT);
@@ -1515,6 +1543,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
        (void) wpa_driver_nl80211_set_bssid(drv,
                                         (u8 *) "\x00\x00\x00\x00\x00\x00");
 #endif /* WEXT_COMPAT */
+       wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, 0);
 
        wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP);
 
@@ -1522,6 +1551,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
 
        if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0)
                (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP);
+       wpa_driver_nl80211_set_mode(drv, 0);
 
        close(drv->wext_event_sock);
        close(drv->ioctl_sock);
@@ -1923,19 +1953,6 @@ static int wpa_driver_nl80211_set_countermeasures(void *priv,
 #endif /* WEXT_COMPAT */
 
 
-static int wpa_driver_nl80211_set_drop_unencrypted(void *priv,
-                                               int enabled)
-{
-       struct wpa_driver_nl80211_data *drv = priv;
-       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-#ifdef WEXT_COMPAT
-       drv->use_crypt = enabled;
-#endif /* WEXT_COMPAT */
-       return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
-                                             enabled);
-}
-
-
 #ifdef WEXT_COMPAT
 static int wpa_driver_nl80211_mlme_wext(struct wpa_driver_nl80211_data *drv,
                                        const u8 *addr, int cmd,
@@ -2129,6 +2146,29 @@ wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static int wpa_driver_nl80211_set_auth_alg(struct wpa_driver_nl80211_data *drv,
+                                          int auth_alg)
+{
+       int algs = 0, res;
+
+       if (auth_alg & AUTH_ALG_OPEN_SYSTEM)
+               algs |= IW_AUTH_ALG_OPEN_SYSTEM;
+       if (auth_alg & AUTH_ALG_SHARED_KEY)
+               algs |= IW_AUTH_ALG_SHARED_KEY;
+       if (auth_alg & AUTH_ALG_LEAP)
+               algs |= IW_AUTH_ALG_LEAP;
+       if (algs == 0) {
+               /* at least one algorithm should be set */
+               algs = IW_AUTH_ALG_OPEN_SYSTEM;
+       }
+
+       res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
+                                            algs);
+       drv->auth_alg_fallback = res == -2;
+       return res;
+}
+
+
 static int wpa_driver_nl80211_associate_wext(
        void *priv, struct wpa_driver_associate_params *params)
 {
@@ -2139,6 +2179,9 @@ static int wpa_driver_nl80211_associate_wext(
 
        wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
 
+       wpa_driver_nl80211_set_mode(drv, params->mode);
+       wpa_driver_nl80211_set_auth_alg(drv, params->auth_alg);
+
        /*
         * If the driver did not support SIOCSIWAUTH, fallback to
         * SIOCSIWENCODE here.
@@ -2209,32 +2252,6 @@ static int wpa_driver_nl80211_associate_wext(
 
        return ret;
 }
-
-
-static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg)
-{
-       struct wpa_driver_nl80211_data *drv = priv;
-       int algs = 0, res;
-
-       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
-               return 0;
-
-       if (auth_alg & AUTH_ALG_OPEN_SYSTEM)
-               algs |= IW_AUTH_ALG_OPEN_SYSTEM;
-       if (auth_alg & AUTH_ALG_SHARED_KEY)
-               algs |= IW_AUTH_ALG_SHARED_KEY;
-       if (auth_alg & AUTH_ALG_LEAP)
-               algs |= IW_AUTH_ALG_LEAP;
-       if (algs == 0) {
-               /* at least one algorithm should be set */
-               algs = IW_AUTH_ALG_OPEN_SYSTEM;
-       }
-
-       res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
-                                            algs);
-       drv->auth_alg_fallback = res == -2;
-       return res;
-}
 #endif /* WEXT_COMPAT */
 
 
@@ -2310,6 +2327,48 @@ nla_put_failure:
 }
 
 
+#ifdef CONFIG_AP
+static int wpa_driver_nl80211_set_freq2(
+       struct wpa_driver_nl80211_data *drv,
+       struct wpa_driver_associate_params *params)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_WIPHY, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       /* TODO: proper channel configuration */
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, 2437);
+
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+nla_put_failure:
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+                                struct wpa_driver_associate_params *params)
+{
+       if (wpa_driver_nl80211_set_mode(drv, params->mode) ||
+           wpa_driver_nl80211_set_freq2(drv, params))
+               return -1;
+
+       /* TODO: setup monitor interface (and add code somewhere to remove this
+        * when AP mode is stopped; associate with mode != 2 or drv_deinit) */
+       /* TODO: setup beacon */
+
+       return 0;
+}
+#endif /* CONFIG_AP */
+
+
 static int wpa_driver_nl80211_associate(
        void *priv, struct wpa_driver_associate_params *params)
 {
@@ -2317,6 +2376,14 @@ static int wpa_driver_nl80211_associate(
        int ret = -1;
        struct nl_msg *msg;
 
+#ifdef CONFIG_AP
+       if (params->mode == 2)
+               return wpa_driver_nl80211_ap(drv, params);
+#endif /* CONFIG_AP */
+
+       wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
+                                         params->drop_unencrypted);
+
 #ifdef WEXT_COMPAT
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
                return wpa_driver_nl80211_associate_wext(drv, params);
@@ -2348,6 +2415,10 @@ static int wpa_driver_nl80211_associate(
                                  params->ssid, params->ssid_len);
                NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
                        params->ssid);
+               if (params->ssid_len > sizeof(drv->ssid))
+                       goto nla_put_failure;
+               os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+               drv->ssid_len = params->ssid_len;
        }
        wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
        if (params->wpa_ie)
@@ -2373,15 +2444,30 @@ nla_put_failure:
 
 /**
  * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * @drv: Pointer to private driver data from wpa_driver_nl80211_init()
  * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
  * Returns: 0 on success, -1 on failure
  */
-static int wpa_driver_nl80211_set_mode(void *priv, int mode)
+static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+                                      int mode)
 {
-       struct wpa_driver_nl80211_data *drv = priv;
        int ret = -1, flags;
        struct nl_msg *msg;
+       int nlmode;
+
+       switch (mode) {
+       case 0:
+               nlmode = NL80211_IFTYPE_STATION;
+               break;
+       case 1:
+               nlmode = NL80211_IFTYPE_ADHOC;
+               break;
+       case 2:
+               nlmode = NL80211_IFTYPE_AP;
+               break;
+       default:
+               return -1;
+       }
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -2390,8 +2476,7 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
        genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
                    0, NL80211_CMD_SET_INTERFACE, 0);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE,
-                   mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (!ret)
@@ -2400,7 +2485,8 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
                goto try_again;
 
 nla_put_failure:
-       wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode");
+       wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)",
+                  ret, strerror(-ret));
        return -1;
 
 try_again:
@@ -2419,13 +2505,12 @@ try_again:
                genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
                            0, NL80211_CMD_SET_INTERFACE, 0);
                NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-               NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE,
-                           mode ? NL80211_IFTYPE_ADHOC :
-                           NL80211_IFTYPE_STATION);
+               NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode);
                ret = send_and_recv_msgs(drv, msg, NULL, NULL);
                if (ret) {
                        wpa_printf(MSG_ERROR, "Failed to set interface %s "
-                                  "mode", drv->ifname);
+                                  "mode(try_again): %d (%s)",
+                                  drv->ifname, ret, strerror(-ret));
                }
 
                /* Ignore return value of get_ifflags to ensure that the device
@@ -2524,12 +2609,10 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .get_bssid = wpa_driver_nl80211_get_bssid,
        .get_ssid = wpa_driver_nl80211_get_ssid,
        .set_key = wpa_driver_nl80211_set_key,
-       .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted,
        .scan2 = wpa_driver_nl80211_scan,
        .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
        .deauthenticate = wpa_driver_nl80211_deauthenticate,
        .disassociate = wpa_driver_nl80211_disassociate,
-       .set_mode = wpa_driver_nl80211_set_mode,
        .authenticate = wpa_driver_nl80211_authenticate,
        .associate = wpa_driver_nl80211_associate,
        .init = wpa_driver_nl80211_init,
@@ -2540,7 +2623,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef WEXT_COMPAT
        .set_wpa = wpa_driver_nl80211_set_wpa,
        .set_countermeasures = wpa_driver_nl80211_set_countermeasures,
-       .set_auth_alg = wpa_driver_nl80211_set_auth_alg,
        .add_pmkid = wpa_driver_nl80211_add_pmkid,
        .remove_pmkid = wpa_driver_nl80211_remove_pmkid,
        .flush_pmkid = wpa_driver_nl80211_flush_pmkid,