Do not wait for monitor to attach if no control interface
[mech_eap.git] / wpa_supplicant / ctrl_iface_unix.c
index e1c4639..7b36751 100644 (file)
@@ -52,6 +52,8 @@ struct ctrl_iface_priv {
        int sock;
        struct dl_list ctrl_dst;
        int android_control_socket;
+       struct dl_list msg_queue;
+       unsigned int throttle_count;
 };
 
 
@@ -60,6 +62,17 @@ struct ctrl_iface_global_priv {
        int sock;
        struct dl_list ctrl_dst;
        int android_control_socket;
+       struct dl_list msg_queue;
+       unsigned int throttle_count;
+};
+
+struct ctrl_iface_msg {
+       struct dl_list list;
+       struct wpa_supplicant *wpa_s;
+       int level;
+       enum wpa_msg_type type;
+       const char *txt;
+       size_t len;
 };
 
 
@@ -334,33 +347,209 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpas_ctrl_iface_throttle(int sock)
+{
+#ifdef __linux__
+       socklen_t optlen;
+       int sndbuf, outq;
+
+       optlen = sizeof(sndbuf);
+       sndbuf = 0;
+       if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0 ||
+           ioctl(sock, SIOCOUTQ, &outq) < 0 ||
+           sndbuf <= 0 || outq < 0)
+               return 0;
+       return outq > sndbuf / 2;
+#else /* __linux__ */
+       return 0;
+#endif /* __linux__ */
+}
+
+
+static void wpas_ctrl_msg_send_pending_global(struct wpa_global *global)
+{
+       struct ctrl_iface_global_priv *gpriv;
+       struct ctrl_iface_msg *msg;
+
+       gpriv = global->ctrl_iface;
+       while (gpriv && !dl_list_empty(&gpriv->msg_queue) &&
+              !wpas_ctrl_iface_throttle(gpriv->sock)) {
+               msg = dl_list_first(&gpriv->msg_queue, struct ctrl_iface_msg,
+                                   list);
+               if (!msg)
+                       break;
+               dl_list_del(&msg->list);
+               wpa_supplicant_ctrl_iface_send(
+                       msg->wpa_s,
+                       msg->type != WPA_MSG_PER_INTERFACE ?
+                       NULL : msg->wpa_s->ifname,
+                       gpriv->sock, &gpriv->ctrl_dst, msg->level,
+                       msg->txt, msg->len, NULL, gpriv);
+               os_free(msg);
+       }
+}
+
+
+static void wpas_ctrl_msg_send_pending_iface(struct wpa_supplicant *wpa_s)
+{
+       struct ctrl_iface_priv *priv;
+       struct ctrl_iface_msg *msg;
+
+       priv = wpa_s->ctrl_iface;
+       while (priv && !dl_list_empty(&priv->msg_queue) &&
+              !wpas_ctrl_iface_throttle(priv->sock)) {
+               msg = dl_list_first(&priv->msg_queue, struct ctrl_iface_msg,
+                                   list);
+               if (!msg)
+                       break;
+               dl_list_del(&msg->list);
+               wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock,
+                                              &priv->ctrl_dst, msg->level,
+                                              msg->txt, msg->len, priv, NULL);
+               os_free(msg);
+       }
+}
+
+
+static void wpas_ctrl_msg_queue_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct ctrl_iface_priv *priv;
+       struct ctrl_iface_global_priv *gpriv;
+       int sock = -1, gsock = -1;
+
+       wpas_ctrl_msg_send_pending_global(wpa_s->global);
+       wpas_ctrl_msg_send_pending_iface(wpa_s);
+
+       priv = wpa_s->ctrl_iface;
+       if (priv && !dl_list_empty(&priv->msg_queue))
+               sock = priv->sock;
+
+       gpriv = wpa_s->global->ctrl_iface;
+       if (gpriv && !dl_list_empty(&gpriv->msg_queue))
+               gsock = gpriv->sock;
+
+       if (sock > -1 || gsock > -1) {
+               /* Continue pending message transmission from a timeout */
+               wpa_printf(MSG_MSGDUMP,
+                          "CTRL: Had to throttle pending event message transmission for (sock %d gsock %d)",
+                          sock, gsock);
+               eloop_register_timeout(0, 20000, wpas_ctrl_msg_queue_timeout,
+                                      wpa_s, NULL);
+       }
+}
+
+
+static void wpas_ctrl_msg_queue(struct dl_list *queue,
+                               struct wpa_supplicant *wpa_s, int level,
+                               enum wpa_msg_type type,
+                               const char *txt, size_t len)
+{
+       struct ctrl_iface_msg *msg;
+
+       msg = os_zalloc(sizeof(*msg) + len);
+       if (!msg)
+               return;
+
+       msg->wpa_s = wpa_s;
+       msg->level = level;
+       msg->type = type;
+       os_memcpy(msg + 1, txt, len);
+       msg->txt = (const char *) (msg + 1);
+       msg->len = len;
+       dl_list_add_tail(queue, &msg->list);
+       eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
+       eloop_register_timeout(0, 0, wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
+}
+
+
+static void wpas_ctrl_msg_queue_limit(unsigned int throttle_count,
+                                     struct dl_list *queue)
+{
+       struct ctrl_iface_msg *msg;
+
+       if (throttle_count < 2000)
+               return;
+
+       msg = dl_list_first(queue, struct ctrl_iface_msg, list);
+       if (msg) {
+               wpa_printf(MSG_DEBUG, "CTRL: Dropped oldest pending message");
+               dl_list_del(&msg->list);
+               os_free(msg);
+       }
+}
+
+
 static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
                                             enum wpa_msg_type type,
                                             const char *txt, size_t len)
 {
        struct wpa_supplicant *wpa_s = ctx;
+       struct ctrl_iface_priv *priv;
+       struct ctrl_iface_global_priv *gpriv;
 
        if (wpa_s == NULL)
                return;
 
-       if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
-               struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
-               if (!dl_list_empty(&priv->ctrl_dst)) {
+       gpriv = wpa_s->global->ctrl_iface;
+
+       if (type != WPA_MSG_NO_GLOBAL && gpriv &&
+           !dl_list_empty(&gpriv->ctrl_dst)) {
+               if (!dl_list_empty(&gpriv->msg_queue) ||
+                   wpas_ctrl_iface_throttle(gpriv->sock)) {
+                       if (gpriv->throttle_count == 0) {
+                               wpa_printf(MSG_MSGDUMP,
+                                          "CTRL: Had to throttle global event message for sock %d",
+                                          gpriv->sock);
+                       }
+                       gpriv->throttle_count++;
+                       wpas_ctrl_msg_queue_limit(gpriv->throttle_count,
+                                                 &gpriv->msg_queue);
+                       wpas_ctrl_msg_queue(&gpriv->msg_queue, wpa_s, level,
+                                           type, txt, len);
+               } else {
+                       if (gpriv->throttle_count) {
+                               wpa_printf(MSG_MSGDUMP,
+                                          "CTRL: Had to throttle %u global event message(s) for sock %d",
+                                          gpriv->throttle_count, gpriv->sock);
+                       }
+                       gpriv->throttle_count = 0;
                        wpa_supplicant_ctrl_iface_send(
                                wpa_s,
                                type != WPA_MSG_PER_INTERFACE ?
                                NULL : wpa_s->ifname,
-                               priv->sock, &priv->ctrl_dst, level, txt, len,
-                               NULL, priv);
+                               gpriv->sock, &gpriv->ctrl_dst, level,
+                               txt, len, NULL, gpriv);
                }
        }
 
-       if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL)
-               return;
-       wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
-                                      &wpa_s->ctrl_iface->ctrl_dst,
-                                      level, txt, len, wpa_s->ctrl_iface,
-                                      NULL);
+       priv = wpa_s->ctrl_iface;
+
+       if (type != WPA_MSG_ONLY_GLOBAL && priv) {
+               if (!dl_list_empty(&priv->msg_queue) ||
+                   wpas_ctrl_iface_throttle(priv->sock)) {
+                       if (priv->throttle_count == 0) {
+                               wpa_printf(MSG_MSGDUMP,
+                                          "CTRL: Had to throttle event message for sock %d",
+                                          priv->sock);
+                       }
+                       priv->throttle_count++;
+                       wpas_ctrl_msg_queue_limit(priv->throttle_count,
+                                                 &priv->msg_queue);
+                       wpas_ctrl_msg_queue(&priv->msg_queue, wpa_s, level,
+                                           type, txt, len);
+               } else {
+                       if (priv->throttle_count) {
+                               wpa_printf(MSG_MSGDUMP,
+                                          "CTRL: Had to throttle %u event message(s) for sock %d",
+                                          priv->throttle_count, priv->sock);
+                       }
+                       priv->throttle_count = 0;
+                       wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock,
+                                                      &priv->ctrl_dst, level,
+                                                      txt, len, priv, NULL);
+               }
+       }
 }
 
 
