Esempio n. 1
0
/**
 * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache  
 * send out queued IP packets. Updates cache with snooped address pairs.
 *
 * Should be called for incoming ARP packets. The pbuf in the argument
 * is freed by this function.
 *
 * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
 * @param pbuf The ARP packet that arrived on netif. Is freed by this function.
 * @param ethaddr Ethernet address of netif.
 *
 * @return NULL
 *
 * @see pbuf_free()
 */
struct pbuf *
etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
  struct etharp_hdr *hdr;
  u8_t i;
  u8_t for_us;

  /* drop short ARP packets */
  if (p->tot_len < sizeof(struct etharp_hdr)) {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%d/%d)\n", p->tot_len, sizeof(struct etharp_hdr)));
    pbuf_free(p);
    return NULL;
  }

  hdr = p->payload;
 
  /* this interface is not configured? */
  if (netif->ip_addr.addr == 0) {
    for_us = 0;
  } else {
    /* ARP packet directed to us? */
    for_us = ip_addr_cmp(&(hdr->dipaddr), &(netif->ip_addr));
  }

  /* add or update entries in the ARP cache */
  if (for_us) {
    /* insert IP address in ARP cache (assume requester wants to talk to us)
     * we might even send out a queued packet to this host */
    update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), ARP_INSERT_FLAG);
  /* request was not directed to us, but snoop anyway */
  } else {
    /* update the source IP address in the cache */
    update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), 0);
  }

  switch (htons(hdr->opcode)) {
  /* ARP request? */
  case ARP_REQUEST:
    /* ARP request. If it asked for our address, we send out a
    reply. In any case, we time-stamp any existing ARP entry,
    and possiby send out an IP packet that was queued on it. */

    LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
    /* we are not configured? */
    if (netif->ip_addr.addr == 0) {
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
      pbuf_free(p);
      return NULL;
    }
    /* ARP request for our address? */
    if (for_us) {

      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
      /* re-use pbuf to send ARP reply */
      hdr->opcode = htons(ARP_REPLY);

      ip_addr_set(&(hdr->dipaddr), &(hdr->sipaddr));
      ip_addr_set(&(hdr->sipaddr), &(netif->ip_addr));

      for(i = 0; i < netif->hwaddr_len; ++i) {
        hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
        hdr->shwaddr.addr[i] = ethaddr->addr[i];
        hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i];
        hdr->ethhdr.src.addr[i] = ethaddr->addr[i];
      }

      hdr->hwtype = htons(HWTYPE_ETHERNET);
      ARPH_HWLEN_SET(hdr, netif->hwaddr_len);

      hdr->proto = htons(ETHTYPE_IP);
      ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));

      hdr->ethhdr.type = htons(ETHTYPE_ARP);
      /* return ARP reply */
      netif->linkoutput(netif, p);

    /* request was not directed to us */
    } else {
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request was not for us.\n"));
    }
    break;
  case ARP_REPLY:
    /* ARP reply. We insert or update the ARP table later. */
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
    /* DHCP wants to know about ARP replies to our wanna-have-address */
    if (for_us) dhcp_arp_reply(netif, &hdr->sipaddr);
#endif
    break;
  default:
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %d\n", htons(hdr->opcode)));
    break;
  }
  /* free ARP packet */
  pbuf_free(p);
  p = NULL;
  /* nothing to send, we did it! */
  return NULL;
}
Esempio n. 2
0
void
ip_input(struct pbuf *p, struct netif *inp) {
  struct ip_hdr *iphdr;
  struct netif *netif;


  PERF_START;

#if IP_DEBUG
  ip_debug_print(p);
#endif /* IP_DEBUG */


#ifdef IP_STATS
  ++lwip_stats.ip.recv;
#endif /* IP_STATS */

  /* identify the IP header */
  iphdr = p->payload;


  if (iphdr->v != 6) {
    LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n"));
#if IP_DEBUG
    ip_debug_print(p);
#endif /* IP_DEBUG */
    pbuf_free(p);
#ifdef IP_STATS
    ++lwip_stats.ip.err;
    ++lwip_stats.ip.drop;
#endif /* IP_STATS */
    return;
  }

  /* is this packet for us? */
  for(netif = netif_list; netif != NULL; netif = netif->next) {
#if IP_DEBUG
    LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest "));
    ip_addr_debug_print(IP_DEBUG, &(iphdr->dest));
    LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr "));
    ip_addr_debug_print(IP_DEBUG, &(netif->ip_addr));
    LWIP_DEBUGF(IP_DEBUG, ("\n"));
#endif /* IP_DEBUG */
    if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) {
      break;
    }
  }


  if (netif == NULL) {
    /* packet not for us, route or discard */
#ifdef IP_FORWARD
    ip_forward(p, iphdr);
#endif
    pbuf_free(p);
    return;
  }

  pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len));

  /* send to upper layers */
#if IP_DEBUG
  /*  LWIP_DEBUGF("ip_input: \n");
  ip_debug_print(p);
  LWIP_DEBUGF("ip_input: p->len %u p->tot_len %u\n", p->len, p->tot_len);*/
#endif /* IP_DEBUG */


  pbuf_header(p, -IP_HLEN);

  switch (iphdr->nexthdr) {
  case IP_PROTO_UDP:
    udp_input(p);
    break;
  case IP_PROTO_TCP:
    tcp_input(p);
    break;
  case IP_PROTO_ICMP:
    icmp_input(p, inp);
    break;
  default:
    /* send ICMP destination protocol unreachable */
    icmp_dest_unreach(p, ICMP_DUR_PROTO);
    pbuf_free(p);
    LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %u\n",
          iphdr->nexthdr));

#ifdef IP_STATS
    ++lwip_stats.ip.proterr;
    ++lwip_stats.ip.drop;
#endif /* IP_STATS */

  }
  PERF_STOP("ip_input");
}
Esempio n. 3
0
/**
 * This function is called by the network interface device driver when
 * an IP packet is received. The function does the basic checks of the
 * IP header such as packet size being at least larger than the header
 * size etc. If the packet was not destined for us, the packet is
 * forwarded (using ip_forward). The IP checksum is always checked.
 *
 * Finally, the packet is sent to the upper layer protocol input function.
 * 
 * @param p the received IP packet (p->payload points to IP header)
 * @param inp the netif on which this packet was received
 * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
 *         processed, but currently always returns ERR_OK)
 */
err_t
ip_input(struct pbuf *p, struct netif *inp)
{
  struct ip_hdr *iphdr;
  struct netif *netif;
  u16_t iphdr_hlen;
  u16_t iphdr_len;
#if LWIP_DHCP
  int check_ip_src=1;
#endif /* LWIP_DHCP */
  
  IP_STATS_INC(ip.recv);
  snmp_inc_ipinreceives();

  /* identify the IP header */
  iphdr = p->payload;
  if (IPH_V(iphdr) != 4) {
    LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
    ip_debug_print(p);
    pbuf_free(p);
    IP_STATS_INC(ip.err);
    IP_STATS_INC(ip.drop);
    snmp_inc_ipinhdrerrors();
    return ERR_OK;
  }

  /* obtain IP header length in number of 32-bit words */
  iphdr_hlen = IPH_HL(iphdr);
  /* calculate IP header length in bytes */
  iphdr_hlen *= 4;
  /* obtain ip length in bytes */
  iphdr_len = ntohs(IPH_LEN(iphdr));

  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
    if (iphdr_hlen > p->len)
    LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
                               iphdr_hlen, p->len));
    if (iphdr_len > p->tot_len)
    LWIP_DEBUGF(IP_DEBUG | 2, ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), "
                               "IP packet dropped.\n",
                               iphdr_len, p->tot_len));
    /* free (drop) packet pbufs */
    pbuf_free(p);
    IP_STATS_INC(ip.lenerr);
    IP_STATS_INC(ip.drop);
    snmp_inc_ipindiscards();
    return ERR_OK;
  }

  /* verify checksum */
#if CHECKSUM_CHECK_IP
  if (inet_chksum(iphdr, iphdr_hlen) != 0) {

    LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
    ip_debug_print(p);
    pbuf_free(p);
    IP_STATS_INC(ip.chkerr);
    IP_STATS_INC(ip.drop);
    snmp_inc_ipinhdrerrors();
    return ERR_OK;
  }
#endif

  /* Trim pbuf. This should have been done at the netif layer,
   * but we'll do it anyway just to be sure that its done. */
  pbuf_realloc(p, iphdr_len);

  /* match packet against an interface, i.e. is this packet for us? */
#if LWIP_IGMP
  if (ip_addr_ismulticast(&(iphdr->dest))) {
    if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &(iphdr->dest)))) {
      netif = inp;
    } else {
      netif = NULL;
    }
  } else
#endif /* LWIP_IGMP */
  {
    /* start trying with inp. if that's not acceptable, start walking the
       list of configured netifs.
       'first' is used as a boolean to mark whether we started walking the list */
    int first = 1;
    netif = inp;
    do {
      LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
          iphdr->dest.addr, netif->ip_addr.addr,
          iphdr->dest.addr & netif->netmask.addr,
          netif->ip_addr.addr & netif->netmask.addr,
          iphdr->dest.addr & ~(netif->netmask.addr)));

      /* interface is up and configured? */
      if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
        /* unicast to this interface address? */
        if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) ||
            /* or broadcast on this interface network address? */
            ip_addr_isbroadcast(&(iphdr->dest), netif)) {
          LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
              netif->name[0], netif->name[1]));
          /* break out of for loop */
          break;
        }
      }
      if (first) {
        first = 0;
        netif = netif_list;
      } else {
        netif = netif->next;
      }
      if (netif == inp) {
        netif = netif->next;
      }
    } while(netif != NULL);
  }

#if LWIP_DHCP
  /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
   * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
   * According to RFC 1542 section 3.1.1, referred by RFC 2131).
   */
  if (netif == NULL) {
    /* remote port is DHCP server? */
    if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
        ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest)));
      if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest) == DHCP_CLIENT_PORT) {
        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n"));
        netif = inp;
        check_ip_src = 0;
      }
    }
  }
#endif /* LWIP_DHCP */

  /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
#if LWIP_DHCP
  if (check_ip_src)
#endif /* LWIP_DHCP */
  {  if ((ip_addr_isbroadcast(&(iphdr->src), inp)) ||
         (ip_addr_ismulticast(&(iphdr->src)))) {
      /* packet source is not valid */
      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: packet source is not valid.\n"));
      /* free (drop) packet pbufs */
      pbuf_free(p);
      IP_STATS_INC(ip.drop);
      snmp_inc_ipinaddrerrors();
      snmp_inc_ipindiscards();
      return ERR_OK;
    }
  }

  /* packet not for us? */
  if (netif == NULL) {
    /* packet not for us, route or discard */
    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: packet not for us.\n"));
#if IP_FORWARD
    /* non-broadcast packet? */
    if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) {
      /* try to forward IP packet on (other) interfaces */
      ip_forward(p, iphdr, inp);
    } else
#endif /* IP_FORWARD */
    {
      snmp_inc_ipinaddrerrors();
      snmp_inc_ipindiscards();
    }
    pbuf_free(p);
    return ERR_OK;
  }
  /* packet consists of multiple fragments? */
  if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
#if IP_REASSEMBLY /* packet fragment reassembly code present? */
    LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
      ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
    /* reassemble the packet*/
    p = ip_reass(p);
    /* packet not fully reassembled yet? */
    if (p == NULL) {
      return ERR_OK;
    }
    iphdr = p->payload;
#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
    pbuf_free(p);
    LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
      ntohs(IPH_OFFSET(iphdr))));
    IP_STATS_INC(ip.opterr);
    IP_STATS_INC(ip.drop);
    /* unsupported protocol feature */
    snmp_inc_ipinunknownprotos();
    return ERR_OK;
#endif /* IP_REASSEMBLY */
  }

#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */

#if LWIP_IGMP
  /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
  if((iphdr_hlen > IP_HLEN &&  (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
#else
  if (iphdr_hlen > IP_HLEN) {
#endif /* LWIP_IGMP */
    LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
    pbuf_free(p);
    IP_STATS_INC(ip.opterr);
    IP_STATS_INC(ip.drop);
    /* unsupported protocol feature */
    snmp_inc_ipinunknownprotos();
    return ERR_OK;
  }
#endif /* IP_OPTIONS_ALLOWED == 0 */

  /* send to upper layers */
  LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
  ip_debug_print(p);
  LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));

#if LWIP_RAW
  /* raw input did not eat the packet? */
  if (raw_input(p, inp) == 0)
#endif /* LWIP_RAW */
  {

    switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
    case IP_PROTO_UDP:
#if LWIP_UDPLITE
    case IP_PROTO_UDPLITE:
#endif /* LWIP_UDPLITE */
      snmp_inc_ipindelivers();
      udp_input(p, inp);
      break;
#endif /* LWIP_UDP */
#if LWIP_TCP
    case IP_PROTO_TCP:
      snmp_inc_ipindelivers();
      tcp_input(p, inp);
      break;
#endif /* LWIP_TCP */
#if LWIP_ICMP
    case IP_PROTO_ICMP:
      snmp_inc_ipindelivers();
      icmp_input(p, inp);
      break;
#endif /* LWIP_ICMP */
#if LWIP_IGMP
    case IP_PROTO_IGMP:
      igmp_input(p,inp,&(iphdr->dest));
      break;
#endif /* LWIP_IGMP */
    default:
#if LWIP_ICMP
      /* send ICMP destination protocol unreachable unless is was a broadcast */
      if (!ip_addr_isbroadcast(&(iphdr->dest), inp) &&
          !ip_addr_ismulticast(&(iphdr->dest))) {
        p->payload = iphdr;
        icmp_dest_unreach(p, ICMP_DUR_PROTO);
      }
#endif /* LWIP_ICMP */
      pbuf_free(p);

      LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));

      IP_STATS_INC(ip.proterr);
      IP_STATS_INC(ip.drop);
      snmp_inc_ipinunknownprotos();
    }
  }

  return ERR_OK;
}

