static void fst_session_handle_setup_request(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, size_t frame_len) { struct fst_session *s; const struct fst_setup_req *req; struct fst_iface *new_iface = NULL; struct fst_group *g; u8 new_iface_peer_addr[ETH_ALEN]; const struct wpabuf *peer_mbies; size_t plen; if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: too short (%zu < %zu)", frame_len, IEEE80211_HDRLEN + 1 + sizeof(*req)); return; } plen = frame_len - IEEE80211_HDRLEN - 1; req = (const struct fst_setup_req *) (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || req->stie.length < 11) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: invalid STIE"); return; } if (req->stie.new_band_id == req->stie.old_band_id) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: new and old band IDs are the same"); return; } g = fst_iface_get_group(iface); if (plen > sizeof(*req)) { fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), plen - sizeof(*req)); fst_printf_iface(iface, MSG_INFO, "FST Request: MB IEs updated for " MACSTR, MAC2STR(mgmt->sa)); } peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa); if (peer_mbies) { new_iface = fst_group_get_new_iface_by_stie_and_mbie( g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies), &req->stie, new_iface_peer_addr); if (new_iface) fst_printf_iface(iface, MSG_INFO, "FST Request: new iface (%s:" MACSTR ") found by MB IEs", fst_iface_get_name(new_iface), MAC2STR(new_iface_peer_addr)); } if (!new_iface) { new_iface = fst_group_find_new_iface_by_stie( g, iface, mgmt->sa, &req->stie, new_iface_peer_addr); if (new_iface) fst_printf_iface(iface, MSG_INFO, "FST Request: new iface (%s:" MACSTR ") found by others", fst_iface_get_name(new_iface), MAC2STR(new_iface_peer_addr)); } if (!new_iface) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: new iface not found"); return; } s = fst_find_session_in_progress(mgmt->sa, g); if (s) { union fst_session_state_switch_extra evext = { .to_initial = { .reason = REASON_SETUP, }, }; /* * 10.32.2.2 Transitioning between states: * Upon receipt of an FST Setup Request frame, the responder * shall respond with an FST Setup Response frame unless it has * a pending FST Setup Request frame addressed to the initiator * and the responder has a numerically larger MAC address than * the initiator’s MAC address, in which case, the responder * shall delete the received FST Setup Request. */ if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { fst_printf_session(s, MSG_WARNING, "FST Request dropped due to MAC comparison (our MAC is " MACSTR ")", MAC2STR(mgmt->da)); return; } if (!fst_session_is_ready_pending(s)) { fst_printf_session(s, MSG_WARNING, "FST Request from " MACSTR " dropped due to inappropriate state %s", MAC2STR(mgmt->da), fst_session_state_name(s->state)); return; } /* * If FST Setup Request arrived with the same FSTS ID as one we * initialized before, it means the other side either didn't * receive our FST Request or skipped it for some reason (for * example, due to numerical MAC comparison). * * In this case, there's no need to tear down the session. * Moreover, as FSTS ID is the same, the other side will * associate this tear down with the session it initiated that * will break the sync. */ if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) fst_session_send_tear_down(s); else fst_printf_session(s, MSG_WARNING, "Skipping TearDown as the FST request has the same FSTS ID as initiated"); fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); fst_session_stt_disarm(s); fst_printf_session(s, MSG_WARNING, "reset due to FST request"); } s = fst_session_create(g); if (!s) { fst_printf(MSG_WARNING, "FST Request dropped: cannot create session for %s and %s", fst_iface_get_name(iface), fst_iface_get_name(new_iface)); return; } fst_session_set_iface(s, iface, TRUE); fst_session_set_peer_addr(s, mgmt->sa, TRUE); fst_session_set_iface(s, new_iface, FALSE); fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); s->data.pending_setup_req_dlgt = req->dialog_token; s->data.fsts_id = le_to_host32(req->stie.fsts_id); fst_session_stt_arm(s); fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); }
static void fst_plugin_connect_next () { FSTNode *node; FSTSession *sess; List *l; int count = 0, nsessions, nconnecting = 0; nsessions = config_get_int (FST_PLUGIN->conf, "main/additional_sessions=0"); if (nsessions > FST_MAX_ADDITIONAL_SESSIONS) nsessions = FST_MAX_ADDITIONAL_SESSIONS; /* determine number of currently connecting sessions */ nconnecting = (FST_PLUGIN->session && FST_PLUGIN->session->state != SessEstablished) ? 1 : 0; for (l = FST_PLUGIN->sessions; l; l = l->next) { assert (l->data); if (((FSTSession*)l->data)->state != SessEstablished) nconnecting++; } /* connect to head node in node cache */ while ((!FST_PLUGIN->session || list_length (FST_PLUGIN->sessions) <= nsessions) && nconnecting <= FST_SESSION_MAX_CONCURRENT_ATTEMPTS) { if (!(node = fst_nodecache_get_front (FST_PLUGIN->nodecache))) { /* node cache empty */ FST_ERR ("All attempts at contacting peers have failed. Trying default nodes file."); if (fst_nodecache_load (FST_PLUGIN->nodecache, stringf ("%s/FastTrack/nodes", platform_data_dir())) <= 0 || !(node = fst_nodecache_get_front (FST_PLUGIN->nodecache))) { FST_ERR ("Failed to load default nodes file. Perhaps your installation is corrupt?"); return; } } /* don't connect anywhere we're already connected to */ if (node->session) { /* move node to back of cache so next loop * uses a different one */ fst_nodecache_move (FST_PLUGIN->nodecache, node, NodeInsertBack); fst_node_release (node); /* we've probably run out of nodes at this point, so * wait a while until we get some more (continuing * tends to produce an infinite loop) */ if (count++ >= list_length (FST_PLUGIN->sessions)) return; continue; } /* don't connect to anywhere too close to an existing node */ if (dataset_lookup (FST_PLUGIN->peers, &node, sizeof(node))) { #if 0 FST_DBG_2 ("not connecting to close node %s:%d", node->host, node->port); #endif /* move node to back of cache so next loop * uses a different one */ fst_nodecache_move (FST_PLUGIN->nodecache, node, NodeInsertBack); fst_node_release (node); /* we've probably run out of nodes at this point, so * wait a while until we get some more (continuing * tends to produce an infinite loop) */ if (count++ >= list_length (FST_PLUGIN->sessions)) return; continue; } /* don't connect to invalid ips */ if (!fst_utils_ip_routable (net_ip (node->host))) { FST_DBG_2 ("not connecting to unroutable node %s:%d", node->host, node->port); /* remove this node from cache */ fst_nodecache_remove (FST_PLUGIN->nodecache, node); fst_node_release (node); continue; } /* don't connect to banned ips */ if (config_get_int (FST_PLUGIN->conf, "main/banlist_filter=0") && fst_ipset_contains (FST_PLUGIN->banlist, net_ip (node->host))) { FST_DBG_2 ("not connecting to banned supernode %s:%d", node->host, node->port); /* remove this node from cache */ fst_nodecache_remove (FST_PLUGIN->nodecache, node); fst_node_release (node); continue; } /* create session and connect */ sess = fst_session_create (fst_plugin_session_callback); if (!fst_session_connect (sess, node)) { /* free session */ fst_session_free (sess); sess = NULL; /* TODO: check if name resolution in fst_session_connect() failed */ /* network down, wait a while before retrying */ FST_WARN_1 ("Internet connection seems down, sleeping...", FST_SESSION_NETFAIL_INTERVAL / SECONDS); /* move node to back of cache so next loop uses a different one; this * won't help if the network really is down, but might under other * circumstances */ fst_nodecache_move (FST_PLUGIN->nodecache, node, NodeInsertBack); fst_node_release (node); /* just wait until fst_plugin_try_connect() is next called */ return; } /* move node to back of cache so next loop uses a different one */ fst_nodecache_move (FST_PLUGIN->nodecache, node, NodeInsertBack); fst_node_release (node); /* We now have a new session object. Use it as primary session if we * don't already have one. Otherwise use it as an additional one. */ if (!FST_PLUGIN->session) { FST_PLUGIN->session = sess; } else { FST_PLUGIN->sessions = list_prepend (FST_PLUGIN->sessions, sess); } nconnecting++; } /* don't ping if we're currently connected */ if (FST_PLUGIN->stats->sessions > 0) return; /* We started a connection attempt with the head node from nodecache. * Try to quickly find some online nodes with udp in parallel now. */ if (FST_PLUGIN->discover && FST_PLUGIN->discover->pinged_nodes == 0) { List *item = FST_PLUGIN->nodecache->list; int i = 0; while (i < FST_UDP_DISCOVER_MAX_PINGS && item && item->data) { node = (FSTNode*)item->data; if (!fst_udp_discover_ping_node (FST_PLUGIN->discover, node)) { /* This may fail due to the network being down. While * we could handle this in a special way doing nothing * works fine if being somewhat inefficient. */ } item = item->next; i++; } FST_DBG_1 ("discovery cycle started with %d UDP pings", i); } }
static void fst_session_handle_setup_request(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, size_t frame_len) { struct fst_session *s; const struct fst_setup_req *req; struct fst_iface *new_iface = NULL; struct fst_group *g; u8 new_iface_peer_addr[ETH_ALEN]; size_t plen; if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: too short (%zu < %zu)", frame_len, IEEE80211_HDRLEN + 1 + sizeof(*req)); return; } plen = frame_len - IEEE80211_HDRLEN - 1; req = (const struct fst_setup_req *) (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || req->stie.length < 11) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: invalid STIE"); return; } if (req->stie.new_band_id == req->stie.old_band_id) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: new and old band IDs are the same"); return; } g = fst_iface_get_group(iface); if (plen > sizeof(*req)) { fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), plen - sizeof(*req)); fst_printf_iface(iface, MSG_INFO, "FST Request: MB IEs updated for " MACSTR, MAC2STR(mgmt->sa)); } new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa, req->stie.new_band_id, new_iface_peer_addr); if (!new_iface) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: new iface not found"); return; } fst_printf_iface(iface, MSG_INFO, "FST Request: new iface (%s:" MACSTR ") found", fst_iface_get_name(new_iface), MAC2STR(new_iface_peer_addr)); s = fst_find_session_in_progress(mgmt->sa, g); if (s) { union fst_session_state_switch_extra evext = { .to_initial = { .reason = REASON_SETUP, }, }; /* * 10.32.2.2 Transitioning between states: * Upon receipt of an FST Setup Request frame, the responder * shall respond with an FST Setup Response frame unless it has * a pending FST Setup Request frame addressed to the initiator * and the responder has a numerically larger MAC address than * the initiator’s MAC address, in which case, the responder * shall delete the received FST Setup Request. */ if (fst_session_is_ready_pending(s) && /* waiting for Setup Response */ os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { fst_printf_session(s, MSG_WARNING, "FST Request dropped due to MAC comparison (our MAC is " MACSTR ")", MAC2STR(mgmt->da)); return; } /* * State is SETUP_COMPLETION (either in transition or not) or * TRANSITION_DONE (in transition). * Setup Request arriving in this state could mean: * 1. peer sent it before receiving our Setup Request (race * condition) * 2. peer didn't receive our Setup Response. Peer is retrying * after STT timeout * 3. peer's FST state machines are out of sync due to some * other reason * * We will reset our session and create a new one instead. */ fst_printf_session(s, MSG_WARNING, "resetting due to FST request"); /* * If FST Setup Request arrived with the same FSTS ID as one we * initialized before, there's no need to tear down the session. * Moreover, as FSTS ID is the same, the other side will * associate this tear down with the session it initiated that * will break the sync. */ if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) fst_session_send_tear_down(s); else fst_printf_session(s, MSG_WARNING, "Skipping TearDown as the FST request has the same FSTS ID as initiated"); fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); fst_session_stt_disarm(s); } s = fst_session_create(g); if (!s) { fst_printf(MSG_WARNING, "FST Request dropped: cannot create session for %s and %s", fst_iface_get_name(iface), fst_iface_get_name(new_iface)); return; } fst_session_set_iface(s, iface, TRUE); fst_session_set_peer_addr(s, mgmt->sa, TRUE); fst_session_set_iface(s, new_iface, FALSE); fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); s->data.pending_setup_req_dlgt = req->dialog_token; s->data.fsts_id = le_to_host32(req->stie.fsts_id); fst_session_stt_arm(s); fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); }