Exemplo n.º 1
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;
}
Exemplo n.º 2
0
Arquivo: netif.c Projeto: harmv/lwip
/**
 * Add a network interface to the list of lwIP netifs.
 *
 * @param netif a pre-allocated netif structure
 * @param ipaddr IP address for the new netif
 * @param netmask network mask for the new netif
 * @param gw default gateway IP address for the new netif
 * @param state opaque data passed to the new netif
 * @param init callback function that initializes the interface
 * @param input callback function that is called to pass
 * ingress packets up in the protocol layer stack.
 *
 * @return netif, or NULL if failed.
 */
struct netif *
netif_add(struct netif *netif,
#if LWIP_IPV4
          const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
#endif /* LWIP_IPV4 */
          void *state, netif_init_fn init, netif_input_fn input)
{
#if LWIP_IPV6
  u32_t i;
#endif

  LWIP_ASSERT("No init function given", init != NULL);

  /* reset new interface configuration state */
#if LWIP_IPV4
  ip_addr_set_zero_ip4(&netif->ip_addr);
  ip_addr_set_zero_ip4(&netif->netmask);
  ip_addr_set_zero_ip4(&netif->gw);
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
    ip_addr_set_zero_ip6(&netif->ip6_addr[i]);
    netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
  }
  netif->output_ip6 = netif_null_output_ip6;
#endif /* LWIP_IPV6 */
  NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
  netif->flags = 0;
#if LWIP_DHCP
  /* netif not under DHCP control by default */
  netif->dhcp = NULL;
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
  /* netif not under AutoIP control by default */
  netif->autoip = NULL;
#endif /* LWIP_AUTOIP */
#if LWIP_IPV6_AUTOCONFIG
  /* IPv6 address autoconfiguration not enabled by default */
  netif->ip6_autoconfig_enabled = 0;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
  netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if LWIP_IPV6_DHCP6
  /* netif not under DHCPv6 control by default */
  netif->dhcp6 = NULL;
#endif /* LWIP_IPV6_DHCP6 */
#if LWIP_NETIF_STATUS_CALLBACK
  netif->status_callback = NULL;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
  netif->link_callback = NULL;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_IGMP
  netif->igmp_mac_filter = NULL;
#endif /* LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
  netif->mld_mac_filter = NULL;
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
#if ENABLE_LOOPBACK
  netif->loop_first = NULL;
  netif->loop_last = NULL;
#endif /* ENABLE_LOOPBACK */

  /* remember netif specific state information data */
  netif->state = state;
  netif->num = netif_num++;
  netif->input = input;
  NETIF_SET_HWADDRHINT(netif, NULL);
#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
  netif->loop_cnt_current = 0;
#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */

#if LWIP_IPV4
  netif_set_addr(netif, ipaddr, netmask, gw);
#endif /* LWIP_IPV4 */

  /* call user specified initialization function for netif */
  if (init(netif) != ERR_OK) {
    return NULL;
  }

  /* add this netif to the list */
  netif->next = netif_list;
  netif_list = netif;
  mib2_netif_added(netif);

#if LWIP_IGMP
  /* start IGMP processing */
  if (netif->flags & NETIF_FLAG_IGMP) {
    igmp_start(netif);
  }
#endif /* LWIP_IGMP */

  LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP",
    netif->name[0], netif->name[1]));
#if LWIP_IPV4
  LWIP_DEBUGF(NETIF_DEBUG, (" addr "));
  ip4_addr_debug_print(NETIF_DEBUG, ipaddr);
  LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
  ip4_addr_debug_print(NETIF_DEBUG, netmask);
  LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
  ip4_addr_debug_print(NETIF_DEBUG, gw);
#endif /* LWIP_IPV4 */
  LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
  return netif;
}
Exemplo n.º 3
0
/**
 * Shrink memory returned by mem_malloc().
 *
 * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
 * @param newsize required size after shrinking (needs to be smaller than or
 *                equal to the previous size)
 * @return for compatibility reasons: is always == rmem, at the moment
 *         or NULL if newsize is > old size, in which case rmem is NOT touched
 *         or freed!
 */
