Esempio n. 1
0
/*-----------------------------------------------------------------------------------*/
void
tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg)
{
  u32_t wnd;
  u16_t len, tot_len;
  struct netif *netif;
  
  DEBUGF(TCP_REXMIT_DEBUG, ("tcp_rexmit_seg: skickar %ld:%ld\n",
			    ntohl(seg->tcphdr->seqno),
			    ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));

  wnd = MIN(pcb->snd_wnd, pcb->cwnd);
  
  if(ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {

    /* Count the number of retranmissions. */
    ++pcb->nrtx;
    
    if((netif = ip_route((struct ip_addr *)&(pcb->remote_ip))) == NULL) {
      DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_rexmit_segment: No route to 0x%lx\n", pcb->remote_ip.addr));
#ifdef TCP_STATS
      ++stats.tcp.rterr;
#endif /* TCP_STATS */
      return;
    }

    seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
    seg->tcphdr->wnd = htons(pcb->rcv_wnd);

    /* Recalculate checksum. */
    seg->tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
    seg->tcphdr->chksum = inet_chksum_pseudo(seg->p,
                                             &(pcb->local_ip),
                                             &(pcb->remote_ip),
                                             IP_PROTO_TCP, seg->p->tot_len);
#endif
    
    len = seg->p->len;
    tot_len = seg->p->tot_len;
    pbuf_header(seg->p, IP_HLEN);
    ip_output_if(seg->p, NULL, IP_HDRINCL, TCP_TTL, IP_PROTO_TCP, netif);
    seg->p->len = len;
    seg->p->tot_len = tot_len;
    seg->p->payload = seg->tcphdr;

#ifdef TCP_STATS
    ++stats.tcp.xmit;
    ++stats.tcp.rexmit;
#endif /* TCP_STATS */

    pcb->rtime = 0;
    
    /* Don't take any rtt measurements after retransmitting. */    
    pcb->rttest = 0;
  } else {
    DEBUGF(TCP_REXMIT_DEBUG, ("tcp_rexmit_seg: no room in window %lu to send %lu (ack %lu)\n",
                              wnd, ntohl(seg->tcphdr->seqno), pcb->lastack));
  }
}
Esempio n. 2
0
/*-----------------------------------------------------------------------------------*/
static void
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
{
  u16_t len, tot_len;
  struct netif *netif;

  /* The TCP header has already been constructed, but the ackno and
   wnd fields remain. */
  seg->tcphdr->ackno = htonl(pcb->rcv_nxt);

  /* silly window avoidance */
  if(pcb->rcv_wnd < pcb->mss) {
    seg->tcphdr->wnd = 0;
  } else {
    seg->tcphdr->wnd = htons(pcb->rcv_wnd);
  }

  /* If we don't have a local IP address, we get one by
     calling ip_route(). */
  if(ip_addr_isany(&(pcb->local_ip))) {
    netif = ip_route(&(pcb->remote_ip));
    if(netif == NULL) {
      return;
    }
    ip_addr_set(&(pcb->local_ip), &(netif->ip_addr));
  }

  pcb->rtime = 0;
  
  if(pcb->rttest == 0) {
    pcb->rttest = tcp_ticks;
    pcb->rtseq = ntohl(seg->tcphdr->seqno);

    DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %lu\n", pcb->rtseq));
  }
  DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %lu:%lu\n",
			    htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
			    seg->len));

  seg->tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
  seg->tcphdr->chksum = inet_chksum_pseudo(seg->p,
					   &(pcb->local_ip),
					   &(pcb->remote_ip),
					   IP_PROTO_TCP, seg->p->tot_len);
#endif

#ifdef TCP_STATS
  ++stats.tcp.xmit;
#endif /* TCP_STATS */

  len = seg->p->len;
  tot_len = seg->p->tot_len;
  ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), TCP_TTL,
	    IP_PROTO_TCP);
  seg->p->len = len;
  seg->p->tot_len = tot_len;
  seg->p->payload = seg->tcphdr;

}
void
tcp_rst(u32_t seqno, u32_t ackno,
  struct ip_addr *local_ip, struct ip_addr *remote_ip,
  u16_t local_port, u16_t remote_port)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
  if (p == NULL) {
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
      return;
  }

  tcphdr = p->payload;
  tcphdr->src = htons(local_port);
  tcphdr->dest = htons(remote_port);
  tcphdr->seqno = htonl(seqno);
  tcphdr->ackno = htonl(ackno);
  TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK);
  tcphdr->wnd = htons(TCP_WND);
  tcphdr->urgp = 0;
  TCPH_HDRLEN_SET(tcphdr, 5);

  tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
  tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
              IP_PROTO_TCP, p->tot_len);
#endif
  TCP_STATS_INC(tcp.xmit);
  snmp_inc_tcpoutrsts();
   /* Send output with hardcoded TTL since we have no access to the pcb */
  ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
  pbuf_free(p);
  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
}
Esempio n. 4
0
/**
 * Actually send a TCP segment over IP
 */
static void
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
{
  u16_t len;
  struct netif *netif;

  /* The TCP header has already been constructed, but the ackno and
   wnd fields remain. */
  seg->tcphdr->ackno = htonl(pcb->rcv_nxt);

  /* silly window avoidance */
  if (pcb->rcv_wnd < pcb->mss) {
    seg->tcphdr->wnd = 0;
  } else {
    /* advertise our receive window size in this TCP segment */
    seg->tcphdr->wnd = htons(pcb->rcv_wnd);
  }

  /* If we don't have a local IP address, we get one by
     calling ip_route(). */
  if (ip_addr_isany(&(pcb->local_ip))) {
    netif = ip_route(&(pcb->remote_ip));
    if (netif == NULL) {
      return;
    }
    ip_addr_set(&(pcb->local_ip), &(netif->ip_addr));
  }

  pcb->rtime = 0;

  if (pcb->rttest == 0) {
    pcb->rttest = tcp_ticks;
    pcb->rtseq = ntohl(seg->tcphdr->seqno);

    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
  }
  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
          htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
          seg->len));

  len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);

  seg->p->len -= len;
  seg->p->tot_len -= len;

  seg->p->payload = seg->tcphdr;

  seg->tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
  seg->tcphdr->chksum = inet_chksum_pseudo(seg->p,
             &(pcb->local_ip),
             &(pcb->remote_ip),
             IP_PROTO_TCP, seg->p->tot_len);
#endif
  TCP_STATS_INC(tcp.xmit);

  ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
      IP_PROTO_TCP);
}
Esempio n. 5
0
File: udp.c Progetto: HarryR/sanos
err_t udp_send(struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, unsigned short dst_port, struct netif *netif) {
  struct udp_hdr *udphdr;
  struct ip_addr *src_ip;
  err_t err;

  if (!dst_ip) dst_ip = &pcb->remote_ip;
  if (ip_addr_isany(dst_ip)) return -EDESTADDRREQ;

  if (dst_port == 0) dst_port = pcb->remote_port;
  if (dst_port == 0) return -ENOTCONN;

  if (pbuf_header(p, UDP_HLEN) < 0) {
    kprintf(KERN_ERR "udp_send: not enough room for UDP header in pbuf\n");
    stats.udp.err++;
    return -EBUF;
  }

  udphdr = p->payload;
  udphdr->src = htons(pcb->local_port);
  udphdr->dest = htons(dst_port);
  udphdr->chksum = 0x0000;

  if (netif == NULL) {
    if ((netif = ip_route(dst_ip)) == NULL) {
      kprintf(KERN_ERR "udp_send: No route to %a\n", dst_ip);
      stats.udp.rterr++;
      return -EROUTE;
    }
  }

  if (ip_addr_isbroadcast(dst_ip, &netif->netmask) && (pcb->flags & UDP_FLAGS_BROADCAST) == 0) return -EACCES;

  if (ip_addr_isany(&pcb->local_ip)) {
    src_ip = &netif->ipaddr;
  } else {
    src_ip = &pcb->local_ip;
  }

  //kprintf("udp_send: sending datagram of length %d\n", p->tot_len);
  
  udphdr->len = htons((unsigned short) p->tot_len);
  
  // Calculate checksum
  if ((netif->flags & NETIF_UDP_TX_CHECKSUM_OFFLOAD) == 0) {
    if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
      udphdr->chksum = inet_chksum_pseudo(p, src_ip, dst_ip, IP_PROTO_UDP, p->tot_len);
      if (udphdr->chksum == 0x0000) udphdr->chksum = 0xFFFF;
    }
  }

  //udp_debug_print(udphdr);
  err = ip_output_if(p, src_ip, dst_ip, UDP_TTL, IP_PROTO_UDP, netif);
  
  stats.udp.xmit++;

  return err;
}
Esempio n. 6
0
/* 
 * Validate a TCP segment's checksum,return TRUE if pass the validation,
 * FALSE will be returned otherwise.
 */
static BOOL validateTCPChksum(struct ip_hdr* p, struct pbuf* pb)
{
	struct tcp_hdr* pTcpHdr = NULL;
	int iph_len = IPH_HL(p);
	int ip_len = ntohs(IPH_LEN(p)); /* Total IP packet length. */
	int chksum_old = 0, chksum_new = 0;
	ip_addr_t src, dest;

	iph_len *= 4;
	/* Locate TCP header. */
	pTcpHdr = (struct tcp_hdr*)((char*)p + iph_len);

	/* Validate pbuf object. */
	if (pb->tot_len < (iph_len + sizeof(struct tcp_hdr)))
	{
		return FALSE;
	}

	/* Validate the length of this packet. */
	if (ip_len < (iph_len + sizeof(struct tcp_hdr)))
	{
		return FALSE;
	}

	/*
	* Preserve TCP segment's check sum value before re-calculate
	* the value.
	*/
	chksum_old = pTcpHdr->chksum;
	ip_addr_copy(src, p->src);
	ip_addr_copy(dest, p->dest);
	pbuf_header(pb, -iph_len); /* move to TCP header. */
	pTcpHdr->chksum = 0; /* Reset the original check sum. */
	/* Recalculate the segment's checksum value. */
	chksum_new = inet_chksum_pseudo(pb, &src, &dest, IP_PROTO_TCP,
		/* IP payload length,e.i,TCP header plus data. */
		ip_len - iph_len);
		//pb->tot_len);
	pbuf_header(pb, iph_len); /* move back. */
	if (chksum_new != chksum_old)
	{
		__LOG("TCP checksum fail:ip_len = %d,pbuf_tot_len = %d,src_addr:%s\r\n",
			ip_len,
			pb->tot_len,
			inet_ntoa(p->src.addr));
		IP_STATS_INC(tcp.chkerr);
		IP_STATS_INC(tcp.drop);
		return FALSE;
	}
	return TRUE;
}
Esempio n. 7
0
/*-----------------------------------------------------------------------------------*/
void
tcp_rst(u32_t seqno, u32_t ackno,
	struct ip_addr *local_ip, struct ip_addr *remote_ip,
	u16_t local_port, u16_t remote_port)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM);
  if(p == NULL) {
#if MEM_RECLAIM
    mem_reclaim(sizeof(struct pbuf));
    p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM);