/**
 * Sends an IP packet on a network interface. This function constructs
 * the IP header and calculates the IP header checksum. If the source
 * IP address is NULL, the IP address of the outgoing network
 * interface is filled in as source address.
 * If the destination IP address is IP_HDRINCL, p is assumed to already
 * include an IP header and p->payload points to it instead of the data.
 *
 * @param p the packet to send (p->payload points to the data, e.g. next
            protocol header; if dest == IP_HDRINCL, p already includes an IP
            header and p->payload points to that IP header)
 * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
 *         IP  address of the netif used to send is used as source address)
 * @param dest the destination IP address to send the packet to
 * @param ttl the TTL value to be set in the IP header
 * @param tos the TOS value to be set in the IP header
 * @param proto the PROTOCOL to be set in the IP header
 * @param netif the netif on which to send this packet
 * @return ERR_OK if the packet was sent OK
 *         ERR_BUF if p doesn't have enough space for IP/LINK headers
 *         returns errors returned by netif->output
 *
 * @note ip_id: RFC791 "some host may be able to simply use
 *  unique identifiers independent of destination"
 */
err_t
ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
             u8_t ttl, u8_t tos,
             u8_t proto, struct netif *netif)
{
  struct ip_hdr *iphdr;
  static u16_t ip_id = 0;

  snmp_inc_ipoutrequests();

  /* Should the IP header be generated or is it already included in p? */
  if (dest != IP_HDRINCL) {
    /* generate IP header */
    if (pbuf_header(p, IP_HLEN)) {
      LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n"));

      IP_STATS_INC(ip.err);
      snmp_inc_ipoutdiscards();
      return ERR_BUF;
    }

    iphdr = p->payload;
    LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
               (p->len >= sizeof(struct ip_hdr)));

    IPH_TTL_SET(iphdr, ttl);
    IPH_PROTO_SET(iphdr, proto);

    ip_addr_set(&(iphdr->dest), dest);

    IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos);
    IPH_LEN_SET(iphdr, htons(p->tot_len));
    IPH_OFFSET_SET(iphdr, 0);
    IPH_ID_SET(iphdr, htons(ip_id));
    ++ip_id;

    if (ip_addr_isany(src)) {
      ip_addr_set(&(iphdr->src), &(netif->ip_addr));
    } else {
      ip_addr_set(&(iphdr->src), src);
    }

    IPH_CHKSUM_SET(iphdr, 0);
#if CHECKSUM_GEN_IP
    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
#endif
  } else {
    /* IP header already included in p */
    iphdr = p->payload;
    dest = &(iphdr->dest);
  }

#if IP_FRAG
  /* don't fragment if interface has mtu set to 0 [loopif] */
  if (netif->mtu && (p->tot_len > netif->mtu))
    return ip_frag(p,netif,dest);
#endif

  IP_STATS_INC(ip.xmit);

  LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
  ip_debug_print(p);

  LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));

  return netif->output(netif, p, dest);
}

/**
 * Simple interface to ip_output_if. It finds the outgoing network
 * interface and calls upon ip_output_if to do the actual work.
 *
 * @param p the packet to send (p->payload points to the data, e.g. next
            protocol header; if dest == IP_HDRINCL, p already includes an IP
            header and p->payload points to that IP header)
 * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
 *         IP  address of the netif used to send is used as source address)
 * @param dest the destination IP address to send the packet to
 * @param ttl the TTL value to be set in the IP header
 * @param tos the TOS value to be set in the IP header
 * @param proto the PROTOCOL to be set in the IP header
 *
 * @return ERR_RTE if no route is found
 *         see ip_output_if() for more return values
 */
err_t
ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
          u8_t ttl, u8_t tos, u8_t proto)
{
  struct netif *netif;

  if ((netif = ip_route(dest)) == NULL) {
    return ERR_RTE;
  }

  return ip_output_if(p, src, dest, ttl, tos, proto, netif);
}

#if IP_DEBUG
/* Print an IP header by using LWIP_DEBUGF
 * @param p an IP packet, p->payload pointing to the IP header
 */
void
ip_debug_print(struct pbuf *p)
{
  struct ip_hdr *iphdr = p->payload;
  u8_t *payload;

  payload = (u8_t *)iphdr + IP_HLEN;

  LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
  LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" |  0x%02"X16_F" |     %5"U16_F"     | (v, hl, tos, len)\n",
                    IPH_V(iphdr),
                    IPH_HL(iphdr),
                    IPH_TOS(iphdr),
                    ntohs(IPH_LEN(iphdr))));
  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
  LWIP_DEBUGF(IP_DEBUG, ("|    %5"U16_F"      |%"U16_F"%"U16_F"%"U16_F"|    %4"U16_F"   | (id, flags, offset)\n",
                    ntohs(IPH_ID(iphdr)),
                    ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
                    ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
                    ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
                    ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |    0x%04"X16_F"     | (ttl, proto, chksum)\n",
                    IPH_TTL(iphdr),
                    IPH_PROTO(iphdr),
                    ntohs(IPH_CHKSUM(iphdr))));
  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (src)\n",
                    ip4_addr1(&iphdr->src),
                    ip4_addr2(&iphdr->src),
                    ip4_addr3(&iphdr->src),
                    ip4_addr4(&iphdr->src)));
  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (dest)\n",
                    ip4_addr1(&iphdr->dest),
                    ip4_addr2(&iphdr->dest),
                    ip4_addr3(&iphdr->dest),
                    ip4_addr4(&iphdr->dest)));
  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
}
Esempio n. 4
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 | 1, ("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 | 2, ("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 | 2, ("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 | 2, ("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. 5
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 = 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(&(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(&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) &&
                    ((!broadcast && 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 */
#if IP_SOF_BROADCAST_RECV
                     (broadcast && (pcb->so_options & SOF_BROADCAST)))) {
#else  /* IP_SOF_BROADCAST_RECV */
                     (broadcast))) {
#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), &(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 */
                pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), 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 (!broadcast &&
                    !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. 6
0
static void ip_addr_cmp_check_(struct ip_addr const *a, struct ip_addr const *b, int expected)
{
    int cmp = ip_addr_cmp(a, b);
    assert(cmp == expected);
}
Esempio n. 7
0
/**
 * Search the ARP table for a matching or new entry.
 * 
 * If an IP address is given, return a pending or stable ARP entry that matches
 * the address. If no match is found, create a new entry with this address set,
 * but in state ETHARP_EMPTY. The caller must check and possibly change the
 * state of the returned entry.
 * 
 * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
 * 
 * In all cases, attempt to create new entries from an empty entry. If no
 * empty entries are available and ETHARP_TRY_HARD flag is set, recycle
 * old entries. Heuristic choose the least important entry for recycling.
 *
 * @param ipaddr IP address to find in ARP cache, or to add if not found.
 * @param flags
 * - ETHARP_TRY_HARD: Try hard to create a entry by allowing recycling of
 * active (stable or pending) entries.
 *  
 * @return The ARP entry index that matched or is created, ERR_MEM if no
 * entry is found or could be recycled.
 */
static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
{
  s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
  s8_t empty = ARP_TABLE_SIZE;
  u8_t i = 0, age_pending = 0, age_stable = 0;
#if ARP_QUEUEING
  /* oldest entry with packets on queue */
  s8_t old_queue = ARP_TABLE_SIZE;
  /* its age */
  u8_t age_queue = 0;
#endif

  /**
   * a) do a search through the cache, remember candidates
   * b) select candidate entry
   * c) create new entry
   */

  /* a) in a single search sweep, do all of this
   * 1) remember the first empty entry (if any)
   * 2) remember the oldest stable entry (if any)
   * 3) remember the oldest pending entry without queued packets (if any)
   * 4) remember the oldest pending entry with queued packets (if any)
   * 5) search for a matching IP entry, either pending or stable
   *    until 5 matches, or all entries are searched for.
   */

  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
    /* no empty entry found yet and now we do find one? */
    if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) {
      LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
      /* remember first empty entry */
      empty = i;
    }
    /* pending entry? */
    else if (arp_table[i].state == ETHARP_STATE_PENDING) {
      /* if given, does IP address match IP address in ARP entry? */
      if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i));
        /* found exact IP address match, simply bail out */
        return i;
#if ARP_QUEUEING
      /* pending with queued packets? */
      } else if (arp_table[i].p != NULL) {
        if (arp_table[i].ctime >= age_queue) {
          old_queue = i;
          age_queue = arp_table[i].ctime;
        }
#endif
      /* pending without queued packets? */
      } else {
        if (arp_table[i].ctime >= age_pending) {
          old_pending = i;
          age_pending = arp_table[i].ctime;
        }
      }        
    }
    /* stable entry? */
    else if (arp_table[i].state == ETHARP_STATE_STABLE) {
      /* if given, does IP address match IP address in ARP entry? */
      if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i));
        /* found exact IP address match, simply bail out */
        return i;
      /* remember entry with oldest stable entry in oldest, its age in maxtime */
      } else if (arp_table[i].ctime >= age_stable) {
        old_stable = i;
        age_stable = arp_table[i].ctime;
      }
    }
  }
  /* { we have no match } => try to create a new entry */
   
  /* no empty entry found and not allowed to recycle? */
  if ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0))
  {
    return (s8_t)ERR_MEM;
  }
  
  /* b) choose the least destructive entry to recycle:
   * 1) empty entry
   * 2) oldest stable entry
   * 3) oldest pending entry without queued packets
   * 4) oldest pending entry without queued packets
   * 
   * { ETHARP_TRY_HARD is set at this point }
   */ 

  /* 1) empty entry available? */
  if (empty < ARP_TABLE_SIZE) {
    i = empty;
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
  }
  /* 2) found recyclable stable entry? */
  else if (old_stable < ARP_TABLE_SIZE) {
    /* recycle oldest stable*/
    i = old_stable;
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
#if ARP_QUEUEING
    /* no queued packets should exist on stable entries */
    LWIP_ASSERT("arp_table[i].p == NULL", arp_table[i].p == NULL);
#endif
  /* 3) found recyclable pending entry without queued packets? */
  } else if (old_pending < ARP_TABLE_SIZE) {
    /* recycle oldest pending */
    i = old_pending;
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
#if ARP_QUEUEING
  /* 4) found recyclable pending entry with queued packets? */
  } else if (old_queue < ARP_TABLE_SIZE) {
    /* recycle oldest pending */
    i = old_queue;
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].p)));
    pbuf_free(arp_table[i].p);
    arp_table[i].p = NULL;
#endif
    /* no empty or recyclable entries found */
  } else {
    return (s8_t)ERR_MEM;
  }

  /* { empty or recyclable entry found } */
  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);

  if (arp_table[i].state != ETHARP_STATE_EMPTY)
  {
    snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
  }
  /* recycle entry (no-op for an already empty entry) */
  arp_table[i].state = ETHARP_STATE_EMPTY;

  /* IP address given? */
  if (ipaddr != NULL) {
    /* set IP address */
    ip_addr_set(&arp_table[i].ipaddr, ipaddr);
  }
  arp_table[i].ctime = 0;
  return (err_t)i;
}
Esempio n. 8
0
s8_t uip_ipinput(struct uip_pbuf *p,struct uip_netif *inp)
{
	u16_t iphdr_len;
	struct uip_ip_hdr *iphdr;
	struct uip_netif *netif;

	iphdr = p->payload;
	if(UIP_IPH_V(iphdr)!=4) {
		UIP_ERROR("uip_ipinput: ip packet dropped due to bad version number.\n");
		uip_pbuf_free(p);
		return 0;
	}
	
	iphdr_len = UIP_IPH_HL(iphdr);
	iphdr_len *= 4;

	if(iphdr_len>p->len) {
		UIP_ERROR("uip_ipinput: ip packet dropped due to too small packet size.\n");
		uip_pbuf_free(p);
		return 0;
	}
	
	if(uip_ipchksum(iphdr,iphdr_len)!=0) {
	    UIP_STAT(++uip_stat.ip.drop);
	    UIP_STAT(++uip_stat.ip.chkerr);
		UIP_ERROR("uip_ipinput: bad checksum.\n");
		uip_pbuf_free(p);
		return 0;
	}
	
	uip_pbuf_realloc(p,ntohs(UIP_IPH_LEN(iphdr)));
	
	for(netif=uip_netif_list;netif!=NULL;netif=netif->next) {
		if(uip_netif_isup(netif) && !ip_addr_isany(&netif->ip_addr)) {
			if(ip_addr_cmp(&iphdr->dst,&netif->ip_addr) ||
				ip_addr_isbroadcast(&iphdr->dst,netif)) break;
		}
	}

	if(!netif) {
		UIP_ERROR("uip_ipinput: no route found.\n");
		uip_pbuf_free(p);
		return 0;
	}
	
	if((UIP_IPH_OFFSET(iphdr)&htons(UIP_IP_OFFMASK|UIP_IP_MF))!=0) {
#if UIP_IP_REASSEMBLY
		p = uip_ipreass(p);
		if(p==NULL) return UIP_ERR_OK;

		iphdr = (struct uip_ip_hdr*)p->payload;
#else
		uip_pbuf_free(p);
	    UIP_STAT(++uip_stat.ip.drop);
		UIP_ERROR("ip: fragment dropped.\n");
		return 0;
#endif
	}

	switch(UIP_IPH_PROTO(iphdr)) {
		case UIP_PROTO_TCP:
			uip_tcpinput(p,inp);
			break;
		case UIP_PROTO_ICMP:
			uip_icmpinput(p,inp);
			break;
		default:
			UIP_LOG("uip_ipinput: Unsupported protocol.\n");
			if(!ip_addr_isbroadcast(&(iphdr->dst),inp)
				&& !ip_addr_ismulticast(&(iphdr->dst))) {
				p->payload = iphdr;
				uip_icmp_destunreach(p,UIP_ICMP_DUR_PROTO);
			}
			uip_pbuf_free(p);
			break;
	}
	return 0;
}
Esempio n. 9
0
static struct uip_pbuf* uip_ipreass(struct uip_pbuf *p)
{
  u16_t offset, len;
  u16_t i;
  struct uip_pbuf *q;
  struct uip_ip_hdr *iphdr,*fraghdr;