void *
mem_trim(void *rmem, mem_size_t newsize) {
	mem_size_t size;
	mem_size_t ptr, ptr2;
	struct mem *mem, *mem2;
	/* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
	LWIP_MEM_FREE_DECL_PROTECT();

	/* Expand the size of the allocated memory region so that we can
	   adjust for alignment. */
	newsize = LWIP_MEM_ALIGN_SIZE(newsize);

	if (newsize < MIN_SIZE_ALIGNED) {
		/* every data block must be at least MIN_SIZE_ALIGNED long */
		newsize = MIN_SIZE_ALIGNED;
	}

	if (newsize > MEM_SIZE_ALIGNED) {
		return NULL;
	}

	LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
				(u8_t *)rmem < (u8_t *)ram_end);

	if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
		SYS_ARCH_DECL_PROTECT(lev);
		LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
		/* protect mem stats from concurrent access */
		SYS_ARCH_PROTECT(lev);
		MEM_STATS_INC(illegal);
		SYS_ARCH_UNPROTECT(lev);
		return rmem;
	}
	/* Get the corresponding struct mem ... */
	mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
	/* ... and its offset pointer */
	ptr = (mem_size_t)((u8_t *)mem - ram);

	size = mem->next - ptr - SIZEOF_STRUCT_MEM;
	LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
	if (newsize > size) {
		/* not supported */
		return NULL;
	}
	if (newsize == size) {
		/* No change in size, simply return */
		return rmem;
	}

	/* protect the heap from concurrent access */
	LWIP_MEM_FREE_PROTECT();

	mem2 = (struct mem *)(void *)&ram[mem->next];
	if (mem2->used == 0) {
		/* The next struct is unused, we can simply move it at little */
		mem_size_t next;
		/* remember the old next pointer */
		next = mem2->next;
		/* create new struct mem which is moved directly after the shrinked mem */
		ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
		if (lfree == mem2) {
			lfree = (struct mem *)(void *)&ram[ptr2];
		}
		mem2 = (struct mem *)(void *)&ram[ptr2];
		mem2->used = 0;
		/* restore the next pointer */
		mem2->next = next;
		/* link it back to mem */
		mem2->prev = ptr;
		/* link mem to it */
		mem->next = ptr2;
		/* last thing to restore linked list: as we have moved mem2,
		 * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
		 * the end of the heap */
		if (mem2->next != MEM_SIZE_ALIGNED) {
			((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
		}
		MEM_STATS_DEC_USED(used, (size - newsize));
		/* no need to plug holes, we've already done that */
	} else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
		/* Next struct is used but there's room for another struct mem with
		 * at least MIN_SIZE_ALIGNED of data.
		 * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
		 * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
		 * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
		 *       region that couldn't hold data, but when mem->next gets freed,
		 *       the 2 regions would be combined, resulting in more free memory */
		ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
		mem2 = (struct mem *)(void *)&ram[ptr2];
		if (mem2 < lfree) {
			lfree = mem2;
		}
		mem2->used = 0;
		mem2->next = mem->next;
		mem2->prev = ptr;
		mem->next = ptr2;
		if (mem2->next != MEM_SIZE_ALIGNED) {
			((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
		}
		MEM_STATS_DEC_USED(used, (size - newsize));
		/* the original mem->next is used, so no need to plug holes! */
	}
	/* else {
	  next struct mem is used but size between mem and mem2 is not big enough
	  to create another struct mem
	  -> don't do anyhting.
	  -> the remaining space stays unused since it is too small
	} */
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
	mem_free_count = 1;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
	LWIP_MEM_FREE_UNPROTECT();
	return rmem;
}
Exemplo n.º 4
0
/**
 * Checks and decodes incoming SNMP message header, logs header errors.
 *
 * @param p points to pbuf chain of SNMP message (UDP payload)
 * @param ofs points to first octet of SNMP message
 * @param pdu_len the length of the UDP payload
 * @param ofs_ret returns the ofset of the variable bindings
 * @param m_stat points to the current message request state return
 * @return
 * - ERR_OK SNMP header is sane and accepted
 * - ERR_ARG SNMP header is either malformed or rejected
 */
static err_t
snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
  err_t derr;
  u16_t len, ofs_base;
  u8_t  len_octets;
  u8_t  type;
  s32_t version;

  ofs_base = ofs;
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) ||
      (pdu_len != (1 + len_octets + len)) ||
      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (version) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  if (version != 0)
  {
    /* not version 1 */
    snmp_inc_snmpinbadversions();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
  {
    /* can't decode or no octet string (community) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
  if (derr != ERR_OK)
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* add zero terminator */
  len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
  m_stat->community[len] = 0;
  m_stat->com_strlen = (u8_t)len;
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
#if SNMP_COMMUNITY_EXT
  if (strncmp(snmp_community_write, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
  {
    /* community does not match the write-access community, check if this is a SetRequest */
    if (type == (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ))
    {
      /* wrong community for SetRequest PDU */
      snmp_inc_snmpinbadcommunitynames();
      snmp_authfail_trap();
      return ERR_ARG;
    }
#else /* SNMP_COMMUNITY_EXT */
  {
#endif /* SNMP_COMMUNITY_EXT */
    if (strncmp(snmp_community, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
    {
      snmp_inc_snmpinbadcommunitynames();
      snmp_authfail_trap();
      return ERR_ARG;
    }
  }
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if (derr != ERR_OK)
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  switch(type)
  {
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
      /* GetRequest PDU */
      snmp_inc_snmpingetrequests();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
      /* GetNextRequest PDU */
      snmp_inc_snmpingetnexts();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
      /* GetResponse PDU */
      snmp_inc_snmpingetresponses();
      derr = ERR_ARG;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
      /* SetRequest PDU */
      snmp_inc_snmpinsetrequests();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
      /* Trap PDU */
      snmp_inc_snmpintraps();
      derr = ERR_ARG;
      break;
    default:
      snmp_inc_snmpinasnparseerrs();
      derr = ERR_ARG;
      break;
  }
  if (derr != ERR_OK)
  {
    /* unsupported input PDU for this agent (no parse error) */
    return ERR_ARG;
  }
  m_stat->rt = type & 0x1F;
  ofs += (1 + len_octets);
  if (len != (pdu_len - (ofs - ofs_base)))
  {
    /* decoded PDU length does not equal actual payload length */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (request ID) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (error-status) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* must be noError (0) for incoming requests.
     log errors for mib-2 completeness and for debug purposes */
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  switch (m_stat->error_status)
  {
    case SNMP_ES_NOERROR:
      /* nothing to do */
      break;
    case SNMP_ES_TOOBIG:
      snmp_inc_snmpintoobigs();
      break;
    case SNMP_ES_NOSUCHNAME:
      snmp_inc_snmpinnosuchnames();
      break;
    case SNMP_ES_BADVALUE:
      snmp_inc_snmpinbadvalues();
      break;
    case SNMP_ES_READONLY:
      snmp_inc_snmpinreadonlys();
      break;
    case SNMP_ES_GENERROR:
      snmp_inc_snmpingenerrs();
      break;
    default:
      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check(): unknown error_status: %d\n", (int)m_stat->error_status));
      break;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (error-index) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* must be 0 for incoming requests.
     decode anyway to catch bad integers (and dirty tricks) */
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  *ofs_ret = ofs;
  return ERR_OK;
}

static err_t
snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
  err_t derr;
  u16_t len, vb_len;
  u8_t  len_octets;
  u8_t type;

  /* variable binding list */
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
  if ((derr != ERR_OK) ||
      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets);

  /* start with empty list */
  m_stat->invb.count = 0;
  m_stat->invb.head = NULL;
  m_stat->invb.tail = NULL;

  while (vb_len > 0)
  {
    struct snmp_obj_id oid, oid_value;
    struct snmp_varbind *vb;

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) ||
        (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
        (len == 0) || (len > vb_len))
    {
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets);
    vb_len -= (1 + len_octets);

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
    {
      /* can't decode object name length */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
    if (derr != ERR_OK)
    {
      /* can't decode object name */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    vb_len -= (1 + len_octets + len);

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if (derr != ERR_OK)
    {
      /* can't decode object value length */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }

    switch (type)
    {
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
        vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
        if (vb != NULL)
        {
          s32_t *vptr = (s32_t*)vb->value;

          derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
        vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
        if (vb != NULL)
        {
          u32_t *vptr = (u32_t*)vb->value;

          derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
        LWIP_ASSERT("invalid length", len <= 0xff);
        vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
        if (vb != NULL)
        {
          derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
        vb = snmp_varbind_alloc(&oid, type, 0);
        if (vb != NULL)
        {
          snmp_varbind_tail_add(&m_stat->invb, vb);
          derr = ERR_OK;
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
        derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
        if (derr == ERR_OK)
        {
          vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
          if (vb != NULL)
          {
            u8_t i = oid_value.len;
            s32_t *vptr = (s32_t*)vb->value;

            while(i > 0)
            {
              i--;
              vptr[i] = oid_value.id[i];
            }
            snmp_varbind_tail_add(&m_stat->invb, vb);
            derr = ERR_OK;
          }
          else
          {
            derr = ERR_ARG;
          }
        }
        break;
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
        if (len == 4)
        {
          /* must be exactly 4 octets! */
          vb = snmp_varbind_alloc(&oid, type, 4);
          if (vb != NULL)
          {
            derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
            snmp_varbind_tail_add(&m_stat->invb, vb);
          }
          else
          {
            derr = ERR_ARG;
          }
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      default:
        derr = ERR_ARG;
        break;
    }
    if (derr != ERR_OK)
    {
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    vb_len -= (1 + len_octets + len);
  }

  if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
  {
    snmp_add_snmpintotalsetvars(m_stat->invb.count);
  }
  else
  {
    snmp_add_snmpintotalreqvars(m_stat->invb.count);
  }

  *ofs_ret = ofs;
  return ERR_OK;
}
Exemplo n.º 5
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);
}
Exemplo n.º 6
0
/**
 * Service an internal or external event for SNMP GET.
 *
 * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
 * @param msg_ps points to the associated message process state
 */
static void
snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
{
  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));

  if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
  {
    const struct mib_external_node *en;
    struct snmp_name_ptr np;

    /* get_object_def() answer*/
    en = msg_ps->ext_mib_node;
    np = msg_ps->ext_name_ptr;

    /* translate answer into a known lifeform */
    en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    {
      msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
      en->get_value_q(request_id, &msg_ps->ext_object_def);
    }
    else
    {
      en->get_object_def_pc(request_id, np.ident_len, np.ident);
      /* search failed, object id points to unknown object (nosuchname) */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }
  else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
  {
    const struct mib_external_node *en;
    struct snmp_varbind *vb;

    /* get_value() answer */
    en = msg_ps->ext_mib_node;

    /* allocate output varbind */
    vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
    if (vb != NULL)
    {
      vb->next = NULL;
      vb->prev = NULL;

      /* move name from invb to outvb */
      vb->ident = msg_ps->vb_ptr->ident;
      vb->ident_len = msg_ps->vb_ptr->ident_len;
      /* ensure this memory is referenced once only */
      msg_ps->vb_ptr->ident = NULL;
      msg_ps->vb_ptr->ident_len = 0;

      vb->value_type = msg_ps->ext_object_def.asn_type;
      LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
      vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
      if (vb->value_len > 0)
      {
        LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
        vb->value = memp_malloc(MEMP_SNMP_VALUE);
        if (vb->value != NULL)
        {
          en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
          snmp_varbind_tail_add(&msg_ps->outvb, vb);
          /* search again (if vb_idx < msg_ps->invb.count) */
          msg_ps->state = SNMP_MSG_SEARCH_OBJ;
          msg_ps->vb_idx += 1;
        }
        else
        {
          en->get_value_pc(request_id, &msg_ps->ext_object_def);
          LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
          msg_ps->vb_ptr->ident = vb->ident;
          msg_ps->vb_ptr->ident_len = vb->ident_len;
          memp_free(MEMP_SNMP_VARBIND, vb);
          snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
        }
      }
      else
      {
        /* vb->value_len == 0, empty value (e.g. empty string) */
        en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
        vb->value = NULL;
        snmp_varbind_tail_add(&msg_ps->outvb, vb);
        /* search again (if vb_idx < msg_ps->invb.count) */
        msg_ps->state = SNMP_MSG_SEARCH_OBJ;
        msg_ps->vb_idx += 1;
      }
    }
    else
    {
      en->get_value_pc(request_id, &msg_ps->ext_object_def);
      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
      snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    }
  }

  while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
         (msg_ps->vb_idx < msg_ps->invb.count))
  {
    const struct mib_node *mn;
    struct snmp_name_ptr np;

    if (msg_ps->vb_idx == 0)
    {
      msg_ps->vb_ptr = msg_ps->invb.head;
    }
    else
    {
      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    }
    /** test object identifier for .iso.org.dod.internet prefix */
    if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
    {
      mn = snmp_search_tree((const struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
                             msg_ps->vb_ptr->ident + 4, &np);
      if (mn != NULL)
      {
        if (mn->node_type == MIB_NODE_EX)
        {
          /* external object */
          const struct mib_external_node *en = (const struct mib_external_node*)mn;

          msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
          /* save en && args in msg_ps!! */
          msg_ps->ext_mib_node = en;
          msg_ps->ext_name_ptr = np;

          en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
        }
        else
        {
          /* internal object */
          struct obj_def object_def;

          msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
          mn->get_object_def(np.ident_len, np.ident, &object_def);
          if (object_def.instance != MIB_OBJECT_NONE)
          {
            mn = mn;
          }
          else
          {
            /* search failed, object id points to unknown object (nosuchname) */
            mn =  NULL;
          }
          if (mn != NULL)
          {
            struct snmp_varbind *vb;

            msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
            /* allocate output varbind */
            vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
            if (vb != NULL)
            {
              vb->next = NULL;
              vb->prev = NULL;

              /* move name from invb to outvb */
              vb->ident = msg_ps->vb_ptr->ident;
              vb->ident_len = msg_ps->vb_ptr->ident_len;
              /* ensure this memory is referenced once only */
              msg_ps->vb_ptr->ident = NULL;
              msg_ps->vb_ptr->ident_len = 0;

              vb->value_type = object_def.asn_type;
              LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
              vb->value_len = (u8_t)object_def.v_len;
              if (vb->value_len > 0)
              {
                LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
                  vb->value_len <= SNMP_MAX_VALUE_SIZE);
                vb->value = memp_malloc(MEMP_SNMP_VALUE);
                if (vb->value != NULL)
                {
                  mn->get_value(&object_def, vb->value_len, vb->value);
                  snmp_varbind_tail_add(&msg_ps->outvb, vb);
                  msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                  msg_ps->vb_idx += 1;
                }
                else
                {
                  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
                  msg_ps->vb_ptr->ident = vb->ident;
                  msg_ps->vb_ptr->ident_len = vb->ident_len;
                  vb->ident = NULL;
                  vb->ident_len = 0;
                  memp_free(MEMP_SNMP_VARBIND, vb);
                  snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
                }
              }
              else
              {
                /* vb->value_len == 0, empty value (e.g. empty string) */
                vb->value = NULL;
                snmp_varbind_tail_add(&msg_ps->outvb, vb);
                msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                msg_ps->vb_idx += 1;
              }
            }
            else
            {
              LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
              snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
            }
          }
        }
      }
    }
    else
    {
      mn = NULL;
    }
    if (mn == NULL)
    {
      /* mn == NULL, noSuchName */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }
  if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
      (msg_ps->vb_idx == msg_ps->invb.count))
  {
    snmp_ok_response(msg_ps);
  }
}
Exemplo n.º 7
0
/**
 * Service an internal or external event for SNMP SET.
 *
 * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
 * @param msg_ps points to the associated message process state
 */
static void
snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
{
  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));

  if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
  {
    const struct mib_external_node *en;
    struct snmp_name_ptr np;

    /* get_object_def() answer*/
    en = msg_ps->ext_mib_node;
    np = msg_ps->ext_name_ptr;

    /* translate answer into a known lifeform */
    en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    {
      msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
      en->set_test_q(request_id, &msg_ps->ext_object_def);
    }
    else
    {
      en->get_object_def_pc(request_id, np.ident_len, np.ident);
      /* search failed, object id points to unknown object (nosuchname) */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }
  else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
  {
    const struct mib_external_node *en;

    /* set_test() answer*/
    en = msg_ps->ext_mib_node;

    if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
    {
       if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
           (en->set_test_a(request_id,&msg_ps->ext_object_def,
                           msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
      {
        msg_ps->state = SNMP_MSG_SEARCH_OBJ;
        msg_ps->vb_idx += 1;
      }
      else
      {
        en->set_test_pc(request_id,&msg_ps->ext_object_def);
        /* bad value */
        snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
      }
    }
    else
    {
      en->set_test_pc(request_id,&msg_ps->ext_object_def);
      /* object not available for set */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }
  else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
  {
    const struct mib_external_node *en;
    struct snmp_name_ptr np;

    /* get_object_def() answer*/
    en = msg_ps->ext_mib_node;
    np = msg_ps->ext_name_ptr;

    /* translate answer into a known lifeform */
    en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    {
      msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
      en->set_value_q(request_id, &msg_ps->ext_object_def,
                      msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
    }
    else
    {
      en->get_object_def_pc(request_id, np.ident_len, np.ident);
      /* set_value failed, object has disappeared for some odd reason?? */
      snmp_error_response(msg_ps,SNMP_ES_GENERROR);
    }
  }
  else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
  {
    const struct mib_external_node *en;

    /** set_value_a() */
    en = msg_ps->ext_mib_node;
    en->set_value_a(request_id, &msg_ps->ext_object_def,
      msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);

    /** @todo use set_value_pc() if toobig */
    msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
    msg_ps->vb_idx += 1;
  }

  /* test all values before setting */
  while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
         (msg_ps->vb_idx < msg_ps->invb.count))
  {
    const struct mib_node *mn;
    struct snmp_name_ptr np;

    if (msg_ps->vb_idx == 0)
    {
      msg_ps->vb_ptr = msg_ps->invb.head;
    }
    else
    {
      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    }
    /** test object identifier for .iso.org.dod.internet prefix */
    if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
    {
      mn = snmp_search_tree((const struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
                             msg_ps->vb_ptr->ident + 4, &np);
      if (mn != NULL)
      {
        if (mn->node_type == MIB_NODE_EX)
        {
          /* external object */
          const struct mib_external_node *en = (const struct mib_external_node*)mn;

          msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
          /* save en && args in msg_ps!! */
          msg_ps->ext_mib_node = en;
          msg_ps->ext_name_ptr = np;

          en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
        }
        else
        {
          /* internal object */
          struct obj_def object_def;

          msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
          mn->get_object_def(np.ident_len, np.ident, &object_def);
          if (object_def.instance != MIB_OBJECT_NONE)
          {
            mn = mn;
          }
          else
          {
            /* search failed, object id points to unknown object (nosuchname) */
            mn = NULL;
          }
          if (mn != NULL)
          {
            msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;

            if (object_def.access & MIB_ACCESS_WRITE)
            {
              if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
                  (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
              {
                msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                msg_ps->vb_idx += 1;
              }
              else
              {
                /* bad value */
                snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
              }
            }
            else
            {
              /* object not available for set */
              snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
            }
          }
        }
      }
    }
    else
    {
      mn = NULL;
    }
    if (mn == NULL)
    {
      /* mn == NULL, noSuchName */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }

  if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
      (msg_ps->vb_idx == msg_ps->invb.count))
  {
    msg_ps->vb_idx = 0;
    msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
  }

  /* set all values "atomically" (be as "atomic" as possible) */
  while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
         (msg_ps->vb_idx < msg_ps->invb.count))
  {
    const struct mib_node *mn;
    struct snmp_name_ptr np;

    if (msg_ps->vb_idx == 0)
    {
      msg_ps->vb_ptr = msg_ps->invb.head;
    }
    else
    {
      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    }
    /* skip iso prefix test, was done previously while settesting() */
    mn = snmp_search_tree((const struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
                           msg_ps->vb_ptr->ident + 4, &np);
    /* check if object is still available
       (e.g. external hot-plug thingy present?) */
    if (mn != NULL)
    {
      if (mn->node_type == MIB_NODE_EX)
      {
        /* external object */
        const struct mib_external_node *en = (const struct mib_external_node*)mn;

        msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
        /* save en && args in msg_ps!! */
        msg_ps->ext_mib_node = en;
        msg_ps->ext_name_ptr = np;

        en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
      }
      else
      {
        /* internal object */
        struct obj_def object_def;

        msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
        mn->get_object_def(np.ident_len, np.ident, &object_def);
        msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
        mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
        msg_ps->vb_idx += 1;
      }
    }
  }
  if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
      (msg_ps->vb_idx == msg_ps->invb.count))
  {
    /* simply echo the input if we can set it
       @todo do we need to return the actual value?
       e.g. if value is silently modified or behaves sticky? */
    msg_ps->outvb = msg_ps->invb;
    msg_ps->invb.head = NULL;
    msg_ps->invb.tail = NULL;
    msg_ps->invb.count = 0;
    snmp_ok_response(msg_ps);
  }
}
Exemplo n.º 8
0
/**
 * Reassembles incoming IP fragments into an IP datagram.
 *
 * @param p points to a pbuf chain of the fragment
 * @return NULL if reassembly is incomplete, ? otherwise
 */
struct pbuf *
ip_reass(struct pbuf *p)
{
  struct pbuf *r;
  struct ip_hdr *fraghdr;
  struct ip_reassdata *ipr;
  struct ip_reass_helper *iprh;
  u16_t offset, len;
  u8_t clen;
  struct ip_reassdata *ipr_prev = NULL;

  IPFRAG_STATS_INC(ip_frag.recv);
  snmp_inc_ipreasmreqds();

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

  if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
    LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
    IPFRAG_STATS_INC(ip_frag.err);
    goto nullreturn;
  }

  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;

  /* Check if we are allowed to enqueue more datagrams. */
  clen = pbuf_clen(p);
  if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
#if IP_REASS_FREE_OLDEST
    if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
        ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
#endif /* IP_REASS_FREE_OLDEST */
    {
      /* No datagram could be freed and still too many pbufs enqueued */
      LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
        ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
      IPFRAG_STATS_INC(ip_frag.memerr);
      /* @todo: send ICMP time exceeded here? */
      /* drop this pbuf */
      goto nullreturn;
    }
  }

  /* Look for the datagram the fragment belongs to in the current datagram queue,
   * remembering the previous in the queue for later dequeueing. */
  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
    /* Check if the incoming fragment matches the one currently present
       in the reassembly buffer. If so, we proceed with copying the
       fragment into the buffer. */
    if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
        ntohs(IPH_ID(fraghdr))));
      IPFRAG_STATS_INC(ip_frag.cachehit);
      break;
    }
    ipr_prev = ipr;
  }

  if (ipr == NULL) {
  /* Enqueue a new datagram into the datagram queue */
    ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
    /* Bail if unable to enqueue */
    if(ipr == NULL) {
      goto nullreturn;
    }
  } else {
    if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
      ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
      /* ipr->iphdr is not the header from the first fragment, but fraghdr is
       * -> copy fraghdr into ipr->iphdr since we want to have the header
       * of the first fragment (for ICMP time exceeded and later, for copying
       * all options, if supported)*/
      SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
    }
  }
  /* Track the current number of pbufs current 'in-flight', in order to limit 
  the number of fragments that may be enqueued at any one time */
  ip_reass_pbufcount += clen;

  /* At this point, we have either created a new entry or pointing 
   * to an existing one */

  /* check for 'no more fragments', and update queue entry*/
  if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
    ipr->flags |= IP_REASS_FLAG_LASTFRAG;
    ipr->datagram_len = offset + len;
    LWIP_DEBUGF(IP_REASS_DEBUG,
     ("ip_reass: last fragment seen, total len %"S16_F"\n",
      ipr->datagram_len));
  }
  /* find the right place to insert this pbuf */
  /* @todo: trim pbufs if fragments are overlapping */
  if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
    /* the totally last fragment (flag more fragments = 0) was received at least
     * once AND all fragments are received */
    ipr->datagram_len += IP_HLEN;

    /* save the second pbuf before copying the header over the pointer */
    r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;

    /* copy the original ip header back to the first pbuf */
    fraghdr = (struct ip_hdr*)(ipr->p->payload);
    SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
    IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
    IPH_OFFSET_SET(fraghdr, 0);
    IPH_CHKSUM_SET(fraghdr, 0);
    /* @todo: do we need to set calculate the correct checksum? */
    IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));

    p = ipr->p;

    /* chain together the pbufs contained within the reass_data list. */
    while(r != NULL) {
      iprh = (struct ip_reass_helper*)r->payload;

      /* hide the ip header for every succeding fragment */
      pbuf_header(r, -IP_HLEN);
      pbuf_cat(p, r);
      r = iprh->next_pbuf;
    }
    /* release the sources allocate for the fragment queue entry */
    ip_reass_dequeue_datagram(ipr, ipr_prev);

    /* and adjust the number of pbufs currently queued for reassembly. */
    ip_reass_pbufcount -= pbuf_clen(p);

    /* Return the pbuf chain */
    return p;
  }
  /* the datagram is not (yet?) reassembled completely */
  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
  return (NULL);

nullreturn:
  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
  IPFRAG_STATS_INC(ip_frag.drop);
  pbuf_free(p);
  return (NULL);
}
Exemplo n.º 9
0
/**
 * Fragment an IP datagram if too large for the netif.
 *
 * Chop the datagram in MTU sized chunks and send them in order
 * by using a fixed size static memory buffer (PBUF_REF) or
 * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
 *
 * @param p ip packet to send
 * @param netif the netif on which to send
 * @param dest destination ip address to which to send
 *
 * @return ERR_OK if sent successfully, err_t otherwise
 */
err_t 
ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
{
  struct pbuf *rambuf;
#if IP_FRAG_USES_STATIC_BUF
  struct pbuf *header;
#else
#if !LWIP_NETIF_TX_SINGLE_PBUF
  struct pbuf *newpbuf;
#endif
  struct ip_hdr *original_iphdr;
#endif
  struct ip_hdr *iphdr;
  u16_t nfb;
  u16_t left, cop;
  u16_t mtu = netif->mtu;
  u16_t ofo, omf;
  u16_t last;
  u16_t poff = IP_HLEN;
  u16_t tmp;
#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
  u16_t newpbuflen = 0;
  u16_t left_to_copy;
#endif

  /* Get a RAM based MTU sized pbuf */
#if IP_FRAG_USES_STATIC_BUF
  /* When using a static buffer, we use a PBUF_REF, which we will
   * use to reference the packet (without link header).
   * Layer and length is irrelevant.
   */
  rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
  if (rambuf == NULL) {
    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
    return (ERR_MEM);
  }
  rambuf->tot_len = rambuf->len = mtu;
  rambuf->payload = LWIP_MEM_ALIGN((void *)buf);

  /* Copy the IP header in it */
  iphdr = (struct ip_hdr *)rambuf->payload;
  SMEMCPY(iphdr, p->payload, IP_HLEN);
#else /* IP_FRAG_USES_STATIC_BUF */
  original_iphdr = (struct ip_hdr *)p->payload;
  iphdr = original_iphdr;
#endif /* IP_FRAG_USES_STATIC_BUF */

  /* Save original offset */
  tmp = ntohs(IPH_OFFSET(iphdr));
  ofo = tmp & IP_OFFMASK;
  omf = tmp & IP_MF;

  left = p->tot_len - IP_HLEN;

  nfb = (mtu - IP_HLEN) / 8;

  while (left) {
    last = (left <= mtu - IP_HLEN);

    /* Set new offset and MF flag */
    tmp = omf | (IP_OFFMASK & (ofo));
    if (!last) {
      tmp = tmp | IP_MF;
    }

    /* Fill this fragment */
    cop = last ? left : nfb * 8;

#if IP_FRAG_USES_STATIC_BUF
    poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
#else /* IP_FRAG_USES_STATIC_BUF */
#if LWIP_NETIF_TX_SINGLE_PBUF
    rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
    if (rambuf == NULL) {
      return (ERR_MEM);
    }
    LWIP_ASSERT("this needs a pbuf in one piece!",
      (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
    poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
    /* make room for the IP header */
    if(pbuf_header(rambuf, IP_HLEN)) {
      pbuf_free(rambuf);
      return (ERR_MEM);
    }
    /* fill in the IP header */
    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
    iphdr = rambuf->payload;
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
    /* When not using a static buffer, create a chain of pbufs.
     * The first will be a PBUF_RAM holding the link and IP header.
     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
     * but limited to the size of an mtu.
     */
    rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
    if (rambuf == NULL) {
      return (ERR_MEM);
    }
    LWIP_ASSERT("this needs a pbuf in one piece!",
                (p->len >= (IP_HLEN)));
    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
    iphdr = (struct ip_hdr *)rambuf->payload;

    /* Can just adjust p directly for needed offset. */
    p->payload = (u8_t *)p->payload + poff;
    p->len -= poff;

    left_to_copy = cop;
    while (left_to_copy) {
      struct pbuf_custom_ref *pcr;
      newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
      /* Is this pbuf already empty? */
      if (!newpbuflen) {
        p = p->next;
        continue;
      }
      pcr = ip_frag_alloc_pbuf_custom_ref();
      if (pcr == NULL) {
        pbuf_free(rambuf);
        return (ERR_MEM);
      }
      /* Mirror this pbuf, although we might not need all of it. */
      newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
      if (newpbuf == NULL) {
        ip_frag_free_pbuf_custom_ref(pcr);
        pbuf_free(rambuf);
        return (ERR_MEM);
      }
      pbuf_ref(p);
      pcr->original = p;
      pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;

      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
       * so that it is removed when pbuf_dechain is later called on rambuf.
       */
      pbuf_cat(rambuf, newpbuf);
      left_to_copy -= newpbuflen;
      if (left_to_copy) {
        p = p->next;
      }
    }
    poff = newpbuflen;
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
#endif /* IP_FRAG_USES_STATIC_BUF */

    /* Correct header */
    IPH_OFFSET_SET(iphdr, htons(tmp));
    IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
    IPH_CHKSUM_SET(iphdr, 0);
    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));

#if IP_FRAG_USES_STATIC_BUF
    if (last) {
      pbuf_realloc(rambuf, left + IP_HLEN);
    }

    /* This part is ugly: we alloc a RAM based pbuf for 
     * the link level header for each chunk and then 
     * free it.A PBUF_ROM style pbuf for which pbuf_header
     * worked would make things simpler.
     */
    header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
    if (header != NULL) {
      pbuf_chain(header, rambuf);
      netif->output(netif, header, dest);
      IPFRAG_STATS_INC(ip_frag.xmit);
      snmp_inc_ipfragcreates();
      pbuf_free(header);
    } else {
      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
      pbuf_free(rambuf);
      return (ERR_MEM);
    }
#else /* IP_FRAG_USES_STATIC_BUF */
    /* No need for separate header pbuf - we allowed room for it in rambuf
     * when allocated.
     */
    netif->output(netif, rambuf, dest);
    IPFRAG_STATS_INC(ip_frag.xmit);

    /* Unfortunately we can't reuse rambuf - the hardware may still be
     * using the buffer. Instead we free it (and the ensuing chain) and
     * recreate it next time round the loop. If we're lucky the hardware
     * will have already sent the packet, the free will really free, and
     * there will be zero memory penalty.
     */
    
    pbuf_free(rambuf);
#endif /* IP_FRAG_USES_STATIC_BUF */
    left -= cop;
    ofo += nfb;
  }
#if IP_FRAG_USES_STATIC_BUF
  pbuf_free(rambuf);
#endif /* IP_FRAG_USES_STATIC_BUF */
  snmp_inc_ipfragoks();
  return (ERR_OK);
}
Exemplo n.º 10
0
Arquivo: pbuf.c Projeto: janfj/dd-wrt
/**
 * Dereference a pbuf chain or queue and deallocate any no-longer-used
 * pbufs at the head of this chain or queue.
 *
 * Decrements the pbuf reference count. If it reaches
 * zero, the pbuf is deallocated.
 *
 * For a pbuf chain, this is repeated for each pbuf in the chain,
 * up to the first pbuf which has a non-zero reference count after
 * decrementing. (This might de-allocate the whole chain.)
 *
 * @param pbuf The pbuf (chain) to be dereferenced.
 *
 * @return the number of pbufs that were de-allocated
 * from the head of the chain.
 *
 * @note MUST NOT be called on a packet queue.
 * @note the reference counter of a pbuf equals the number of pointers
 * that refer to the pbuf (or into the pbuf).
 *
 * @internal examples:
 *
 * Assuming existing chains a->b->c with the following reference
 * counts, calling pbuf_free(a) results in:
 *
 * 1->2->3 becomes ...1->3
 * 3->3->3 becomes 2->3->3
 * 1->1->2 becomes ......1
 * 2->1->1 becomes 1->1->1
 * 1->1->1 becomes .......
 *
 */
u8_t
pbuf_free(struct pbuf *p)
{
    struct pbuf *q;
    u8_t count;
    SYS_ARCH_DECL_PROTECT(old_level);

    LWIP_ASSERT("p != NULL", p != NULL);
    /* if assertions are disabled, proceed with debug output */
    if (p == NULL) {
        LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_free(p == NULL) was called.\n"));
        return 0;
    }
    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_free(%p)\n", (void *)p));

    PERF_START;

    LWIP_ASSERT("pbuf_free: sane flags",
                p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_ROM ||
                p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_POOL);

    count = 0;
    /* Since decrementing ref cannot be guaranteed to be a single machine operation
     * we must protect it. Also, the later test of ref must be protected.
     */
    SYS_ARCH_PROTECT(old_level);
    /* de-allocate all consecutive pbufs from the head of the chain that
     * obtain a zero reference count after decrementing*/
    while (p != NULL) {
        /* all pbufs in a chain are referenced at least once */
        LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
        /* decrease reference count (number of pointers to pbuf) */
        p->ref--;
        /* this pbuf is no longer referenced to? */
        if (p->ref == 0) {
            /* remember next pbuf in chain for next iteration */
            q = p->next;
            LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p));
            /* is this a pbuf from the pool? */
            if (p->flags == PBUF_FLAG_POOL) {
                p->len = p->tot_len = PBUF_POOL_BUFSIZE;
                p->payload = (void *)((u8_t *)p + sizeof(struct pbuf));
                PBUF_POOL_FREE(p);
                /* a ROM or RAM referencing pbuf */
            } else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) {
                memp_free(MEMP_PBUF, p);
                /* p->flags == PBUF_FLAG_RAM */
            } else {
                mem_free(p);
            }
            count++;
            /* proceed to next pbuf */
            p = q;
            /* p->ref > 0, this pbuf is still referenced to */
            /* (and so the remaining pbufs in chain as well) */
        } else {
            LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: %p has ref %u, ending here.\n", (void *)p, (unsigned int)p->ref));
            /* stop walking through chain */
            p = NULL;
        }
    }
    SYS_ARCH_UNPROTECT(old_level);
    PERF_STOP("pbuf_free");
    /* return number of de-allocated pbufs */
    return count;
}
Exemplo n.º 11
0
Arquivo: pbuf.c Projeto: janfj/dd-wrt
/**
 *
 * Create PBUF_POOL (or PBUF_RAM) copies of PBUF_REF pbufs.
 *
 * Used to queue packets on behalf of the lwIP stack, such as
 * ARP based queueing.
 *
 * Go through a pbuf chain and replace any PBUF_REF buffers
 * with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of
 * the referenced data.
 *
 * @note You MUST explicitly use p = pbuf_take(p);
 * The pbuf you give as argument, may have been replaced
 * by pbuf_take()!
 *
 * @note Any replaced pbufs will be freed through pbuf_free().
 * This may deallocate them if they become no longer referenced.
 *
 * @param p Head of pbuf chain to process
 *
 * @return Pointer to head of pbuf chain
 */