#endif /* MEM_RECLAIM */    
    if(p == NULL) {
      DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
      return;
    }
  }
  if(pbuf_header(p, TCP_HLEN)) {
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_send_data: no room for TCP header in pbuf.\n"));

#ifdef TCP_STATS
    ++stats.tcp.err;
#endif /* TCP_STATS */
    return;
  }

  tcphdr = p->payload;
  tcphdr->src = htons(local_port);
  tcphdr->dest = htons(remote_port);
  tcphdr->seqno = htonl(seqno);
  tcphdr->ackno = htonl(ackno);
  TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK);
  tcphdr->wnd = 0;
  tcphdr->urgp = 0;
  TCPH_OFFSET_SET(tcphdr, 5 << 4);
  
  tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
  tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
				      IP_PROTO_TCP, p->tot_len);
#endif

#ifdef TCP_STATS
  ++stats.tcp.xmit;
#endif /* TCP_STATS */
  ip_output(p, local_ip, remote_ip, TCP_TTL, IP_PROTO_TCP);
  pbuf_free(p);
  DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %lu ackno %lu.\n", seqno, ackno));
}
Esempio n. 8
0
/** Create a TCP segment usable for passing to tcp_input */
struct pbuf*
tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
                   u16_t src_port, u16_t dst_port, void* data, size_t data_len,
                   u32_t seqno, u32_t ackno, u8_t headerflags)
{
  struct pbuf* p;
  struct ip_hdr* iphdr;
  struct tcp_hdr* tcphdr;
  u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);

  p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
  EXPECT_RETNULL(p != NULL);
  EXPECT_RETNULL(p->next == NULL);

  memset(p->payload, 0, p->len);

  iphdr = p->payload;
  /* fill IP header */
  iphdr->dest.addr = dst_ip->addr;
  iphdr->src.addr = src_ip->addr;
  IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, 0);
  IPH_LEN_SET(iphdr, htons(p->tot_len));
  IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));

  pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));

  tcphdr = p->payload;
  tcphdr->src   = htons(src_port);
  tcphdr->dest  = htons(dst_port);
  tcphdr->seqno = htonl(seqno);
  tcphdr->ackno = htonl(ackno);
  TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4);
  TCPH_FLAGS_SET(tcphdr, headerflags);
  tcphdr->wnd   = htons(TCP_WND);

  /* copy data */
  memcpy((char*)tcphdr + sizeof(struct tcp_hdr), data, data_len);

  /* calculate checksum */

  tcphdr->chksum = inet_chksum_pseudo(p, src_ip, dst_ip,
          IP_PROTO_TCP, p->tot_len);

  pbuf_header(p, sizeof(struct ip_hdr));

  return p;
}
Esempio n. 9
0
/**
 * Send keepalive packets to keep a connection active although
 * no data is sent over it.
 *
 * Called by tcp_slowtmr()
 *
 * @param pcb the tcp_pcb for which to send a keepalive packet
 */
void
tcp_keepalive(struct tcp_pcb *pcb)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;

  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
                          ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
                          ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));

  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
                          tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));

  p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
  if(p == NULL) {
    LWIP_DEBUGF(TCP_DEBUG,
                ("tcp_keepalive: could not allocate memory for pbuf\n"));
    return;
  }
  tcphdr = (struct tcp_hdr *)p->payload;

#if CHECKSUM_GEN_TCP
  tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
                                      IP_PROTO_TCP, (u16_t)p->tot_len);
#endif
  TCP_STATS_INC(tcp.xmit);

  /* Send output to IP */
#if LWIP_NETIF_HWADDRHINT
  ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
    &(pcb->addr_hint));
#elif LWIP_3RD_PARTY_L3
  pcb->ip_output(p, pcb, 0);
#else /* LWIP_NETIF_HWADDRHINT*/
  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/

  tcp_tx_pbuf_free(pcb, p);

  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
                          pcb->snd_nxt - 1, pcb->rcv_nxt));
  (void)tcphdr; /* Fix warning -Wunused-but-set-variable*/
}
Esempio n. 10
0
/* Update TCP segment's check sum. */
static void _tcp_check_sum(struct ip_hdr* p, struct pbuf* pb)
{
	struct tcp_hdr* pTcpHdr = NULL;
	int iph_len = IPH_HL(p);
	int ip_len = ntohs(IPH_LEN(p)); /* Total IP packet's length. */
	int chksum = 0;
	ip_addr_t src, dest;

	iph_len *= 4;
	/* Locate TCP header. */
	pTcpHdr = (struct tcp_hdr*)((char*)p + iph_len);

	/* Validate pbuf object. */
	if (pb->tot_len < (iph_len + sizeof(struct tcp_hdr)))
	{
		return;
	}

	/* Validate IP packet's total length. */
	if (ip_len < (iph_len + sizeof(struct tcp_hdr)))
	{
		return;
	}

	/*
	* Reset TCP header's check sum since it's source address
	* or source port is changed after NATing.
	*/
	chksum = pTcpHdr->chksum;
	ip_addr_copy(src, p->src);
	ip_addr_copy(dest, p->dest);
	pbuf_header(pb, -iph_len); /* move to TCP header. */
	pTcpHdr->chksum = 0; /* Reset the original check sum. */
	pTcpHdr->chksum = inet_chksum_pseudo(pb, &src, &dest, IP_PROTO_TCP,
		ip_len - iph_len);
	pbuf_header(pb, iph_len); /* move back. */
	__NATDEBUG("%s: TCP check sum updated[%X] -> [%X]\r\n",
		__func__,
		chksum,
		pTcpHdr->chksum);
}
void
tcp_keepalive(struct tcp_pcb *pcb)
{
   struct pbuf *p;
   struct tcp_hdr *tcphdr;

   LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
                           ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
                           ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));

   LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F"  pcb->keep_cnt %"U16_F"\n", tcp_ticks, pcb->tmr, pcb->keep_cnt));
   
   p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);

   if(p == NULL) {
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: could not allocate memory for pbuf\n"));
      return;
   }

   tcphdr = p->payload;
   tcphdr->src = htons(pcb->local_port);
   tcphdr->dest = htons(pcb->remote_port);
   tcphdr->seqno = htonl(pcb->snd_nxt - 1);
   tcphdr->ackno = htonl(pcb->rcv_nxt);
   tcphdr->wnd = htons(pcb->rcv_wnd);
   tcphdr->urgp = 0;
   TCPH_HDRLEN_SET(tcphdr, 5);
   
   tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
   tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, IP_PROTO_TCP, p->tot_len);
#endif
  TCP_STATS_INC(tcp.xmit);

   /* Send output to IP */
  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);

  pbuf_free(p);

  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", pcb->snd_nxt - 1, pcb->rcv_nxt));
}
Esempio n. 12
0
void
icmp_input(struct pbuf *p, struct netif *inp)
{
    u8_t type;
    struct icmp_echo_hdr *iecho;
    struct ip_hdr *iphdr;
    struct ip_addr tmpaddr;

    ICMP_STATS_INC(icmp.recv);

    /* TODO: check length before accessing payload! */

    type = ((u8_t *)p->payload)[0];

    switch (type) {
        case ICMP6_ECHO:
            LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));

            if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
                LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));

                pbuf_free(p);
                ICMP_STATS_INC(icmp.lenerr);
                return;
            }
            iecho = p->payload;
            iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN);
            if (inet_chksum_pbuf(p) != 0) {
                LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
                ICMP_STATS_INC(icmp.chkerr);
                /*      return;*/
            }
            LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len));
            ip_addr_set(&tmpaddr, &(iphdr->src));
            ip_addr_set(&(iphdr->src), &(iphdr->dest));
            ip_addr_set(&(iphdr->dest), &tmpaddr);
            iecho->type = ICMP6_ER;
            /* adjust the checksum */
            if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) {
                iecho->chksum += htons(ICMP6_ECHO << 8) + 1;
            } else {
                iecho->chksum += htons(ICMP6_ECHO << 8);
            }
            LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
            ICMP_STATS_INC(icmp.xmit);

            /*    LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/
            ip_output_if (p, &(iphdr->src), IP_HDRINCL,
                          iphdr->hoplim, IP_PROTO_ICMP, inp);
            break;
        default:
            LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type));
            ICMP_STATS_INC(icmp.proterr);
            ICMP_STATS_INC(icmp.drop);
    }

    pbuf_free(p);
}
Esempio n. 13
0
/**
 * Send data to a specified address using UDP.
 * The netif used for sending can be specified.
 *
 * This function exists mainly for DHCP, to be able to send UDP packets
 * on a netif that is still down.
 *
 * @param pcb UDP PCB used to send the data.
 * @param p chain of pbuf's to be sent.
 * @param dst_ip Destination IP address.
 * @param dst_port Destination UDP port.
 * @param netif the netif used for sending.
 *
 * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
 *
 * @return lwIP error code (@see udp_send for possible error codes)
 *
 * @see udp_disconnect() udp_send()
 */
err_t
udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
  struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif)
{
  struct udp_hdr *udphdr;
  struct ip_addr *src_ip;
  err_t err;
  struct pbuf *q; /* q will be sent down the stack */

#if IP_SOF_BROADCAST
  /* broadcast filter? */
  if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) {
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
      ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
    return ERR_VAL;
  }