  /* If ip_reasstmr is zero, no packet is present in the buffer, so we
     write the IP header of the fragment into the reassembly
     buffer. The timer is updated with the maximum age. */
  iphdr = (struct uip_ip_hdr*)uip_reassbuf;
  fraghdr = (struct uip_ip_hdr*)p->payload;
  if(uip_reasstmr == 0) {
    UIP_MEMCPY(iphdr, fraghdr, UIP_IP_HLEN);
    uip_reasstmr = UIP_REASS_MAXAGE;
    uip_reassflags = 0;
	uip_reasstime = gettime();
    /* Clear the bitmap. */
    UIP_MEMSET(uip_reassbitmap, 0,sizeof(uip_reassbitmap));
  }

  /* Check if the incoming fragment matches the one currently present
     in the reasembly buffer. If so, we proceed with copying the
     fragment into the buffer. */
  if(ip_addr_cmp(&iphdr->src,&fraghdr->src) 
	  && ip_addr_cmp(&iphdr->dst,&fraghdr->dst)
	  && UIP_IPH_ID(iphdr) == UIP_IPH_ID(fraghdr)) {

	len = ntohs(UIP_IPH_LEN(fraghdr)) - UIP_IPH_HL(fraghdr)*4;
	offset = (ntohs(UIP_IPH_OFFSET(fraghdr))&UIP_IP_OFFMASK)*8;

    /* If the offset or the offset + fragment length overflows the
       reassembly buffer, we discard the entire packet. */
    if(offset > UIP_REASS_BUFSIZE ||
       offset + len > UIP_REASS_BUFSIZE) {
      uip_reasstmr = 0;
	  uip_reasstime = 0;
      goto nullreturn;
    }

    /* Copy the fragment into the reassembly buffer, at the right
       offset. */
	i = UIP_IPH_HL(fraghdr)*4;
    uip_copyfrom_pbuf(p,&i,&uip_reassbuf[UIP_IP_HLEN+offset],len);
      
    /* Update the bitmap. */
    if(offset / (8 * 8) == (offset + len) / (8 * 8)) {
      /* If the two endpoints are in the same byte, we only update
	 that byte. */
	     
      uip_reassbitmap[offset / (8 * 8)] |=
	     bitmap_bits[(offset / 8 ) & 7] &
	     ~bitmap_bits[((offset + len) / 8 ) & 7];
    } else {
      /* If the two endpoints are in different bytes, we update the
	 bytes in the endpoints and fill the stuff inbetween with
	 0xff. */
      uip_reassbitmap[offset / (8 * 8)] |=
	bitmap_bits[(offset / 8 ) & 7];
      for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
	uip_reassbitmap[i] = 0xff;
      }      
      uip_reassbitmap[(offset + len) / (8 * 8)] |=
	~bitmap_bits[((offset + len) / 8 ) & 7];
    }
    
    /* If this fragment has the More Fragments flag set to zero, we
       know that this is the last fragment, so we can calculate the
       size of the entire packet. We also set the
       IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
       the final fragment. */

    if((ntohs(UIP_IPH_OFFSET(fraghdr))&UIP_IP_MF)==0) {
      uip_reassflags |= UIP_REASS_FLAG_LASTFRAG;
      uip_reasslen = offset + len;
    }
    
    /* Finally, we check if we have a full packet in the buffer. We do
       this by checking if we have the last fragment and if all bits
       in the bitmap are set. */
    if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) {
      /* Check all bytes up to and including all but the last byte in
	 the bitmap. */
      for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) {
	if(uip_reassbitmap[i] != 0xff) {
	  goto nullreturn;
	}
      }
      /* Check the last byte in the bitmap. It should contain just the
	 right amount of bits. */
      if(uip_reassbitmap[uip_reasslen / (8 * 8)] !=
	 (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) {
	goto nullreturn;
      }

	  uip_reasslen += UIP_IP_HLEN;

      /* Pretend to be a "normal" (i.e., not fragmented) IP packet
	 from now on. */
	  UIP_IPH_LEN_SET(iphdr,htons(uip_reasslen));
	  UIP_IPH_OFFSET_SET(iphdr,0);
	  UIP_IPH_CHKSUM_SET(iphdr,0);
	  UIP_IPH_CHKSUM_SET(iphdr,uip_ipchksum(iphdr,UIP_IP_HLEN));
	
      /* If we have come this far, we have a full packet in the
	 buffer, so we allocate a pbuf and copy the packet into it. We
	 also reset the timer. */
      uip_reasstmr = 0;
	  uip_reasstime = 0;
	  uip_pbuf_free(p);
	  p = uip_pbuf_alloc(UIP_PBUF_LINK,uip_reasslen,UIP_PBUF_POOL);
	  if(p==NULL) return NULL;
	  
	  i = 0;
	  for(q=p;q!=NULL;q=q->next) {
		  UIP_MEMCPY(q->payload,&uip_reassbuf[i],((q->len>(uip_reasslen-i))?(uip_reasslen-i):q->len));
		  i += q->len;
	  }
      return p;
    }
  }

 nullreturn:
  uip_pbuf_free(p);
  return NULL;
}
Esempio n. 10
0
/*!
 * \brief Perform outbound processing - force local socket and set destination URI
 * \param _m SIP message
 * \param flow_token string containing the flow-token extracted from the Route: header
 * \param dst_uri string to write the destination URI to (extracted from flow-token)
 * \return -1 on error, 0 when outbound not in use, 1 when outbound in use
 */
static inline int process_outbound(struct sip_msg *_m, str flow_token)
{
	int ret;
	struct receive_info *rcv = NULL;
	struct socket_info *si;
	str dst_uri;

	if (!rr_obb.decode_flow_token)
		return 0;

	ret = rr_obb.decode_flow_token(_m, &rcv, flow_token);

	if (ret == -2) {
		LM_DBG("no flow token found - outbound not in use\n");
		return 0;
	} else if (ret == -1) {
		LM_INFO("failed to decode flow token\n");
		return -1;
	} else if (!ip_addr_cmp(&rcv->src_ip, &_m->rcv.src_ip)
			|| rcv->src_port != _m->rcv.src_port) {
		LM_DBG("\"incoming\" request found. Using flow-token for"
			"routing\n");

		/* First, force the local socket */
		si = find_si(&rcv->dst_ip, rcv->dst_port, rcv->proto);
		if (si)
			set_force_socket(_m, si);
		else {
			LM_INFO("cannot find socket from flow-token\n");
			return -1;
		}

		/* Second, override the destination URI */
		dst_uri.s = uri_buf;
		dst_uri.len = 0;

		dst_uri.len += snprintf(dst_uri.s + dst_uri.len,
					MAX_ROUTE_URI_LEN - dst_uri.len,
					"sip:%s",
					rcv->src_ip.af == AF_INET6 ? "[" : "");
		dst_uri.len += ip_addr2sbuf(&rcv->src_ip,
					dst_uri.s + dst_uri.len,
					MAX_ROUTE_URI_LEN - dst_uri.len);
		dst_uri.len += snprintf(dst_uri.s + dst_uri.len,
					MAX_ROUTE_URI_LEN - dst_uri.len,
					"%s:%d;transport=%s",
					rcv->src_ip.af == AF_INET6 ? "]" : "",
					rcv->src_port,
					get_proto_name(rcv->proto));

		if (set_dst_uri(_m, &dst_uri) < 0) {
			LM_ERR("failed to set dst_uri\n");
			return -1;
		}
		ruri_mark_new();

		return 1;
	}

	LM_DBG("Not using flow-token for routing\n");
	return 0;
}
Esempio n. 11
0
/*
 * Check if from user is a valid enum based user, and check to make sure
 * that the src_ip == an srv record that maps to the enum from user.
 */
int is_from_user_enum_2(struct sip_msg* _msg, char* _suffix, char* _service)
{
	struct ip_addr addr;
	struct hostent* he;
	unsigned short zp;
	unsigned short proto;
	char *user_s;
	int user_len, i, j;
	char name[MAX_DOMAIN_SIZE];
	char uri[MAX_URI_SIZE];
	struct sip_uri *furi;
	struct sip_uri luri;
	struct rdata* head;

	str* suffix;
	str* service;

	struct rdata* l;
	struct naptr_rdata* naptr;

	str pattern, replacement, result;
	char string[17];

	if (parse_from_header(_msg) < 0) {
	    LM_ERR("Failed to parse From header\n");
	    return -1;
	}

	if(_msg->from==NULL || get_from(_msg)==NULL) {
	    LM_DBG("No From header\n");
	    return -1;
	}

	if ((furi = parse_from_uri(_msg)) == NULL) {
	    LM_ERR("Failed to parse From URI\n");
	    return -1;
	}

	suffix = (str*)_suffix;
	service = (str*)_service;

	if (is_e164(&(furi->user)) == -1) {
	    LM_ERR("From URI user is not an E164 number\n");
	    return -1;
	}

	/* assert: the from user is a valid formatted e164 string */

	user_s = furi->user.s;
	user_len = furi->user.len;

	j = 0;
	for (i = user_len - 1; i > 0; i--) {
		name[j] = user_s[i];
		name[j + 1] = '.';
		j = j + 2;
	}

	memcpy(name + j, suffix->s, suffix->len + 1);

	head = get_record(name, T_NAPTR);

	if (head == 0) {
		LM_DBG("No NAPTR record found for %s.\n", name);
		return -3;
	}

	/* we have the naptr records, loop and find an srv record with */
	/* same ip address as source ip address, if we do then true is returned */

	for (l = head; l; l = l->next) {

		if (l->type != T_NAPTR) continue; /*should never happen*/
		naptr = (struct naptr_rdata*)l->rdata;
		if (naptr == 0) {
			LM_ERR("Null rdata in DNS response\n");
			free_rdata_list(head);
			return -4;
		}

		LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags "
		       "'%.*s', slen %u, services '%.*s', rlen %u, "
		       "regexp '%.*s'\n",
		       name, naptr->order, naptr->pref,
		    naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len,
		    (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len,
		    (int)(naptr->regexp_len), ZSW(naptr->regexp));

		if (sip_match(naptr, service) != 0) {
			if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len,
					 &pattern, &replacement) < 0) {
				free_rdata_list(head); /*clean up*/
				LM_ERR("Parsing of NAPTR regexp failed\n");
				return -5;
			}
#ifdef LATER
			if ((pattern.len == 4) && (strncmp(pattern.s, "^.*$", 4) == 0)) {
				LM_DBG("Resulted in replacement: '%.*s'\n",
				       replacement.len, ZSW(replacement.s));
				retval = set_uri(_msg, replacement.s, replacement.len);
				free_rdata_list(head); /*clean up*/
				return retval;
			}
#endif
			result.s = &(uri[0]);
			result.len = MAX_URI_SIZE;
			/* Avoid making copies of pattern and replacement */
			pattern.s[pattern.len] = (char)0;
			replacement.s[replacement.len] = (char)0;
			/* We have already checked the size of
			   _msg->parsed_uri.user.s */
			memcpy(&(string[0]), user_s, user_len);
			string[user_len] = (char)0;
			if (reg_replace(pattern.s, replacement.s, &(string[0]),
					&result) < 0) {
				pattern.s[pattern.len] = '!';
				replacement.s[replacement.len] = '!';
				LM_ERR("Regexp replace failed\n");
				free_rdata_list(head); /*clean up*/
				return -6;
			}
			LM_DBG("Resulted in replacement: '%.*s'\n",
			    result.len, ZSW(result.s));

			if(parse_uri(result.s, result.len, &luri) < 0)
			{
				LM_ERR("Parsing of URI <%.*s> failed\n",
				       result.len, result.s);
				free_rdata_list(head); /*clean up*/
				return -7;
			}

			pattern.s[pattern.len] = '!';
			replacement.s[replacement.len] = '!';

			zp = 0;
			proto = PROTO_NONE;
			he = sip_resolvehost(&luri.host, &zp, &proto,
				(luri.type==SIPS_URI_T)?1:0 , 0);
			if (he == NULL){
				LM_ERR("Resolving URI <%.*s> failed\n",
					   result.len, result.s);
				free_rdata_list(head); /*clean up*/
				return -9;
			}

			hostent2ip_addr(&addr, he, 0);

			if(ip_addr_cmp(&addr, &_msg->rcv.src_ip))
			{
				free_rdata_list(head);
				return(1);
			}
		}
	}
	free_rdata_list(head); /*clean up*/
	LM_DBG("FAIL\n");

    /* must not have found the record */
    return(-8);
}
Esempio n. 12
0
/**
 * Search the ARP table for a matching or new entry.
 * 
 * If an IP address is given, return a pending or stable ARP entry that matches
 * the address. If no match is found, create a new entry with this address set,
 * but in state ETHARP_EMPTY. The caller must check and possibly change the
 * state of the returned entry.
 * 
 * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
 * 
 * In all cases, attempt to create new entries from an empty entry. If no
 * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
 * old entries. Heuristic choose the least important entry for recycling.
 *
 * @param ipaddr IP address to find in ARP cache, or to add if not found.
 * @param flags @see definition of ETHARP_FLAG_*
 * @param netif netif related to this address (used for NETIF_HWADDRHINT)
 *  
 * @return The ARP entry index that matched or is created, ERR_MEM if no
 * entry is found or could be recycled.
 */
