Example #1
0
/**
 * Insert segment into the list (segments covered with new one will be deleted)
 *
 * Called from tcp_receive()
 */
static void
tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
{
	struct tcp_seg *old_seg;

	if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
		/* received segment overlaps all following segments */
		tcp_segs_free(next);
		next = NULL;
	} else {
		/* delete some following segments
		   oos queue may have segments with FIN flag */
		while (next &&
			   TCP_SEQ_GEQ((seqno + cseg->len),
						   (next->tcphdr->seqno + next->len))) {
			/* cseg with FIN already processed */
			if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
				TCPH_FLAGS_SET(cseg->tcphdr, TCPH_FLAGS(cseg->tcphdr) | TCP_FIN);
			}
			old_seg = next;
			next = next->next;
			tcp_seg_free(old_seg);
		}
		if (next &&
			TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
			/* We need to trim the incoming segment. */
			cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
			pbuf_realloc(cseg->p, cseg->len);
		}
	}
	cseg->next = next;
}
Example #2
0
/**
 * Called by tcp_input() when a segment arrives for a connection in
 * TIME_WAIT.
 *
 * @param pcb the tcp_pcb for which a segment arrived
 *
 * @note the segment which arrived is saved in global variables, therefore only the pcb
 *       involved is passed as a parameter to this function
 */
static err_t
tcp_timewait_input(struct tcp_pcb *pcb)
{
  if (TCP_SEQ_GT(seqno + tcplen, pcb->rcv_nxt)) {
    pcb->rcv_nxt = seqno + tcplen;
  }
  if (tcplen > 0) {
    tcp_ack_now(pcb);
  }
  return tcp_output(pcb);
}
Example #3
0
/** 
 * Update the state that tracks the available window space to advertise.
 *
 * Returns how much extra window would be advertised if we sent an
 * update now.
 */
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
{
  u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;

  if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
    /* we can advertise more window */
    pcb->rcv_ann_wnd = pcb->rcv_wnd;
    return new_right_edge - pcb->rcv_ann_right_edge;
  } else {
    if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
      /* Can happen due to other end sending out of advertised window,
       * but within actual available (but not yet advertised) window */
      pcb->rcv_ann_wnd = 0;
    } else {
      /* keep the right edge of window constant */
      pcb->rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
    }
    return 0;
  }
}
Example #4
0
/*----------------------------------------------------------------------------*/
static inline int
ValidateSequence(mtcp_manager_t mtcp, tcp_stream *cur_stream, uint32_t cur_ts, 
		struct tcphdr *tcph, uint32_t seq, uint32_t ack_seq, int payloadlen)
{
	/* Protect Against Wrapped Sequence number (PAWS) */
	if (!tcph->rst && cur_stream->saw_timestamp) {
		struct tcp_timestamp ts;
		
		if (!ParseTCPTimestamp(cur_stream, &ts, 
				(uint8_t *)tcph + TCP_HEADER_LEN, 
				(tcph->doff << 2) - TCP_HEADER_LEN)) {
			/* if there is no timestamp */
			/* TODO: implement here */
			TRACE_DBG("No timestamp found.\n");
			return FALSE;
		}

		/* RFC1323: if SEG.TSval < TS.Recent, drop and send ack */
		if (TCP_SEQ_LT(ts.ts_val, cur_stream->rcvvar->ts_recent)) {
			/* TODO: ts_recent should be invalidated 
					 before timestamp wraparound for long idle flow */
			TRACE_DBG("PAWS Detect wrong timestamp. "
					"seq: %u, ts_val: %u, prev: %u\n", 
					seq, ts.ts_val, cur_stream->rcvvar->ts_recent);
			EnqueueACK(mtcp, cur_stream, cur_ts, ACK_OPT_NOW);
			return FALSE;
		} else {
			/* valid timestamp */
			if (TCP_SEQ_GT(ts.ts_val, cur_stream->rcvvar->ts_recent)) {
				TRACE_TSTAMP("Timestamp update. cur: %u, prior: %u "
					"(time diff: %uus)\n", 
					ts.ts_val, cur_stream->rcvvar->ts_recent, 
					TS_TO_USEC(cur_ts - cur_stream->rcvvar->ts_last_ts_upd));
				cur_stream->rcvvar->ts_last_ts_upd = cur_ts;
			}

			cur_stream->rcvvar->ts_recent = ts.ts_val;
			cur_stream->rcvvar->ts_lastack_rcvd = ts.ts_ref;
		}
	}

	/* TCP sequence validation */
	if (!TCP_SEQ_BETWEEN(seq + payloadlen, cur_stream->rcv_nxt, 
				cur_stream->rcv_nxt + cur_stream->rcvvar->rcv_wnd)) {

		/* if RST bit is set, ignore the segment */
		if (tcph->rst)
			return FALSE;

		if (cur_stream->state == TCP_ST_ESTABLISHED) {
			/* check if it is to get window advertisement */
			if (seq + 1 == cur_stream->rcv_nxt) {
#if 0
				TRACE_DBG("Window update request. (seq: %u, rcv_wnd: %u)\n", 
						seq, cur_stream->rcvvar->rcv_wnd);
#endif
				EnqueueACK(mtcp, cur_stream, cur_ts, ACK_OPT_AGGREGATE);
				return FALSE;

			}

			if (TCP_SEQ_LEQ(seq, cur_stream->rcv_nxt)) {
				EnqueueACK(mtcp, cur_stream, cur_ts, ACK_OPT_AGGREGATE);
			} else {
				EnqueueACK(mtcp, cur_stream, cur_ts, ACK_OPT_NOW);
			}
		} else {
			if (cur_stream->state == TCP_ST_TIME_WAIT) {
				TRACE_DBG("Stream %d: tw expire update to %u\n", 
						cur_stream->id, cur_stream->rcvvar->ts_tw_expire);
				AddtoTimewaitList(mtcp, cur_stream, cur_ts);
			}
			AddtoControlList(mtcp, cur_stream, cur_ts);
		}
		return FALSE;
	}

