diff options
Diffstat (limited to 'contrib/wpa/src/wps/wps_upnp_event.c')
-rw-r--r-- | contrib/wpa/src/wps/wps_upnp_event.c | 150 |
1 files changed, 108 insertions, 42 deletions
diff --git a/contrib/wpa/src/wps/wps_upnp_event.c b/contrib/wpa/src/wps/wps_upnp_event.c index ae5efdb..2c8ed4f1 100644 --- a/contrib/wpa/src/wps/wps_upnp_event.c +++ b/contrib/wpa/src/wps/wps_upnp_event.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * * See wps_upnp.c for more details on licensing and code history. */ @@ -31,7 +31,7 @@ */ #define MAX_EVENTS_QUEUED 20 /* How far behind queued events */ -#define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */ +#define MAX_FAILURES 10 /* Drop subscription after this many failures */ /* How long to wait before sending event */ #define EVENT_DELAY_SECONDS 0 @@ -73,6 +73,7 @@ static void event_clean(struct wps_event_ *e) */ static void event_delete(struct wps_event_ *e) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e); event_clean(e); wpabuf_free(e->data); os_free(e); @@ -86,8 +87,11 @@ static struct wps_event_ *event_dequeue(struct subscription *s) { struct wps_event_ *e; e = dl_list_first(&s->event_queue, struct wps_event_, list); - if (e) + if (e) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for " + "subscription %p", e, s); dl_list_del(&e->list); + } return e; } @@ -115,14 +119,22 @@ static void event_retry(struct wps_event_ *e, int do_next_address) struct subscription *s = e->s; struct upnp_wps_device_sm *sm = s->sm; + wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p", + e, s); event_clean(e); /* will set: s->current_event = NULL; */ - if (do_next_address) + if (do_next_address) { e->retry++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry); + } if (e->retry >= dl_list_len(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); + event_delete(e); + s->last_event_failed = 1; + if (!dl_list_empty(&s->event_queue)) + event_send_all_later(s->sm); return; } dl_list_add(&s->event_queue, &e->list); @@ -158,17 +170,60 @@ static struct wpabuf * event_build_message(struct wps_event_ *e) } +static void event_addr_failure(struct wps_event_ *e) +{ + struct subscription *s = e->s; + + e->addr->num_failures++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s " + "(num_failures=%u)", + e, e->addr->domain_and_port, e->addr->num_failures); + + if (e->addr->num_failures < MAX_FAILURES) { + /* Try other addresses, if available */ + event_retry(e, 1); + return; + } + + /* + * If other side doesn't like what we say, forget about them. + * (There is no way to tell other side that we are dropping them...). + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p " + "address %s due to errors", s, e->addr->domain_and_port); + dl_list_del(&e->addr->list); + subscr_addr_delete(e->addr); + e->addr = NULL; + + if (dl_list_empty(&s->addr_list)) { + /* if we've given up on all addresses */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p " + "with no addresses", s); + dl_list_del(&s->list); + subscription_destroy(s); + return; + } + + /* Try other addresses, if available */ + event_retry(e, 0); +} + + static void event_http_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_event_ *e = ctx; struct subscription *s = e->s; + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p " + "event=%d", e, c, event); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, - "WPS UPnP: Got event reply OK from " - "%s", e->addr->domain_and_port); + "WPS UPnP: Got event %p reply OK from %s", + e, e->addr->domain_and_port); + e->addr->num_failures = 0; + s->last_event_failed = 0; event_delete(e); /* Schedule sending more if there is more to send */ @@ -176,24 +231,17 @@ static void event_http_cb(void *ctx, struct http_client *c, event_send_all_later(s->sm); break; case HTTP_CLIENT_FAILED: + wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure"); + event_addr_failure(e); + break; case HTTP_CLIENT_INVALID_REPLY: - wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s", - e->addr->domain_and_port); - - /* - * If other side doesn't like what we say, forget about them. - * (There is no way to tell other side that we are dropping - * them...). - * Alternately, we could just do event_delete(e) - */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to " - "errors"); - dl_list_del(&s->list); - subscription_destroy(s); + wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply"); + event_addr_failure(e); break; case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); - event_retry(e, 1); + event_addr_failure(e); + break; } } @@ -228,9 +276,12 @@ static int event_send_start(struct subscription *s) * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - assert(!dl_list_empty(&s->addr_list)); - assert(s->current_event == NULL); - assert(!dl_list_empty(&s->event_queue)); + if (dl_list_empty(&s->addr_list)) + return -1; + if (s->current_event) + return -1; + if (dl_list_empty(&s->event_queue)) + return -1; s->current_event = e = event_dequeue(s); @@ -270,18 +321,10 @@ static void event_send_all_later_handler(void *eloop_data, void *user_ctx) sm->event_send_all_queued = 0; dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, list) { - if (dl_list_empty(&s->addr_list)) { - /* if we've given up on all addresses */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " - "subscription with no addresses"); - dl_list_del(&s->list); - subscription_destroy(s); - } else { - if (s->current_event == NULL /* not busy */ && - !dl_list_empty(&s->event_queue) /* more to do */) { - if (event_send_start(s)) - nerrors++; - } + if (s->current_event == NULL /* not busy */ && + !dl_list_empty(&s->event_queue) /* more to do */) { + if (event_send_start(s)) + nerrors++; } } @@ -326,31 +369,54 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm) * event_add - Add a new event to a queue * @s: Subscription * @data: Event data (is copied; caller retains ownership) - * Returns: 0 on success, 1 on error + * @probereq: Whether this is a Probe Request event + * Returns: 0 on success, -1 on error, 1 on max event queue limit reached */ -int event_add(struct subscription *s, const struct wpabuf *data) +int event_add(struct subscription *s, const struct wpabuf *data, int probereq) { struct wps_event_ *e; + unsigned int len; - if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) { + len = dl_list_len(&s->event_queue); + if (len >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " - "subscriber"); - return 1; + "subscriber %p", s); + if (probereq) + return 1; + + /* Drop oldest entry to allow EAP event to be stored. */ + e = event_dequeue(s); + if (!e) + return 1; + event_delete(e); + } + + if (s->last_event_failed && probereq && len > 0) { + /* + * Avoid queuing frames for subscribers that may have left + * without unsubscribing. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe " + "Request frames for subscription %p since last " + "delivery failed", s); + return -1; } e = os_zalloc(sizeof(*e)); if (e == NULL) - return 1; + return -1; dl_list_init(&e->list); e->s = s; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); - return 1; + return -1; } e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p " + "(queue len %u)", e, s, len + 1); dl_list_add_tail(&s->event_queue, &e->list); event_send_all_later(s->sm); return 0; |