struct pbuf *
pbuf_take(struct pbuf *p)
{
    struct pbuf *q , *prev, *head;
    LWIP_ASSERT("pbuf_take: p != NULL\n", p != NULL);
    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)p));

    prev = NULL;
    head = p;
    /* iterate through pbuf chain */
    do
    {
        /* pbuf is of type PBUF_REF? */
        if (p->flags == PBUF_FLAG_REF) {
            LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p));
            /* allocate a pbuf (w/ payload) fully in RAM */
            /* PBUF_POOL buffers are faster if we can use them */
            if (p->len <= PBUF_POOL_BUFSIZE) {
                q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL);
                if (q == NULL) LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n"));
            } else {
                /* no replacement pbuf yet */
                q = NULL;
                LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: PBUF_POOL too small to replace PBUF_REF\n"));
            }
            /* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */
            if (q == NULL) {
                q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM);
                if (q == NULL) LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAM\n"));
            }
            /* replacement pbuf could be allocated? */
            if (q != NULL)
            {
                /* copy p to q */
                /* copy successor */
                q->next = p->next;
                /* remove linkage from original pbuf */
                p->next = NULL;
                /* remove linkage to original pbuf */
                if (prev != NULL) {
                    /* prev->next == p at this point */
                    LWIP_ASSERT("prev->next == p", prev->next == p);
                    /* break chain and insert new pbuf instead */
                    prev->next = q;
                    /* prev == NULL, so we replaced the head pbuf of the chain */
                } else {
                    head = q;
                }
                /* copy pbuf payload */
                memcpy(q->payload, p->payload, p->len);
                q->tot_len = p->tot_len;
                q->len = p->len;
                /* in case p was the first pbuf, it is no longer refered to by
                 * our caller, as the caller MUST do p = pbuf_take(p);
                 * in case p was not the first pbuf, it is no longer refered to
                 * by prev. we can safely free the pbuf here.
                 * (note that we have set p->next to NULL already so that
                 * we will not free the rest of the chain by accident.)
                 */
                pbuf_free(p);
                /* do not copy ref, since someone else might be using the old buffer */
                LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_take: replaced PBUF_REF %p with %p\n", (void *)p, (void *)q));
                p = q;
            } else {
                /* deallocate chain */
                pbuf_free(head);
                LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p));
                return NULL;
            }
            /* p->flags != PBUF_FLAG_REF */
        } else {
            LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: skipping pbuf not of type PBUF_REF\n"));
        }
        /* remember this pbuf */
        prev = p;
        /* proceed to next pbuf in original chain */
        p = p->next;
    } while (p);
    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: end of chain reached.\n"));

    return head;
}
Exemplo n.º 12
0
Arquivo: pbuf.c Projeto: janfj/dd-wrt
/**
 * Allocates a pbuf.
 *
 * The actual memory allocated for the pbuf is determined by the
 * layer at which the pbuf is allocated and the requested size
 * (from the size parameter).
 *
 * @param flag this parameter decides how and where the pbuf
 * should be allocated as follows:
 *
 * - PBUF_RAM: buffer memory for pbuf is allocated as one large
 *             chunk. This includes protocol headers as well.
 * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
 *             protocol headers. Additional headers must be prepended
 *             by allocating another pbuf and chain in to the front of
 *             the ROM pbuf. It is assumed that the memory used is really
 *             similar to ROM in that it is immutable and will not be
 *             changed. Memory which is dynamic should generally not
 *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
 * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
 *             protocol headers. It is assumed that the pbuf is only
 *             being used in a single thread. If the pbuf gets queued,
 *             then pbuf_take should be called to copy the buffer.
 * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
 *              the pbuf pool that is allocated during pbuf_init().
 *
 * @return the allocated pbuf. If multiple pbufs where allocated, this
 * is the first pbuf of a pbuf chain.
 */
