コード例 #1
0
ファイル: tcp.c プロジェクト: sdwuyawen/lwip_V1.3.2
/**
 * Connects to another host. The function given as the "connected"
 * argument will be called when the connection has been established.
 *
 * @param pcb the tcp_pcb used to establish the connection
 * @param ipaddr the remote ip address to connect to
 * @param port the remote tcp port to connect to
 * @param connected callback function to call when connected (or on error)
 * @return ERR_VAL if invalid arguments are given
 *         ERR_OK if connect request has been sent
 *         other err_t values if connect request couldn't be sent
 */
err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
      err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
{
  err_t ret;
  u32_t iss;

  LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);

  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
	/* 远端IP地址有效,则记录到TCP控制块中 */
  if (ipaddr != NULL) {
    pcb->remote_ip = *ipaddr;
  } else {
    return ERR_VAL;
  }
	/* 记录远端PORT */
  pcb->remote_port = port;
	/* 如果本地端口未绑定,则绑定一个短暂端口 */
  if (pcb->local_port == 0) {
    pcb->local_port = tcp_new_port();
  }
	/* 获取初始序号 */
  iss = tcp_next_iss();
	/* 设置接收窗口下一个接收的序号 */
  pcb->rcv_nxt = 0;
	/* 设置发送窗口各个字段 */
  pcb->snd_nxt = iss;
  pcb->lastack = iss - 1;
  pcb->snd_lbb = iss - 1;
	/* 设置接收窗口各个字段 */
  pcb->rcv_wnd = TCP_WND;
  pcb->rcv_ann_wnd = TCP_WND;
  pcb->rcv_ann_right_edge = pcb->rcv_nxt;
  pcb->snd_wnd = TCP_WND;
  /* As initial send MSS, we use TCP_MSS but limit it to 536.
     The send MSS is updated when an MSS option is received. */
	/* 设置MSS */
  pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
  pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
	/* 初始化阻塞窗口 */
  pcb->cwnd = 1;
  pcb->ssthresh = pcb->mss * 10;
	/* TCP控制块状态设为SYN_SENT */
  pcb->state = SYN_SENT;
#if LWIP_CALLBACK_API
	/* 设置回调函数 */
  pcb->connected = connected;
#endif /* LWIP_CALLBACK_API */
	/* 把TCP控制块从tcp_bound_pcbs链表移除 */
  TCP_RMV(&tcp_bound_pcbs, pcb);
	/* 把TCP控制块插入tcp_active_pcbs链表 */
  TCP_REG(&tcp_active_pcbs, pcb);

  snmp_inc_tcpactiveopens();
  
	/* 构造TCP报文,长度为0,SYN置1,并包含MSS选项字段 */
  ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS
#if LWIP_TCP_TIMESTAMPS
                    | TF_SEG_OPTS_TS
#endif
                    );
  if (ret == ERR_OK) { 
		/* 发送构造的TCP报文 */
    tcp_output(pcb);
  }
  return ret;
} 
コード例 #2
0
ファイル: tcp_in.c プロジェクト: YTakami/makecontroller
/**
 * Called by tcp_input() when a segment arrives for a listening
 * connection (from tcp_input()).
 *
 * @param pcb the tcp_pcb_listen for which a segment arrived
 * @return ERR_OK if the segment was processed
 *         another err_t on error
 *
 * @note the return value is not (yet?) used in tcp_input()
 * @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_listen_input(struct tcp_pcb_listen *pcb)
{
  struct tcp_pcb *npcb;
  u32_t optdata;

  /* In the LISTEN state, we check for incoming SYN segments,
     creates a new PCB, and responds with a SYN|ACK. */
  if (flags & TCP_ACK) {
    /* For incoming segments with the ACK flag set, respond with a
       RST. */
    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
    tcp_rst(ackno + 1, seqno + tcplen,
      &(iphdr->dest), &(iphdr->src),
      tcphdr->dest, tcphdr->src);
  } else if (flags & TCP_SYN) {
    LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
#if TCP_LISTEN_BACKLOG
    if (pcb->accepts_pending >= pcb->backlog) {
      return ERR_ABRT;
    }
#endif /* TCP_LISTEN_BACKLOG */
    npcb = tcp_alloc(pcb->prio);
    /* If a new PCB could not be created (probably due to lack of memory),
       we don't do anything, but rely on the sender will retransmit the
       SYN at a time when we have more memory available. */
    if (npcb == NULL) {
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
      TCP_STATS_INC(tcp.memerr);
      return ERR_MEM;
    }
#if TCP_LISTEN_BACKLOG
    pcb->accepts_pending++;
#endif /* TCP_LISTEN_BACKLOG */
    /* Set up the new PCB. */
    ip_addr_set(&(npcb->local_ip), &(iphdr->dest));
    npcb->local_port = pcb->local_port;
    ip_addr_set(&(npcb->remote_ip), &(iphdr->src));
    npcb->remote_port = tcphdr->src;
    npcb->state = SYN_RCVD;
    npcb->rcv_nxt = seqno + 1;
    npcb->snd_wnd = tcphdr->wnd;
    npcb->ssthresh = npcb->snd_wnd;
    npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
    npcb->callback_arg = pcb->callback_arg;
#if LWIP_CALLBACK_API
    npcb->accept = pcb->accept;
#endif /* LWIP_CALLBACK_API */
    /* inherit socket options */
    npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER);
    /* Register the new PCB so that we can begin receiving segments
       for it. */
    TCP_REG(&tcp_active_pcbs, npcb);

    /* Parse any options in the SYN. */
    tcp_parseopt(npcb);