static s8_t
find_entry(ip_addr_t *ipaddr, u8_t flags)
{
  s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
  s8_t empty = ARP_TABLE_SIZE;
  u8_t i = 0, age_pending = 0, age_stable = 0;
  /* oldest entry with packets on queue */
  s8_t old_queue = ARP_TABLE_SIZE;
  /* its age */
  u8_t age_queue = 0;

  /**
   * a) do a search through the cache, remember candidates
   * b) select candidate entry
   * c) create new entry
   */

  /* a) in a single search sweep, do all of this
   * 1) remember the first empty entry (if any)
   * 2) remember the oldest stable entry (if any)
   * 3) remember the oldest pending entry without queued packets (if any)
   * 4) remember the oldest pending entry with queued packets (if any)
   * 5) search for a matching IP entry, either pending or stable
   *    until 5 matches, or all entries are searched for.
   */

  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
    u8_t state = arp_table[i].state;
    /* no empty entry found yet and now we do find one? */
    if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
      LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
      /* remember first empty entry */
      empty = i;
    } else if (state != ETHARP_STATE_EMPTY) {
      LWIP_ASSERT("state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE",
        state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE);
      /* if given, does IP address match IP address in ARP entry? */
      if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i));
        /* found exact IP address match, simply bail out */
        return i;
      }
      /* pending entry? */
      if (state == ETHARP_STATE_PENDING) {
        /* pending with queued packets? */
        if (arp_table[i].q != NULL) {
          if (arp_table[i].ctime >= age_queue) {
            old_queue = i;
            age_queue = arp_table[i].ctime;
          }
        } else
        /* pending without queued packets? */
        {
          if (arp_table[i].ctime >= age_pending) {
            old_pending = i;
            age_pending = arp_table[i].ctime;
          }
        }
      /* stable entry? */
      } else if (state == ETHARP_STATE_STABLE) {
#if ETHARP_SUPPORT_STATIC_ENTRIES
        /* don't record old_stable for static entries since they never expire */
        if (arp_table[i].static_entry == 0)
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
        {
          /* remember entry with oldest stable entry in oldest, its age in maxtime */
          if (arp_table[i].ctime >= age_stable) {
            old_stable = i;
            age_stable = arp_table[i].ctime;
          }
        }
      }
    }
  }
  /* { we have no match } => try to create a new entry */
   
  /* don't create new entry, only search? */
  if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
      /* or no empty entry found and not allowed to recycle? */
      ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
    return (s8_t)ERR_MEM;
  }
  
  /* b) choose the least destructive entry to recycle:
   * 1) empty entry
   * 2) oldest stable entry
   * 3) oldest pending entry without queued packets
   * 4) oldest pending entry with queued packets
   * 
   * { ETHARP_FLAG_TRY_HARD is set at this point }
   */ 

  /* 1) empty entry available? */
  if (empty < ARP_TABLE_SIZE) {
    i = empty;
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
  } else {
    /* 2) found recyclable stable entry? */
    if (old_stable < ARP_TABLE_SIZE) {
      /* recycle oldest stable*/
      i = old_stable;
      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
      /* no queued packets should exist on stable entries */
      LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
    /* 3) found recyclable pending entry without queued packets? */
    } else if (old_pending < ARP_TABLE_SIZE) {
      /* recycle oldest pending */
      i = old_pending;
      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
    /* 4) found recyclable pending entry with queued packets? */
    } else if (old_queue < ARP_TABLE_SIZE) {
      /* recycle oldest pending (queued packets are free in free_entry) */
      i = old_queue;
      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
      /* no empty or recyclable entries found */
    } else {
      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n"));
      return (s8_t)ERR_MEM;
    }

    /* { empty or recyclable entry found } */
    LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
    free_entry(i);
  }

  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
  LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
    arp_table[i].state == ETHARP_STATE_EMPTY);

  /* IP address given? */
  if (ipaddr != NULL) {
    /* set IP address */
    ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
  }
  arp_table[i].ctime = 0;
#if ETHARP_SUPPORT_STATIC_ENTRIES
  arp_table[i].static_entry = 0;
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
  return (err_t)i;
}
Esempio n. 13
0
/**
 * Send an ARP request for the given IP address.
 *
 * Sends an ARP request for the given IP address, unless
 * a request for this address is already pending. Optionally
 * queues an outgoing packet on the resulting ARP entry.
 *
 * @param netif The lwIP network interface where ipaddr
 * must be queried for.
 * @param ipaddr The IP address to be resolved.
 * @param q If non-NULL, a pbuf that must be queued on the
 * ARP entry for the ipaddr IP address.
 *
 * @return NULL.
 *
 * @note Might be used in the future by manual IP configuration
 * as well.
 *
 * TODO: use the ctime field to see how long ago an ARP request was sent,
 * possibly retry.
 */
err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
{
  struct eth_addr *srcaddr;
  struct etharp_hdr *hdr;
  err_t result = ERR_OK;
  s8_t i;
  u8_t perform_arp_request = 1;
  /* prevent 'unused argument' warning if ARP_QUEUEING == 0 */
  (void)q;
  srcaddr = (struct eth_addr *)netif->hwaddr;
  /* bail out if this IP address is pending */
  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
    if (ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
      if (arp_table[i].state == ETHARP_STATE_PENDING) {
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already pending as entry %u\n", i));
        /* break out of for-loop, user may wish to queue a packet on a pending entry */
        /* TODO: we will issue a new ARP request, which should not occur too often */
        /* we might want to run a faster timer on ARP to limit this */
        break;
      }
      else if (arp_table[i].state == ETHARP_STATE_STABLE) {
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already stable as entry %u\n", i));
        /* User wishes to queue a packet on a stable entry (or does she want to send
         * out the packet immediately, we will not know), so we force an ARP request.
         * Upon response we will send out the queued packet in etharp_update().
         * 
         * Alternatively, we could accept the stable entry, and just send out the packet
         * immediately. I chose to implement the former approach.
         */
        perform_arp_request = (q?1:0);
        break;
      }
    }
  }
  /* queried address not yet in ARP table? */
  if (i == ARP_TABLE_SIZE) {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: IP address not found in ARP table\n"));
    /* find an available (unused or old) entry */
    i = find_arp_entry();
    /* bail out if no ARP entries are available */
    if (i == ERR_MEM) {
      LWIP_DEBUGF(ETHARP_DEBUG | 2, ("etharp_query: no more ARP entries available. Should seldom occur.\n"));
      return ERR_MEM;
    }
    /* i is available, create ARP entry */
    arp_table[i].state = ETHARP_STATE_PENDING;
    ip_addr_set(&arp_table[i].ipaddr, ipaddr);
  }
  /* { i is now valid } */
#if ARP_QUEUEING /* queue packet (even on a stable entry, see above) */
  etharp_enqueue(i, q);
#endif
  /* ARP request? */
  if (perform_arp_request)
  {
    struct pbuf *p;
    /* allocate a pbuf for the outgoing ARP request packet */
    p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM);
    /* could allocate pbuf? */
    if (p != NULL) {
      u8_t j;
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending ARP request.\n"));
      hdr = p->payload;
      hdr->opcode = htons(ARP_REQUEST);
      for (j = 0; j < netif->hwaddr_len; ++j)
      {
        hdr->shwaddr.addr[j] = srcaddr->addr[j];
        /* the hardware address is what we ask for, in
         * a request it is a don't-care, we use 0's */
        hdr->dhwaddr.addr[j] = 0x00;
      }
      ip_addr_set(&(hdr->dipaddr), ipaddr);
      ip_addr_set(&(hdr->sipaddr), &(netif->ip_addr));

      hdr->hwtype = htons(HWTYPE_ETHERNET);
      ARPH_HWLEN_SET(hdr, netif->hwaddr_len);

      hdr->proto = htons(ETHTYPE_IP);
      ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));
      for (j = 0; j < netif->hwaddr_len; ++j)
      {
        hdr->ethhdr.dest.addr[j] = 0xff;
        hdr->ethhdr.src.addr[j] = srcaddr->addr[j];
      }
      hdr->ethhdr.type = htons(ETHTYPE_ARP);
      /* send ARP query */
      result = netif->linkoutput(netif, p);
      /* free ARP query packet */
      pbuf_free(p);
      p = NULL;
    } else {
      result = ERR_MEM;
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_query: could not allocate pbuf for ARP request.\n"));
    }
  }
  return result;
}
Esempio n. 14
0
/**
 * Resolve and fill-in Ethernet address header for outgoing packet.
 *
 * If ARP has the Ethernet address in cache, the given packet is
 * returned, ready to be sent.
 *
 * If ARP does not have the Ethernet address in cache the packet is
 * queued (if enabled and space available) and a ARP request is sent.
 * This ARP request is returned as a pbuf, which should be sent by
 * the caller.
 *
 * A returned non-NULL packet should be sent by the caller.
 *
 * If ARP failed to allocate resources, NULL is returned.
 *
 * @param netif The lwIP network interface which the IP packet will be sent on.
 * @param ipaddr The IP address of the packet destination.
 * @param pbuf The pbuf(s) containing the IP packet to be sent.
 *
 * @return If non-NULL, a packet ready to be sent by caller.
 *
 */
struct pbuf *
etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
{
  struct eth_addr *dest, *srcaddr, mcastaddr;
  struct eth_hdr *ethhdr;
  s8_t i;

  /* make room for Ethernet header */
  if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
    /* The pbuf_header() call shouldn't fail, and we'll just bail
    out if it does.. */
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_output: could not allocate room for header.\n"));
    LINK_STATS_INC(link.lenerr);
    return NULL;
  }

  /* assume unresolved Ethernet address */
  dest = NULL;
  /* Construct Ethernet header. Start with looking up deciding which
     MAC address to use as a destination address. Broadcasts and
     multicasts are special, all other addresses are looked up in the
     ARP table. */

  /* destination IP address is an IP broadcast address? */
  if (ip_addr_isany(ipaddr) ||
    ip_addr_isbroadcast(ipaddr, &(netif->netmask))) {
    /* broadcast on Ethernet also */
    dest = (struct eth_addr *)&ethbroadcast;
  }
  /* destination IP address is an IP multicast address? */
  else if (ip_addr_ismulticast(ipaddr)) {
    /* Hash IP multicast address to MAC address. */
    mcastaddr.addr[0] = 0x01;
    mcastaddr.addr[1] = 0x00;
    mcastaddr.addr[2] = 0x5e;
    mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
    mcastaddr.addr[4] = ip4_addr3(ipaddr);
    mcastaddr.addr[5] = ip4_addr4(ipaddr);
    /* destination Ethernet address is multicast */
    dest = &mcastaddr;
  }
  /* destination IP address is an IP unicast address */
  else {
    /* destination IP network address not on local network?
     * IP layer wants us to forward to the default gateway */
    if (!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
      /* interface has default gateway? */
      if (netif->gw.addr != 0)
      {
        /* route to default gateway IP address */
        ipaddr = &(netif->gw);
      }
      /* no gateway available? */
      else
      {
        /* IP destination address outside local network, but no gateway available */
        /* { packet is discarded } */
        return NULL;
      }
    }

    /* Ethernet address for IP destination address is in ARP cache? */
    for (i = 0; i < ARP_TABLE_SIZE; ++i) {
      /* match found? */
      if (arp_table[i].state == ETHARP_STATE_STABLE &&
        ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
        dest = &arp_table[i].ethaddr;
        break;
      }
    }
    /* could not find the destination Ethernet address in ARP cache? */
    if (dest == NULL) {
      /* ARP query for the IP address, submit this IP packet for queueing */
      /* TODO: How do we handle netif->ipaddr == ipaddr? */
      etharp_query(netif, ipaddr, q);
      /* { packet was queued (ERR_OK), or discarded } */
      /* return nothing */
      return NULL;
    }
    /* destination Ethernet address resolved from ARP cache */
    else
    {
      /* fallthrough */
    }
  }

  /* destination Ethernet address known */
  if (dest != NULL) {
    /* obtain source Ethernet address of the given interface */
    srcaddr = (struct eth_addr *)netif->hwaddr;

    /* A valid IP->MAC address mapping was found, fill in the
     * Ethernet header for the outgoing packet */
    ethhdr = q->payload;

    for(i = 0; i < netif->hwaddr_len; i++) {
      ethhdr->dest.addr[i] = dest->addr[i];
      ethhdr->src.addr[i] = srcaddr->addr[i];
    }

    ethhdr->type = htons(ETHTYPE_IP);
    /* return the outgoing packet */
    return q;
  }
  /* never reached; here for safety */
  return NULL;
}
Esempio n. 15
0
 * Join a group on one network interface.
 *
 * @param ifaddr ip address of the network interface which should join a new group
 * @param groupaddr the ip address of the group which to join
 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
 */