struct pbuf *
pbuf_alloc(pbuf_layer l, u16_t length, pbuf_flag flag)
{
    struct pbuf *p, *q, *r;
    u16_t offset;
    s32_t rem_len; /* remaining length */
    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%u)\n", length));

    /* determine header offset */
    offset = 0;
    switch (l) {
    case PBUF_TRANSPORT:
        /* add room for transport (often TCP) layer header */
        offset += PBUF_TRANSPORT_HLEN;
    /* FALLTHROUGH */
    case PBUF_IP:
        /* add room for IP layer header */
        offset += PBUF_IP_HLEN;
    /* FALLTHROUGH */
    case PBUF_LINK:
        /* add room for link layer header */
        offset += PBUF_LINK_HLEN;
        break;
    case PBUF_RAW:
        break;
    default:
        LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
        return NULL;
    }

    switch (flag) {
    case PBUF_POOL:
        /* allocate head of pbuf chain into p */
        p = pbuf_pool_alloc();
        LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
        if (p == NULL) {
#if PBUF_STATS
            ++lwip_stats.pbuf.err;
#endif /* PBUF_STATS */
            return NULL;
        }
        p->next = NULL;

        /* make the payload pointer point 'offset' bytes into pbuf data memory */
        p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset)));
        LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
                    ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
        /* the total length of the pbuf chain is the requested size */
        p->tot_len = length;
        /* set the length of the first pbuf in the chain */
        p->len = length > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: length;
        /* set reference count (needed here in case we fail) */
        p->ref = 1;

        /* now allocate the tail of the pbuf chain */

        /* remember first pbuf for linkage in next iteration */
        r = p;
        /* remaining length to be allocated */
        rem_len = length - p->len;
        /* any remaining pbufs to be allocated? */
        while (rem_len > 0) {
            q = pbuf_pool_alloc();
            if (q == NULL) {
                LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n"));
#if PBUF_STATS
                ++lwip_stats.pbuf.err;
#endif /* PBUF_STATS */
                /* free chain so far allocated */
                pbuf_free(p);
                /* bail out unsuccesfully */
                return NULL;
            }
            q->next = NULL;
            /* make previous pbuf point to this pbuf */
            r->next = q;
            /* set total length of this pbuf and next in chain */
            q->tot_len = rem_len;
            /* this pbuf length is pool size, unless smaller sized tail */
            q->len = rem_len > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rem_len;
            q->payload = (void *)((u8_t *)q + sizeof(struct pbuf));
            LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
                        ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
            q->ref = 1;
            /* calculate remaining length to be allocated */
            rem_len -= q->len;
            /* remember this pbuf for linkage in next iteration */
            r = q;
        }
        /* end of chain */
        /*r->next = NULL;*/

        break;
    case PBUF_RAM:
        /* If pbuf is to be allocated in RAM, allocate memory for it. */
        p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + length + offset));
        if (p == NULL) {
            return NULL;
        }
        /* Set up internal structure of the pbuf. */
        p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf) + offset));
        p->len = p->tot_len = length;
        p->next = NULL;
        p->flags = PBUF_FLAG_RAM;

        LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
                    ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
        break;
    /* pbuf references existing (static constant) ROM payload? */
    case PBUF_ROM:
    /* pbuf references existing (externally allocated) RAM payload? */
    case PBUF_REF:
        /* only allocate memory for the pbuf structure */
        p = memp_malloc(MEMP_PBUF);
        if (p == NULL) {
            LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", flag == PBUF_ROM?"ROM":"REF"));
            return NULL;
        }
        /* caller must set this field properly, afterwards */
        p->payload = NULL;
        p->len = p->tot_len = length;
        p->next = NULL;
        p->flags = (flag == PBUF_ROM? PBUF_FLAG_ROM: PBUF_FLAG_REF);
        break;
    default:
        LWIP_ASSERT("pbuf_alloc: erroneous flag", 0);
        return NULL;
    }
    /* set reference count */
    p->ref = 1;
    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%u) == %p\n", length, (void *)p));
    return p;
}
Exemplo n.º 13
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");
}
Exemplo n.º 14
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;
}
Exemplo n.º 15
0
/* find out what we can send and send it */
err_t
tcp_output(struct tcp_pcb *pcb)
{
  struct pbuf *p;
  struct tcp_hdr *tcphdr;
  struct tcp_seg *seg, *useg;
  u32_t wnd;
#if TCP_CWND_DEBUG
  int i = 0;
#endif /* TCP_CWND_DEBUG */

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

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

  seg = pcb->unsent;

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

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

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

    return ERR_OK;
  }

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

    pcb->unsent = seg->next;

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

    tcp_output_segment(seg, pcb);
    pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
    if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
      pcb->snd_max = pcb->snd_nxt;
    }
    /* put segment on unacknowledged list if length > 0 */
    if (TCP_TCPLEN(seg) > 0) {
      seg->next = NULL;
      /* unacked list is empty? */
      if (pcb->unacked == NULL) {
        pcb->unacked = seg;
        useg = seg;
      /* unacked list is not empty? */
      } else {
        /* In the case of fast retransmit, the packet should not go to the tail
         * of the unacked queue, but rather at the head. We need to check for
         * this case. -STJ Jul 27, 2004 */
        if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){
          /* add segment to head of unacked list */
          seg->next = pcb->unacked;
          pcb->unacked = seg;
        } else {
          /* add segment to tail of unacked list */
          useg->next = seg;
          useg = useg->next;
        }
      }
    /* do not queue empty segments on the unacked list */
    } else {
      tcp_seg_free(seg);
    }
    seg = pcb->unsent;
  }
  return ERR_OK;
}
Exemplo n.º 16
0
/******************************************************************************
 * FunctionName : espconn_udp_server_recv
 * Description  : This callback will be called when receiving a datagram.
 * Parameters   : arg -- user supplied argument
 *                upcb -- the udp_pcb which received data
 *                p -- the packet buffer that was received
 *                addr -- the remote IP address from which the packet was received
 *                port -- the remote port from which the packet was received
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p,
                 struct ip_addr *addr, u16_t port)
{
    espconn_msg *precv = arg;
    struct pbuf *q = NULL;
    u8_t *pdata = NULL;
    u16_t length = 0;
    struct ip_info ipconfig;

    LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %p\n", __LINE__, upcb));

    upcb->remote_port = port;
    upcb->remote_ip = *addr;

    precv->pcommon.remote_ip[0] = ip4_addr1_16(&upcb->remote_ip);
    precv->pcommon.remote_ip[1] = ip4_addr2_16(&upcb->remote_ip);
    precv->pcommon.remote_ip[2] = ip4_addr3_16(&upcb->remote_ip);
    precv->pcommon.remote_ip[3] = ip4_addr4_16(&upcb->remote_ip);
    os_memcpy(precv->pespconn->proto.udp->remote_ip, precv->pcommon.remote_ip, 4);
    precv->pespconn->proto.udp->remote_port = port;
    precv->pcommon.remote_port = port;
    precv->pcommon.pcb = upcb;

	if (wifi_get_opmode() != 1) {
		wifi_get_ip_info(1, &ipconfig);

		if (!ip_addr_netcmp((struct ip_addr *)precv->pespconn->proto.udp->remote_ip, &ipconfig.ip, &ipconfig.netmask)) {
			wifi_get_ip_info(0, &ipconfig);
		}
	} else {
		wifi_get_ip_info(0, &ipconfig);
	}
	upcb->local_ip = ipconfig.ip;
	precv->pespconn->proto.udp->local_ip[0] = ip4_addr1_16(&upcb->local_ip);
	precv->pespconn->proto.udp->local_ip[1] = ip4_addr2_16(&upcb->local_ip);
	precv->pespconn->proto.udp->local_ip[2] = ip4_addr3_16(&upcb->local_ip);
	precv->pespconn->proto.udp->local_ip[3] = ip4_addr4_16(&upcb->local_ip);

    if (p != NULL) {
//        q = p;

//        while (q != NULL) {
//            pdata = (u8_t *)os_zalloc(q ->len + 1);
//            length = pbuf_copy_partial(q, pdata, q ->len, 0);
//
//            LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %x\n", __LINE__, length));
//            precv->pcommon.pcb = upcb;
//
//            if (length != 0) {
//                if (precv->pespconn->recv_callback != NULL) {
//                    precv->pespconn->recv_callback(precv->pespconn, pdata, length);
//                }
//            }
//
//            q = q->next;
//            os_free(pdata);
//        }
    	pdata = (u8_t *)os_zalloc(p ->tot_len + 1);
    	length = pbuf_copy_partial(p, pdata, p ->tot_len, 0);
    	precv->pcommon.pcb = upcb;
        pbuf_free(p);
		if (length != 0) {
			if (precv->pespconn->recv_callback != NULL) {
				precv->pespconn->recv_callback(precv->pespconn, pdata, length);
			}
		}
		os_free(pdata);
    } else {
        return;
    }
}
Exemplo n.º 17
0
struct snmp_varbind*
snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
{
  struct snmp_varbind *vb;

  vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
  if (vb != NULL)
  {
    u8_t i;

    vb->next = NULL;
    vb->prev = NULL;
    i = oid->len;
    vb->ident_len = i;
    if (i > 0)
    {
      LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
      /* allocate array of s32_t for our object identifier */
      vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
      if (vb->ident == NULL)
      {
        LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n"));
        memp_free(MEMP_SNMP_VARBIND, vb);
        return NULL;
      }
      while(i > 0)
      {
        i--;
        vb->ident[i] = oid->id[i];
      }
    }
    else
    {
      /* i == 0, pass zero length object identifier */
      vb->ident = NULL;
    }
    vb->value_type = type;
    vb->value_len = len;
    if (len > 0)
    {
      LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
      /* allocate raw bytes for our object value */
      vb->value = memp_malloc(MEMP_SNMP_VALUE);
      if (vb->value == NULL)
      {
        LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n"));
        if (vb->ident != NULL)
        {
          memp_free(MEMP_SNMP_VALUE, vb->ident);
        }
        memp_free(MEMP_SNMP_VARBIND, vb);
        return NULL;
      }
    }
    else
    {
      /* ASN1_NUL type, or zero length ASN1_OC_STR */
      vb->value = NULL;
    }
  }
  else
  {
    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n"));
  }
  return vb;
}
Exemplo n.º 18
0
/******************************************************************************
 * FunctionName : espconn_udp_sent
 * Description  : sent data for client or server
 * Parameters   : void *arg -- client or server to send
 * 				  uint8* psent -- Data to send
 *                uint16 length -- Length of data to send
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
espconn_udp_sent(void *arg, uint8 *psent, uint16 length)
{
    espconn_msg *pudp_sent = arg;
    struct udp_pcb *upcb = pudp_sent->pcommon.pcb;
    struct pbuf *p, *q;
    u8_t *data = NULL;
    u16_t cnt = 0;
    u16_t datalen = 0;
    u16_t i = 0;
    err_t err;
    LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb));

    if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) {
        return;
    }

    if (1024 < length) {
        datalen = 1024;
    } else {
        datalen = length;
    }

    p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
    LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));

    if (p != NULL) {
        q = p;

        while (q != NULL) {
            data = (u8_t *)q->payload;
            LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data));

            for (i = 0; i < q->len; i++) {
                data[i] = ((u8_t *) psent)[cnt++];
            }

            q = q->next;
        }
    } else {
        return;
    }

    upcb->remote_port = pudp_sent->pespconn->proto.udp->remote_port;
    IP4_ADDR(&upcb->remote_ip, pudp_sent->pespconn->proto.udp->remote_ip[0],
    		pudp_sent->pespconn->proto.udp->remote_ip[1],
    		pudp_sent->pespconn->proto.udp->remote_ip[2],
    		pudp_sent->pespconn->proto.udp->remote_ip[3]);

    LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port));
    err = udp_send(upcb, p);
    LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d\n", __LINE__, err));

    if (p->ref != 0) {
        LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));
        pbuf_free(p);
        pudp_sent->pcommon.ptrbuf = psent + datalen;
        pudp_sent->pcommon.cntr = length - datalen;
        espconn_data_sent(pudp_sent);
    }
}
Exemplo n.º 19
0
/**
 * Service an internal or external event for SNMP GETNEXT.
 *
 * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
 * @param msg_ps points to the associated message process state
 */