#endif /* IP_SOF_BROADCAST */

  /* if the PCB is not yet bound to a port, bind it here */
  if (pcb->local_port == 0) {
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
    err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
    if (err != ERR_OK) {
      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
      return err;
    }
  }

  /* not enough space to add an UDP header to first pbuf in given p chain? */
  if (pbuf_header(p, UDP_HLEN)) {
    /* allocate header in a separate new pbuf */
    q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
    /* new header pbuf could not be allocated? */
    if (q == NULL) {
      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
      return ERR_MEM;
    }
    /* chain header q in front of given pbuf p */
    pbuf_chain(q, p);
    /* first pbuf q points to header pbuf */
    LWIP_DEBUGF(UDP_DEBUG,
                ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
  } else {
    /* adding space for header within p succeeded */
    /* first pbuf q equals given pbuf */
    q = p;
    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
  }
  LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
              (q->len >= sizeof(struct udp_hdr)));
  /* q now represents the packet to be sent */
  udphdr = q->payload;
  udphdr->src = htons(pcb->local_port);
  udphdr->dest = htons(dst_port);
  /* in UDP, 0 checksum means 'no checksum' */
  udphdr->chksum = 0x0000; 

  /* PCB local address is IP_ANY_ADDR? */
  if (ip_addr_isany(&pcb->local_ip)) {
    /* use outgoing network interface IP address as source address */
    src_ip = &(netif->ip_addr);
  } else {
    /* check if UDP PCB local IP address is correct
     * this could be an old address if netif->ip_addr has changed */
    if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) {
      /* local_ip doesn't match, drop the packet */
      if (q != p) {
        /* free the header pbuf */
        pbuf_free(q);
        q = NULL;
        /* p is still referenced by the caller, and will live on */
      }
      return ERR_VAL;
    }
    /* use UDP PCB local IP address as source address */
    src_ip = &(pcb->local_ip);
  }

  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));

#if LWIP_UDPLITE
  /* UDP Lite protocol? */
  if (pcb->flags & UDP_FLAGS_UDPLITE) {
    u16_t chklen, chklen_hdr;
    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
    /* set UDP message length in UDP header */
    chklen_hdr = chklen = pcb->chksum_len_tx;
    if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
      if (chklen != 0) {
        LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
      }
      /* For UDP-Lite, checksum length of 0 means checksum
         over the complete packet. (See RFC 3828 chap. 3.1)
         At least the UDP-Lite header must be covered by the
         checksum, therefore, if chksum_len has an illegal
         value, we generate the checksum over the complete
         packet to be safe. */
      chklen_hdr = 0;
      chklen = q->tot_len;
    }
    udphdr->len = htons(chklen_hdr);
    /* calculate checksum */
#if CHECKSUM_GEN_UDP
    udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip,
                                        IP_PROTO_UDPLITE, q->tot_len, chklen);
    /* chksum zero must become 0xffff, as zero means 'no checksum' */
    if (udphdr->chksum == 0x0000)
      udphdr->chksum = 0xffff;
#endif /* CHECKSUM_CHECK_UDP */
    /* output to IP */
    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n"));
#if LWIP_NETIF_HWADDRHINT
    netif->addr_hint = &(pcb->addr_hint);
#endif /* LWIP_NETIF_HWADDRHINT*/
    err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif);
#if LWIP_NETIF_HWADDRHINT
    netif->addr_hint = NULL;
#endif /* LWIP_NETIF_HWADDRHINT*/
  } else
#endif /* LWIP_UDPLITE */
  {      /* UDP */
    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
    udphdr->len = htons(q->tot_len);
    /* calculate checksum */
#if CHECKSUM_GEN_UDP
    if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
      udphdr->chksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len);
      /* chksum zero must become 0xffff, as zero means 'no checksum' */
      if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff;
    }
#endif /* CHECKSUM_CHECK_UDP */
    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n"));
    /* output to IP */
#if LWIP_NETIF_HWADDRHINT
    netif->addr_hint = &(pcb->addr_hint);
#endif /* LWIP_NETIF_HWADDRHINT*/
    err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);
#if LWIP_NETIF_HWADDRHINT
    netif->addr_hint = NULL;
#endif /* LWIP_NETIF_HWADDRHINT*/
  }
  /* TODO: must this be increased even if error occured? */
  snmp_inc_udpoutdatagrams();

  /* did we chain a separate header pbuf earlier? */
  if (q != p) {
    /* free the header pbuf */
    pbuf_free(q);
    q = NULL;
    /* p is still referenced by the caller, and will live on */
  }

  UDP_STATS_INC(udp.xmit);
  return err;
}
Esempio n. 14
0
/**
 * Process an incoming UDP datagram.
 *
 * Given an incoming UDP datagram (as a chain of pbufs) this function
 * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
 * recv function. If no pcb is found or the datagram is incorrect, the
 * pbuf is freed.
 *
 * @param p pbuf to be demultiplexed to a UDP PCB.
 * @param inp network interface on which the datagram was received.
 *
 */
void
udp_input(struct pbuf *p, struct netif *inp)
{
  struct udp_hdr *udphdr;
  struct udp_pcb *pcb, *prev;
  struct udp_pcb *uncon_pcb;
  struct ip_hdr *iphdr;
  u16_t src, dest;
  u8_t local_match;

  PERF_START;

  UDP_STATS_INC(udp.recv);

  iphdr = (struct ip_hdr *)(p->payload);

  /* Check minimum length (IP header + UDP header)
   * and move payload pointer to UDP header */
  if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) {
    /* drop short packets */
    LWIP_DEBUGF(UDP_DEBUG,
                ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
    UDP_STATS_INC(udp.lenerr);
    UDP_STATS_INC(udp.drop);
    snmp_inc_udpinerrors();
    pbuf_free(p);
    goto end;
  }

  udphdr = (struct udp_hdr *)p->payload;

  LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));

  /* convert src and dest ports to host byte order */
  src = ntohs(udphdr->src);
  dest = ntohs(udphdr->dest);

  udp_debug_print(udphdr);

  /* print the UDP source and destination */
  LWIP_DEBUGF(UDP_DEBUG,
              ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- "
               "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
               ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest),
               ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest),
               ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src),
               ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src)));

#if LWIP_DHCP
  pcb = NULL;
  /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
     the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
  if (dest == DHCP_CLIENT_PORT) {
    /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
    if (src == DHCP_SERVER_PORT) {
      if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
        /* accept the packe if 
           (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
           - inp->dhcp->pcb->remote == ANY or iphdr->src */
        if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) ||
           ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &(iphdr->src)))) {
          pcb = inp->dhcp->pcb;
        }
      }
    }
  } else
#endif /* LWIP_DHCP */
  {
    prev = NULL;
    local_match = 0;
    uncon_pcb = NULL;
    /* Iterate through the UDP pcb list for a matching pcb.
     * 'Perfect match' pcbs (connected to the remote port & ip address) are
     * preferred. If no perfect match is found, the first unconnected pcb that
     * matches the local port and ip address gets the datagram. */
    for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
      local_match = 0;
      /* print the PCB local and remote address */
      LWIP_DEBUGF(UDP_DEBUG,
                  ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- "
                   "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
                   ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip),
                   ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port,
                   ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
                   ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port));

      /* compare PCB local addr+port to UDP destination addr+port */
      if ((pcb->local_port == dest) &&
          (ip_addr_isany(&pcb->local_ip) ||
           ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)) || 
#if LWIP_IGMP
           ip_addr_ismulticast(&(iphdr->dest)) ||
#endif /* LWIP_IGMP */
           ip_addr_isbroadcast(&(iphdr->dest), inp))) {
        local_match = 1;
        if ((uncon_pcb == NULL) && 
            ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
          /* the first unconnected matching PCB */     
          uncon_pcb = pcb;
        }
      }
      /* compare PCB remote addr+port to UDP source addr+port */
      if ((local_match != 0) &&
          (pcb->remote_port == src) &&
          (ip_addr_isany(&pcb->remote_ip) ||
           ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) {
        /* the first fully matching PCB */
        if (prev != NULL) {
          /* move the pcb to the front of udp_pcbs so that is
             found faster next time */
          prev->next = pcb->next;
          pcb->next = udp_pcbs;
          udp_pcbs = pcb;
        } else {
          UDP_STATS_INC(udp.cachehit);
        }
        break;
      }
      prev = pcb;
    }
    /* no fully matching pcb found? then look for an unconnected pcb */
    if (pcb == NULL) {
      pcb = uncon_pcb;
    }
  }

  /* Check checksum if this is a match or if it was directed at us. */
  if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) {
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
#if LWIP_UDPLITE
    if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {
      /* Do the UDP Lite checksum */
#if CHECKSUM_CHECK_UDP
      u16_t chklen = ntohs(udphdr->len);
      if (chklen < sizeof(struct udp_hdr)) {
        if (chklen == 0) {
          /* For UDP-Lite, checksum length of 0 means checksum
             over the complete packet (See RFC 3828 chap. 3.1) */
          chklen = p->tot_len;
        } else {
          /* At least the UDP-Lite header must be covered by the
             checksum! (Again, see RFC 3828 chap. 3.1) */
          UDP_STATS_INC(udp.chkerr);
          UDP_STATS_INC(udp.drop);
          snmp_inc_udpinerrors();
          pbuf_free(p);
          goto end;
        }
      }
      if (inet_chksum_pseudo_partial(p, (struct ip_addr *)&(iphdr->src),
                             (struct ip_addr *)&(iphdr->dest),
                             IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) {
        LWIP_DEBUGF(UDP_DEBUG | 2,
                    ("udp_input: UDP Lite datagram discarded due to failing checksum\n"));
        UDP_STATS_INC(udp.chkerr);
        UDP_STATS_INC(udp.drop);
        snmp_inc_udpinerrors();
        pbuf_free(p);
        goto end;
      }
#endif /* CHECKSUM_CHECK_UDP */
    } else
#endif /* LWIP_UDPLITE */
    {
#if CHECKSUM_CHECK_UDP
      if (udphdr->chksum != 0) {
        if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
                               (struct ip_addr *)&(iphdr->dest),
                               IP_PROTO_UDP, p->tot_len) != 0) {
          LWIP_DEBUGF(UDP_DEBUG | 2,
                      ("udp_input: UDP datagram discarded due to failing checksum\n"));
          UDP_STATS_INC(udp.chkerr);
          UDP_STATS_INC(udp.drop);
          snmp_inc_udpinerrors();
          pbuf_free(p);
          goto end;
        }
      }
#endif /* CHECKSUM_CHECK_UDP */
    }
    if(pbuf_header(p, -UDP_HLEN)) {
      /* Can we cope with this failing? Just assert for now */
      LWIP_ASSERT("pbuf_header failed\n", 0);
      UDP_STATS_INC(udp.drop);
      snmp_inc_udpinerrors();
      pbuf_free(p);
      goto end;
    }
    if (pcb != NULL) {
      snmp_inc_udpindatagrams();
      /* callback */
      if (pcb->recv != NULL) {
        /* now the recv function is responsible for freeing p */
        struct ip_addr iphdrsrc;                                                  // __packed hack
        iphdrsrc.addr = iphdr->src.addr;
        pcb->recv(pcb->recv_arg, pcb, p, &iphdrsrc, src);
      } else {
        /* no recv function registered? then we have to free the pbuf! */
        pbuf_free(p);
        goto end;
      }
    } else {
      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));

