Exemplo n.º 1
0
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);
	}
}
Exemplo n.º 3
0
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);
}