static void
snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
{
  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));

  if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
  {
    const struct mib_external_node *en;

    /* get_object_def() answer*/
    en = msg_ps->ext_mib_node;

    /* translate answer into a known lifeform */
    en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    {
      msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
      en->get_value_q(request_id, &msg_ps->ext_object_def);
    }
    else
    {
      en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
      /* search failed, object id points to unknown object (nosuchname) */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }
  else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
  {
    const struct mib_external_node *en;
    struct snmp_varbind *vb;

    /* get_value() answer */
    en = msg_ps->ext_mib_node;

    LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
    vb = snmp_varbind_alloc(&msg_ps->ext_oid,
                            msg_ps->ext_object_def.asn_type,
                            (u8_t)msg_ps->ext_object_def.v_len);
    if (vb != NULL)
    {
      en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
      snmp_varbind_tail_add(&msg_ps->outvb, vb);
      msg_ps->state = SNMP_MSG_SEARCH_OBJ;
      msg_ps->vb_idx += 1;
    }
    else
    {
      en->get_value_pc(request_id, &msg_ps->ext_object_def);
      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
      snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    }
  }

  while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
         (msg_ps->vb_idx < msg_ps->invb.count))
  {
    const struct mib_node *mn;
    struct snmp_obj_id oid;

    if (msg_ps->vb_idx == 0)
    {
      msg_ps->vb_ptr = msg_ps->invb.head;
    }
    else
    {
      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    }
    if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
    {
      if (msg_ps->vb_ptr->ident_len > 3)
      {
        /* can offset ident_len and ident */
        mn = snmp_expand_tree((const struct mib_node*)&internet,
                              msg_ps->vb_ptr->ident_len - 4,
                              msg_ps->vb_ptr->ident + 4, &oid);
      }
      else
      {
        /* can't offset ident_len -4, ident + 4 */
        mn = snmp_expand_tree((const struct mib_node*)&internet, 0, NULL, &oid);
      }
    }
    else
    {
      mn = NULL;
    }
    if (mn != NULL)
    {
      if (mn->node_type == MIB_NODE_EX)
      {
        /* external object */
        const struct mib_external_node *en = (const struct mib_external_node*)mn;

        msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
        /* save en && args in msg_ps!! */
        msg_ps->ext_mib_node = en;
        msg_ps->ext_oid = oid;

        en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
      }
      else
      {
        /* internal object */
        struct obj_def object_def;
        struct snmp_varbind *vb;

        msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
        mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);

        LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
        vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
        if (vb != NULL)
        {
          msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
          mn->get_value(&object_def, object_def.v_len, vb->value);
          snmp_varbind_tail_add(&msg_ps->outvb, vb);
          msg_ps->state = SNMP_MSG_SEARCH_OBJ;
          msg_ps->vb_idx += 1;
        }
        else
        {
          LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
          snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
        }
      }
    }
    if (mn == NULL)
    {
      /* mn == NULL, noSuchName */
      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    }
  }
  if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
      (msg_ps->vb_idx == msg_ps->invb.count))
  {
    snmp_ok_response(msg_ps);
  }
}
Exemplo n.º 20
0
/** State machine-like implementation of an SMTP client.
 */
static void
smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
{
  struct smtp_session* s = (struct smtp_session*)arg;
  u16_t response_code = 0;
  u16_t tx_buf_len = 0;
  enum smtp_session_state next_state;

  if (arg == NULL) {
    /* already closed SMTP connection */
    if (p != NULL) {
      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
        p->tot_len, smtp_pbuf_str(p)));
      pbuf_free(p);
    }
    return;
  }

  next_state = s->state;

  if (p != NULL) {
    /* received data */
    if (s->p == NULL) {
      s->p = p;
    } else {
      pbuf_cat(s->p, p);
    }
  } else {
    /* idle timer, close connection if timed out */
    if (s->timer == 0) {
      LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
      smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
      return;
    }
    if (s->state == SMTP_BODY) {
      smtp_send_body(s, pcb);
      return;
    }
  }
  response_code = smtp_is_response(s);
  if (response_code) {
    LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
    if (smtp_is_response_finished(s) != ERR_OK) {
      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
      /* wait for next packet to complete the respone */
      return;
    }
  } else {
    if (s->p != NULL) {
      LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
        smtp_pbuf_str(s->p)));
      pbuf_free(s->p);
      s->p = NULL;
    }
    return;
  }

  switch(s->state)
  {
  case(SMTP_NULL):
    /* wait for 220 */
    if (response_code == 220) {
      /* then send EHLO */
      next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
    }
    break;
  case(SMTP_HELO):
    /* wait for 250 */
    if (response_code == 250) {
#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
      /* then send AUTH or MAIL */
      next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
    }
    break;
  case(SMTP_AUTH_LOGIN):
  case(SMTP_AUTH_PLAIN):
    /* wait for 235 */
    if (response_code == 235) {
#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
      /* send MAIL */
      next_state = smtp_prepare_mail(s, &tx_buf_len);
    }
    break;
#if SMTP_SUPPORT_AUTH_LOGIN
  case(SMTP_AUTH_LOGIN_UNAME):
    /* wait for 334 Username */
    if (response_code == 334) {
      if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
        /* send username */
        next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
      }
    }
    break;
  case(SMTP_AUTH_LOGIN_PASS):
    /* wait for 334 Password */
    if (response_code == 334) {
      if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
        /* send username */
        next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
      }
    }
    break;