#if LWIP_ICMP
      /* No match was found, send ICMP destination port unreachable unless
         destination address was broadcast/multicast. */
      if (!ip_addr_isbroadcast(&(iphdr->dest), inp) &&
          !ip_addr_ismulticast(&iphdr->dest)) {
        /* move payload pointer back to ip header */
        pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN);
        LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr));
        icmp_dest_unreach(p, ICMP_DUR_PORT);
      }
#endif /* LWIP_ICMP */
      UDP_STATS_INC(udp.proterr);
      UDP_STATS_INC(udp.drop);
      snmp_inc_udpnoports();
      pbuf_free(p);
    }
  } else {
    pbuf_free(p);
  }
end:
  PERF_STOP("udp_input");
}
Esempio n. 15
0
/** Create a TCP segment usable for passing to tcp_input */
static struct pbuf*
tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
                   u16_t src_port, u16_t dst_port, void* data, size_t data_len,
                   u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd)
{
  struct pbuf *p, *q;
  struct ip_hdr* iphdr;
  struct tcp_hdr* tcphdr;
  u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);

  p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
  EXPECT_RETNULL(p != NULL);
  /* first pbuf must be big enough to hold the headers */
  EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
  if (data_len > 0) {
    /* first pbuf must be big enough to hold at least 1 data byte, too */
    EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
  }

  for(q = p; q != NULL; q = q->next) {
    memset(q->payload, 0, q->len);
  }

  iphdr = p->payload;
  /* fill IP header */
  iphdr->dest.addr = dst_ip->addr;
  iphdr->src.addr = src_ip->addr;
  IPH_VHL_SET(iphdr, 4, IP_HLEN / 4);
  IPH_TOS_SET(iphdr, 0);
  IPH_LEN_SET(iphdr, htons(p->tot_len));
  IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));

  /* let p point to TCP header */
  pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));

  tcphdr = p->payload;
  tcphdr->src   = htons(src_port);
  tcphdr->dest  = htons(dst_port);
  tcphdr->seqno = htonl(seqno);
  tcphdr->ackno = htonl(ackno);
  TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4);
  TCPH_FLAGS_SET(tcphdr, headerflags);
  tcphdr->wnd   = htons(wnd);

  if (data_len > 0) {
    /* let p point to TCP data */
    pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr));
    /* copy data */
    pbuf_take(p, data, data_len);
    /* let p point to TCP header again */
    pbuf_header(p, sizeof(struct tcp_hdr));
  }

  /* calculate checksum */

  tcphdr->chksum = inet_chksum_pseudo(p, src_ip, dst_ip,
          IP_PROTO_TCP, p->tot_len);

  pbuf_header(p, sizeof(struct ip_hdr));

  return p;
}
/* find out what we can send and send it */
err_t
tcp_output(struct tcp_pcb *pcb)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  struct tcp_seg *seg, *useg;
  u32_t wnd;
#if TCP_CWND_DEBUG
  s16_t i = 0;
#endif /* TCP_CWND_DEBUG */

  /* First, check if we are invoked by the TCP input processing
     code. If so, we do not output anything. Instead, we rely on the
     input processing code to call us when input processing is done
     with. */
  if (tcp_input_pcb == pcb) {
    return ERR_OK;
  }

  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);

  seg = pcb->unsent;

  /* useg should point to last segment on unacked queue */
  useg = pcb->unacked;
  if (useg != NULL) {
    for (; useg->next != NULL; useg = useg->next);
  }                                                                             
   
  /* If the TF_ACK_NOW flag is set and no data will be sent (either
   * because the ->unsent queue is empty or because the window does
   * not allow it), construct an empty ACK segment and send it.
   *
   * If data is to be sent, we will just piggyback the ACK (see below).
   */
  if (pcb->flags & TF_ACK_NOW &&
     (seg == NULL ||
      ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
    p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
    if (p == NULL) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
      return ERR_BUF;
    }
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
    /* remove ACK flags from the PCB, as we send an empty ACK now */
    pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);

    tcphdr = p->payload;
    tcphdr->src = htons(pcb->local_port);
    tcphdr->dest = htons(pcb->remote_port);
    tcphdr->seqno = htonl(pcb->snd_nxt);
    tcphdr->ackno = htonl(pcb->rcv_nxt);
    TCPH_FLAGS_SET(tcphdr, TCP_ACK);
    tcphdr->wnd = htons(pcb->rcv_wnd);
    tcphdr->urgp = 0;
    TCPH_HDRLEN_SET(tcphdr, 5);

    tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
    tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
          IP_PROTO_TCP, p->tot_len);
#endif
    ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
        IP_PROTO_TCP);
    pbuf_free(p);

    return ERR_OK;
  }

#if TCP_OUTPUT_DEBUG
  if (seg == NULL) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", (void*)pcb->unsent));
  }
#endif /* TCP_OUTPUT_DEBUG */
#if TCP_CWND_DEBUG
  if (seg == NULL) {
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", seg == NULL, ack %"U32_F"\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            pcb->lastack));
  } else {
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
                            ntohl(seg->tcphdr->seqno), pcb->lastack));
  }
#endif /* TCP_CWND_DEBUG */
  /* data available and window allows it to be sent? */
  while (seg != NULL &&
  ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
#if TCP_CWND_DEBUG
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            ntohl(seg->tcphdr->seqno) + seg->len -
                            pcb->lastack,
                            ntohl(seg->tcphdr->seqno), pcb->lastack, i));
    ++i;
#endif /* TCP_CWND_DEBUG */

    pcb->unsent = seg->next;

    if (pcb->state != SYN_SENT) {
      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
    }

    tcp_output_segment(seg, pcb);
    pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
    if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
      pcb->snd_max = pcb->snd_nxt;
    }
    /* put segment on unacknowledged list if length > 0 */
    if (TCP_TCPLEN(seg) > 0) {
      seg->next = NULL;
      /* unacked list is empty? */
      if (pcb->unacked == NULL) {
        pcb->unacked = seg;
        useg = seg;
      /* unacked list is not empty? */
      } else {
        /* In the case of fast retransmit, the packet should not go to the tail
         * of the unacked queue, but rather at the head. We need to check for
         * this case. -STJ Jul 27, 2004 */
        if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){
          /* add segment to head of unacked list */
          seg->next = pcb->unacked;
          pcb->unacked = seg;
        } else {
          /* add segment to tail of unacked list */
          useg->next = seg;
          useg = useg->next;
        }
      }
    /* do not queue empty segments on the unacked list */
    } else {
      tcp_seg_free(seg);
    }
    seg = pcb->unsent;
  }
  return ERR_OK;
}
Esempio n. 17
0
/* find out what we can send and send it */
err_t
tcp_output(struct tcp_pcb *pcb)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  struct tcp_seg *seg, *useg;
  u32_t wnd;
#if TCP_CWND_DEBUG
  int i = 0;
#endif /* TCP_CWND_DEBUG */
  
  wnd = MIN(pcb->snd_wnd, pcb->cwnd);

  seg = pcb->unsent;

  if(((seg == NULL)
	|| (ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd))
	&& (pcb->flags & TF_ACK_NOW)) {

    /* If no segments are enqueued but we should send an ACK, we
       construct the ACK and send it. */
    pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
    pcb->rcv_adv = pcb->rcv_nxt + pcb->rcv_wnd;
    p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM);
    if(p == NULL) {
      DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: (ACK) could not allocate pbuf\n"));
      return ERR_BUF;
    }
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: sending ACK for %lu\n", pcb->rcv_nxt));    
    if(pbuf_header(p, TCP_HLEN)) {
      DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: (ACK) no room for TCP header in pbuf.\n"));
      
#ifdef TCP_STATS
      ++stats.tcp.err;
#endif /* TCP_STATS */
      pbuf_free(p);
      return ERR_BUF;
    }
    
    tcphdr = p->payload;
    tcphdr->src = htons(pcb->local_port);
    tcphdr->dest = htons(pcb->remote_port);
    tcphdr->seqno = htonl(pcb->snd_nxt);
    tcphdr->ackno = htonl(pcb->rcv_nxt);
    TCPH_FLAGS_SET(tcphdr, TCP_ACK);
    tcphdr->wnd = htons(pcb->rcv_wnd);
    tcphdr->urgp = 0;
    TCPH_OFFSET_SET(tcphdr, 5 << 4);
    
    tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP
    tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
					IP_PROTO_TCP, p->tot_len);
#endif

    ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), TCP_TTL,
	      IP_PROTO_TCP);
    pbuf_free(p);

    return ERR_OK;
  } 
  
#if TCP_OUTPUT_DEBUG
  if(seg == NULL) {
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send\n"));
  }
#endif /* TCP_OUTPUT_DEBUG */
#if TCP_CWND_DEBUG
  if(seg == NULL) {
    DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, seg == NULL, ack %lu\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            pcb->lastack));
  } else {
    DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, effwnd %lu, seq %lu, ack %lu\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
                            ntohl(seg->tcphdr->seqno), pcb->lastack));
  }
#endif /* TCP_CWND_DEBUG */
  
  while(seg != NULL &&
	ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
    pcb->rtime = 0;
#if TCP_CWND_DEBUG
    DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, effwnd %lu, seq %lu, ack %lu, i%d\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            ntohl(seg->tcphdr->seqno) + seg->len -
                            pcb->lastack,
                            ntohl(seg->tcphdr->seqno), pcb->lastack, i));
    ++i;