err_t
igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
{
  err_t              err = ERR_VAL; /* no matching interface */
  struct igmp_group *group;
  struct netif      *netif;

  /* make sure it is multicast address */
  LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
  LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);

  /* loop through netif's */
  netif = netif_list;
  while (netif != NULL) {
    /* Should we join this interface ? */
    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
      /* find group or create a new one if not found */
      group = igmp_lookup_group(netif, groupaddr);

      if (group != NULL) {
        /* This should create a new group, check the state to make sure */
        if (group->group_state != IGMP_GROUP_NON_MEMBER) {
          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
        } else {
          /* OK - it was new group */
Esempio n. 16
0
static struct pbuf *
ip_reass(struct pbuf *p)
{
  struct pbuf *q;
  struct ip_hdr *fraghdr, *iphdr;
  u16_t offset, len;
  u16_t i;
  
  iphdr = (struct ip_hdr *)ip_reassbuf;
  fraghdr = (struct ip_hdr *)p->payload;

  /* If ip_reasstmr is zero, no packet is present in the buffer, so we
     write the IP header of the fragment into the reassembly
     buffer. The timer is updated with the maximum age. */
  if(ip_reasstmr == 0) {
    DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
    memcpy(iphdr, fraghdr, IP_HLEN);
    ip_reasstmr = IP_REASS_MAXAGE;
    ip_reassflags = 0;
    /* Clear the bitmap. */
    bzero(ip_reassbitmap, sizeof(ip_reassbitmap));
  }

  /* Check if the incoming fragment matches the one currently present
     in the reasembly buffer. If so, we proceed with copying the
     fragment into the buffer. */
  if(ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
     ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
     IPH_ID(iphdr) == IPH_ID(fraghdr)) {
    DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching old packet\n"));
    /* Find out the offset in the reassembly buffer where we should
       copy the fragment. */
    len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
    offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;

    /* If the offset or the offset + fragment length overflows the
       reassembly buffer, we discard the entire packet. */
    if(offset > IP_REASS_BUFSIZE ||
       offset + len > IP_REASS_BUFSIZE) {
      DEBUGF(IP_REASS_DEBUG, ("ip_reass: fragment outside of buffer (%d:%d/%d).\n",
			      offset, offset + len, IP_REASS_BUFSIZE));
      ip_reasstmr = 0;
      goto nullreturn;
    }

    /* Copy the fragment into the reassembly buffer, at the right
       offset. */
    DEBUGF(IP_REASS_DEBUG, ("ip_reass: copying with offset %d into %d:%d\n",
			    offset, IP_HLEN + offset, IP_HLEN + offset + len));
    memcpy(&ip_reassbuf[IP_HLEN + offset], 
	  (u8_t *)fraghdr + IPH_HL(fraghdr) * 4, len);

    /* Update the bitmap. */
    if(offset / (8 * 8) == (offset + len) / (8 * 8)) {
      DEBUGF(IP_REASS_DEBUG, ("ip_reass: updating single byte in bitmap.\n"));
      /* If the two endpoints are in the same byte, we only update
	 that byte. */
      ip_reassbitmap[offset / (8 * 8)] |=
	bitmap_bits[(offset / 8 ) & 7] &
	~bitmap_bits[((offset + len) / 8 ) & 7];
    } else {
      /* If the two endpoints are in different bytes, we update the
	 bytes in the endpoints and fill the stuff inbetween with
	 0xff. */
      ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8 ) & 7];
      DEBUGF(IP_REASS_DEBUG, ("ip_reass: updating many bytes in bitmap (%d:%d).\n",
			      1 + offset / (8 * 8), (offset + len) / (8 * 8)));
      for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
	ip_reassbitmap[i] = 0xff;
      }      
      ip_reassbitmap[(offset + len) / (8 * 8)] |= ~bitmap_bits[((offset + len) / 8 ) & 7];
    }
    
    /* If this fragment has the More Fragments flag set to zero, we
       know that this is the last fragment, so we can calculate the
       size of the entire packet. We also set the
       IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
       the final fragment. */

    if((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
      ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
      ip_reasslen = offset + len;
      DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, total len %d\n", ip_reasslen));
    }
    
    /* Finally, we check if we have a full packet in the buffer. We do
       this by checking if we have the last fragment and if all bits
       in the bitmap are set. */
    if(ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
      /* Check all bytes up to and including all but the last byte in
	 the bitmap. */
      for(i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
	if(ip_reassbitmap[i] != 0xff) {
	  DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, bitmap %d/%d failed (%x)\n", i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
	  goto nullreturn;
	}
      }
      /* Check the last byte in the bitmap. It should contain just the
	 right amount of bits. */
      if(ip_reassbitmap[ip_reasslen / (8 * 8)] !=
	 (u8_t)~bitmap_bits[ip_reasslen / 8 & 7]) {
	DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, bitmap %d didn't contain %x (%x)\n",
				ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
				ip_reassbitmap[ip_reasslen / (8 * 8)]));
	goto nullreturn;
      }

      /* Pretend to be a "normal" (i.e., not fragmented) IP packet
	 from now on. */
      IPH_OFFSET_SET(iphdr, 0);
      IPH_CHKSUM_SET(iphdr, 0);
#if CHECKSUM_GEN_IP || CHECKSUM_CHECK_IP /* FIXME: correct? */
      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
#endif
      
      /* If we have come this far, we have a full packet in the
	 buffer, so we allocate a pbuf and copy the packet into it. We
	 also reset the timer. */
      ip_reasstmr = 0;
      pbuf_free(p);
      p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
      if(p != NULL) {
	i = 0;
	for(q = p; q != NULL; q = q->next) {
	  /* Copy enough bytes to fill this pbuf in the chain. The
	     avaliable data in the pbuf is given by the q->len
	     variable. */
	  DEBUGF(IP_REASS_DEBUG, ("ip_reass: memcpy from %p (%d) to %p, %d bytes\n",
				  &ip_reassbuf[i], i, q->payload, q->len > ip_reasslen - i? ip_reasslen - i: q->len));
	  memcpy(q->payload, &ip_reassbuf[i],
		q->len > ip_reasslen - i? ip_reasslen - i: q->len);
	  i += q->len;
	}
      }
      DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", p));
      return p;
    }
  }

 nullreturn:
  pbuf_free(p);
  return NULL;
}
Esempio n. 17
0
/**
 * Called from ip_input() if a new IGMP packet is received.
 *
 * @param p received igmp packet, p->payload pointing to the ip header
 * @param inp network interface on which the packet was received
 * @param dest destination ip address of the igmp packet
 */
void
igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
{
  struct ip_hdr *    iphdr;
  struct igmp_msg*   igmp;
  struct igmp_group* group;
  struct igmp_group* groupref;

  IGMP_STATS_INC(igmp.recv);

  /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */    
  iphdr = (struct ip_hdr *)p->payload;
  if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
    pbuf_free(p);
    IGMP_STATS_INC(igmp.lenerr);
    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
    return;
  }

  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
  LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));

  /* Now calculate and check the checksum */
  igmp = (struct igmp_msg *)p->payload;
  if (inet_chksum(igmp, p->len)) {
    pbuf_free(p);
    IGMP_STATS_INC(igmp.chkerr);
    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
    return;
  }

  /* Packet is ok so find an existing group */
  group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
  
  /* If group can be found or create... */
  if (!group) {
    pbuf_free(p);
    IGMP_STATS_INC(igmp.drop);
    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
    return;
  }

  /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
  switch (igmp->igmp_msgtype) {
   case IGMP_MEMB_QUERY: {
     /* IGMP_MEMB_QUERY to the "all systems" address ? */
     if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
       /* THIS IS THE GENERAL QUERY */
       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));

       if (igmp->igmp_maxresp == 0) {
         IGMP_STATS_INC(igmp.rx_v1);
         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
         igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
       } else {
         IGMP_STATS_INC(igmp.rx_general);
       }

       groupref = igmp_group_list;
       while (groupref) {
         /* Do not send messages on the all systems group address! */
         if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
           igmp_delaying_member(groupref, igmp->igmp_maxresp);
         }
         groupref = groupref->next;
       }
     } else {
       /* IGMP_MEMB_QUERY to a specific group ? */
       if (!ip_addr_isany(&igmp->igmp_group_address)) {
         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
         ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
         if (ip_addr_cmp(dest, &allsystems)) {
           ip_addr_t groupaddr;
           LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
           /* we first need to re-look for the group since we used dest last time */
           ip_addr_copy(groupaddr, igmp->igmp_group_address);
           group = igmp_lookfor_group(inp, &groupaddr);
         } else {
           LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
         }

         if (group != NULL) {
           IGMP_STATS_INC(igmp.rx_group);
           igmp_delaying_member(group, igmp->igmp_maxresp);
         } else {
           IGMP_STATS_INC(igmp.drop);
         }
       } else {
         IGMP_STATS_INC(igmp.proterr);
       }
     }
     break;
   }
   case IGMP_V2_MEMB_REPORT: {
     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
     IGMP_STATS_INC(igmp.rx_report);
     if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
       /* This is on a specific group we have already looked up */
       group->timer = 0; /* stopped */
       group->group_state = IGMP_GROUP_IDLE_MEMBER;
       group->last_reporter_flag = 0;
     }
     break;
   }
   default: {
     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
       igmp->igmp_msgtype, group->group_state, &group, group->netif));
     IGMP_STATS_INC(igmp.proterr);
     break;
   }
  }

  pbuf_free(p);
  return;
}
Esempio n. 18
0
/*-----------------------------------------------------------------------------------*/
err_t
ip_input(struct pbuf *p, struct netif *inp) {
  static struct ip_hdr *iphdr;
  static struct netif *netif;
  static u8_t hl;

  
  
#ifdef IP_STATS
  ++stats.ip.recv;
#endif /* IP_STATS */

  /* identify the IP header */
  iphdr = p->payload;
  if(IPH_V(iphdr) != 4) {
    DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number %d\n", IPH_V(iphdr)));
#if IP_DEBUG
    ip_debug_print(p);
#endif /* IP_DEBUG */
    pbuf_free(p);
#ifdef IP_STATS
    ++stats.ip.err;
    ++stats.ip.drop;
#endif /* IP_STATS */
    return ERR_OK;
  }
  
  hl = IPH_HL(iphdr);
  
  if(hl * 4 > p->len) {
    DEBUGF(IP_DEBUG, ("IP packet dropped due to too short packet %d\n", p->len));

    pbuf_free(p);
#ifdef IP_STATS
    ++stats.ip.lenerr;
    ++stats.ip.drop;
#endif /* IP_STATS */
    return ERR_OK;
  }

  /* verify checksum */
#if CHECKSUM_CHECK_IP
  if(inet_chksum(iphdr, hl * 4) != 0) {

    DEBUGF(IP_DEBUG, ("IP packet dropped due to failing checksum 0x%x\n", inet_chksum(iphdr, hl * 4)));
#if IP_DEBUG
    ip_debug_print(p);
#endif /* IP_DEBUG */
    pbuf_free(p);
#ifdef IP_STATS
    ++stats.ip.chkerr;
    ++stats.ip.drop;
#endif /* IP_STATS */
    return ERR_OK;
  }
#endif /* CHECKSUM_CHECK_IP */
  
  /* Trim pbuf. This should have been done at the netif layer,
     but we'll do it anyway just to be sure that its done. */
  pbuf_realloc(p, ntohs(IPH_LEN(iphdr)));

  /* is this packet for us? */
  for(netif = netif_list; netif != NULL; netif = netif->next) {

    DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%lx netif->ip_addr 0x%lx (0x%lx, 0x%lx, 0x%lx)\n",
		      iphdr->dest.addr, netif->ip_addr.addr,
		      iphdr->dest.addr & netif->netmask.addr,
		      netif->ip_addr.addr & netif->netmask.addr,
		      iphdr->dest.addr & ~(netif->netmask.addr)));

    if(ip_addr_isany(&(netif->ip_addr)) ||
       ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) ||
       (ip_addr_isbroadcast(&(iphdr->dest), &(netif->netmask)) &&
	ip_addr_maskcmp(&(iphdr->dest), &(netif->ip_addr), &(netif->netmask))) ||
       ip_addr_cmp(&(iphdr->dest), IP_ADDR_BROADCAST)) {
      break;
    }
  }

#if LWIP_DHCP
  /* If a DHCP packet has arrived on the interface, we pass it up the
     stack regardless of destination IP address. The reason is that
     DHCP replies are sent to the IP adress that will be given to this
     node (as recommended by RFC 1542 section 3.1.1, referred by RFC
     2131). */
  if(IPH_PROTO(iphdr) == IP_PROTO_UDP &&
     ((struct udp_hdr *)((u8_t *)iphdr + IPH_HL(iphdr) * 4/sizeof(u8_t)))->src ==
     DHCP_SERVER_PORT) {
    netif = inp;
  }  
#endif /* LWIP_DHCP */
	  
  if(netif == NULL) {
    /* packet not for us, route or discard */
    DEBUGF(IP_DEBUG, ("ip_input: packet not for us.\n"));
#if IP_FORWARD
    if(!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask))) {
      ip_forward(p, iphdr, inp);
    }
#endif /* IP_FORWARD */
    pbuf_free(p);
    return ERR_OK;
  }