#endif /* SMTP_SUPPORT_AUTH_LOGIN */
  case(SMTP_MAIL):
    /* wait for 250 */
    if (response_code == 250) {
      /* send RCPT */
      next_state = smtp_prepare_rcpt(s, &tx_buf_len);
    }
    break;
  case(SMTP_RCPT):
    /* wait for 250 */
    if (response_code == 250) {
      /* send DATA */
      SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
      tx_buf_len = SMTP_CMD_DATA_LEN;
      next_state = SMTP_DATA;
    }
    break;
  case(SMTP_DATA):
    /* wait for 354 */
    if (response_code == 354) {
      /* send email header */
      next_state = smtp_prepare_header(s, &tx_buf_len);
    }
    break;
  case(SMTP_BODY):
    /* nothing to be done here, handled somewhere else */
    break;
  case(SMTP_QUIT):
    /* wait for 250 */
    if (response_code == 250) {
      /* send QUIT */
      next_state = smtp_prepare_quit(s, &tx_buf_len);
    }
    break;
  case(SMTP_CLOSED):
    /* nothing to do, wait for connection closed from server */
    return;
  default:
    LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
      smtp_state_str[s->state]));
    break;
  }
  if (s->state == next_state) {
    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
      smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
    /* close connection */
    smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
    return;
  }
  if (tx_buf_len > 0) {
    SMTP_TX_BUF_MAX(tx_buf_len);
    if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
        smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
        smtp_state_str[s->state], tx_buf_len, s->tx_buf));
      s->timer = SMTP_TIMEOUT;
      pbuf_free(s->p);
      s->p = NULL;
      LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
        smtp_state_str[s->state], smtp_state_str[next_state]));
      s->state = next_state;
      if (next_state == SMTP_BODY) {
        /* try to stream-send body data right now */
        smtp_send_body(s, pcb);
      } else if (next_state == SMTP_CLOSED) {
        /* sent out all data, delete structure */
        altcp_arg(pcb, NULL);
        smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
      }
    }
  }
}
Exemplo n.º 21
0
/* lwIP UDP receive callback function */
static void
snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
  struct snmp_msg_pstat *msg_ps;
  u8_t req_idx;
  err_t err_ret;
  u16_t payload_len = p->tot_len;
  u16_t payload_ofs = 0;
  u16_t varbind_ofs = 0;

  /* suppress unused argument warning */
  LWIP_UNUSED_ARG(arg);

  /* traverse input message process list, look for SNMP_MSG_EMPTY */
  msg_ps = &msg_input_list[0];
  req_idx = 0;
  while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
  {
    req_idx++;
    msg_ps++;
  }
  if (req_idx == SNMP_CONCURRENT_REQUESTS)
  {
    /* exceeding number of concurrent requests */
    pbuf_free(p);
    return;
  }

  /* accepting request */
  snmp_inc_snmpinpkts();
  /* record used 'protocol control block' */
  msg_ps->pcb = pcb;
  /* source address (network order) */
  msg_ps->sip = *addr;
  /* source port (host order (lwIP oddity)) */
  msg_ps->sp = port;

  /* check total length, version, community, pdu type */
  err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
  /* Only accept requests and requests without error (be robust) */
  /* Reject response and trap headers or error requests as input! */
  if ((err_ret != ERR_OK) ||
      ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
       (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
       (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
      ((msg_ps->error_status != SNMP_ES_NOERROR) ||
       (msg_ps->error_index != 0)) )
  {
    /* header check failed drop request silently, do not return error! */
    pbuf_free(p);
    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
    return;
  }
  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));

  /* Builds a list of variable bindings. Copy the varbinds from the pbuf
    chain to glue them when these are divided over two or more pbuf's. */
  err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
  /* we've decoded the incoming message, release input msg now */
  pbuf_free(p);
  if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
  {
    /* varbind-list decode failed, or varbind list empty.
       drop request silently, do not return error!
       (errors are only returned for a specific varbind failure) */
    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
    return;
  }

  msg_ps->error_status = SNMP_ES_NOERROR;
  msg_ps->error_index = 0;
  /* find object for each variable binding */
  msg_ps->state = SNMP_MSG_SEARCH_OBJ;
  /* first variable binding from list to inspect */
  msg_ps->vb_idx = 0;

  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));

  /* handle input event and as much objects as possible in one go */
  snmp_msg_event(req_idx);
}
Exemplo n.º 22
0
/** The actual mail-sending function, called by smtp_send_mail and
 * smtp_send_mail_static after setting up the struct smtp_session.
 */
static err_t
smtp_send_mail_alloced(struct smtp_session *s)
{
  err_t err;
  struct altcp_pcb* pcb = NULL;
  ip_addr_t addr;

  LWIP_ASSERT("no smtp_session supplied", s != NULL);

#if SMTP_CHECK_DATA
  /* check that body conforms to RFC:
   * - convert all single-CR or -LF in body to CRLF
   * - only 7-bit ASCII is allowed
   */
  if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
    err = ERR_ARG;
    goto leave;
  }
  if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
    err = ERR_ARG;
    goto leave;
  }
  if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
    err = ERR_ARG;
    goto leave;
  }
#if SMTP_BODYDH
  if (s->bodydh == NULL)
#endif /* SMTP_BODYDH */
  {
    if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
      err = ERR_ARG;
      goto leave;
    }
  }
#endif /* SMTP_CHECK_DATA */

#if SMTP_COPY_AUTHDATA
  /* copy auth data, ensuring the first byte is always zero */
  MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
  s->auth_plain_len = smtp_auth_plain_len;
  /* default username and pass is empty string */
  s->username = s->auth_plain;
  s->pass = s->auth_plain;
  if (smtp_username != NULL) {
    s->username += smtp_username - smtp_auth_plain;
  }
  if (smtp_pass != NULL) {
    s->pass += smtp_pass - smtp_auth_plain;
  }
#endif /* SMTP_COPY_AUTHDATA */

  s->state = SMTP_NULL;
  s->timer = SMTP_TIMEOUT;

#if LWIP_DNS
  err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
#else /* LWIP_DNS */
  err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
#endif /* LWIP_DNS */
  if (err == ERR_OK) {
    pcb = smtp_setup_pcb(s, &addr);
    if (pcb == NULL) {
      err = ERR_MEM;
      goto leave;
    }
    err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
    if (err != ERR_OK) {
      LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
      goto deallocate_and_leave;
    }
  } else if (err != ERR_INPROGRESS) {
    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
    goto deallocate_and_leave;
  }
  return ERR_OK;

deallocate_and_leave:
  if (pcb != NULL) {
    altcp_arg(pcb, NULL);
    altcp_close(pcb);
  }
leave:
  smtp_free_struct(s);
  /* no need to call the callback here since we return != ERR_OK */
  return err;
}
Exemplo n.º 23
0
/**
 * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
 *
 * The actual memory allocated for the pbuf is determined by the
 * layer at which the pbuf is allocated and the requested size
 * (from the size parameter).
 *
 * @param layer flag to define header size
 * @param length size of the pbuf's payload
 * @param type this parameter decides how and where the pbuf
 * should be allocated as follows:
 *
 * - PBUF_RAM: buffer memory for pbuf is allocated as one large
 *             chunk. This includes protocol headers as well.
 * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
 *             protocol headers. Additional headers must be prepended
 *             by allocating another pbuf and chain in to the front of
 *             the ROM pbuf. It is assumed that the memory used is really
 *             similar to ROM in that it is immutable and will not be
 *             changed. Memory which is dynamic should generally not
 *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
 * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
 *             protocol headers. It is assumed that the pbuf is only
 *             being used in a single thread. If the pbuf gets queued,
 *             then pbuf_take should be called to copy the buffer.
 * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
 *              the pbuf pool that is allocated during pbuf_init().
 *
 * @return the allocated pbuf. If multiple pbufs where allocated, this
 * is the first pbuf of a pbuf chain.
 */