#endif /* TCP_CWND_DEBUG */

    pcb->unsent = seg->next;
    
    
    if(pcb->state != SYN_SENT) {
      TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_ACK);
      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
      pcb->rcv_adv = pcb->rcv_nxt + pcb->rcv_wnd;
    }
    
    tcp_output_segment(seg, pcb);
    pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
    if(TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
      pcb->snd_max = pcb->snd_nxt;
    }
    /* put segment on unacknowledged list if length > 0 */
    if(TCP_TCPLEN(seg) > 0) {
      seg->next = NULL;
      if(pcb->unacked == NULL) {
        pcb->unacked = seg;
      } else {
        for(useg = pcb->unacked; useg->next != NULL; useg = useg->next);
        useg->next = seg;
      }
      /*      seg->rtime = 0;*/      
    } else {
      tcp_seg_free(seg);
    }
    seg = pcb->unsent;
  }  
  return ERR_OK;
}
Esempio n. 18
0
/*-----------------------------------------------------------------------------------*/
void
udp_input(struct pbuf *p, struct netif *inp)
{
  struct udp_hdr *udphdr;  
  struct udp_pcb *pcb;
  struct ip_hdr *iphdr;
  uint16_t src, dest;
  
  
#ifdef UDP_STATS
  ++stats.udp.recv;
#endif /* UDP_STATS */

  iphdr = (struct ip_hdr *)p->payload;

  pbuf_header(p, -(UDP_HLEN + IPH_HL(iphdr) * 4));

  udphdr = (struct udp_hdr *)((uint8_t *)p->payload - UDP_HLEN);
  
  DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %d\n", p->tot_len));
	
  src = NTOHS(udphdr->src);
  dest = NTOHS(udphdr->dest);

#if UDP_DEBUG
  udp_debug_print(udphdr);
#endif /* UDP_DEBUG */
  
  /* Demultiplex packet. First, go for a perfect match. */
  for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
    DEBUGF(UDP_DEBUG, ("udp_input: pcb local port %d (dgram %d)\n",
		       pcb->local_port, ntohs(udphdr->dest)));
    if(pcb->remote_port == src &&
       pcb->local_port == dest &&
       (ip_addr_isany(&pcb->remote_ip) ||
	ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) &&
       (ip_addr_isany(&pcb->local_ip) ||
	ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) {
      break;
    }
  }

  if(pcb == NULL) {
    for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
      DEBUGF(UDP_DEBUG, ("udp_input: pcb local port %d (dgram %d)\n",
			 pcb->local_port, dest));
      if(pcb->local_port == dest &&
	 (ip_addr_isany(&pcb->remote_ip) ||
	  ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) &&
	 (ip_addr_isany(&pcb->local_ip) ||
	  ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) {
	break;
      }      
    }
  }


  /* Check checksum if this is a match or if it was directed at us. */
  /*  if(pcb != NULL ||
      ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) {*/
  if(pcb != NULL) {
    DEBUGF(UDP_DEBUG, ("udp_input: calculating checksum\n"));
    pbuf_header(p, UDP_HLEN);    
#ifdef IPv6
    if(iphdr->nexthdr == IP_PROTO_UDPLITE) {    
#else
    if(IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {    
#endif /* IPv4 */
      /* Do the UDP Lite checksum */
      if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
			    (struct ip_addr *)&(iphdr->dest),
			    IP_PROTO_UDPLITE, ntohs(udphdr->len)) != 0) {
	DEBUGF(UDP_DEBUG, ("udp_input: UDP Lite datagram discarded due to failing checksum\n"));
#ifdef UDP_STATS
	++stats.udp.chkerr;
	++stats.udp.drop;
#endif /* UDP_STATS */
	pbuf_free(p);
	goto end;
      }
    } else {
      if(udphdr->chksum != 0) {
	if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
			      (struct ip_addr *)&(iphdr->dest),
			      IP_PROTO_UDP, p->tot_len) != 0) {
	  DEBUGF(UDP_DEBUG, ("udp_input: UDP datagram discarded due to failing checksum\n"));
	  
#ifdef UDP_STATS
	  ++stats.udp.chkerr;
	  ++stats.udp.drop;
#endif /* UDP_STATS */
	  pbuf_free(p);
	  goto end;
	}
      }
    }
    pbuf_header(p, -UDP_HLEN);    
    if(pcb != NULL) {
      pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src);
    } else {
      DEBUGF(UDP_DEBUG, ("udp_input: not for us.\n"));
      
      /* No match was found, send ICMP destination port unreachable unless
	 destination address was broadcast/multicast. */
      
      if(!ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) &&
	 !ip_addr_ismulticast(&iphdr->dest)) {
	
	/* deconvert from host to network byte order */
	udphdr->src = htons(udphdr->src);
	udphdr->dest = htons(udphdr->dest); 
	
	/* adjust pbuf pointer */
	p->payload = iphdr;
	icmp_dest_unreach(p, ICMP_DUR_PORT);
      }
#ifdef UDP_STATS
      ++stats.udp.proterr;
      ++stats.udp.drop;
#endif /* UDP_STATS */
      pbuf_free(p);
    }
  } else {
    pbuf_free(p);
  }

  end:
	while(0); /* hack to remove compiler warning */
	
}
/*-----------------------------------------------------------------------------------*/
err_t
udp_send(struct udp_pcb *pcb, struct pbuf *p)
{
  struct udp_hdr *udphdr;
  struct ip_addr *src_ip;
  err_t err;
  struct pbuf *q;
  
  if(pbuf_header(p, UDP_HLEN)) {
    q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
    if(q == NULL) {
      return ERR_MEM;
    }
    pbuf_chain(q, p);
    p = q;
  }

  udphdr = (struct udp_hdr *)p->payload;
  udphdr->src = htons(pcb->local_port);
  udphdr->dest = htons(pcb->remote_port);
  udphdr->chksum = 0x0000;

  src_ip = &(pcb->local_ip);
  
  DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %d\n", p->tot_len));
  
  if(pcb->flags & UDP_FLAGS_UDPLITE) {
    udphdr->len = htons(pcb->chksum_len);
    /* calculate checksum */
    udphdr->chksum = inet_chksum_pseudo(p, src_ip, &(pcb->remote_ip),
					IP_PROTO_UDP, pcb->chksum_len);
    if(udphdr->chksum == 0x0000) {
      udphdr->chksum = 0xffff;
    }
    err = sr_lwip_output(p, &pcb->local_ip, &pcb->remote_ip, IP_PROTO_UDPLITE);
  } else {
    udphdr->len = htons(p->tot_len);
    /* calculate checksum */
    if((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
      udphdr->chksum = inet_chksum_pseudo(p, src_ip, &pcb->remote_ip,
					  IP_PROTO_UDP, p->tot_len);
      if(udphdr->chksum == 0x0000) {
	udphdr->chksum = 0xffff;
      }
    }
    err = sr_lwip_output(p,&pcb->local_ip, &pcb->remote_ip, IP_PROTO_UDP);
  }
  
#ifdef UDP_STATS
  ++stats.udp.xmit;
#endif /* UDP_STATS */
  return err;
}
/*-----------------------------------------------------------------------------------*/
err_t
udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, uint16_t port)
{
  struct udp_pcb *ipcb;
  ip_addr_set(&pcb->local_ip, ipaddr);
  pcb->local_port = port;

  /* Insert UDP PCB into the list of active UDP PCBs. */
  for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
    if(pcb == ipcb) {
      /* Already on the list, just return. */
      return ERR_OK;
    }
  }
  /* We need to place the PCB on the list. */
  pcb->next = udp_pcbs;
  udp_pcbs = pcb;

  DEBUGF(UDP_DEBUG, ("udp_bind: bound to port %d\n", port));
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
err_t
udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, uint16_t port)
{
  struct udp_pcb *ipcb;
  ip_addr_set(&pcb->remote_ip, ipaddr);
  pcb->remote_port = port;

  /* Insert UDP PCB into the list of active UDP PCBs. */
  for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
    if(pcb == ipcb) {
      /* Already on the list, just return. */
      return ERR_OK;
    }
  }
  /* We need to place the PCB on the list. */
  pcb->next = udp_pcbs;
  udp_pcbs = pcb;
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
void
udp_recv(struct udp_pcb *pcb,
	 void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p,
		       struct ip_addr *addr, uint16_t port),
	 void *recv_arg)
{
  pcb->recv = recv;
  pcb->recv_arg = recv_arg;
}
Esempio n. 19
0
/*-----------------------------------------------------------------------------------*/
void
icmp_input(struct pbuf *p, struct netif *inp)
{
  unsigned char type;
  struct icmp_echo_hdr *iecho;
  struct ip_hdr *iphdr;
  struct ip_addr tmpaddr;
  
 
#ifdef ICMP_STATS
  ++stats.icmp.recv;
#endif /* ICMP_STATS */

  type = ((char *)p->payload)[0];

  switch(type) {
  case ICMP6_ECHO:
    DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));

    if(p->tot_len < sizeof(struct icmp_echo_hdr)) {
      DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));

      pbuf_free(p);
#ifdef ICMP_STATS
      ++stats.icmp.lenerr;
#endif /* ICMP_STATS */

      return;      
    }
    iecho = p->payload;
    iphdr = (struct ip_hdr *)((char *)p->payload - IP_HLEN);
    if(inet_chksum_pbuf(p) != 0) {
      DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%x)\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));

#ifdef ICMP_STATS
      ++stats.icmp.chkerr;
#endif /* ICMP_STATS */
    /*      return;*/
    }
    DEBUGF(ICMP_DEBUG, ("icmp: p->len %d p->tot_len %d\n", p->len, p->tot_len));
    ip_addr_set(&tmpaddr, &(iphdr->src));
    ip_addr_set(&(iphdr->src), &(iphdr->dest));
    ip_addr_set(&(iphdr->dest), &tmpaddr);
    iecho->type = ICMP6_ER;
    /* adjust the checksum */
    if(iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) {
      iecho->chksum += htons(ICMP6_ECHO << 8) + 1;
    } else {
      iecho->chksum += htons(ICMP6_ECHO << 8);
    }
    DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%x)\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
#ifdef ICMP_STATS
    ++stats.icmp.xmit;
#endif /* ICMP_STATS */

    /*    DEBUGF("icmp: p->len %d p->tot_len %d\n", p->len, p->tot_len);*/
    ip_output_if(p, &(iphdr->src), IP_HDRINCL,
		 iphdr->hoplim, IP_PROTO_ICMP, inp);
    break; 
  default:
    DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type not supported.\n"));

#ifdef ICMP_STATS
    ++stats.icmp.proterr;
    ++stats.icmp.drop;