	return TRUE;
}
Example #5
0
END_TEST

START_TEST(test_tcp_rto_tracking)
{
  struct netif netif;
  struct test_tcp_txcounters txcounters;
  struct test_tcp_counters counters;
  struct tcp_pcb* pcb;
  struct pbuf* p;
  err_t err;
  size_t i;
  u16_t sent_total = 0;
  LWIP_UNUSED_ARG(_i);

  for (i = 0; i < sizeof(tx_data); i++) {
    tx_data[i] = (u8_t)i;
  }

  /* initialize local vars */
  test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
  memset(&counters, 0, sizeof(counters));

  /* create and initialize the pcb */
  tcp_ticks = SEQNO1 - ISS;
  pcb = test_tcp_new_counters_pcb(&counters);
  EXPECT_RET(pcb != NULL);
  tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
  pcb->mss = TCP_MSS;
  /* Set congestion window large enough to send all our segments */
  pcb->cwnd = 5*TCP_MSS;

  /* send 5 mss-sized segments */
  for (i = 0; i < 5; i++) {
    err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
    EXPECT_RET(err == ERR_OK);
    sent_total += TCP_MSS;
  }
  check_seqnos(pcb->unsent, 5, seqnos);
  EXPECT(pcb->unacked == NULL);
  err = tcp_output(pcb);
  EXPECT(txcounters.num_tx_calls == 5);
  EXPECT(txcounters.num_tx_bytes == 5 * (TCP_MSS + 40U));
  memset(&txcounters, 0, sizeof(txcounters));
  /* Check all 5 are in-flight */
  EXPECT(pcb->unsent == NULL);
  check_seqnos(pcb->unacked, 5, seqnos);

  /* Force us into retransmisson timeout */
  while (!(pcb->flags & TF_RTO)) {
    test_tcp_tmr();
  }
  /* Ensure 4 remaining segments are back on unsent, ready for retransmission */
  check_seqnos(pcb->unsent, 4, &seqnos[1]);
  /* Ensure 1st segment is on unacked (already retransmitted) */
  check_seqnos(pcb->unacked, 1, seqnos);
  EXPECT(txcounters.num_tx_calls == 1);
  EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
  memset(&txcounters, 0, sizeof(txcounters));
  /* Ensure rto_end points to next byte */
  EXPECT(pcb->rto_end == seqnos[5]);
  EXPECT(pcb->rto_end == pcb->snd_nxt);
  /* Check cwnd was reset */
  EXPECT(pcb->cwnd == pcb->mss);

  /* Add another segment to send buffer which is outside of RTO */
  err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
  EXPECT_RET(err == ERR_OK);
  sent_total += TCP_MSS;
  check_seqnos(pcb->unsent, 5, &seqnos[1]);
  /* Ensure no new data was sent */
  EXPECT(txcounters.num_tx_calls == 0);
  EXPECT(txcounters.num_tx_bytes == 0);
  EXPECT(pcb->rto_end == pcb->snd_nxt);

  /* ACK first segment */
  p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK);
  test_tcp_input(p, &netif);
  /* Next two retranmissions should go out, due to cwnd in slow start */
  EXPECT(txcounters.num_tx_calls == 2);
  EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
  memset(&txcounters, 0, sizeof(txcounters));
  check_seqnos(pcb->unacked, 2, &seqnos[1]);
  check_seqnos(pcb->unsent, 3, &seqnos[3]);
  /* RTO should still be marked */
  EXPECT(pcb->flags & TF_RTO);
  /* cwnd should have only grown by 1 MSS */
  EXPECT(pcb->cwnd == (tcpwnd_size_t)(2 * pcb->mss));
  /* Ensure no new data was sent */
  EXPECT(pcb->rto_end == pcb->snd_nxt);

  /* ACK the next two segments */
  p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK);
  test_tcp_input(p, &netif);
  /* Final 2 retransmissions and 1 new data should go out */
  EXPECT(txcounters.num_tx_calls == 3);
  EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U));
  memset(&txcounters, 0, sizeof(txcounters));
  check_seqnos(pcb->unacked, 3, &seqnos[3]);
  EXPECT(pcb->unsent == NULL);
  /* RTO should still be marked */
  EXPECT(pcb->flags & TF_RTO);
  /* cwnd should have only grown by 1 MSS */
  EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss));
  /* snd_nxt should have been advanced past rto_end */
  EXPECT(TCP_SEQ_GT(pcb->snd_nxt, pcb->rto_end));

  /* ACK the next two segments, finishing our RTO, leaving new segment unacked */
  p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK);
  test_tcp_input(p, &netif);
  EXPECT(!(pcb->flags & TF_RTO));
  check_seqnos(pcb->unacked, 1, &seqnos[5]);
  /* We should be in ABC congestion avoidance, so no change in cwnd */
  EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss));
  EXPECT(pcb->cwnd >= pcb->ssthresh);
  /* Ensure ABC congestion avoidance is tracking bytes acked */
  EXPECT(pcb->bytes_acked == (tcpwnd_size_t)(2 * pcb->mss));

  /* make sure the pcb is freed */
  EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
  tcp_abort(pcb);
  EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}