struct pbuf *
pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
{
  struct pbuf *p, *q, *r;
  u16_t offset;
  s32_t rem_len; /* remaining length */
  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));

  /* determine header offset */
  switch (layer) {
  case PBUF_TRANSPORT:
    /* add room for transport (often TCP) layer header */
    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
    break;
  case PBUF_IP:
    /* add room for IP layer header */
    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
    break;
  case PBUF_LINK:
    /* add room for link layer header */
    offset = PBUF_LINK_HLEN;
    break;
  case PBUF_RAW:
    offset = 0;
    break;
  default:
    LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
    return NULL;
  }

  switch (type) {
  case PBUF_POOL:
    /* allocate head of pbuf chain into p */
    p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
    if (p == NULL) {
      PBUF_POOL_IS_EMPTY();
      return NULL;
    }
    p->type = type;
    p->next = NULL;

    /* make the payload pointer point 'offset' bytes into pbuf data memory */
    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
    LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
            ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
    /* the total length of the pbuf chain is the requested size */
    p->tot_len = length;
    /* set the length of the first pbuf in the chain */
    p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
    LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
                ((u8_t*)p->payload + p->len <=
                 (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
    LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
      (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
    /* set reference count (needed here in case we fail) */
    p->ref = 1;

    /* now allocate the tail of the pbuf chain */

    /* remember first pbuf for linkage in next iteration */
    r = p;
    /* remaining length to be allocated */
    rem_len = length - p->len;
    /* any remaining pbufs to be allocated? */
    while (rem_len > 0) {
      q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
      if (q == NULL) {
        PBUF_POOL_IS_EMPTY();
        /* free chain so far allocated */
        pbuf_free(p);
        /* bail out unsuccesfully */
        return NULL;
      }
      q->type = type;
      q->flags = 0;
      q->next = NULL;
      /* make previous pbuf point to this pbuf */
      r->next = q;
      /* set total length of this pbuf and next in chain */
      LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
      q->tot_len = (u16_t)rem_len;
      /* this pbuf length is pool size, unless smaller sized tail */
      q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
      q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
      LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
              ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
      LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
                  ((u8_t*)p->payload + p->len <=
                   (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
      q->ref = 1;
      /* calculate remaining length to be allocated */
      rem_len -= q->len;
      /* remember this pbuf for linkage in next iteration */
      r = q;
    }
    /* end of chain */
    /*r->next = NULL;*/

    break;
  case PBUF_RAM:
    /* If pbuf is to be allocated in RAM, allocate memory for it. */
    p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
    if (p == NULL) {
      return NULL;
    }
    /* Set up internal structure of the pbuf. */
    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
    p->len = p->tot_len = length;
    p->next = NULL;
    p->type = type;

    LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
           ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
    break;
  /* pbuf references existing (non-volatile static constant) ROM payload? */
  case PBUF_ROM:
  /* pbuf references existing (externally allocated) RAM payload? */
  case PBUF_REF:
    /* only allocate memory for the pbuf structure */
    p = (struct pbuf *)memp_malloc(MEMP_PBUF);
    if (p == NULL) {
      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
                  ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
                  (type == PBUF_ROM) ? "ROM" : "REF"));
      return NULL;
    }
    /* caller must set this field properly, afterwards */
    p->payload = NULL;
    p->len = p->tot_len = length;
    p->next = NULL;
    p->type = type;
    break;
  default:
    LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
    return NULL;
  }
  /* set reference count */
  p->ref = 1;
  /* set flags */
  p->flags = 0;
  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
  return p;
}
Exemplo n.º 24
0
/**
 * Handle the incoming SLIP stream character by character
 *
 * Poll the serial layer by calling sio_read() or sio_tryread().
 *
 * @param netif the lwip network interface structure for this slipif
 * @param block if 1, block until data is received; if 0, return when all data
 *        from the buffer is received (multiple calls to this function will
 *        return a complete packet, NULL is returned before - used for polling)
 * @return The IP packet when SLIP_END is received
 */
static struct pbuf *
slipif_input(struct netif *netif, u8_t block)
{
	struct slipif_priv *priv;
	u8_t c;
	struct pbuf *t;

	LWIP_ASSERT("netif != NULL", (netif != NULL));
	LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));

	priv = netif->state;

	while (slip_sio_read(priv->sd, &c, 1, block) > 0) {
		switch (priv->state) {
		case SLIP_RECV_NORMAL:
			switch (c) {
			case SLIP_END:
				if (priv->recved > 0) {
					/* Received whole packet. */
					/* Trim the pbuf to the size of the received packet. */
					pbuf_realloc(priv->q, priv->recved);

					LINK_STATS_INC(link.recv);

					LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n"));
					t = priv->q;
					priv->p = priv->q = NULL;
					priv->i = priv->recved = 0;
					return t;
				}
				continue;
			case SLIP_ESC:
				priv->state = SLIP_RECV_ESCAPE;
				continue;
			}
			break;
		case SLIP_RECV_ESCAPE:
			switch (c) {
			case SLIP_ESC_END:
				c = SLIP_END;
				break;
			case SLIP_ESC_ESC:
				c = SLIP_ESC;
				break;
			}
			priv->state = SLIP_RECV_NORMAL;
			/* FALLTHROUGH */
		}

		/* byte received, packet not yet completely received */
		if (priv->p == NULL) {
			/* allocate a new pbuf */
			LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n"));
			priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL);

			if (priv->p == NULL) {
				LINK_STATS_INC(link.drop);
				LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n"));
				/* don't process any further since we got no pbuf to receive to */
				break;
			}

			if (priv->q != NULL) {
				/* 'chain' the pbuf to the existing chain */
				pbuf_cat(priv->q, priv->p);
			} else {
				/* p is the first pbuf in the chain */
				priv->q = priv->p;
			}
		}

		/* this automatically drops bytes if > SLIP_MAX_SIZE */
		if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) {
			((u8_t *)priv->p->payload)[priv->i] = c;
			priv->recved++;
			priv->i++;
			if (priv->i >= priv->p->len) {
				/* on to the next pbuf */
				priv->i = 0;
				if (priv->p->next != NULL && priv->p->next->len > 0) {
					/* p is a chain, on to the next in the chain */
					priv->p = priv->p->next;
				} else {
					/* p is a single pbuf, set it to NULL so next time a new
					 * pbuf is allocated */
					priv->p = NULL;
				}
			}
		}
	}

	return NULL;
}
Exemplo n.º 25
0
static err_t prvLowLevelOutput( struct netif *pxNetIf, struct pbuf *p )
{

	/* This is taken from lwIP example code and therefore does not conform
	to the FreeRTOS coding standard. */

struct pbuf *q;
static unsigned char ucBuffer[ 1520 ];
unsigned char *pucBuffer = ucBuffer;
unsigned char *pucChar;
struct eth_hdr *pxHeader;
u16_t usTotalLength = p->tot_len - ETH_PAD_SIZE;
err_t xReturn = ERR_OK;

	( void ) pxNetIf;

	#if defined(LWIP_DEBUG) && LWIP_NETIF_TX_SINGLE_PBUF
		LWIP_ASSERT("p->next == NULL && p->len == p->tot_len", p->next == NULL && p->len == p->tot_len);
	#endif

	/* Initiate transfer. */
	if( p->len == p->tot_len ) 
	{
		/* No pbuf chain, don't have to copy -> faster. */
		pucBuffer = &( ( unsigned char * ) p->payload )[ ETH_PAD_SIZE ];
	} 
	else 
	{
		/* pbuf chain, copy into contiguous ucBuffer. */
		if( p->tot_len >= sizeof( ucBuffer ) ) 
		{
			LINK_STATS_INC( link.lenerr );
			LINK_STATS_INC( link.drop );
			snmp_inc_ifoutdiscards( pxNetIf );
			xReturn = ERR_BUF;
		}
		else
		{
			pucChar = ucBuffer;

			for( q = p; q != NULL; q = q->next ) 
			{
				/* Send the data from the pbuf to the interface, one pbuf at a
				time. The size of the data in each pbuf is kept in the ->len
				variable. */
				/* send data from(q->payload, q->len); */
				LWIP_DEBUGF( NETIF_DEBUG, ("NETIF: send pucChar %p q->payload %p q->len %i q->next %p\n", pucChar, q->payload, ( int ) q->len, ( void* ) q->next ) );
				if( q == p ) 
				{
					memcpy( pucChar, &( ( char * ) q->payload )[ ETH_PAD_SIZE ], q->len - ETH_PAD_SIZE );
					pucChar += q->len - ETH_PAD_SIZE;
				} 
				else 
				{
					memcpy( pucChar, q->payload, q->len );
					pucChar += q->len;
				}
			}
		}
	}

	if( xReturn == ERR_OK )
	{
		/* signal that packet should be sent */
		if( pcap_sendpacket( pxOpenedInterfaceHandle, pucBuffer, usTotalLength ) < 0 ) 
		{
			LINK_STATS_INC( link.memerr );
			LINK_STATS_INC( link.drop );
			snmp_inc_ifoutdiscards( pxNetIf );
			xReturn = ERR_BUF;
		}
		else
		{
			LINK_STATS_INC( link.xmit );
			snmp_add_ifoutoctets( pxNetIf, usTotalLength );
			pxHeader = ( struct eth_hdr * )p->payload;

			if( ( pxHeader->dest.addr[ 0 ] & 1 ) != 0 ) 
			{
				/* broadcast or multicast packet*/
				snmp_inc_ifoutnucastpkts( pxNetIf );
			} 
			else 
			{
				/* unicast packet */
				snmp_inc_ifoutucastpkts( pxNetIf );
			}
		}
	}
	
	return xReturn;
}
Exemplo n.º 26
0
/**
 * The main lwIP thread. This thread has exclusive access to lwIP core functions
 * (unless access to them is not locked). Other threads communicate with this
 * thread using message boxes.
 *
 * It also starts all the timers to make sure they are running in the right
 * thread context.
 *
 * @param arg unused argument
 */
static void
tcpip_thread(void *arg)
{
  struct tcpip_msg *msg;
  LWIP_UNUSED_ARG(arg);

#if IP_REASSEMBLY
  sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
#endif /* IP_REASSEMBLY */
#if LWIP_ARP
  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
#endif /* LWIP_ARP */
#if LWIP_DHCP
  sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
  sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
  sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
#endif /* LWIP_AUTOIP */
#if LWIP_IGMP
  sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
#endif /* LWIP_IGMP */
#if LWIP_DNS
  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
#endif /* LWIP_DNS */

  if (tcpip_init_done != NULL) {
    tcpip_init_done(tcpip_init_done_arg);
  }

  LOCK_TCPIP_CORE();
  while (1) {                          /* MAIN Loop */
    sys_mbox_fetch(mbox, (void *)&msg);
    switch (msg->type) {
#if LWIP_NETCONN
    case TCPIP_MSG_API:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
      msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
      break;
#endif /* LWIP_NETCONN */

    case TCPIP_MSG_INPKT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
#if LWIP_ARP
      if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) {
        ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
      } else
#endif /* LWIP_ARP */
      { ip_input(msg->msg.inp.p, msg->msg.inp.netif);
      }
      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
      break;

#if LWIP_NETIF_API
    case TCPIP_MSG_NETIFAPI:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
      msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
      break;
#endif /* LWIP_NETIF_API */

    case TCPIP_MSG_CALLBACK:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
      msg->msg.cb.f(msg->msg.cb.ctx);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    case TCPIP_MSG_TIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
    case TCPIP_MSG_UNTIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    default:
      break;
    }
  }
}
Exemplo n.º 27
0
Arquivo: netif.c Projeto: harmv/lwip
/**
 * Remove a network interface from the list of lwIP netifs.
 *
 * @param netif the network interface to remove
 */
void
netif_remove(struct netif *netif)
{
  if (netif == NULL) {
    return;
  }

#if LWIP_IPV4
  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
#if LWIP_TCP
    tcp_netif_ipv4_addr_changed(netif_ip4_addr(netif), NULL);
#endif /* LWIP_TCP */
    /* cannot do this for UDP, as there is no 'err' callback in udp pcbs */
  }

#if LWIP_IGMP
  /* stop IGMP processing */
  if (netif->flags & NETIF_FLAG_IGMP) {
    igmp_stop(netif);
  }
#endif /* LWIP_IGMP */
#endif /* LWIP_IPV4*/

#if LWIP_IPV6 && LWIP_IPV6_MLD
  /* stop MLD processing */
  mld6_stop(netif);
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
  if (netif_is_up(netif)) {
    /* set netif down before removing (call callback function) */
    netif_set_down(netif);
  }

  mib2_remove_ip4(netif);

  /* this netif is default? */
  if (netif_default == netif) {
    /* reset default netif */
    netif_set_default(NULL);
  }
  /*  is it the first netif? */
  if (netif_list == netif) {
    netif_list = netif->next;
  } else {
    /*  look for netif further down the list */
    struct netif * tmp_netif;
    for (tmp_netif = netif_list; tmp_netif != NULL; tmp_netif = tmp_netif->next) {
      if (tmp_netif->next == netif) {
        tmp_netif->next = netif->next;
        break;
      }
    }
    if (tmp_netif == NULL) {
      return; /* netif is not on the list */
    }
  }
  mib2_netif_removed(netif);
#if LWIP_NETIF_REMOVE_CALLBACK
  if (netif->remove_callback) {
    netif->remove_callback(netif);
  }
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
  LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
}
Exemplo n.º 28
0
/**
 * Enqueue either data or TCP options (but not both) for tranmission
 * 
 * 
 * 
 * @arg pcb Protocol control block for the TCP connection to enqueue data for.
 * @arg arg Pointer to the data to be enqueued for sending.
 * @arg len Data length in bytes
 * @arg flags
 * @arg copy 1 if data must be copied, 0 if data is non-volatile and can be
 * referenced.
 * @arg optdata
 * @arg optlen
 */