@@ -578,12 +767,60 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        if (priv == NULL)
                return NULL;
        dl_list_init(&priv->ctrl_dst);
+       dl_list_init(&priv->msg_queue);
        priv->wpa_s = wpa_s;
        priv->sock = -1;
 
        if (wpa_s->conf->ctrl_interface == NULL)
                return priv;
 
+#ifdef ANDROID
+       if (wpa_s->global->params.ctrl_interface) {
+               int same = 0;
+
+               if (wpa_s->global->params.ctrl_interface[0] == '/') {
+                       if (os_strcmp(wpa_s->global->params.ctrl_interface,
+                                     wpa_s->conf->ctrl_interface) == 0)
+                               same = 1;
+               } else if (os_strncmp(wpa_s->global->params.ctrl_interface,
+                                     "@android:", 9) == 0 ||
+                          os_strncmp(wpa_s->global->params.ctrl_interface,
+                                     "@abstract:", 10) == 0) {
+                       char *pos;
+
+                       /*
+                        * Currently, Android uses @android:wpa_* as the naming
+                        * convention for the global ctrl interface. This logic
+                        * needs to be revisited if the above naming convention
+                        * is modified.
+                        */
+                       pos = os_strchr(wpa_s->global->params.ctrl_interface,
+                                       '_');
+                       if (pos &&
+                           os_strcmp(pos + 1,
+                                     wpa_s->conf->ctrl_interface) == 0)
+                               same = 1;
+               }
+
+               if (same) {
+                       /*
+                        * The invalid configuration combination might be
+                        * possible to hit in an Android OTA upgrade case, so
+                        * instead of refusing to start the wpa_supplicant
+                        * process, do not open the per-interface ctrl_iface
+                        * and continue with the global control interface that
+                        * was set from the command line since the Wi-Fi
+                        * framework will use it for operations.
+                        */
+                       wpa_printf(MSG_ERROR,
+                                  "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
+                                  wpa_s->global->params.ctrl_interface,
+                                  wpa_s->conf->ctrl_interface);
+                       return priv;
+               }
+       }
+#endif /* ANDROID */
+
        if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
                os_free(priv);
                return NULL;