#endif /* ICMP_STATS */
  }

  pbuf_free(p);
}
Esempio n. 20
0
/* find out what we can send and send it */
err_t
tcp_output(struct tcp_pcb *pcb)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  struct tcp_seg *seg, *useg;
  u32_t wnd;
#if TCP_CWND_DEBUG
  int i = 0;
#endif /* TCP_CWND_DEBUG */

  /* First, check if we are invoked by the TCP input processing
     code. If so, we do not output anything. Instead, we rely on the
     input processing code to call us when input processing is done
     with. */
  if (tcp_input_pcb == pcb) {
    return ERR_OK;
  }

  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);


  seg = pcb->unsent;

  /* useg should point to last segment on unacked queue */
  useg = pcb->unacked;
  if (useg != NULL) {
    for (; useg->next != NULL; useg = useg->next);
  }                                                                             

   
  /* If the TF_ACK_NOW flag is set, we check if there is data that is
     to be sent. If data is to be sent out, we'll just piggyback our
     acknowledgement with the outgoing segment. If no data will be
     sent (either because the ->unsent queue is empty or because the
     window doesn't allow it) we'll have to construct an empty ACK
     segment and send it. */
  if (pcb->flags & TF_ACK_NOW &&
     (seg == NULL ||
      ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
    pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
    p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
    if (p == NULL) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
      return ERR_BUF;
    }
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %lu\n", pcb->rcv_nxt));

    tcphdr = p->payload;
    tcphdr->src = htons(pcb->local_port);
    tcphdr->dest = htons(pcb->remote_port);
    tcphdr->seqno = htonl(pcb->snd_nxt);
    tcphdr->ackno = htonl(pcb->rcv_nxt);
    TCPH_FLAGS_SET(tcphdr, TCP_ACK);
    tcphdr->wnd = htons(pcb->rcv_wnd);
    tcphdr->urgp = 0;
    TCPH_HDRLEN_SET(tcphdr, 5);

    tcphdr->chksum = 0;
    tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
          IP_PROTO_TCP, p->tot_len);

    ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
        IP_PROTO_TCP);
    pbuf_free(p);

    return ERR_OK;
  }

#if TCP_OUTPUT_DEBUG
  if (seg == NULL) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", pcb->unsent));
  }
#endif /* TCP_OUTPUT_DEBUG */
#if TCP_CWND_DEBUG
  if (seg == NULL) {
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, seg == NULL, ack %lu\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            pcb->lastack));
  } else {
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, effwnd %lu, seq %lu, ack %lu\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
                            ntohl(seg->tcphdr->seqno), pcb->lastack));
  }
#endif /* TCP_CWND_DEBUG */

  while (seg != NULL &&
  ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
#if TCP_CWND_DEBUG
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, effwnd %lu, seq %lu, ack %lu, i%d\n",
                            pcb->snd_wnd, pcb->cwnd, wnd,
                            ntohl(seg->tcphdr->seqno) + seg->len -
                            pcb->lastack,
                            ntohl(seg->tcphdr->seqno), pcb->lastack, i));
    ++i;
#endif /* TCP_CWND_DEBUG */

    pcb->unsent = seg->next;

    if (pcb->state != SYN_SENT) {
      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
    }

    tcp_output_segment(seg, pcb);
    pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
    if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
      pcb->snd_max = pcb->snd_nxt;
    }
    /* put segment on unacknowledged list if length > 0 */
    if (TCP_TCPLEN(seg) > 0) {
      seg->next = NULL;
      if (pcb->unacked == NULL) {
        pcb->unacked = seg;
        useg = seg;
      } else {
        useg->next = seg;
        useg = useg->next;
      }
    } else {
      tcp_seg_free(seg);
    }
    seg = pcb->unsent;
  }
  return ERR_OK;
}
Esempio n. 21
0
/**
 * Send persist timer zero-window probes to keep a connection active
 * when a window update is lost.
 *
 * Called by tcp_slowtmr()
 *
 * @param pcb the tcp_pcb for which to send a zero-window probe packet
 */
void
tcp_zero_window_probe(struct tcp_pcb *pcb)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  struct tcp_seg *seg;
  u16_t len;
  u8_t is_fin;

  LWIP_DEBUGF(TCP_DEBUG,
              ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
               U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
               ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
               ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));

  LWIP_DEBUGF(TCP_DEBUG,
              ("tcp_zero_window_probe: tcp_ticks %"U32_F
               "   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
               tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));

  seg = pcb->unacked;

  if(seg == NULL) {
    seg = pcb->unsent;
  }
  if(seg == NULL) {
    return;
  }

  is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
  /* we want to send one seqno: either FIN or data (no options) */
  len = is_fin ? 0 : 1;

  p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
  if(p == NULL) {
    LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
    return;
  }
  tcphdr = (struct tcp_hdr *)p->payload;

  if (is_fin) {
    /* FIN segment, no data */
    TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
  } else {
    /* Data segment, copy in one byte from the head of the unacked queue */
    *((char *)p->payload + TCP_HLEN) = *(char *)seg->dataptr;
  }

#if CHECKSUM_GEN_TCP
  tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
                                      IP_PROTO_TCP, (u16_t)p->tot_len);
#endif
  TCP_STATS_INC(tcp.xmit);

  /* Send output to IP */
#if LWIP_NETIF_HWADDRHINT
  ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
    &(pcb->addr_hint));
#elif LWIP_3RD_PARTY_L3
  pcb->ip_output(p, pcb, 0);
#else /* LWIP_NETIF_HWADDRHINT*/
  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/

  tcp_tx_pbuf_free(pcb, p);

  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
                          " ackno %"U32_F".\n",
                          pcb->snd_nxt - 1, pcb->rcv_nxt));
}
Esempio n. 22
0
/**
 * Process an incoming UDP datagram.
 *
 * Given an incoming UDP datagram (as a chain of pbufs) this function
 * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
 * recv function. If no pcb is found or the datagram is incorrect, the
 * pbuf is freed.
 *
 * @param p pbuf to be demultiplexed to a UDP PCB.
 * @param inp network interface on which the datagram was received.
 *
 */
void
udp_input(struct pbuf *p, struct netif *inp)
{
    struct udp_hdr *udphdr;
    struct udp_pcb *pcb, *prev;
    struct udp_pcb *uncon_pcb;
    struct ip_hdr *iphdr;
    u16_t src, dest;
    u8_t local_match;
    u8_t broadcast;

    PERF_START;

    UDP_STATS_INC(udp.recv);

    iphdr = (struct ip_hdr *)p->payload;

    /* Check minimum length (IP header + UDP header)
     * and move payload pointer to UDP header */
    if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) {
        /* drop short packets */
        LWIP_DEBUGF(UDP_DEBUG,
                    ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
        UDP_STATS_INC(udp.lenerr);
        UDP_STATS_INC(udp.drop);
        snmp_inc_udpinerrors();
        pbuf_free(p);
        goto end;
    }

    udphdr = (struct udp_hdr *)p->payload;

    /* is broadcast packet ? */
    broadcast = ip_addr_isbroadcast(&current_iphdr_dest, inp);

    LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));

    /* convert src and dest ports to host byte order */
    src = ntohs(udphdr->src);
    dest = ntohs(udphdr->dest);

    udp_debug_print(udphdr);

    /* print the UDP source and destination */
    LWIP_DEBUGF(UDP_DEBUG,
                ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- "
                 "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
                 ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
                 ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest),
                 ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src),
                 ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src)));

#if LWIP_DHCP
    pcb = NULL;
    /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
       the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
    if (dest == DHCP_CLIENT_PORT) {
        /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
        if (src == DHCP_SERVER_PORT) {
            if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
                /* accept the packe if
                   (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
                   - inp->dhcp->pcb->remote == ANY or iphdr->src */
                if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) ||
                        ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &current_iphdr_src))) {
                    pcb = inp->dhcp->pcb;
                }
            }
        }
    } else
#endif /* LWIP_DHCP */
    {
        prev = NULL;
        local_match = 0;
        uncon_pcb = NULL;
        /* Iterate through the UDP pcb list for a matching pcb.
         * 'Perfect match' pcbs (connected to the remote port & ip address) are
         * preferred. If no perfect match is found, the first unconnected pcb that
         * matches the local port and ip address gets the datagram. */
        for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
            local_match = 0;
            /* print the PCB local and remote address */
            LWIP_DEBUGF(UDP_DEBUG,
                        ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- "
                         "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
                         ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
                         ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port,
                         ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
                         ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port));

            /* compare PCB local addr+port to UDP destination addr+port */
            if (pcb->local_port == dest) {
                if (
                    (!broadcast && ip_addr_isany(&pcb->local_ip)) ||
                    ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest) ||
#if LWIP_IGMP
                    ip_addr_ismulticast(&current_iphdr_dest) ||
#endif /* LWIP_IGMP */
#if IP_SOF_BROADCAST_RECV
                    (broadcast && ip_get_option(pcb, SOF_BROADCAST) &&
                     (ip_addr_isany(&pcb->local_ip) ||
                      ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) {
#else /* IP_SOF_BROADCAST_RECV */
                    (broadcast &&
                     (ip_addr_isany(&pcb->local_ip) ||
                      ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) {
#endif /* IP_SOF_BROADCAST_RECV */
                    local_match = 1;
                    if ((uncon_pcb == NULL) &&
                            ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
                        /* the first unconnected matching PCB */
                        uncon_pcb = pcb;
                    }
                }
            }
            /* compare PCB remote addr+port to UDP source addr+port */
            if ((local_match != 0) &&
                    (pcb->remote_port == src) &&
                    (ip_addr_isany(&pcb->remote_ip) ||
                     ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src))) {
                /* the first fully matching PCB */
                if (prev != NULL) {
                    /* move the pcb to the front of udp_pcbs so that is
                       found faster next time */
                    prev->next = pcb->next;
                    pcb->next = udp_pcbs;
                    udp_pcbs = pcb;
                } else {
                    UDP_STATS_INC(udp.cachehit);
                }
                break;
            }
            prev = pcb;
        }
        /* no fully matching pcb found? then look for an unconnected pcb */
        if (pcb == NULL) {
            pcb = uncon_pcb;
        }
    }

    /* Check checksum if this is a match or if it was directed at us. */
    if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &current_iphdr_dest)) {
        LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
#if LWIP_UDPLITE
        if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {
            /* Do the UDP Lite checksum */
#if CHECKSUM_CHECK_UDP
            u16_t chklen = ntohs(udphdr->len);
            if (chklen < sizeof(struct udp_hdr)) {
                if (chklen == 0) {
                    /* For UDP-Lite, checksum length of 0 means checksum
                       over the complete packet (See RFC 3828 chap. 3.1) */
                    chklen = p->tot_len;
                } else {
                    /* At least the UDP-Lite header must be covered by the
                       checksum! (Again, see RFC 3828 chap. 3.1) */
                    UDP_STATS_INC(udp.chkerr);
                    UDP_STATS_INC(udp.drop);
                    snmp_inc_udpinerrors();
                    pbuf_free(p);
                    goto end;
                }
            }
            if (inet_chksum_pseudo_partial(p, &current_iphdr_src, &current_iphdr_dest,
                                           IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) {
                LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
                            ("udp_input: UDP Lite datagram discarded due to failing checksum\n"));
                UDP_STATS_INC(udp.chkerr);
                UDP_STATS_INC(udp.drop);
                snmp_inc_udpinerrors();
                pbuf_free(p);
                goto end;
            }
#endif /* CHECKSUM_CHECK_UDP */
        } else
#endif /* LWIP_UDPLITE */
        {
#if CHECKSUM_CHECK_UDP
            if (udphdr->chksum != 0) {
                if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
                                       IP_PROTO_UDP, p->tot_len) != 0) {
                    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
                                ("udp_input: UDP datagram discarded due to failing checksum\n"));
                    UDP_STATS_INC(udp.chkerr);
                    UDP_STATS_INC(udp.drop);
                    snmp_inc_udpinerrors();
                    pbuf_free(p);
                    goto end;
                }
            }