err_t
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
  u8_t flags, u8_t copy,
  u8_t *optdata, u8_t optlen)
{
  struct pbuf *p;
  struct tcp_seg *seg, *useg, *queue;
  u32_t left, seqno;
  u16_t seglen;
  void *ptr;
  u8_t queuelen;

  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%u, flags=%x, copy=%u)\n",
    (void *)pcb, arg, len, (unsigned int)flags, (unsigned int)copy));
  LWIP_ASSERT("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)",
      len == 0 || optlen == 0);
  LWIP_ASSERT("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)",
      arg == NULL || optdata == NULL);
  /* fail on too much data */
  if (len > pcb->snd_buf) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%u > snd_buf=%u)\n", len, pcb->snd_buf));
    return ERR_MEM;
  }
  left = len;
  ptr = arg;

  /* seqno will be the sequence number of the first segment enqueued
   * by the call to this function. */
  seqno = pcb->snd_lbb;

  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %u\n", (unsigned int)pcb->snd_queuelen));

  /* If total number of pbufs on the unsent/unacked queues exceeds the
   * configured maximum, return an error */
  queuelen = pcb->snd_queuelen;
  if (queuelen >= TCP_SND_QUEUELEN) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %u (max %u)\n", queuelen, TCP_SND_QUEUELEN));
    TCP_STATS_INC(tcp.memerr);
    return ERR_MEM;
  }
  if (queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty",
      pcb->unacked != NULL || pcb->unsent != NULL);
  } else {
    LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty",
      pcb->unacked == NULL && pcb->unsent == NULL);
  }

  /* First, break up the data into segments and tuck them together in
   * the local "queue" variable. */
  useg = queue = seg = NULL;
  seglen = 0;
  while (queue == NULL || left > 0) {

    /* The segment length should be the MSS if the data to be enqueued
     * is larger than the MSS. */
    seglen = left > pcb->mss? pcb->mss: left;

    /* Allocate memory for tcp_seg, and fill in fields. */
    seg = memp_malloc(MEMP_TCP_SEG);
    if (seg == NULL) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n"));
      goto memerr;
    }
    seg->next = NULL;
    seg->p = NULL;

    /* first segment of to-be-queued data? */
    if (queue == NULL) {
      queue = seg;
    }
    /* subsequent segments of to-be-queued data */
    else {
      /* Attach the segment to the end of the queued segments */
      LWIP_ASSERT("useg != NULL", useg != NULL);
      useg->next = seg;
    }
    /* remember last segment of to-be-queued data for next iteration */
    useg = seg;

    /* If copy is set, memory should be allocated
     * and data copied into pbuf, otherwise data comes from
     * ROM or other static memory, and need not be copied. If
     * optdata is != NULL, we have options instead of data. */
     
    /* options? */
    if (optdata != NULL) {
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
        goto memerr;
      }
      ++queuelen;
      seg->dataptr = seg->p->payload;
    }
    /* copy from volatile memory? */
    else if (copy) {
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) {
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %u\n", seglen));
        goto memerr;
      }
      ++queuelen;
      if (arg != NULL) {
        memcpy(seg->p->payload, ptr, seglen);
      }
      seg->dataptr = seg->p->payload;
    }
    /* do not copy data */
    else {
      /* First, allocate a pbuf for holding the data.
       * since the referenced data is available at least until it is sent out on the
       * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM
       * instead of PBUF_REF here.
       */
      if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n"));
        goto memerr;
      }
      ++queuelen;
      /* reference the non-volatile payload data */
      p->payload = ptr;
      seg->dataptr = ptr;

      /* Second, allocate a pbuf for the headers. */
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) {
        /* If allocation fails, we have to deallocate the data pbuf as
         * well. */
        pbuf_free(p);
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n"));
        goto memerr;
      }
      ++queuelen;

      /* Concatenate the headers and data pbufs together. */
      pbuf_cat(seg->p/*header*/, p/*data*/);
      p = NULL;
    }

    /* Now that there are more segments queued, we check again if the
    length of the queue exceeds the configured maximum. */
    if (queuelen > TCP_SND_QUEUELEN) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %u (%u)\n", queuelen, TCP_SND_QUEUELEN));
      goto memerr;
    }

    seg->len = seglen;

    /* build TCP header */
    if (pbuf_header(seg->p, TCP_HLEN)) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n"));
      TCP_STATS_INC(tcp.err);
      goto memerr;
    }
    seg->tcphdr = seg->p->payload;
    seg->tcphdr->src = htons(pcb->local_port);
    seg->tcphdr->dest = htons(pcb->remote_port);
    seg->tcphdr->seqno = htonl(seqno);
    seg->tcphdr->urgp = 0;
    TCPH_FLAGS_SET(seg->tcphdr, flags);
    /* don't fill in tcphdr->ackno and tcphdr->wnd until later */

    /* Copy the options into the header, if they are present. */
    if (optdata == NULL) {
      TCPH_HDRLEN_SET(seg->tcphdr, 5);
    }
    else {
      TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
      /* Copy options into data portion of segment.
       Options can thus only be sent in non data carrying
       segments such as SYN|ACK. */
      memcpy(seg->dataptr, optdata, optlen);
    }
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE, ("tcp_enqueue: queueing %lu:%lu (0x%x)\n",
      ntohl(seg->tcphdr->seqno),
      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
      flags));

    left -= seglen;
    seqno += seglen;
    ptr = (void *)((char *)ptr + seglen);
  }

  /* Now that the data to be enqueued has been broken up into TCP
  segments in the queue variable, we add them to the end of the
  pcb->unsent queue. */
  if (pcb->unsent == NULL) {
    useg = NULL;
  }
  else {
    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
  }
  /* { useg is last segment on the unsent queue, NULL if list is empty } */

  /* If there is room in the last pbuf on the unsent queue,
  chain the first pbuf on the queue together with that. */
  if (useg != NULL &&
    TCP_TCPLEN(useg) != 0 &&
    !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
    !(flags & (TCP_SYN | TCP_FIN)) &&
    /* fit within max seg size */
    useg->len + queue->len <= pcb->mss) {
    /* Remove TCP header from first segment of our to-be-queued list */
    pbuf_header(queue->p, -TCP_HLEN);
    pbuf_cat(useg->p, queue->p);
    useg->len += queue->len;
    useg->next = queue->next;

    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining segments, new len %u\n", useg->len));
    if (seg == queue) {
      seg = NULL;
    }
    memp_free(MEMP_TCP_SEG, queue);
  }
  else {
    /* empty list */
    if (useg == NULL) {
      /* initialize list with this segment */
      pcb->unsent = queue;
    }
    /* enqueue segment */
    else {
      useg->next = queue;
    }
  }
  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
    ++len;
  }
  pcb->snd_lbb += len;

  /* FIX: Data split over odd boundaries */
  pcb->snd_buf -= ((len+1) & ~0x1); /* Even the send buffer */

  /* update number of segments on the queues */
  pcb->snd_queuelen = queuelen;
  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen));
  if (pcb->snd_queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: valid queue length",
      pcb->unacked != NULL || pcb->unsent != NULL);
  }

  /* Set the PSH flag in the last segment that we enqueued, but only
  if the segment has data (indicated by seglen > 0). */
  if (seg != NULL && seglen > 0 && seg->tcphdr != NULL) {
    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
  }

  return ERR_OK;
memerr:
  TCP_STATS_INC(tcp.memerr);

  if (queue != NULL) {
    tcp_segs_free(queue);
  }
  if (pcb->snd_queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
      pcb->unsent != NULL);
  }
  LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen));
  return ERR_MEM;
}
Exemplo n.º 29
0
/**
 * Adam's mem_malloc() plus solution for bug #17922
 * Allocate a block of memory with a minimum of 'size' bytes.
 *
 * @param size is the minimum size of the requested block in bytes.
 * @return pointer to allocated memory or NULL if no free memory was found.
 *
 * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
 */
void *
mem_malloc(mem_size_t size) {
	mem_size_t ptr, ptr2;
	struct mem *mem, *mem2;
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
	u8_t local_mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
	LWIP_MEM_ALLOC_DECL_PROTECT();

	if (size == 0) {
		return NULL;
	}

	/* Expand the size of the allocated memory region so that we can
	   adjust for alignment. */
	size = LWIP_MEM_ALIGN_SIZE(size);

	if (size < MIN_SIZE_ALIGNED) {
		/* every data block must be at least MIN_SIZE_ALIGNED long */
		size = MIN_SIZE_ALIGNED;
	}

	if (size > MEM_SIZE_ALIGNED) {
		return NULL;
	}

	/* protect the heap from concurrent access */
	sys_mutex_lock(&mem_mutex);
	LWIP_MEM_ALLOC_PROTECT();
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
	/* run as long as a mem_free disturbed mem_malloc */
	do {
		local_mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */

		/* Scan through the heap searching for a free block that is big enough,
		 * beginning with the lowest free block.
		 */
		for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
				ptr = ((struct mem *)(void *)&ram[ptr])->next) {
			mem = (struct mem *)(void *)&ram[ptr];
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
			mem_free_count = 0;
			LWIP_MEM_ALLOC_UNPROTECT();
			/* allow mem_free to run */
			LWIP_MEM_ALLOC_PROTECT();
			if (mem_free_count != 0) {
				local_mem_free_count = mem_free_count;
			}
			mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */

			if ((!mem->used) &&
					(mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
				/* mem is not used and at least perfect fit is possible:
				 * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */

				if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
					/* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
					 * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
					 * -> split large block, create empty remainder,
					 * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
					 * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
					 * struct mem would fit in but no data between mem2 and mem2->next
					 * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
					 *       region that couldn't hold data, but when mem->next gets freed,
					 *       the 2 regions would be combined, resulting in more free memory
					 */
					ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
					/* create mem2 struct */
					mem2 = (struct mem *)(void *)&ram[ptr2];
					mem2->used = 0;
					mem2->next = mem->next;
					mem2->prev = ptr;
					/* and insert it between mem and mem->next */
					mem->next = ptr2;
					mem->used = 1;

					if (mem2->next != MEM_SIZE_ALIGNED) {
						((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
					}
					MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
				} else {
					/* (a mem2 struct does no fit into the user data space of mem and mem->next will always
					 * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
					 * take care of this).
					 * -> near fit or excact fit: do not split, no mem2 creation
					 * also can't move mem->next directly behind mem, since mem->next
					 * will always be used at this point!
					 */
					mem->used = 1;
					MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
				}

				if (mem == lfree) {
					/* Find next free block after mem and update lowest free pointer */
					while (lfree->used && lfree != ram_end) {
						LWIP_MEM_ALLOC_UNPROTECT();
						/* prevent high interrupt latency... */
						LWIP_MEM_ALLOC_PROTECT();
						lfree = (struct mem *)(void *)&ram[lfree->next];
					}
					LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
				}
				LWIP_MEM_ALLOC_UNPROTECT();
				sys_mutex_unlock(&mem_mutex);
				LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
							(mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
				LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
							((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
				LWIP_ASSERT("mem_malloc: sanity check alignment",
							(((mem_ptr_t)mem) & (MEM_ALIGNMENT - 1)) == 0);

				return (u8_t *)mem + SIZEOF_STRUCT_MEM;
			}
		}
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
		/* if we got interrupted by a mem_free, try again */
	} while (local_mem_free_count != 0);
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
	LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
	MEM_STATS_INC(err);
	LWIP_MEM_ALLOC_UNPROTECT();
	sys_mutex_unlock(&mem_mutex);
	return NULL;
}
Exemplo n.º 30
0
/**
 * Send an ARP request for the given IP address and/or queue a packet.
 *
 * If the IP address was not yet in the cache, a pending ARP cache entry
 * is added and an ARP request is sent for the given address. The packet
 * is queued on this entry.
 *
 * If the IP address was already pending in the cache, a new ARP request
 * is sent for the given address. The packet is queued on this entry.
 *
 * If the IP address was already stable in the cache, and a packet is
 * given, it is directly sent and no ARP request is sent out. 
 * 
 * If the IP address was already stable in the cache, and no packet is
 * given, an ARP request is sent out.
 * 
 * @param netif The lwIP network interface on which ipaddr
 * must be queried for.
 * @param ipaddr The IP address to be resolved.
 * @param q If non-NULL, a pbuf that must be delivered to the IP address.
 * q is not freed by this function.
 *
 * @return
 * - ERR_BUF Could not make room for Ethernet header.
 * - ERR_MEM Hardware address unknown, and no more ARP entries available
 *   to query for address or queue the packet.
 * - ERR_MEM Could not queue packet due to memory shortage.
 * - ERR_RTE No route to destination (no gateway to external networks).
 * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
 *
 */
err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
{
  struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
  err_t result = ERR_MEM;
  s8_t i; /* ARP entry index */
  u8_t k; /* Ethernet address octet index */

  /* non-unicast address? */
  if (ip_addr_isbroadcast(ipaddr, netif) ||
      ip_addr_ismulticast(ipaddr) ||
      ip_addr_isany(ipaddr)) {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
    return ERR_ARG;
  }

  /* find entry in ARP cache, ask to create entry if queueing packet */
  i = find_entry(ipaddr, ETHARP_TRY_HARD);

  /* could not find or create entry? */
  if (i < 0)
  {
    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
    if (q) LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: packet dropped\n"));
    return (err_t)i;
  }

  /* mark a fresh entry as pending (we just sent a request) */
  if (arp_table[i].state == ETHARP_STATE_EMPTY) {
    arp_table[i].state = ETHARP_STATE_PENDING;
  }

  /* { i is either a STABLE or (new or existing) PENDING entry } */
  LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
  ((arp_table[i].state == ETHARP_STATE_PENDING) ||
   (arp_table[i].state == ETHARP_STATE_STABLE)));

  /* do we have a pending entry? or an implicit query request? */
  if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
    /* try to resolve it; send out ARP request */
    result = etharp_request(netif, ipaddr);
  }
  
  /* packet given? */
  if (q != NULL) {
    /* stable entry? */
    if (arp_table[i].state == ETHARP_STATE_STABLE) {
      /* we have a valid IP->Ethernet address mapping,
       * fill in the Ethernet header for the outgoing packet */
      struct eth_hdr *ethhdr = q->payload;
      k = netif->hwaddr_len;
      while(k > 0) {
        k--;
        ethhdr->dest.addr[k] = arp_table[i].ethaddr.addr[k];
        ethhdr->src.addr[k]  = srcaddr->addr[k];
      }
      ethhdr->type = htons(ETHTYPE_IP);
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending packet %p\n", (void *)q));
      /* send the packet */
      result = netif->linkoutput(netif, q);
    /* pending entry? (either just created or already pending */
    } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
#if ARP_QUEUEING /* queue the given q packet */
      struct pbuf *p;
      /* copy any PBUF_REF referenced payloads into PBUF_RAM */
      /* (the caller of lwIP assumes the referenced payload can be
       * freed after it returns from the lwIP call that brought us here) */
      p = pbuf_take(q);
      /* packet could be taken over? */
      if (p != NULL) {
        /* queue packet ... */
        if (arp_table[i].p == NULL) {
          /* ... in the empty queue */
          pbuf_ref(p);
          arp_table[i].p = p;
#if 0 /* multi-packet-queueing disabled, see bug #11400 */
        } else {
          /* ... at tail of non-empty queue */
          pbuf_queue(arp_table[i].p, p);
#endif
        }
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
        result = ERR_OK;
      } else {
        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
        /* { result == ERR_MEM } through initialization */
      }
#else /* ARP_QUEUEING == 0 */
      /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */
      /* { result == ERR_MEM } through initialization */
      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q));
#endif
    }
  }
  return result;
}