@@ -624,6 +861,8 @@ static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
 {
        struct wpa_ctrl_dst *dst, *prev;
+       struct ctrl_iface_msg *msg, *prev_msg;
+       struct ctrl_iface_global_priv *gpriv;
 
        if (priv->sock > -1) {
                char *fname;
@@ -677,8 +916,26 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
 
 free_dst:
        dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
-                             list)
+                             list) {
+               dl_list_del(&dst->list);
                os_free(dst);
+       }
+       dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
+                             struct ctrl_iface_msg, list) {
+               dl_list_del(&msg->list);
+               os_free(msg);
+       }
+       gpriv = priv->wpa_s->global->ctrl_iface;
+       if (gpriv) {
+               dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue,
+                                     struct ctrl_iface_msg, list) {
+                       if (msg->wpa_s == priv->wpa_s) {
+                               dl_list_del(&msg->list);
+                               os_free(msg);
+                       }
+               }
+       }
+       eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, priv->wpa_s, NULL);
        os_free(priv);
 }
 
@@ -801,6 +1058,9 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
        struct sockaddr_un from;
        socklen_t fromlen = sizeof(from);
 
+       if (priv->sock == -1)
+               return;
+
        for (;;) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
                           "attach", priv->wpa_s->ifname);
@@ -1108,6 +1368,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
        if (priv == NULL)
                return NULL;
        dl_list_init(&priv->ctrl_dst);
+       dl_list_init(&priv->msg_queue);
        priv->global = global;
        priv->sock = -1;
 
@@ -1157,6 +1418,7 @@ void
 wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
 {
        struct wpa_ctrl_dst *dst, *prev;
+       struct ctrl_iface_msg *msg, *prev_msg;
 
        if (priv->sock >= 0) {
                eloop_unregister_read_sock(priv->sock);
@@ -1165,7 +1427,14 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
        if (priv->global->params.ctrl_interface)
                unlink(priv->global->params.ctrl_interface);
        dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
-                             list)
+                             list) {
+               dl_list_del(&dst->list);
                os_free(dst);
+       }
+       dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
+                             struct ctrl_iface_msg, list) {
+               dl_list_del(&msg->list);
+               os_free(msg);
+       }
        os_free(priv);
 }