#endif /* CHECKSUM_CHECK_UDP */
        }
        if(pbuf_header(p, -UDP_HLEN)) {
            /* Can we cope with this failing? Just assert for now */
            LWIP_ASSERT("pbuf_header failed\n", 0);
            UDP_STATS_INC(udp.drop);
            snmp_inc_udpinerrors();
            pbuf_free(p);
            goto end;
        }
        if (pcb != NULL) {
            snmp_inc_udpindatagrams();
#if SO_REUSE && SO_REUSE_RXTOALL
            if ((broadcast || ip_addr_ismulticast(&current_iphdr_dest)) &&
                    ip_get_option(pcb, SOF_REUSEADDR)) {
                /* pass broadcast- or multicast packets to all multicast pcbs
                   if SOF_REUSEADDR is set on the first match */
                struct udp_pcb *mpcb;
                u8_t p_header_changed = 0;
                for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
                    if (mpcb != pcb) {
                        /* compare PCB local addr+port to UDP destination addr+port */
                        if ((mpcb->local_port == dest) &&
                                ((!broadcast && ip_addr_isany(&mpcb->local_ip)) ||
                                 ip_addr_cmp(&(mpcb->local_ip), &current_iphdr_dest) ||
#if LWIP_IGMP
                                 ip_addr_ismulticast(&current_iphdr_dest) ||
#endif /* LWIP_IGMP */
#if IP_SOF_BROADCAST_RECV
                                 (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))) {
#else  /* IP_SOF_BROADCAST_RECV */
                                 (broadcast))) {
#endif /* IP_SOF_BROADCAST_RECV */
                            /* pass a copy of the packet to all local matches */
                            if (mpcb->recv != NULL) {
                                struct pbuf *q;
                                /* for that, move payload to IP header again */
                                if (p_header_changed == 0) {
                                    pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
                                    p_header_changed = 1;
                                }
                                q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
                                if (q != NULL) {
                                    err_t err = pbuf_copy(q, p);
                                    if (err == ERR_OK) {
                                        /* move payload to UDP data */
                                        pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
                                        mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
                                    }
                                }
                            }
                        }
                    }
                }
                if (p_header_changed) {
                    /* and move payload to UDP data again */
                    pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
                }
            }
#endif /* SO_REUSE && SO_REUSE_RXTOALL */
            /* callback */
            if (pcb->recv != NULL) {
                /* now the recv function is responsible for freeing p */
                pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
            } else {
                /* no recv function registered? then we have to free the pbuf! */
                pbuf_free(p);
                goto end;
            }
        } else {
Esempio n. 23
0
/**
 * The initial input processing of TCP. It verifies the TCP header, demultiplexes
 * the segment between the PCBs and passes it on to tcp_process(), which implements
 * the TCP finite state machine. This function is called by the IP layer (in
 * ip_input()).
 *
 * @param p received TCP segment to process (p->payload pointing to the IP header)
 * @param inp network interface on which this segment was received
 */
void
tcp_input(struct pbuf *p, struct netif *inp)
{
  struct tcp_pcb *pcb, *prev;
  struct tcp_pcb_listen *lpcb;
  u8_t hdrlen;
  err_t err;

  PERF_START;

  TCP_STATS_INC(tcp.recv);
  snmp_inc_tcpinsegs();

  iphdr = p->payload;
  tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);

#if TCP_INPUT_DEBUG
  tcp_debug_print(tcphdr);
#endif

  /* remove header from payload */
  if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
    /* drop short packets */
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
    TCP_STATS_INC(tcp.lenerr);
    TCP_STATS_INC(tcp.drop);
    snmp_inc_tcpinerrs();
    pbuf_free(p);
    return;
  }

  /* Don't even process incoming broadcasts/multicasts. */
  if (ip_addr_isbroadcast(&(iphdr->dest), inp) ||
      ip_addr_ismulticast(&(iphdr->dest))) {
    TCP_STATS_INC(tcp.proterr);
    TCP_STATS_INC(tcp.drop);
    snmp_inc_tcpinerrs();
    pbuf_free(p);
    return;
  }

#if CHECKSUM_CHECK_TCP
  /* Verify TCP checksum. */
  if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
      (struct ip_addr *)&(iphdr->dest),
      IP_PROTO_TCP, p->tot_len) != 0) {
      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
        inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest),
      IP_PROTO_TCP, p->tot_len)));
#if TCP_DEBUG
    tcp_debug_print(tcphdr);
#endif /* TCP_DEBUG */
    TCP_STATS_INC(tcp.chkerr);
    TCP_STATS_INC(tcp.drop);
    snmp_inc_tcpinerrs();
    pbuf_free(p);
    return;
  }
#endif

  /* Move the payload pointer in the pbuf so that it points to the
     TCP data instead of the TCP header. */
  hdrlen = TCPH_HDRLEN(tcphdr);
  if(pbuf_header(p, -(hdrlen * 4))){
    /* drop short packets */
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
    TCP_STATS_INC(tcp.lenerr);
    TCP_STATS_INC(tcp.drop);
    snmp_inc_tcpinerrs();
    pbuf_free(p);
    return;
  }

  /* Convert fields in TCP header to host byte order. */
  tcphdr->src = ntohs(tcphdr->src);
  tcphdr->dest = ntohs(tcphdr->dest);
  seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
  ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
  tcphdr->wnd = ntohs(tcphdr->wnd);

  flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS;
  tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0);

  /* Demultiplex an incoming segment. First, we check if it is destined
     for an active connection. */
  prev = NULL;


  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
    LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
    LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
    LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
    if (pcb->remote_port == tcphdr->src &&
       pcb->local_port == tcphdr->dest &&
       ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
       ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {

      /* Move this PCB to the front of the list so that subsequent
         lookups will be faster (we exploit locality in TCP segment
         arrivals). */
      LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
      if (prev != NULL) {
        prev->next = pcb->next;
        pcb->next = tcp_active_pcbs;
        tcp_active_pcbs = pcb;
      }
      LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
      break;
    }
    prev = pcb;
  }

  if (pcb == NULL) {
    /* If it did not go to an active connection, we check the connections
       in the TIME-WAIT state. */
    for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
      LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
      if (pcb->remote_port == tcphdr->src &&
         pcb->local_port == tcphdr->dest &&
         ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
         ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {
        /* We don't really care enough to move this PCB to the front
           of the list since we are not very likely to receive that
           many segments for connections in TIME-WAIT. */
        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
        tcp_timewait_input(pcb);
        pbuf_free(p);
        return;
      }
    }

  /* Finally, if we still did not get a match, we check all PCBs that
     are LISTENing for incoming connections. */
    prev = NULL;
    for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
      if ((ip_addr_isany(&(lpcb->local_ip)) ||
        ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) &&
        lpcb->local_port == tcphdr->dest) {
        /* Move this PCB to the front of the list so that subsequent
           lookups will be faster (we exploit locality in TCP segment
           arrivals). */
        if (prev != NULL) {
          ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
                /* our successor is the remainder of the listening list */
          lpcb->next = tcp_listen_pcbs.listen_pcbs;
                /* put this listening pcb at the head of the listening list */
          tcp_listen_pcbs.listen_pcbs = lpcb;
        }

        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
        tcp_listen_input(lpcb);
        pbuf_free(p);
        return;
      }
      prev = (struct tcp_pcb *)lpcb;
    }
  }

#if TCP_INPUT_DEBUG
  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