#if IP_REASSEMBLY
  if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
    p = ip_reass(p);
    if(p == NULL) {
      return ERR_OK;
    }
    iphdr = p->payload;
  }
#else /* IP_REASSEMBLY */
  if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
    pbuf_free(p);
    DEBUGF(IP_DEBUG, ("IP packet dropped since it was fragmented (0x%x).\n",
		      ntohs(IPH_OFFSET(iphdr))));
#ifdef IP_STATS
    ++stats.ip.opterr;
    ++stats.ip.drop;
#endif /* IP_STATS */
    return ERR_OK;
  }
#endif /* IP_REASSEMBLY */
  
#if IP_OPTIONS == 0
  if(hl * 4 > IP_HLEN) {
    DEBUGF(IP_DEBUG, ("IP packet dropped since there were IP options.\n"));

    pbuf_free(p);    
#ifdef IP_STATS
    ++stats.ip.opterr;
    ++stats.ip.drop;
#endif /* IP_STATS */
    return ERR_OK;
  }  
#endif /* IP_OPTIONS == 0 */


  /* send to upper layers */
#if IP_DEBUG
  DEBUGF(IP_DEBUG, ("ip_input: \n"));
  ip_debug_print(p);
  DEBUGF(IP_DEBUG, ("ip_input: p->len %d p->tot_len %d\n", p->len, p->tot_len));
#endif /* IP_DEBUG */   

  switch(IPH_PROTO(iphdr)) {
#if LWIP_UDP > 0    
  case IP_PROTO_UDP:
    udp_input(p, inp);
    break;
#endif /* LWIP_UDP */
#if LWIP_TCP > 0    
  case IP_PROTO_TCP:
    tcp_input(p, inp);
    break;
#endif /* LWIP_TCP */
  case IP_PROTO_ICMP:
    icmp_input(p, inp);
    break;
  default:
    /* send ICMP destination protocol unreachable unless is was a broadcast */
    if(!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask)) &&
       !ip_addr_ismulticast(&(iphdr->dest))) {
      p->payload = iphdr;
      icmp_dest_unreach(p, ICMP_DUR_PROTO);
    }
    pbuf_free(p);

    DEBUGF(IP_DEBUG, ("Unsupported transportation protocol %d\n", IPH_PROTO(iphdr)));

#ifdef IP_STATS
    ++stats.ip.proterr;
    ++stats.ip.drop;
#endif /* IP_STATS */

  }
  return ERR_OK;
}
Esempio n. 19
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);
	tcplen = p->tot_len + ((flags & (TCP_FIN | 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);
		/* 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);
				}

				tcp_input_pcb = NULL;
				/* Try to send something out. */
				tcp_output(pcb);
#if TCP_INPUT_DEBUG
#if TCP_DEBUG
				tcp_debug_print_state(pcb->state);
#endif /* TCP_DEBUG */
#endif /* TCP_INPUT_DEBUG */
			}
		}
		tcp_input_pcb = NULL;


		/* give up our reference to inseg.p */
		if (inseg.p != NULL) {
			pbuf_free(inseg.p);
			inseg.p = NULL;
		}
	} 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");
}
/**
 * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache  
 * send out queued IP packets. Updates cache with snooped address pairs.
 *
 * Should be called for incoming ARP packets. The pbuf in the argument
 * is freed by this function.
 *
 * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
 * @param pbuf The ARP packet that arrived on netif. Is freed by this function.
 * @param ethaddr Ethernet address of netif.
 *
 * @return NULL
 *
 * @see pbuf_free()
 */
void
etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
  struct etharp_hdr *hdr;
  /* these are aligned properly, whereas the ARP header fields might not be */
  struct ip_addr sipaddr, dipaddr;
  u8_t i;
  u8_t for_us;

  /* drop short ARP packets */
  if (p->tot_len < sizeof(struct etharp_hdr)) {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%d/%d)\n", p->tot_len, sizeof(struct etharp_hdr)));
    pbuf_free(p);
    return;
  }

  hdr = p->payload;
 
  /* get aligned copies of addresses */
  *(struct ip_addr2 *)&sipaddr = hdr->sipaddr;
  *(struct ip_addr2 *)&dipaddr = hdr->dipaddr;

  /* this interface is not configured? */
  if (netif->ip_addr.addr == 0) {
    for_us = 0;
  } else {
    /* ARP packet directed to us? */
    for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr));
  }

  /* ARP message directed to us? */
  if (for_us) {
    /* add IP address in ARP cache; assume requester wants to talk to us.
     * can result in directly sending the queued packets for this host. */
    update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_CREATE);
  /* ARP message not directed to us? */
  } else {
    /* update the source IP address in the cache, if present */
    update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0);
  }

  /* now act on the message itself */
  switch (htons(hdr->opcode)) {
  /* ARP request? */
  case ARP_REQUEST:
    /* ARP request. If it asked for our address, we send out a
     * reply. In any case, we time-stamp any existing ARP entry,
     * and possiby send out an IP packet that was queued on it. */

    LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
    /* ARP request for our address? */
    if (for_us) {

      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
      /* re-use pbuf to send ARP reply */
      hdr->opcode = htons(ARP_REPLY);

      hdr->dipaddr = hdr->sipaddr;
      hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr;

      for(i = 0; i < netif->hwaddr_len; ++i) {
        hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
        hdr->shwaddr.addr[i] = ethaddr->addr[i];
        hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i];
        hdr->ethhdr.src.addr[i] = ethaddr->addr[i];
      }

      hdr->hwtype = htons(HWTYPE_ETHERNET);
      ARPH_HWLEN_SET(hdr, netif->hwaddr_len);

      hdr->proto = htons(ETHTYPE_IP);
      ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));

      hdr->ethhdr.type = htons(ETHTYPE_ARP);
      /* return ARP reply */
      netif->linkoutput(netif, p);
      
#ifdef LINKLOCAL_IP //Ron Add for Rendzvous 12/10/04    
	  if( mRENVEnable && (LinkLocal_get_current_state() != NO_USE ) 
	  		&& is_linklocal_ip( dipaddr.addr ) )
	  {
	  		if( (LinkLocal_get_current_state() != IDLE) && ip_conflict_cnt++ == 0 )
  	  			linklocal_alarm();
  	  		else if( (LinkLocal_get_current_state() == IDLE) && ip_conflict_cnt++ == 0) 	
  	  		{
  	  			linklocal_alarm();
  	  		}
  	  }
#endif	  
    /* we are not configured? */
    } else if (netif->ip_addr.addr == 0) {
      /* { for_us == 0 and netif->ip_addr.addr == 0 } */
#ifdef LINKLOCAL_IP //Ron Add for Rendzvous 12/10/04    
		if( mRENVEnable && (LinkLocal_get_current_state() != NO_USE )
		  		&& is_linklocal_ip( dipaddr.addr ) )
		{
			if( (LinkLocal_get_current_state() != IDLE) && ip_conflict_cnt++ == 0 )
  	  			linklocal_alarm();
  	  		else if( (LinkLocal_get_current_state() == IDLE) && ip_conflict_cnt++ == 0) 	
  	  		{
  	  			linklocal_alarm();
  	  		}
  	  		
   		    	hdr->opcode = htons(ARP_REPLY);
				
				hdr->sipaddr = hdr->dipaddr;
				hdr->dipaddr = *(struct ip_addr2 *)&netif->ip_addr;
//		      hdr->dipaddr = hdr->sipaddr;
//		      hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr;
				
		
				for(i = 0; i < netif->hwaddr_len; ++i) {
					hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
					hdr->shwaddr.addr[i] = ethaddr->addr[i];
					hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i];
					hdr->ethhdr.src.addr[i] = ethaddr->addr[i];
				}
		
				hdr->hwtype = htons(HWTYPE_ETHERNET);
				ARPH_HWLEN_SET(hdr, netif->hwaddr_len);
				
				hdr->proto = htons(ETHTYPE_IP);
				ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));
				
				hdr->ethhdr.type = htons(ETHTYPE_ARP);
				
				netif->linkoutput(netif, p);
		      
		}
#endif 
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
    /* request was not directed to us */
    } else {
      /* { for_us == 0 and netif->ip_addr.addr != 0 } */
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
    }
    break;
  case ARP_REPLY:
    /* ARP reply. We already updated the ARP cache earlier. */
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
    /* DHCP wants to know about ARP replies to our wanna-have-address */
    if (for_us){ 
    	dhcp_arp_reply(netif, &sipaddr);
    }
    	
#endif

#ifdef LINKLOCAL_IP //Ron Add for Rendzvous 12/10/04    
		if( mRENVEnable && (LinkLocal_get_current_state() != NO_USE ) && is_linklocal_ip( sipaddr.addr )  )
		{
	  		if( (LinkLocal_get_current_state() != IDLE) && ip_conflict_cnt++ == 0 )
  	  			linklocal_alarm();
  	  		else if( (LinkLocal_get_current_state() == IDLE) && ip_conflict_cnt++ == 0) 	
  	  		{
  	  			linklocal_alarm();
  	  		}
  	  	}
#endif	  

    break;
  default:
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %d\n", htons(hdr->opcode)));
    break;
  }
  /* free ARP packet */
  pbuf_free(p);
}
Esempio n. 21
0
/**
 * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache  
 * send out queued IP packets. Updates cache with snooped address pairs.
 *
 * Should be called for incoming ARP packets. The pbuf in the argument
 * is freed by this function.
 *
 * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
 * @param pbuf The ARP packet that arrived on netif. Is freed by this function.
 * @param ethaddr Ethernet address of netif.
 *
 * @return NULL
 *
 * @see pbuf_free()
 */
void
etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
  struct etharp_hdr *hdr;
  /* these are aligned properly, whereas the ARP header fields might not be */
  struct ip_addr sipaddr, dipaddr;
  u8_t i;
  u8_t for_us;

  LWIP_ASSERT("netif != NULL", netif != NULL);
  
  /* drop short ARP packets */
  if (p->tot_len < sizeof(struct etharp_hdr)) {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, (s16_t)sizeof(struct etharp_hdr)));
    pbuf_free(p);
    return;
  }

  hdr = p->payload;
 
  /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
   * structure packing (not using structure copy which breaks strict-aliasing rules). */
  memcpy(&sipaddr, &hdr->sipaddr, sizeof(sipaddr));
  memcpy(&dipaddr, &hdr->dipaddr, sizeof(dipaddr));

  /* this interface is not configured? */
  if (netif->ip_addr.addr == 0) {
    for_us = 0;
  } else {
    /* ARP packet directed to us? */
    for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr));
  }

  /* ARP message directed to us? */
  if (for_us) {
    /* add IP address in ARP cache; assume requester wants to talk to us.
     * can result in directly sending the queued packets for this host. */
    update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD);
  /* ARP message not directed to us? */
  } else {
    /* update the source IP address in the cache, if present */
    update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0);
  }

  /* now act on the message itself */
  switch (htons(hdr->opcode)) {
  /* ARP request? */
  case ARP_REQUEST:
    /* ARP request. If it asked for our address, we send out a
     * reply. In any case, we time-stamp any existing ARP entry,
     * and possiby send out an IP packet that was queued on it. */

    LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
    /* ARP request for our address? */
    if (for_us) {

      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
      /* re-use pbuf to send ARP reply */
      hdr->opcode = htons(ARP_REPLY);

      hdr->dipaddr = hdr->sipaddr;
      hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr;

      i = netif->hwaddr_len;
      while(i > 0) {
        i--;
        hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
        hdr->shwaddr.addr[i] = ethaddr->addr[i];
        hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i];
        hdr->ethhdr.src.addr[i] = ethaddr->addr[i];
      }

      hdr->hwtype = htons(HWTYPE_ETHERNET);
      ARPH_HWLEN_SET(hdr, netif->hwaddr_len);

      hdr->proto = htons(ETHTYPE_IP);
      ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));

      hdr->ethhdr.type = htons(ETHTYPE_ARP);
      /* return ARP reply */
      netif->linkoutput(netif, p);
    /* we are not configured? */
    } else if (netif->ip_addr.addr == 0) {
      /* { for_us == 0 and netif->ip_addr.addr == 0 } */
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
    /* request was not directed to us */
    } else {
      /* { for_us == 0 and netif->ip_addr.addr != 0 } */
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
    }
    break;
  case ARP_REPLY:
    /* ARP reply. We already updated the ARP cache earlier. */
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
    /* DHCP wants to know about ARP replies from any host with an
     * IP address also offered to us by the DHCP server. We do not
     * want to take a duplicate IP address on a single network.
     * @todo How should we handle redundant (fail-over) interfaces?
     * */
    dhcp_arp_reply(netif, &sipaddr);