#if TCP_CALCULATE_EFF_SEND_MSS
    npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */

    snmp_inc_tcppassiveopens();

    /* Build an MSS option. */
    optdata = TCP_BUILD_MSS_OPTION();
    /* Send a SYN|ACK together with the MSS option. */
    tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4);
    return tcp_output(npcb);
  }
  return ERR_OK;
}
コード例 #3
0
ファイル: tcp_in.c プロジェクト: YTakami/makecontroller
/**
 * Implements the TCP state machine. Called by tcp_input. In some
 * states tcp_receive() is called to receive data. The tcp_seg
 * argument will be freed by the caller (tcp_input()) unless the
 * recv_data pointer in the pcb is set.
 *
 * @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_process(struct tcp_pcb *pcb)
{
  struct tcp_seg *rseg;
  u8_t acceptable = 0;
  err_t err;
  u8_t accepted_inseq;

  err = ERR_OK;

  /* Process incoming RST segments. */
  if (flags & TCP_RST) {
    /* First, determine if the reset is acceptable. */
    if (pcb->state == SYN_SENT) {
      if (ackno == pcb->snd_nxt) {
        acceptable = 1;
      }
    } else {
      if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, 
                          pcb->rcv_nxt+pcb->rcv_ann_wnd)) {
        acceptable = 1;
      }
    }

    if (acceptable) {
      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
      LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
      recv_flags = TF_RESET;
      pcb->flags &= ~TF_ACK_DELAY;
      return ERR_RST;
    } else {
      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
       seqno, pcb->rcv_nxt));
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
       seqno, pcb->rcv_nxt));
      return ERR_OK;
    }
  }

  /* Update the PCB (in)activity timer. */
  pcb->tmr = tcp_ticks;
  pcb->keep_cnt_sent = 0;

  /* Do different things depending on the TCP state. */
  switch (pcb->state) {
  case SYN_SENT:
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
    /* received SYN ACK with expected sequence number? */
    if ((flags & TCP_ACK) && (flags & TCP_SYN)
        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
      pcb->snd_buf++;
      pcb->rcv_nxt = seqno + 1;
      pcb->lastack = ackno;
      pcb->snd_wnd = tcphdr->wnd;
      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
      pcb->state = ESTABLISHED;

      /* Parse any options in the SYNACK before using pcb->mss since that
       * can be changed by the received options! */
      tcp_parseopt(pcb);
#if TCP_CALCULATE_EFF_SEND_MSS
      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */

      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
       * but for the default value of pcb->mss) */
      pcb->ssthresh = pcb->mss * 10;

      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
      --pcb->snd_queuelen;
      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
      rseg = pcb->unacked;
      pcb->unacked = rseg->next;

      /* If there's nothing left to acknowledge, stop the retransmit
         timer, otherwise reset it to start again */
      if(pcb->unacked == NULL)
        pcb->rtime = -1;
      else {
        pcb->rtime = 0;
        pcb->nrtx = 0;
      }

      tcp_seg_free(rseg);

      /* Call the user specified function to call when sucessfully
       * connected. */
      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
      tcp_ack_now(pcb);
    }
    /* received ACK? possibly a half-open connection */
    else if (flags & TCP_ACK) {
      /* send a RST to bring the other side in a non-synchronized state. */
      tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
        tcphdr->dest, tcphdr->src);
    }
    break;
  case SYN_RCVD:
    if (flags & TCP_ACK &&
       !(flags & TCP_RST)) {
      /* expected ACK number? */
      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
        u16_t old_cwnd;
        pcb->state = ESTABLISHED;
        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
#if LWIP_CALLBACK_API
        LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
#endif
        /* Call the accept function. */
        TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
        if (err != ERR_OK) {
          /* If the accept function returns with an error, we abort
           * the connection. */
          tcp_abort(pcb);
          return ERR_ABRT;
        }
        old_cwnd = pcb->cwnd;
        /* If there was any data contained within this ACK,
         * we'd better pass it on to the application as well. */
        accepted_inseq = tcp_receive(pcb);

        pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);

        if ((flags & TCP_FIN) && accepted_inseq) {
          tcp_ack_now(pcb);
          pcb->state = CLOSE_WAIT;
        }
      }
      /* incorrect ACK number */
      else {
        /* send RST */
        tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
                tcphdr->dest, tcphdr->src);
      }
    }
    break;
  case CLOSE_WAIT:
    /* FALLTHROUGH */
  case ESTABLISHED:
    accepted_inseq = tcp_receive(pcb);
    if ((flags & TCP_FIN) && accepted_inseq) { /* passive close */
      tcp_ack_now(pcb);
      pcb->state = CLOSE_WAIT;
    }
    break;
  case FIN_WAIT_1:
    tcp_receive(pcb);
    if (flags & TCP_FIN) {
      if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
        LWIP_DEBUGF(TCP_DEBUG,
          ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
        tcp_ack_now(pcb);
        tcp_pcb_purge(pcb);
        TCP_RMV(&tcp_active_pcbs, pcb);
        pcb->state = TIME_WAIT;
        TCP_REG(&tcp_tw_pcbs, pcb);
      } else {
        tcp_ack_now(pcb);
        pcb->state = CLOSING;
      }
    } else if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      pcb->state = FIN_WAIT_2;
    }
    break;
  case FIN_WAIT_2:
    tcp_receive(pcb);
    if (flags & TCP_FIN) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      tcp_ack_now(pcb);
      tcp_pcb_purge(pcb);
      TCP_RMV(&tcp_active_pcbs, pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);
    }
    break;
  case CLOSING:
    tcp_receive(pcb);
    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      tcp_ack_now(pcb);
      tcp_pcb_purge(pcb);
      TCP_RMV(&tcp_active_pcbs, pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);
    }
    break;
  case LAST_ACK:
    tcp_receive(pcb);
    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
      recv_flags = TF_CLOSED;
    }
    break;
  default:
    break;
  }
  return ERR_OK;
}
コード例 #4
0
ファイル: tcp.c プロジェクト: malooei/yeejoin-workspace
/**
 * Connects to another host. The function given as the "connected"
 * argument will be called when the connection has been established.
 *
 * @param pcb the tcp_pcb used to establish the connection
 * @param ipaddr the remote ip address to connect to
 * @param port the remote tcp port to connect to
 * @param connected callback function to call when connected (or on error)
 * @return ERR_VAL if invalid arguments are given
 *         ERR_OK if connect request has been sent
 *         other err_t values if connect request couldn't be sent
 */