#endif /* TCP_INPUT_DEBUG */


  if (pcb != NULL) {
    /* The incoming segment belongs to a connection. */
#if TCP_INPUT_DEBUG
#if TCP_DEBUG
    tcp_debug_print_state(pcb->state);
#endif /* TCP_DEBUG */
#endif /* TCP_INPUT_DEBUG */

    /* Set up a tcp_seg structure. */
    inseg.next = NULL;
    inseg.len = p->tot_len;
    inseg.dataptr = p->payload;
    inseg.p = p;
    inseg.tcphdr = tcphdr;

    recv_data = NULL;
    recv_flags = 0;

    /* If there is data which was previously "refused" by upper layer */
    if (pcb->refused_data != NULL) {
      /* Notify again application with data previously received. */
      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
      TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
      if (err == ERR_OK) {
        pcb->refused_data = NULL;
      } else {
        /* drop incoming packets, because pcb is "full" */
        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
        TCP_STATS_INC(tcp.drop);
        snmp_inc_tcpinerrs();
        pbuf_free(p);
        return;
      }
    }

    tcp_input_pcb = pcb;
    err = tcp_process(pcb);
    tcp_input_pcb = NULL;
    /* A return value of ERR_ABRT means that tcp_abort() was called
       and that the pcb has been freed. If so, we don't do anything. */
    if (err != ERR_ABRT) {
      if (recv_flags & TF_RESET) {
        /* TF_RESET means that the connection was reset by the other
           end. We then call the error callback to inform the
           application that the connection is dead before we
           deallocate the PCB. */
        TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
        tcp_pcb_remove(&tcp_active_pcbs, pcb);
        memp_free(MEMP_TCP_PCB, pcb);
      } else if (recv_flags & TF_CLOSED) {
        /* The connection has been closed and we will deallocate the
           PCB. */
        tcp_pcb_remove(&tcp_active_pcbs, pcb);
        memp_free(MEMP_TCP_PCB, pcb);
      } else {
        err = ERR_OK;
        /* If the application has registered a "sent" function to be
           called when new send buffer space is available, we call it
           now. */
        if (pcb->acked > 0) {
          TCP_EVENT_SENT(pcb, pcb->acked, err);
        }

        if (recv_data != NULL) {
          if(flags & TCP_PSH) {
            recv_data->flags |= PBUF_FLAG_PUSH;
          }

          /* Notify application that data has been received. */
          TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);

          /* If the upper layer can't receive this data, store it */
          if (err != ERR_OK) {
            pcb->refused_data = recv_data;
            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
          }
        }

        /* If a FIN segment was received, we call the callback
           function with a NULL buffer to indicate EOF. */
        if (recv_flags & TF_GOT_FIN) {
          TCP_EVENT_RECV(pcb, NULL, ERR_OK, err);
        }

        /* If there were no errors, we try to send something out. */
        if (err == ERR_OK) {
          tcp_output(pcb);
        }
      }
    }


    /* give up our reference to inseg.p */
    if (inseg.p != NULL)
    {
      pbuf_free(inseg.p);
      inseg.p = NULL;
    }
#if TCP_INPUT_DEBUG
#if TCP_DEBUG
    tcp_debug_print_state(pcb->state);
#endif /* TCP_DEBUG */
#endif /* TCP_INPUT_DEBUG */

  } else {

    /* If no matching PCB was found, send a TCP RST (reset) to the
       sender. */
    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
    if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
      TCP_STATS_INC(tcp.proterr);
      TCP_STATS_INC(tcp.drop);
      tcp_rst(ackno, seqno + tcplen,
        &(iphdr->dest), &(iphdr->src),
        tcphdr->dest, tcphdr->src);
    }
    pbuf_free(p);
  }

  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
  PERF_STOP("tcp_input");
}
Esempio n. 24
0
/*-----------------------------------------------------------------------------------*/
void
tcpdump(struct pbuf *p)
{
  struct ip_hdr *iphdr;
#if LWIP_UDP
  struct udp_hdr *udphdr;
#endif
#if LWIP_TCP
  struct tcp_hdr *tcphdr;
  char flags[5];
  int i;
  int len;
  int offset;
#endif

  if (file == NULL) {
    return;
  }
  iphdr = (struct ip_hdr *)p->payload;
  switch (IPH_PROTO(iphdr)) {
#if LWIP_TCP
  case IP_PROTO_TCP:
    tcphdr = (struct tcp_hdr *)((char *)iphdr + IP_HLEN);

    pbuf_header(p, -IP_HLEN);
    if (inet_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, 
                           (ip4_addr_t *)&(iphdr->src),
                           (ip4_addr_t *)&(iphdr->dest)) != 0) {
      LWIP_DEBUGF(TCPDUMP_DEBUG, ("tcpdump: IP checksum failed!\n"));
      /*
      fprintf(file, "chksum 0x%lx ", tcphdr->chksum);
      tcphdr->chksum = 0;
      fprintf(file, "should be 0x%lx ", inet_chksum_pseudo(p, (ip_addr_t *)&(iphdr->src),
              (ip_addr_t *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len));*/
      fprintf(file, "!chksum ");
    }

    i = 0;
    if (TCPH_FLAGS(tcphdr) & TCP_SYN) {
      flags[i++] = 'S';
    }
    if (TCPH_FLAGS(tcphdr) & TCP_PSH) {
      flags[i++] = 'P';
    }
    if (TCPH_FLAGS(tcphdr) & TCP_FIN) {
      flags[i++] = 'F';
    }
    if (TCPH_FLAGS(tcphdr) & TCP_RST) {
      flags[i++] = 'R';
    }
    if (i == 0) {
      flags[i++] = '.';
    }
    flags[i++] = 0;



    fprintf(file, "%d.%d.%d.%d.%u > %d.%d.%d.%d.%u: ",
           (int)(ntohl(iphdr->src.addr) >> 24) & 0xff,
           (int)(ntohl(iphdr->src.addr) >> 16) & 0xff,
           (int)(ntohl(iphdr->src.addr) >> 8) & 0xff,
           (int)(ntohl(iphdr->src.addr) >> 0) & 0xff,
           ntohs(tcphdr->src),
           (int)(ntohl(iphdr->dest.addr) >> 24) & 0xff,
           (int)(ntohl(iphdr->dest.addr) >> 16) & 0xff,
           (int)(ntohl(iphdr->dest.addr) >> 8) & 0xff,
           (int)(ntohl(iphdr->dest.addr) >> 0) & 0xff,
           ntohs(tcphdr->dest));
    offset = TCPH_HDRLEN(tcphdr);

    len = ntohs(IPH_LEN(iphdr)) - offset * 4 - IP_HLEN;
    if (len != 0 || flags[0] != '.') {
      fprintf(file, "%s %u:%u(%u) ", flags, ntohl(tcphdr->seqno),
              ntohl(tcphdr->seqno) + len, len);
    }
    if (TCPH_FLAGS(tcphdr) & TCP_ACK) {
      fprintf(file, "ack %u ", ntohl(tcphdr->ackno));
    }
    fprintf(file, "wnd %u\n", ntohs(tcphdr->wnd));

    fflush(file);

    pbuf_header(p, IP_HLEN);
    break;
#endif /* LWIP_TCP */

#if LWIP_UDP
  case IP_PROTO_UDP:
    udphdr = (struct udp_hdr *)((char *)iphdr + IP_HLEN);

    pbuf_header(p, -IP_HLEN);
    if (inet_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
                           (ip4_addr_t *)&(iphdr->src),
                           (ip4_addr_t *)&(iphdr->dest)) != 0) {
      LWIP_DEBUGF(TCPDUMP_DEBUG, ("tcpdump: IP checksum failed!\n"));
      /*
      fprintf(file, "chksum 0x%lx ", tcphdr->chksum);
      tcphdr->chksum = 0;
      fprintf(file, "should be 0x%lx ", inet_chksum_pseudo(p, (ip_addr_t *)&(iphdr->src),
             (ip_addr_t *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len));*/
      fprintf(file, "!chksum ");
    }

    fprintf(file, "%d.%d.%d.%d.%u > %d.%d.%d.%d.%u: ",
            (int)(ntohl(iphdr->src.addr) >> 24) & 0xff,
            (int)(ntohl(iphdr->src.addr) >> 16) & 0xff,
            (int)(ntohl(iphdr->src.addr) >> 8) & 0xff,
            (int)(ntohl(iphdr->src.addr) >> 0) & 0xff,
            (int)(ntohl(iphdr->dest.addr) >> 24) & 0xff,
            ntohs(udphdr->src),
            (int)(ntohl(iphdr->dest.addr) >> 16) & 0xff,
            (int)(ntohl(iphdr->dest.addr) >> 8) & 0xff,
            (int)(ntohl(iphdr->dest.addr) >> 0) & 0xff,
            ntohs(udphdr->dest));
    fprintf(file, "U ");
    len = ntohs(IPH_LEN(iphdr)) - sizeof(struct udp_hdr) - IP_HLEN;
    fprintf(file, " %d\n", len);

    fflush(file);

    pbuf_header(p, IP_HLEN);
    break;
#endif /* LWIP_UDP */
  default:
    LWIP_DEBUGF(TCPDUMP_DEBUG, ("unhandled IP protocol: %d\n", (int)IPH_PROTO(iphdr)));
    break;

  }
}
Esempio n. 25
0
File: udp.c Progetto: HarryR/sanos
err_t udp_input(struct pbuf *p, struct netif *inp) {
  struct udp_hdr *udphdr;  
  struct udp_pcb *pcb;
  struct ip_hdr *iphdr;
  unsigned short src, dest;

  stats.udp.recv++;
  
  iphdr = p->payload;
  if (pbuf_header(p, -(IPH_HL(iphdr) * 4)) < 0 || p->tot_len < sizeof(struct udp_hdr)) {
    kprintf(KERN_WARNING "udp_input: short packet (%u bytes) discarded\n", p->tot_len);
    stats.udp.lenerr++;
    stats.udp.drop++;
    return -EPROTO;
  }
  udphdr = p->payload;

  //udp_debug_print(udphdr);

#ifdef CHECK_UDP_CHECKSUM
  // Check checksum
  if ((inp->flags & NETIF_UDP_RX_CHECKSUM_OFFLOAD) == 0) {
    if (udphdr->chksum != 0) {
      if (inet_chksum_pseudo(p, &iphdr->src, &iphdr->dest, IP_PROTO_UDP, p->tot_len) != 0) {
        kprintf(KERN_WARNING "udp_input: UDP datagram discarded due to failing checksum\n");

        stats.udp.chkerr++;
        stats.udp.drop++;
        return -ECHKSUM;
      }
    }
  }
#endif

  src = NTOHS(udphdr->src);
  dest = NTOHS(udphdr->dest);

  // Demultiplex packet. First, go for a perfect match
  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
    if (pcb->remote_port == src && pcb->local_port == dest &&
        (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, &iphdr->src)) &&
        (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, &iphdr->dest))) {
      break;
    }
  }

  if (pcb == NULL) {
    // No fully matching pcb found, look for an unconnected pcb
    for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
      if (!(pcb->flags & UDP_FLAGS_CONNECTED) &&
          pcb->local_port == dest &&
          (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, &iphdr->src)) &&
          (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, &iphdr->dest))) {
        break;
      }      
    }
  }

  if (pcb == NULL) {
    // No match was found, send ICMP destination port unreachable unless
    // destination address was broadcast/multicast.
    if (!ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) && !ip_addr_ismulticast(&iphdr->dest)) {
      // Adjust pbuf pointer
      p->payload = iphdr;
      icmp_dest_unreach(p, ICMP_DUR_PORT);
    }

    ++stats.udp.proterr;
    ++stats.udp.drop;

    return -EHOSTUNREACH;
  }

  pbuf_header(p, -UDP_HLEN);
  return pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, src);
}