#endif
    break;
  default:
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
    break;
  }
  /* free ARP packet */
  pbuf_free(p);
}
Esempio n. 22
0
static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
{
    static char ipbuf[16];
    if(dhcpmsg->htype != DHCP_HTYPE_ETH)
        return;
    if(dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
        return;

    ip_addr_t requested_ip;
    uint8_t *requested_ip_opt = find_dhcp_option(dhcpmsg, DHCP_OPTION_REQUESTED_IP, 4, NULL);
    if(requested_ip_opt) {
            memcpy(&requested_ip.addr, requested_ip_opt, 4);
    } else if(ip_addr_cmp(&requested_ip, IP_ADDR_ANY)) {
        ip_addr_copy(requested_ip, dhcpmsg->ciaddr);
    } else {
        printf("DHCP Server Error: No requested IP\r\n");
        send_dhcp_nak(dhcpmsg);
        return;
    }

    /* Test the first 4 octets match */
    if(ip4_addr1(&requested_ip) != ip4_addr1(&state->first_client_addr)
       || ip4_addr2(&requested_ip) != ip4_addr2(&state->first_client_addr)
       || ip4_addr3(&requested_ip) != ip4_addr3(&state->first_client_addr)) {
        sprintf_ipaddr(&requested_ip, ipbuf);
        printf("DHCP Server Error: %s not an allowed IP\r\n", ipbuf);
        send_dhcp_nak(dhcpmsg);
        return;
    }
    /* Test the last octet is in the MAXCLIENTS range */
    int16_t octet_offs = ip4_addr4(&requested_ip) - ip4_addr4(&state->first_client_addr);
    if(octet_offs < 0 || octet_offs >= state->max_leases) {
        printf("DHCP Server Error: Address out of range\r\n");
        send_dhcp_nak(dhcpmsg);
        return;
    }

    dhcp_lease_t *requested_lease = state->leases + octet_offs;
    if(requested_lease->expires != 0 && memcmp(requested_lease->hwaddr, dhcpmsg->chaddr,dhcpmsg->hlen))
    {
        printf("DHCP Server Error: Lease for address already taken\r\n");
        send_dhcp_nak(dhcpmsg);
        return;
    }

    memcpy(requested_lease->hwaddr, dhcpmsg->chaddr, dhcpmsg->hlen);
    sprintf_ipaddr(&requested_ip, ipbuf);
    printf("DHCP lease addr %s assigned to MAC %02x:%02x:%02x:%02x:%02x:%02x\r\n", ipbuf, requested_lease->hwaddr[0],
           requested_lease->hwaddr[1], requested_lease->hwaddr[2], requested_lease->hwaddr[3], requested_lease->hwaddr[4],
           requested_lease->hwaddr[5]);
    requested_lease->expires = DHCPSERVER_LEASE_TIME * configTICK_RATE_HZ;

    /* Reuse the REQUEST message as the ACK message */
    dhcpmsg->op = DHCP_BOOTREPLY;
    bzero(dhcpmsg->options, DHCP_OPTIONS_LEN);

    ip_addr_copy(dhcpmsg->yiaddr, requested_ip);

    uint8_t *opt = (uint8_t *)&dhcpmsg->options;
    opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_ACK);
    uint32_t expiry = htonl(DHCPSERVER_LEASE_TIME);
    opt = add_dhcp_option_bytes(opt, DHCP_OPTION_LEASE_TIME, &expiry, 4);
    opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
    opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
    opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);

    struct netbuf *netbuf = netbuf_new();
    netbuf_alloc(netbuf, sizeof(struct dhcp_msg));
    netbuf_take(netbuf, dhcpmsg, sizeof(struct dhcp_msg));
    netconn_sendto(state->nc, netbuf, IP_ADDR_BROADCAST, 68);
    netbuf_delete(netbuf);
}
Esempio n. 23
0
/**
 * Bind an UDP PCB.
 *
 * @param pcb UDP PCB to be bound with a local address ipaddr and port.
 * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
 * bind to all local interfaces.
 * @param port local UDP port to bind with. Use 0 to automatically bind
 * to a random port between UDP_LOCAL_PORT_RANGE_START and
 * UDP_LOCAL_PORT_RANGE_END.
 *
 * ipaddr & port are expected to be in the same byte order as in the pcb.
 *
 * @return lwIP error code.
 * - ERR_OK. Successful. No error occured.
 * - ERR_USE. The specified ipaddr and port are already bound to by
 * another UDP PCB.
 *
 * @see udp_disconnect()
 */
err_t
udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
{
    struct udp_pcb *ipcb;
    u8_t rebind;

    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 3, ("udp_bind(ipaddr = "));
    ip_addr_debug_print(UDP_DEBUG, ipaddr);
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | 3, (", port = %"U16_F")\n", port));

    rebind = 0;
    /* Check for double bind and rebind of the same pcb */
    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
        /* is this UDP PCB already on active list? */
        if (pcb == ipcb) {
            /* pcb may occur at most once in active list */
            LWIP_ASSERT("rebind == 0", rebind == 0);
            /* pcb already in list, just rebind */
            rebind = 1;
        }

        /* this code does not allow upper layer to share a UDP port for
           listening to broadcast or multicast traffic (See SO_REUSE_ADDR and
           SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR
           combine with implementation of UDP PCB flags. Leon Woestenberg. */
#ifdef LWIP_UDP_TODO
        /* port matches that of PCB in list? */
        else if ((ipcb->local_port == port) &&
                 /* IP address matches, or one is IP_ADDR_ANY? */
                 (ip_addr_isany(&(ipcb->local_ip)) ||
                  ip_addr_isany(ipaddr) ||
                  ip_addr_cmp(&(ipcb->local_ip), ipaddr))) {
            /* other PCB already binds to this local IP and port */
            LWIP_DEBUGF(UDP_DEBUG,
                        ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
            return ERR_USE;
        }
#endif
    }

    ip_addr_set(&pcb->local_ip, ipaddr);

    /* no port specified? */
    if (port == 0) {
#ifndef UDP_LOCAL_PORT_RANGE_START
#define UDP_LOCAL_PORT_RANGE_START 4096
#define UDP_LOCAL_PORT_RANGE_END   0x7fff
#endif
        port = UDP_LOCAL_PORT_RANGE_START;
        ipcb = udp_pcbs;
        while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {
            if (ipcb->local_port == port) {
                /* port is already used by another udp_pcb */
                port++;
                /* restart scanning all udp pcbs */
                ipcb = udp_pcbs;
            } else
                /* go on with next udp pcb */
                ipcb = ipcb->next;
        }
        if (ipcb != NULL) {
            /* no more ports available in local range */
            LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
            return ERR_USE;
        }
    }
    pcb->local_port = port;
    snmp_insert_udpidx_tree(pcb);
    /* pcb not active yet? */
    if (rebind == 0) {
        /* place the PCB on the active list if not already there */
        pcb->next = udp_pcbs;
        udp_pcbs = pcb;
    }
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
                ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n",
                 (u16_t)((ntohl(pcb->local_ip.addr) >> 24) & 0xff),
                 (u16_t)((ntohl(pcb->local_ip.addr) >> 16) & 0xff),
                 (u16_t)((ntohl(pcb->local_ip.addr) >> 8) & 0xff),
                 (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port));
    return ERR_OK;
}
Esempio n. 24
0
 * @param ifaddr ip address of the network interface which should join a new group
 * @param groupaddr the ip address of the group which to join
 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
 */
err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
{
	err_t err = ERR_VAL;	/* no matching interface */
	struct igmp_group *group;
	struct netif *netif;

	/* make sure it is multicast address */
	LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address",
		   ip_addr_ismulticast(groupaddr), return ERR_VAL;
	    );
	LWIP_ERROR("igmp_joingroup: attempt to join allsystems address",
		   (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;
	    );

	/* loop through netif's */
	netif = netif_list;
	while (netif != NULL) {
		/* Should we join this interface ? */
		if ((netif->flags & NETIF_FLAG_IGMP)
		    &&
		    ((ip_addr_isany(ifaddr)
		      || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
			/* find group or create a new one if not found */
			group = igmp_lookup_group(netif, groupaddr);

			if (group != NULL) {
				/* This should create a new group, check the state to make sure */
Esempio n. 25
0
static void
recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
  u16_t *sbuf = (u16_t *) p->payload;
  int opcode;

  LWIP_UNUSED_ARG(arg);
  LWIP_UNUSED_ARG(upcb);
  
  if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
      (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
    send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
    pbuf_free(p);
    return;
  }

  opcode = sbuf[0];

  tftp_state.last_pkt = tftp_state.timer;
  tftp_state.retries = 0;

  switch (opcode) {
    case PP_HTONS(TFTP_RRQ): /* fall through */
    case PP_HTONS(TFTP_WRQ):
    {
      const char tftp_null = 0;
      char filename[TFTP_MAX_FILENAME_LEN];
      char mode[TFTP_MAX_MODE_LEN];
      u16_t filename_end_offset;
      u16_t mode_end_offset;

      if(tftp_state.handle != NULL) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
        break;
      }
      
      sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);

      /* find \0 in pbuf -> end of filename string */
      filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
      if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
        break;
      }
      pbuf_copy_partial(p, filename, filename_end_offset-2, 2);

      /* find \0 in pbuf -> end of mode string */
      mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
      if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
        break;
      }
      pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
 
      tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
      tftp_state.blknum = 1;

      if (!tftp_state.handle) {
        send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
        break;
      }

      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
      ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));

      ip_addr_copy(tftp_state.addr, *addr);
      tftp_state.port = port;

      if (opcode == PP_HTONS(TFTP_WRQ)) {
        tftp_state.mode_write = 1;
        send_ack(0);
      } else {
        tftp_state.mode_write = 0;
        send_data();
      }

      break;
    }
    
    case PP_HTONS(TFTP_DATA):
    {
      int ret;
      u16_t blknum;
      
      if (tftp_state.handle == NULL) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
        break;
      }

      if (tftp_state.mode_write != 1) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
        break;
      }

      blknum = lwip_ntohs(sbuf[1]);
      pbuf_header(p, -TFTP_HEADER_LENGTH);

      ret = tftp_state.ctx->write(tftp_state.handle, p);
      if (ret < 0) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
        close_handle();
      } else {
        send_ack(blknum);
      }

      if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
        close_handle();
      }
      break;
    }

    case PP_HTONS(TFTP_ACK):
    {
      u16_t blknum;
      int lastpkt;

      if (tftp_state.handle == NULL) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
        break;
      }

      if (tftp_state.mode_write != 0) {
        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
        break;
      }

      blknum = lwip_ntohs(sbuf[1]);
      if (blknum != tftp_state.blknum) {
        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
        break;
      }

      lastpkt = 0;

      if (tftp_state.last_data != NULL) {
        lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
      }

      if (!lastpkt) {
        tftp_state.blknum++;
        send_data();
      } else {
        close_handle();
      }

      break;
    }
    
    default:
      send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
      break;
  }

  pbuf_free(p);
}
Esempio n. 26
0
err_t
ip_input(struct pbuf *p, struct netif *inp) {
    struct ip_hdr *iphdr;
    struct netif *netif;
    u16_t iphdrlen;

    IP_STATS_INC(ip.recv);
    snmp_inc_ipinreceives();

    /* identify the IP header */
    iphdr = p->payload;
    if (IPH_V(iphdr) != 4) {
        LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
        ip_debug_print(p);
        pbuf_free(p);
        IP_STATS_INC(ip.err);
        IP_STATS_INC(ip.drop);
        snmp_inc_ipinhdrerrors();
        return ERR_OK;
    }
    /* obtain IP header length in number of 32-bit words */
    iphdrlen = IPH_HL(iphdr);
    /* calculate IP header length in bytes */
    iphdrlen *= 4;

    /* header length exceeds first pbuf length? */
    if (iphdrlen > p->len) {
        LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet droppped.\n",
                                   iphdrlen, p->len));
        /* free (drop) packet pbufs */
        pbuf_free(p);
        IP_STATS_INC(ip.lenerr);
        IP_STATS_INC(ip.drop);
        snmp_inc_ipindiscards();
        return ERR_OK;
    }

    /* verify checksum */
#if CHECKSUM_CHECK_IP
    if (inet_chksum(iphdr, iphdrlen) != 0) {

        LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdrlen)));
        ip_debug_print(p);
        pbuf_free(p);
        IP_STATS_INC(ip.chkerr);
        IP_STATS_INC(ip.drop);
        snmp_inc_ipinhdrerrors();
        return ERR_OK;
    }
#endif

    /* Trim pbuf. This should have been done at the netif layer,
     * but we'll do it anyway just to be sure that its done. */
    pbuf_realloc(p, ntohs(IPH_LEN(iphdr)));

    /* match packet against an interface, i.e. is this packet for us? */
    for (netif = netif_list; netif != NULL; netif = netif->next) {

        LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
                               iphdr->dest.addr, netif->ip_addr.addr,
                               iphdr->dest.addr & netif->netmask.addr,
                               netif->ip_addr.addr & netif->netmask.addr,
                               iphdr->dest.addr & ~(netif->netmask.addr)));

        /* interface is up and configured? */
        if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr))))
        {
            /* unicast to this interface address? */
            if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) ||
                    /* or broadcast on this interface network address? */
                    ip_addr_isbroadcast(&(iphdr->dest), netif)) {
                LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
                                       netif->name[0], netif->name[1]));
                /* break out of for loop */
                break;
            }
        }
    }
#if LWIP_DHCP
    /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
     * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
     * According to RFC 1542 section 3.1.1, referred by RFC 2131).
     */
    if (netif == NULL) {
        /* remote port is DHCP server? */
        if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
            LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
                                                   ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest)));
            if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest) == DHCP_CLIENT_PORT) {
                LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n"));
                netif = inp;
            }
        }
    }
#endif /* LWIP_DHCP */
    /* packet not for us? */
    if (netif == NULL) {
        /* packet not for us, route or discard */
        LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.\n"));
#if IP_FORWARD
        /* non-broadcast packet? */
        if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) {
            /* try to forward IP packet on (other) interfaces */
            ip_forward(p, iphdr, inp);
        }
        else
#endif /* IP_FORWARD */
        {
            snmp_inc_ipinaddrerrors();
            snmp_inc_ipindiscards();
        }
        pbuf_free(p);
        return ERR_OK;
    }
    /* packet consists of multiple fragments? */
    if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