err_t
tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
			tcp_connected_fn connected)
{
	err_t ret;
	u32_t iss;
	u16_t old_local_port;

	LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);

	LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
	if (ipaddr != NULL) {
		pcb->remote_ip = *ipaddr;
	} else {
		return ERR_VAL;
	}
	pcb->remote_port = port;

	/* check if we have a route to the remote host */
	if (ip_addr_isany(&(pcb->local_ip))) {
		/* no local IP address set, yet. */
		struct netif *netif = ip_route(&(pcb->remote_ip));
		if (netif == NULL) {
			/* Don't even try to send a SYN packet if we have no route
			   since that will fail. */
			return ERR_RTE;
		}
		/* Use the netif's IP address as local address. */
		ip_addr_copy(pcb->local_ip, netif->ip_addr);
	}

	old_local_port = pcb->local_port;
	if (pcb->local_port == 0) {
		pcb->local_port = tcp_new_port();
	}
#if SO_REUSE
	if ((pcb->so_options & SOF_REUSEADDR) != 0) {
		/* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
		   now that the 5-tuple is unique. */
		struct tcp_pcb *cpcb;
		int i;
		/* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
		for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
			for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
				if ((cpcb->local_port == pcb->local_port) &&
					(cpcb->remote_port == port) &&
					ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
					ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
					/* linux returns EISCONN here, but ERR_USE should be OK for us */
					return ERR_USE;
				}
			}
		}
	}
#endif /* SO_REUSE */
	iss = tcp_next_iss();
	pcb->rcv_nxt = 0;
	pcb->snd_nxt = iss;
	pcb->lastack = iss - 1;
	pcb->snd_lbb = iss - 1;
	pcb->rcv_wnd = TCP_WND;
	pcb->rcv_ann_wnd = TCP_WND;
	pcb->rcv_ann_right_edge = pcb->rcv_nxt;
	pcb->snd_wnd = TCP_WND;
	/* As initial send MSS, we use TCP_MSS but limit it to 536.
	   The send MSS is updated when an MSS option is received. */
	pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
	pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
	pcb->cwnd = 1;
	pcb->ssthresh = pcb->mss * 10;
#if LWIP_CALLBACK_API
	pcb->connected = connected;
#else /* LWIP_CALLBACK_API */
	LWIP_UNUSED_ARG(connected);
#endif /* LWIP_CALLBACK_API */

	/* Send a SYN together with the MSS option. */
	ret = tcp_enqueue_flags(pcb, TCP_SYN);
	if (ret == ERR_OK) {
		/* SYN segment was enqueued, changed the pcbs state now */
		pcb->state = SYN_SENT;
		if (old_local_port != 0) {
			TCP_RMV(&tcp_bound_pcbs, pcb);
		}
		TCP_REG(&tcp_active_pcbs, pcb);
		snmp_inc_tcpactiveopens();

		tcp_output(pcb);
	}
	return ret;
}