#if IP_REASSEMBLY /* packet fragment reassembly code present? */
        LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
                               ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
        /* reassemble the packet*/
        p = ip_reass(p);
        /* packet not fully reassembled yet? */
        if (p == NULL) {
            return ERR_OK;
        }
        iphdr = p->payload;
#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
        pbuf_free(p);
        LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
                                   ntohs(IPH_OFFSET(iphdr))));
        IP_STATS_INC(ip.opterr);
        IP_STATS_INC(ip.drop);
        /* unsupported protocol feature */
        snmp_inc_ipinunknownprotos();
        return ERR_OK;
#endif /* IP_REASSEMBLY */
    }

#if IP_OPTIONS == 0 /* no support for IP options in the IP header? */
    if (iphdrlen > IP_HLEN) {
        LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS == 0).\n"));
        pbuf_free(p);
        IP_STATS_INC(ip.opterr);
        IP_STATS_INC(ip.drop);
        /* unsupported protocol feature */
        snmp_inc_ipinunknownprotos();
        return ERR_OK;
    }
#endif /* IP_OPTIONS == 0 */

    /* send to upper layers */
    LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
    ip_debug_print(p);
    LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));

#if LWIP_RAW
    /* raw input did not eat the packet? */
    if (raw_input(p, inp) == 0) {
#endif /* LWIP_RAW */

        switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
        case IP_PROTO_UDP:
        case IP_PROTO_UDPLITE:
            snmp_inc_ipindelivers();
            udp_input(p, inp);
            break;
#endif /* LWIP_UDP */
#if LWIP_TCP
        case IP_PROTO_TCP:
            snmp_inc_ipindelivers();
            tcp_input(p, inp);
            break;
#endif /* LWIP_TCP */
        case IP_PROTO_ICMP:
            snmp_inc_ipindelivers();
            icmp_input(p, inp);
            break;
        default:
            /* send ICMP destination protocol unreachable unless is was a broadcast */
            if (!ip_addr_isbroadcast(&(iphdr->dest), inp) &&
                    !ip_addr_ismulticast(&(iphdr->dest))) {
                p->payload = iphdr;
                icmp_dest_unreach(p, ICMP_DUR_PROTO);
            }
            pbuf_free(p);

            LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));

            IP_STATS_INC(ip.proterr);
            IP_STATS_INC(ip.drop);
            snmp_inc_ipinunknownprotos();
        }
#if LWIP_RAW
    } /* LWIP_RAW */
#endif
    return ERR_OK;
}
Esempio n. 27
0
/* Parse the arp packets and reply for the fake host */
static void parse_arp(struct packet_object *po)
{
   if (!ip_addr_cmp(&fake_host, &po->L3.dst)) 
      send_arp(ARPOP_REPLY, &po->L3.dst, EC_GBL_IFACE->mac, &po->L3.src, po->L2.src);
}
Esempio n. 28
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 (p->payload pointing to the UDP header)
 * @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;
  u16_t src, dest;
  u8_t broadcast;
  u8_t for_us = 0;

  LWIP_UNUSED_ARG(inp);

  PERF_START;

  UDP_STATS_INC(udp.recv);

  /* Check minimum length (UDP header) */
  if (p->len < UDP_HLEN) {
    /* 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);
    MIB2_STATS_INC(mib2.udpinerrors);
    pbuf_free(p);
    goto end;
  }

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

  /* is broadcast packet ? */
  broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());

  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 = lwip_ntohs(udphdr->src);
  dest = lwip_ntohs(udphdr->dest);

  udp_debug_print(udphdr);

  /* print the UDP source and destination */
  LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
  ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr());
  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
  ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr());
  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src)));

  pcb = NULL;
  prev = NULL;
  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) {
    /* print the PCB local and remote address */
    LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
    ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip);
    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
    ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip);
    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));

    /* compare PCB local addr+port to UDP destination addr+port */
    if ((pcb->local_port == dest) &&
        (udp_input_local_match(pcb, inp, broadcast) != 0)) {
      if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) &&
          ((uncon_pcb == NULL)
#if SO_REUSE
          /* prefer specific IPs over cath-all */
          || !ip_addr_isany(&pcb->local_ip)
#endif /* SO_REUSE */
          )) {
        /* the first unconnected matching PCB */
        uncon_pcb = pcb;
      }

      /* compare PCB remote addr+port to UDP source addr+port */
      if ((pcb->remote_port == src) &&
          (ip_addr_isany_val(pcb->remote_ip) ||
          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
        /* 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) {
    for_us = 1;
  } else {
#if LWIP_IPV6
    if (ip_current_is_v6()) {
      for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
    }
#endif /* LWIP_IPV6 */
#if LWIP_IPV4
    if (!ip_current_is_v6()) {
      for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
    }
#endif /* LWIP_IPV4 */
  }

  if (for_us) {
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
#if CHECKSUM_CHECK_UDP
    IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) {
#if LWIP_UDPLITE
      if (ip_current_header_proto() == IP_PROTO_UDPLITE) {
        /* Do the UDP Lite checksum */
        u16_t chklen = lwip_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) */
            goto chkerr;
          }
        }
        if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE,
                     p->tot_len, chklen,
                     ip_current_src_addr(), ip_current_dest_addr()) != 0) {
          goto chkerr;
        }
      } else
#endif /* LWIP_UDPLITE */
      {
        if (udphdr->chksum != 0) {
          if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
                               ip_current_src_addr(),
                               ip_current_dest_addr()) != 0) {
            goto chkerr;
          }
        }
      }
    }
#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);
      MIB2_STATS_INC(mib2.udpinerrors);
      pbuf_free(p);
      goto end;
    }

    if (pcb != NULL) {
      MIB2_STATS_INC(mib2.udpindatagrams);
#if SO_REUSE && SO_REUSE_RXTOALL
      if (ip_get_option(pcb, SOF_REUSEADDR) &&
          (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) {
        /* 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;
        s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN);
        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) &&
                (udp_input_local_match(mpcb, inp, broadcast) != 0)) {
              /* 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_force(p, hdrs_len);
                  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, -hdrs_len);
                    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, -hdrs_len);
        }
      }
#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 {
      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));

#if LWIP_ICMP || LWIP_ICMP6
      /* No match was found, send ICMP destination port unreachable unless
         destination address was broadcast/multicast. */
      if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
        /* move payload pointer back to ip header */
        pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN);
        icmp_port_unreach(ip_current_is_v6(), p);
      }
#endif /* LWIP_ICMP || LWIP_ICMP6 */
      UDP_STATS_INC(udp.proterr);
      UDP_STATS_INC(udp.drop);
      MIB2_STATS_INC(mib2.udpnoports);
      pbuf_free(p);
    }
  } else {
Esempio n. 29
0
/**
 * Binds the connection to a local portnumber and IP address. If the
 * IP address is not given (i.e., ipaddr == NULL), the IP address of
 * the outgoing network interface is used instead.
 *
 * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
 *        already bound!)
 * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind
 *        to any local address
 * @param port the local port to bind to
 * @return ERR_USE if the port is already in use
 *         ERR_OK if bound
 */
err_t
tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
{
    struct tcp_pcb *cpcb;

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

    if (port == 0) {
        port = tcp_new_port();
    }
    /* Check if the address already is in use. */
    /* Check the listen pcbs. */
    for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs;
            cpcb != NULL; cpcb = cpcb->next) {
        if (cpcb->local_port == port) {
            if (ip_addr_isany(&(cpcb->local_ip)) ||
                    ip_addr_isany(ipaddr) ||
                    ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
                return ERR_USE;
            }
        }
    }
    /* Check the connected pcbs. */
    for(cpcb = tcp_active_pcbs;
            cpcb != NULL; cpcb = cpcb->next) {
        if (cpcb->local_port == port) {
            if (ip_addr_isany(&(cpcb->local_ip)) ||
                    ip_addr_isany(ipaddr) ||
                    ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
                return ERR_USE;
            }
        }
    }
    /* Check the bound, not yet connected pcbs. */
    for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) {
        if (cpcb->local_port == port) {
            if (ip_addr_isany(&(cpcb->local_ip)) ||
                    ip_addr_isany(ipaddr) ||
                    ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
                return ERR_USE;
            }
        }
    }
    /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah),
     * we have to check the pcbs in TIME-WAIT state, also: */
    for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) {
        if (cpcb->local_port == port) {
            if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
                return ERR_USE;
            }
        }
    }

    if (!ip_addr_isany(ipaddr)) {
        pcb->local_ip = *ipaddr;
    }
    pcb->local_port = port;
    TCP_REG(&tcp_bound_pcbs, pcb);
    LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
    return ERR_OK;
}
Esempio n. 30
0
/**
 * Update (or insert) a IP/MAC address pair in the ARP cache.
 *
 * If a pending entry is resolved, any queued packets will be sent
 * at this point.
 * 
 * @param ipaddr IP address of the inserted ARP entry.
 * @param ethaddr Ethernet address of the inserted ARP entry.
 * @param flags Defines behaviour:
 * - ARP_INSERT_FLAG Allows ARP to insert this as a new item. If not specified,
 * only existing ARP entries will be updated.
 *
 * @return pbuf If non-NULL, a packet that was queued on a pending entry.
 * You should sent it and must call pbuf_free() afterwards.
 *
 * @see pbuf_free()
 */
static struct pbuf *
update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)
{
  s8_t i, k;
  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("update_arp_entry()\n"));
  LWIP_ASSERT("netif->hwaddr_len != 0", netif->hwaddr_len != 0);
  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: %u.%u.%u.%u - %02x:%02x:%02x:%02x:%02x:%02x\n", ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr),
  ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
  /* do not update for 0.0.0.0 addresses */
  if (ipaddr->addr == 0) {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: will not add 0.0.0.0 to ARP cache\n"));
    return NULL;
  }
  /* Walk through the ARP mapping table and try to find an entry to
  update. If none is found, the IP -> MAC address mapping is
  inserted in the ARP table. */
  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
    /* Check if the source IP address of the incoming packet matches
    the IP address in this ARP table entry. */
    if (ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
      /* pending entry? */
      if (arp_table[i].state == ETHARP_STATE_PENDING) {
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: pending entry %u goes stable\n", i));
        /* A pending entry was found, mark it stable */
        arp_table[i].state = ETHARP_STATE_STABLE;
        /* fall-through to next if */
      }
      /* stable entry? (possibly just marked to become stable) */
      if (arp_table[i].state == ETHARP_STATE_STABLE) {
#if ARP_QUEUEING
        struct pbuf *p;
        struct eth_hdr *ethhdr;
#endif
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: updating stable entry %u\n", i));
        /* An old entry found, update this and return. */
        for (k = 0; k < netif->hwaddr_len; ++k) {
          arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
        }
        /* reset time stamp */
        arp_table[i].ctime = 0;
/* this is where we will send out queued packets! */
#if ARP_QUEUEING
        /* get the first packet on the queue (if any) */
        p = arp_table[i].p;
        /* (another) queued packet present? */
        while (p != NULL) {
          struct pbuf *q, *n;
          /* search for second packet on queue (n) */
          q = p;
          while (q->tot_len > q->len) {
            LWIP_ASSERT("q->next != NULL (while q->tot_len > q->len)", q->next != NULL);
            /* proceed to next pbuf of this packet */
            q = q->next;
          }
          /* { q = last pbuf of this packet, q->tot_len == q->len } */
          LWIP_ASSERT("q->tot_len == q->len", q->tot_len == q->len);
          /* remember next packet on queue */
          n = q->next;
          /* { n = first pbuf of next packet, or NULL if no next packet } */
          /* terminate this packet pbuf chain */
          q->next = NULL;
          /* fill-in Ethernet header */
          ethhdr = p->payload;
          for (k = 0; k < netif->hwaddr_len; ++k) {
            ethhdr->dest.addr[k] = ethaddr->addr[k];
            ethhdr->src.addr[k] = netif->hwaddr[k];
          }
          ethhdr->type = htons(ETHTYPE_IP);
          LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: sending queued IP packet %p.\n", (void *)p));
          /* send the queued IP packet */
          netif->linkoutput(netif, p);
          /* free the queued IP packet */
          pbuf_free(p);
          /* proceed to next packet on queue */
          p = n;
        }
        /* NULL attached buffer*/
        arp_table[i].p = NULL;
#endif
        /* IP addresses should only occur once in the ARP entry, we are done */
        return NULL;
      }
    } /* if STABLE */
  } /* for all ARP entries */

  /* no matching ARP entry was found */
  LWIP_ASSERT("update_arp_entry: i == ARP_TABLE_SIZE", i == ARP_TABLE_SIZE);

  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: IP address not yet in table\n"));
  /* allowed to insert an entry? */
  if ((ETHARP_ALWAYS_INSERT) || (flags & ARP_INSERT_FLAG))
  {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: adding entry to table\n"));
    /* find an empty or old entry. */
    i = find_arp_entry();
    if (i == ERR_MEM) {
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: no available entry found\n"));
      return NULL;
    }
    /* set IP address */
    ip_addr_set(&arp_table[i].ipaddr, ipaddr);
    /* set Ethernet hardware address */
    for (k = 0; k < netif->hwaddr_len; ++k) {
      arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
    }
    /* reset time-stamp */
    arp_table[i].ctime = 0;
    /* mark as stable */
    arp_table[i].state = ETHARP_STATE_STABLE;
    /* no queued packet */
#if ARP_QUEUEING
    arp_table[i].p = NULL;
#endif
  }
  else
  {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: no matching stable entry to update\n"));
  }
  return NULL;
}