err_t netconn_write(struct netconn *conn, void *dataptr, u16_t size, u8_t copy) { struct api_msg *msg; u16_t len; if (conn == NULL) { return ERR_VAL; } if (conn->err != ERR_OK) { return conn->err; } if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { return (conn->err = ERR_MEM); } msg->type = API_MSG_WRITE; msg->msg.conn = conn; conn->state = NETCONN_WRITE; while (conn->err == ERR_OK && size > 0) { msg->msg.msg.w.dataptr = dataptr; msg->msg.msg.w.copy = copy; if (conn->type == NETCONN_TCP) { if (tcp_sndbuf(conn->pcb.tcp) == 0) { sys_sem_wait(conn->sem); if (conn->err != ERR_OK) { goto ret; } } if (size > tcp_sndbuf(conn->pcb.tcp)) { /* We cannot send more than one send buffer's worth of data at a time. */ len = tcp_sndbuf(conn->pcb.tcp); } else { len = size; } } else { len = size; } LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy)); msg->msg.msg.w.len = len; api_msg_post(msg); sys_mbox_fetch(conn->mbox, NULL); if (conn->err == ERR_OK) { dataptr = (void *)((u8_t *)dataptr + len); size -= len; } else if (conn->err == ERR_MEM) { conn->err = ERR_OK; sys_sem_wait(conn->sem); } else { goto ret; } } ret: memp_free(MEMP_API_MSG, msg); conn->state = NETCONN_NONE; return conn->err; }
/** * Reassembles incoming IPv6 fragments into an IPv6 datagram. * * @param p points to the IPv6 Fragment Header * @param len the length of the payload (after Fragment Header) * @return NULL if reassembly is incomplete, pbuf pointing to * IPv6 Header if reassembly is complete */ struct pbuf * ip6_reass(struct pbuf *p) { struct ip6_reassdata *ipr, *ipr_prev; struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; struct ip6_frag_hdr * frag_hdr; u16_t offset, len; u8_t clen, valid = 1; struct pbuf *q; IP6_FRAG_STATS_INC(ip6_frag.recv); frag_hdr = (struct ip6_frag_hdr *) p->payload; clen = pbuf_clen(p); offset = ntohs(frag_hdr->_fragment_offset); /* Calculate fragment length from IPv6 payload length. * Adjust for headers before Fragment Header. * And finally adjust by Fragment Header length. */ len = ntohs(ip6_current_header()->_plen); len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; len -= IP6_FRAG_HLEN; /* 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_prev = NULL; 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 ((frag_hdr->_identification == ipr->identification) && ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { IP6_FRAG_STATS_INC(ip6_frag.cachehit); break; } ipr_prev = ipr; } if (ipr == NULL) { /* Enqueue a new datagram into the datagram queue */ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr == NULL) { #if IP_REASS_FREE_OLDEST /* Make room and try again. */ ip6_reass_remove_oldest_datagram(ipr, clen); ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr != NULL) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { IP6_FRAG_STATS_INC(ip6_frag.memerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } memset(ipr, 0, sizeof(struct ip6_reassdata)); ipr->timer = IP_REASS_MAXAGE; /* enqueue the new structure to the front of the list */ ipr->next = reassdatagrams; reassdatagrams = ipr; /* Use the current IPv6 header for src/dest address reference. * Eventually, we will replace it when we get the first fragment * (it might be this one, in any case, it is done later). */ ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); /* copy the fragmented packet id. */ ipr->identification = frag_hdr->_identification; /* copy the nexth field */ ipr->nexth = frag_hdr->_nexth; } /* Check if we are allowed to enqueue more datagrams. */ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST ip6_reass_remove_oldest_datagram(ipr, clen); if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { /* @todo: send ICMPv6 time exceeded here? */ /* drop this pbuf */ IP6_FRAG_STATS_INC(ip6_frag.memerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } /* Overwrite Fragment Header with our own helper struct. */ iprh = (struct ip6_reass_helper *)p->payload; LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN", sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); iprh->next_pbuf = NULL; iprh->start = (offset & IP6_FRAG_OFFSET_MASK); iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; /* find the right place to insert this pbuf */ /* Iterate through until we either get to the end of the list (append), * or we find on with a larger offset (insert). */ for (q = ipr->p; q != NULL;) { iprh_tmp = (struct ip6_reass_helper*)q->payload; if (iprh->start < iprh_tmp->start) { #if IP_REASS_CHECK_OVERLAP if (iprh->end > iprh_tmp->start) { /* fragment overlaps with following, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } if (iprh_prev != NULL) { if (iprh->start < iprh_prev->end) { /* fragment overlaps with previous, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } #endif /* IP_REASS_CHECK_OVERLAP */ /* the new pbuf should be inserted before this */ iprh->next_pbuf = q; if (iprh_prev != NULL) { /* not the fragment with the lowest offset */ iprh_prev->next_pbuf = p; } else { /* fragment with the lowest offset */ ipr->p = p; } break; } else if(iprh->start == iprh_tmp->start) { /* received the same datagram twice: no need to keep the datagram */ IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; #if IP_REASS_CHECK_OVERLAP } else if(iprh->start < iprh_tmp->end) { /* overlap: no need to keep the new datagram */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; #endif /* IP_REASS_CHECK_OVERLAP */ } else { /* Check if the fragments received so far have no gaps. */ if (iprh_prev != NULL) { if (iprh_prev->end != iprh_tmp->start) { /* There is a fragment missing between the current * and the previous fragment */ valid = 0; } } } q = iprh_tmp->next_pbuf; iprh_prev = iprh_tmp; } /* If q is NULL, then we made it to the end of the list. Determine what to do now */ if (q == NULL) { if (iprh_prev != NULL) { /* this is (for now), the fragment with the highest offset: * chain it to the last fragment */ #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); #endif /* IP_REASS_CHECK_OVERLAP */ iprh_prev->next_pbuf = p; if (iprh_prev->end != iprh->start) { valid = 0; } } else { #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("no previous fragment, this must be the first fragment!", ipr->p == NULL); #endif /* IP_REASS_CHECK_OVERLAP */ /* this is the first fragment we ever received for this ip datagram */ ipr->p = p; } } /* 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 */ ip6_reass_pbufcount += clen; /* Remember IPv6 header if this is the first fragment. */ if (iprh->start == 0) { ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); } /* If this is the last fragment, calculate total packet length. */ if ((offset & IP6_FRAG_MORE_FLAG) == 0) { ipr->datagram_len = iprh->end; } /* Additional validity tests: we have received first and last fragment. */ iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; if (iprh_tmp->start != 0) { valid = 0; } if (ipr->datagram_len == 0) { valid = 0; } /* Final validity test: no gaps between current and last fragment. */ iprh_prev = iprh; q = iprh->next_pbuf; while ((q != NULL) && valid) { iprh = (struct ip6_reass_helper*)q->payload; if (iprh_prev->end != iprh->start) { valid = 0; break; } iprh_prev = iprh; q = iprh->next_pbuf; } if (valid) { /* All fragments have been received */ u8_t* iphdr_ptr; /* chain together the pbufs contained within the ip6_reassdata list. */ iprh = (struct ip6_reass_helper*) ipr->p->payload; while(iprh != NULL) { if (iprh->next_pbuf != NULL) { /* Save next helper struct (will be hidden in next step). */ iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; /* hide the fragment header for every succeding fragment */ pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); pbuf_cat(ipr->p, iprh->next_pbuf); } else { iprh_tmp = NULL; } iprh = iprh_tmp; } /* Adjust datagram length by adding header lengths. */ ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) + IP6_FRAG_HLEN - IP6_HLEN ; /* Set payload length in ip header. */ ipr->iphdr->_plen = htons(ipr->datagram_len); /* Get the furst pbuf. */ p = ipr->p; /* Restore Fragment Header in first pbuf. Mark as "single fragment" * packet. Restore nexth. */ frag_hdr = (struct ip6_frag_hdr *) p->payload; frag_hdr->_nexth = ipr->nexth; frag_hdr->reserved = 0; frag_hdr->_fragment_offset = 0; frag_hdr->_identification = 0; /* release the sources allocate for the fragment queue entry */ if (reassdatagrams == ipr) { /* it was the first in the list */ reassdatagrams = ipr->next; } else { /* it wasn't the first, so it must have a valid 'prev' */ LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); ipr_prev->next = ipr->next; } iphdr_ptr = (u8_t*)ipr->iphdr; memp_free(MEMP_IP6_REASSDATA, ipr); /* adjust the number of pbufs currently queued for reassembly. */ ip6_reass_pbufcount -= pbuf_clen(p); /* Move pbuf back to IPv6 header. */ if (pbuf_header(p, (u8_t*)p->payload - iphdr_ptr)) { LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); pbuf_free(p); return NULL; } /* Return the pbuf chain */ return p; } /* the datagram is not (yet?) reassembled completely */ return NULL; nullreturn: pbuf_free(p); return NULL; }
/** * Update (or insert) a IP/MAC address pair in the ARP cache. * * If a pending entry is resolved, any queued packets will be sent * at this point. * * @param netif netif related to this entry (used for NETIF_ADDRHINT) * @param ipaddr IP address of the inserted ARP entry. * @param ethaddr Ethernet address of the inserted ARP entry. * @param flags @see definition of ETHARP_FLAG_* * * @return * - ERR_OK Succesfully updated ARP cache. * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. * * @see pbuf_free() */ static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); /* non-unicast address? */ if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find or create ARP entry */ i = find_entry(ipaddr, flags); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; } #if ETHARP_SUPPORT_STATIC_ENTRIES if (flags & ETHARP_FLAG_STATIC_ENTRY) { /* record static type */ arp_table[i].static_entry = 1; } #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ /* mark it stable */ arp_table[i].state = ETHARP_STATE_STABLE; #if LWIP_SNMP /* record network interface */ arp_table[i].netif = netif; #endif /* LWIP_SNMP */ /* insert in SNMP ARP index tree */ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); /* update address */ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); /* reset time stamp */ arp_table[i].ctime = 0; /* this is where we will send out queued packets! */ #if ARP_QUEUEING while (arp_table[i].q != NULL) { struct pbuf *p; /* remember remainder of queue */ struct etharp_q_entry *q = arp_table[i].q; /* pop first item off the queue */ arp_table[i].q = q->next; /* get the packet pointer */ p = q->p; /* now queue entry can be freed */ memp_free(MEMP_ARP_QUEUE, q); #else /* ARP_QUEUEING */ if (arp_table[i].q != NULL) { struct pbuf *p = arp_table[i].q; arp_table[i].q = NULL; #endif /* ARP_QUEUEING */ /* send the queued IP packet */ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); /* free the queued IP packet */ pbuf_free(p); } return ERR_OK; } #if ETHARP_SUPPORT_STATIC_ENTRIES /** Add a new static entry to the ARP table. If an entry exists for the * specified IP address, this entry is overwritten. * If packets are queued for the specified IP address, they are sent out. * * @param ipaddr IP address for the new static entry * @param ethaddr ethernet address for the new static entry * @return @see return values of etharp_add_static_entry */ err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) { struct netif *netif; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); netif = ip_route(ipaddr); if (netif == NULL) { return ERR_RTE; } return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); } /** Remove a static entry from the ARP table previously added with a call to * etharp_add_static_entry. * * @param ipaddr IP address of the static entry to remove * @return ERR_OK: entry removed * ERR_MEM: entry wasn't found * ERR_ARG: entry wasn't a static entry but a dynamic one */ err_t etharp_remove_static_entry(ip_addr_t *ipaddr) { s8_t i; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); /* find or create ARP entry */ i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; } if ((arp_table[i].state != ETHARP_STATE_STABLE) || (arp_table[i].static_entry == 0)) { /* entry wasn't a static entry, cannot remove it */ return ERR_ARG; } /* entry found, free it */ free_entry(i); return ERR_OK; }
/** * Create a new netconn (of a specific type) that has a callback function. * The corresponding pcb is NOT created! * * @param t the type of 'connection' to create (@see enum netconn_type) * @param proto the IP protocol for RAW IP pcbs * @param callback a function to call on status changes (RX available, TX'ed) * @return a newly allocated struct netconn or * NULL on memory error */ struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback) { struct netconn *conn; int size; conn = memp_malloc(MEMP_NETCONN); if (conn == NULL) { return NULL; } conn->err = ERR_OK; conn->type = t; conn->pcb.tcp = NULL; #if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) size = DEFAULT_RAW_RECVMBOX_SIZE; #else switch(NETCONNTYPE_GROUP(t)) { #if LWIP_RAW case NETCONN_RAW: size = DEFAULT_RAW_RECVMBOX_SIZE; break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: size = DEFAULT_UDP_RECVMBOX_SIZE; break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: size = DEFAULT_TCP_RECVMBOX_SIZE; break; #endif /* LWIP_TCP */ default: LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); break; } #endif if ((conn->op_completed = sys_sem_new(0)) == SYS_SEM_NULL) { memp_free(MEMP_NETCONN, conn); return NULL; } if ((conn->recvmbox = sys_mbox_new(size)) == SYS_MBOX_NULL) { sys_sem_free(conn->op_completed); memp_free(MEMP_NETCONN, conn); return NULL; } conn->acceptmbox = SYS_MBOX_NULL; conn->state = NETCONN_NONE; /* initialize socket to -1 since 0 is a valid socket */ conn->socket = -1; conn->callback = callback; conn->recv_avail = 0; #if LWIP_TCP conn->write_msg = NULL; conn->write_offset = 0; #if LWIP_TCPIP_CORE_LOCKING conn->write_delayed = 0; #endif /* LWIP_TCPIP_CORE_LOCKING */ #endif /* LWIP_TCP */ #if LWIP_SO_RCVTIMEO conn->recv_timeout = 0; #endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVBUF conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; #endif /* LWIP_SO_RCVBUF */ return conn; }
/** * 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 (tcpip_init_done != NULL) { tcpip_init_done(tcpip_init_done_arg); } LOCK_TCPIP_CORE(); while (1) { /* MAIN Loop */ UNLOCK_TCPIP_CORE(); LWIP_TCPIP_THREAD_ALIVE(); /* wait for a message, timeouts are processed while waiting */ sys_timeouts_mbox_fetch(&mbox, (void **)&msg); LOCK_TCPIP_CORE(); 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 */ #if !LWIP_TCPIP_CORE_LOCKING_INPUT case TCPIP_MSG_INPKT: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); #if LWIP_ETHERNET if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); } else #endif /* LWIP_ETHERNET */ { ip_input(msg->msg.inp.p, msg->msg.inp.netif); } memp_free(MEMP_TCPIP_MSG_INPKT, msg); break; #endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ #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 */ #if LWIP_TCPIP_TIMEOUT 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; #endif /* LWIP_TCPIP_TIMEOUT */ case TCPIP_MSG_CALLBACK: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); msg->msg.cb.function(msg->msg.cb.ctx); memp_free(MEMP_TCPIP_MSG_API, msg); break; case TCPIP_MSG_CALLBACK_STATIC: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); msg->msg.cb.function(msg->msg.cb.ctx); break; default: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); LWIP_ASSERT("tcpip_thread: invalid message", 0); break; } } }
/** * The initial input processing of TCP. It verifies the TCP header, demultiplexes * the segment between the PCBs and passes it on to tcp_process(), which implements * the TCP finite state machine. This function is called by the IP layer (in * ip_input()). * * @param p received TCP segment to process (p->payload pointing to the IP header) * @param inp network interface on which this segment was received */ void tcp_input(struct pbuf *p, struct netif *inp) { struct tcp_pcb *pcb, *prev; struct tcp_pcb_listen *lpcb; u8_t hdrlen; err_t err; PERF_START; TCP_STATS_INC(tcp.recv); snmp_inc_tcpinsegs(); iphdr = p->payload; tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); #if TCP_INPUT_DEBUG tcp_debug_print(tcphdr); #endif /* remove header from payload */ if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } /* Don't even process incoming broadcasts/multicasts. */ if (ip_addr_isbroadcast(&(iphdr->dest), inp) || ip_addr_ismulticast(&(iphdr->dest))) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } #if CHECKSUM_CHECK_TCP /* Verify TCP checksum. */ if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len) != 0) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len))); #if TCP_DEBUG tcp_debug_print(tcphdr); #endif /* TCP_DEBUG */ TCP_STATS_INC(tcp.chkerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } #endif /* Move the payload pointer in the pbuf so that it points to the TCP data instead of the TCP header. */ hdrlen = TCPH_HDRLEN(tcphdr); if(pbuf_header(p, -(hdrlen * 4))){ /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } /* Convert fields in TCP header to host byte order. */ tcphdr->src = ntohs(tcphdr->src); tcphdr->dest = ntohs(tcphdr->dest); seqno = tcphdr->seqno = ntohl(tcphdr->seqno); ackno = tcphdr->ackno = ntohl(tcphdr->ackno); tcphdr->wnd = ntohs(tcphdr->wnd); flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS; tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0); /* Demultiplex an incoming segment. First, we check if it is destined for an active connection. */ prev = NULL; for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); if (prev != NULL) { prev->next = pcb->next; pcb->next = tcp_active_pcbs; tcp_active_pcbs = pcb; } LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); break; } prev = pcb; } if (pcb == NULL) { /* If it did not go to an active connection, we check the connections in the TIME-WAIT state. */ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { /* We don't really care enough to move this PCB to the front of the list since we are not very likely to receive that many segments for connections in TIME-WAIT. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); tcp_timewait_input(pcb); pbuf_free(p); return; } } /* Finally, if we still did not get a match, we check all PCBs that are LISTENing for incoming connections. */ prev = NULL; for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if ((ip_addr_isany(&(lpcb->local_ip)) || ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && lpcb->local_port == tcphdr->dest) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ if (prev != NULL) { ((struct tcp_pcb_listen *)prev)->next = lpcb->next; /* our successor is the remainder of the listening list */ lpcb->next = tcp_listen_pcbs.listen_pcbs; /* put this listening pcb at the head of the listening list */ tcp_listen_pcbs.listen_pcbs = lpcb; } LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); tcp_listen_input(lpcb); pbuf_free(p); return; } prev = (struct tcp_pcb *)lpcb; } } #if TCP_INPUT_DEBUG LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); #endif /* TCP_INPUT_DEBUG */ if (pcb != NULL) { /* The incoming segment belongs to a connection. */ #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ /* Set up a tcp_seg structure. */ inseg.next = NULL; inseg.len = p->tot_len; inseg.dataptr = p->payload; inseg.p = p; inseg.tcphdr = tcphdr; recv_data = NULL; recv_flags = 0; /* If there is data which was previously "refused" by upper layer */ if (pcb->refused_data != NULL) { /* Notify again application with data previously received. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); if (err == ERR_OK) { pcb->refused_data = NULL; } else { /* drop incoming packets, because pcb is "full" */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } } tcp_input_pcb = pcb; err = tcp_process(pcb); tcp_input_pcb = NULL; /* A return value of ERR_ABRT means that tcp_abort() was called and that the pcb has been freed. If so, we don't do anything. */ if (err != ERR_ABRT) { if (recv_flags & TF_RESET) { /* TF_RESET means that the connection was reset by the other end. We then call the error callback to inform the application that the connection is dead before we deallocate the PCB. */ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else if (recv_flags & TF_CLOSED) { /* The connection has been closed and we will deallocate the PCB. */ tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { err = ERR_OK; /* If the application has registered a "sent" function to be called when new send buffer space is available, we call it now. */ if (pcb->acked > 0) { TCP_EVENT_SENT(pcb, pcb->acked, err); } if (recv_data != NULL) { if(flags & TCP_PSH) { recv_data->flags |= PBUF_FLAG_PUSH; } /* Notify application that data has been received. */ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); /* If the upper layer can't receive this data, store it */ if (err != ERR_OK) { pcb->refused_data = recv_data; LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); } } /* If a FIN segment was received, we call the callback function with a NULL buffer to indicate EOF. */ if (recv_flags & TF_GOT_FIN) { TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); } /* If there were no errors, we try to send something out. */ if (err == ERR_OK) { tcp_output(pcb); } } } /* give up our reference to inseg.p */ if (inseg.p != NULL) { pbuf_free(inseg.p); inseg.p = NULL; } #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ } else { /* If no matching PCB was found, send a TCP RST (reset) to the sender. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } pbuf_free(p); } LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); PERF_STOP("tcp_input"); }
/****************************************************************************** * FunctionName : espconn_tcp_client * Description : Initialize the client: set up a connect PCB and bind it to * the defined port * Parameters : espconn -- the espconn used to build client * Returns : none *******************************************************************************/ sint8 ICACHE_FLASH_ATTR espconn_tcp_client(struct espconn *espconn) { struct tcp_pcb *pcb = NULL; struct ip_addr ipaddr; espconn_msg *pclient = NULL; /*Creates a new client control message*/ pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); if (pclient == NULL){ return ESPCONN_MEM; } /*Set an IP address given for Little-endian.*/ IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], espconn->proto.tcp->remote_ip[1], espconn->proto.tcp->remote_ip[2], espconn->proto.tcp->remote_ip[3]); /*Creates a new TCP protocol control block*/ pcb = tcp_new(); if (pcb == NULL) { /*to prevent memory leaks, ensure that each allocated is deleted*/ os_free(pclient); pclient = NULL; return ESPCONN_MEM; } else { /*insert the node to the active connection list*/ espconn_list_creat(&plink_active, pclient); tcp_arg(pcb, (void *)pclient); tcp_err(pcb, espconn_client_err); pclient->preverse = NULL; pclient->pespconn = espconn; pclient->pespconn->state = ESPCONN_WAIT; pclient->pcommon.pcb = pcb; tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); #if 0 pclient->pcommon.err = tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); if (pclient->pcommon.err != ERR_OK){ /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, pclient); memp_free(MEMP_TCP_PCB, pcb); os_free(pclient); pclient = NULL; return ERR_USE; } #endif /*Establish the connection*/ pclient->pcommon.err = tcp_connect(pcb, &ipaddr, pclient->pespconn->proto.tcp->remote_port, espconn_client_connect); if (pclient->pcommon.err == ERR_RTE){ /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, pclient); espconn_kill_pcb(pcb->local_port); os_free(pclient); pclient = NULL; return ESPCONN_RTE; } return pclient->pcommon.err; } }
/** * Reassembles incoming IPv6 fragments into an IPv6 datagram. * * @param p points to the IPv6 Fragment Header * @return NULL if reassembly is incomplete, pbuf pointing to * IPv6 Header if reassembly is complete */ struct pbuf * ip6_reass(struct pbuf *p) { struct ip6_reassdata *ipr, *ipr_prev; struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; struct ip6_frag_hdr *frag_hdr; u16_t offset, len, start, end; ptrdiff_t hdrdiff; u16_t clen; u8_t valid = 1; struct pbuf *q, *next_pbuf; IP6_FRAG_STATS_INC(ip6_frag.recv); /* ip6_frag_hdr must be in the first pbuf, not chained. Checked by caller. */ LWIP_ASSERT("IPv6 fragment header does not fit in first pbuf", p->len >= sizeof(struct ip6_frag_hdr)); frag_hdr = (struct ip6_frag_hdr *) p->payload; clen = pbuf_clen(p); offset = lwip_ntohs(frag_hdr->_fragment_offset); /* Calculate fragment length from IPv6 payload length. * Adjust for headers before Fragment Header. * And finally adjust by Fragment Header length. */ len = lwip_ntohs(ip6_current_header()->_plen); hdrdiff = (u8_t*)p->payload - (const u8_t*)ip6_current_header(); LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff <= 0xFFFF); LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff >= IP6_HLEN); hdrdiff -= IP6_HLEN; hdrdiff += IP6_FRAG_HLEN; if (hdrdiff > len) { IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } len = (u16_t)(len - hdrdiff); start = (offset & IP6_FRAG_OFFSET_MASK); if (start > (0xFFFF - len)) { /* u16_t overflow, cannot handle this */ IP6_FRAG_STATS_INC(ip6_frag.proterr); 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_prev = NULL; 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 ((frag_hdr->_identification == ipr->identification) && ip6_addr_cmp_packed(ip6_current_src_addr(), &(IPV6_FRAG_SRC(ipr)), ipr->src_zone) && ip6_addr_cmp_packed(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)), ipr->dest_zone)) { IP6_FRAG_STATS_INC(ip6_frag.cachehit); break; } ipr_prev = ipr; } if (ipr == NULL) { /* Enqueue a new datagram into the datagram queue */ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr == NULL) { #if IP_REASS_FREE_OLDEST /* Make room and try again. */ ip6_reass_remove_oldest_datagram(ipr, clen); ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr != NULL) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { IP6_FRAG_STATS_INC(ip6_frag.memerr); goto nullreturn; } } memset(ipr, 0, sizeof(struct ip6_reassdata)); ipr->timer = IPV6_REASS_MAXAGE; /* enqueue the new structure to the front of the list */ ipr->next = reassdatagrams; reassdatagrams = ipr; /* Use the current IPv6 header for src/dest address reference. * Eventually, we will replace it when we get the first fragment * (it might be this one, in any case, it is done later). */ /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; #if IPV6_FRAG_COPYHEADER MEMCPY(&ipr->src, &ip6_current_header()->src, sizeof(ipr->src)); MEMCPY(&ipr->dest, &ip6_current_header()->dest, sizeof(ipr->dest)); #endif /* IPV6_FRAG_COPYHEADER */ #if LWIP_IPV6_SCOPES /* Also store the address zone information. * @todo It is possible that due to netif destruction and recreation, the * stored zones end up resolving to a different interface. In that case, we * risk sending a "time exceeded" ICMP response over the wrong link. * Ideally, netif destruction would clean up matching pending reassembly * structures, but custom zone mappings would make that non-trivial. */ ipr->src_zone = ip6_addr_zone(ip6_current_src_addr()); ipr->dest_zone = ip6_addr_zone(ip6_current_dest_addr()); #endif /* LWIP_IPV6_SCOPES */ /* copy the fragmented packet id. */ ipr->identification = frag_hdr->_identification; /* copy the nexth field */ ipr->nexth = frag_hdr->_nexth; } /* Check if we are allowed to enqueue more datagrams. */ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST ip6_reass_remove_oldest_datagram(ipr, clen); if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { /* @todo: send ICMPv6 time exceeded here? */ /* drop this pbuf */ IP6_FRAG_STATS_INC(ip6_frag.memerr); goto nullreturn; } } /* Overwrite Fragment Header with our own helper struct. */ #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4). This cannot fail since we already checked when receiving this fragment. */ u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #else /* IPV6_FRAG_COPYHEADER */ LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1", sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); #endif /* IPV6_FRAG_COPYHEADER */ /* Prepare the pointer to the helper structure, and its initial values. * Do not yet write to the structure itself, as we still have to make a * backup of the original data, and we should not do that until we know for * sure that we are going to add this packet to the list. */ iprh = (struct ip6_reass_helper *)p->payload; next_pbuf = NULL; end = (u16_t)(start + len); /* find the right place to insert this pbuf */ /* Iterate through until we either get to the end of the list (append), * or we find on with a larger offset (insert). */ for (q = ipr->p; q != NULL;) { iprh_tmp = (struct ip6_reass_helper*)q->payload; if (start < iprh_tmp->start) { #if IP_REASS_CHECK_OVERLAP if (end > iprh_tmp->start) { /* fragment overlaps with following, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } if (iprh_prev != NULL) { if (start < iprh_prev->end) { /* fragment overlaps with previous, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } } #endif /* IP_REASS_CHECK_OVERLAP */ /* the new pbuf should be inserted before this */ next_pbuf = q; if (iprh_prev != NULL) { /* not the fragment with the lowest offset */ iprh_prev->next_pbuf = p; } else { /* fragment with the lowest offset */ ipr->p = p; } break; } else if (start == iprh_tmp->start) { /* received the same datagram twice: no need to keep the datagram */ goto nullreturn; #if IP_REASS_CHECK_OVERLAP } else if (start < iprh_tmp->end) { /* overlap: no need to keep the new datagram */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; #endif /* IP_REASS_CHECK_OVERLAP */ } else { /* Check if the fragments received so far have no gaps. */ if (iprh_prev != NULL) { if (iprh_prev->end != iprh_tmp->start) { /* There is a fragment missing between the current * and the previous fragment */ valid = 0; } } } q = iprh_tmp->next_pbuf; iprh_prev = iprh_tmp; } /* If q is NULL, then we made it to the end of the list. Determine what to do now */ if (q == NULL) { if (iprh_prev != NULL) { /* this is (for now), the fragment with the highest offset: * chain it to the last fragment */ #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= start); #endif /* IP_REASS_CHECK_OVERLAP */ iprh_prev->next_pbuf = p; if (iprh_prev->end != start) { valid = 0; } } else { #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("no previous fragment, this must be the first fragment!", ipr->p == NULL); #endif /* IP_REASS_CHECK_OVERLAP */ /* this is the first fragment we ever received for this ip datagram */ ipr->p = p; } } /* 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 */ ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount + clen); /* Remember IPv6 header if this is the first fragment. */ if (start == 0) { /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; /* Make a backup of the part of the packet data that we are about to * overwrite, so that we can restore the original later. */ MEMCPY(ipr->orig_hdr, p->payload, sizeof(*iprh)); /* For IPV6_FRAG_COPYHEADER there is no need to copy src/dst again, as they * will be the same as they were. With LWIP_IPV6_SCOPES, the same applies * to the source/destination zones. */ } /* Only after the backup do we get to fill in the actual helper structure. */ iprh->next_pbuf = next_pbuf; iprh->start = start; iprh->end = end; /* If this is the last fragment, calculate total packet length. */ if ((offset & IP6_FRAG_MORE_FLAG) == 0) { ipr->datagram_len = iprh->end; } /* Additional validity tests: we have received first and last fragment. */ iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; if (iprh_tmp->start != 0) { valid = 0; } if (ipr->datagram_len == 0) { valid = 0; } /* Final validity test: no gaps between current and last fragment. */ iprh_prev = iprh; q = iprh->next_pbuf; while ((q != NULL) && valid) { iprh = (struct ip6_reass_helper*)q->payload; if (iprh_prev->end != iprh->start) { valid = 0; break; } iprh_prev = iprh; q = iprh->next_pbuf; } if (valid) { /* All fragments have been received */ struct ip6_hdr* iphdr_ptr; /* chain together the pbufs contained within the ip6_reassdata list. */ iprh = (struct ip6_reass_helper*) ipr->p->payload; while (iprh != NULL) { next_pbuf = iprh->next_pbuf; if (next_pbuf != NULL) { /* Save next helper struct (will be hidden in next step). */ iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload; /* hide the fragment header for every succeeding fragment */ pbuf_remove_header(next_pbuf, IP6_FRAG_HLEN); #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */ u8_t hdrerr = pbuf_remove_header(next_pbuf, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #endif pbuf_cat(ipr->p, next_pbuf); } else { iprh_tmp = NULL; } iprh = iprh_tmp; } /* Get the first pbuf. */ p = ipr->p; #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { u8_t hdrerr; /* Restore (only) the bytes that we overwrote beyond the fragment header. * Those bytes may belong to either the IPv6 header or an extension * header placed before the fragment header. */ MEMCPY(p->payload, ipr->orig_hdr, IPV6_FRAG_REQROOM); /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */ hdrerr = pbuf_remove_header(p, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #endif /* We need to get rid of the fragment header itself, which is somewhere in * the middle of the packet (but still in the first pbuf of the chain). * Getting rid of the header is required by RFC 2460 Sec. 4.5 and necessary * in order to be able to reassemble packets that are close to full size * (i.e., around 65535 bytes). We simply move up all the headers before the * fragment header, including the IPv6 header, and adjust the payload start * accordingly. This works because all these headers are in the first pbuf * of the chain, and because the caller adjusts all its pointers on * successful reassembly. */ MEMMOVE((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr), ipr->iphdr, (size_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr)); /* This is where the IPv6 header is now. */ iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr)); /* Adjust datagram length by adding header lengths. */ ipr->datagram_len = (u16_t)(ipr->datagram_len + ((u8_t*)p->payload - (u8_t*)iphdr_ptr) - IP6_HLEN); /* Set payload length in ip header. */ iphdr_ptr->_plen = lwip_htons(ipr->datagram_len); /* With the fragment header gone, we now need to adjust the next-header * field of whatever header was originally before it. Since the packet made * it through the original header processing routines at least up to the * fragment header, we do not need any further sanity checks here. */ if (IP6H_NEXTH(iphdr_ptr) == IP6_NEXTH_FRAGMENT) { iphdr_ptr->_nexth = ipr->nexth; } else { u8_t *ptr = (u8_t *)iphdr_ptr + IP6_HLEN; while (*ptr != IP6_NEXTH_FRAGMENT) { ptr += 8 * (1 + ptr[1]); } *ptr = ipr->nexth; } /* release the resources allocated for the fragment queue entry */ if (reassdatagrams == ipr) { /* it was the first in the list */ reassdatagrams = ipr->next; } else { /* it wasn't the first, so it must have a valid 'prev' */ LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); ipr_prev->next = ipr->next; } memp_free(MEMP_IP6_REASSDATA, ipr); /* adjust the number of pbufs currently queued for reassembly. */ clen = pbuf_clen(p); LWIP_ASSERT("ip6_reass_pbufcount >= clen", ip6_reass_pbufcount >= clen); ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - clen); /* Move pbuf back to IPv6 header. This should never fail. */ if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) { LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); pbuf_free(p); return NULL; } /* Return the pbuf chain */ return p; } /* the datagram is not (yet?) reassembled completely */ return NULL; nullreturn: IP6_FRAG_STATS_INC(ip6_frag.drop); pbuf_free(p); return NULL; }
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); LWIP_ASSERT("vb != NULL",vb != NULL); 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); LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL); if (vb->ident == NULL) { 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); LWIP_ASSERT("vb->value != NULL",vb->value != NULL); if (vb->value == NULL) { 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; } } return vb; }
/*-----------------------------------------------------------------------------------*/ 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; left = len; ptr = arg; if(len > pcb->snd_buf) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too much data %d\n", len)); return ERR_MEM; } seqno = pcb->snd_lbb; queue = NULL; DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d\n", pcb->snd_queuelen)); queuelen = pcb->snd_queuelen; if(queuelen >= TCP_SND_QUEUELEN) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN)); goto memerr; } #ifdef LWIP_DEBUG if(pcb->snd_queuelen != 0) { ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } #endif /* LWIP_DEBUG */ seg = NULL; seglen = 0; while(queue == NULL || left > 0) { 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) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for tcp_seg\n")); goto memerr; } seg->next = NULL; seg->p = NULL; if(queue == NULL) { queue = seg; } else { for(useg = queue; useg->next != NULL; useg = useg->next); useg->next = 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. */ if(optdata != NULL) { if((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { goto memerr; } ++queuelen; seg->dataptr = seg->p->payload; } else if(copy) { if((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf copy\n")); goto memerr; } ++queuelen; if(arg != NULL) { memcpy(seg->p->payload, ptr, seglen); } seg->dataptr = seg->p->payload; } else { /* Do not copy the data. */ if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n")); goto memerr; } ++queuelen; p->payload = ptr; seg->dataptr = ptr; if((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { pbuf_free(p); DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for header pbuf\n")); goto memerr; } ++queuelen; pbuf_chain(seg->p, p); } if(queuelen > TCP_SND_QUEUELEN) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); goto memerr; } seg->len = seglen; /* if((flags & TCP_SYN) || (flags & TCP_FIN)) { ++seg->len; }*/ /* build TCP header */ if(pbuf_header(seg->p, TCP_HLEN)) { DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: no room for TCP header in pbuf.\n")); #ifdef TCP_STATS ++stats.tcp.err; #endif /* TCP_STATS */ 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 */ if(optdata == NULL) { TCPH_OFFSET_SET(seg->tcphdr, 5 << 4); } else { TCPH_OFFSET_SET(seg->tcphdr, (5 + optlen / 4) << 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); } DEBUGF(TCP_OUTPUT_DEBUG, ("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); } /* Go to the last segment on the ->unsent queue. */ if(pcb->unsent == NULL) { useg = NULL; } else { for(useg = pcb->unsent; useg->next != NULL; useg = useg->next); } /* 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)) && useg->len + queue->len <= pcb->mss) { /* Remove TCP header from first segment. */ pbuf_header(queue->p, -TCP_HLEN); pbuf_chain(useg->p, queue->p); useg->len += queue->len; useg->next = queue->next; DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: chaining, new len %u\n", useg->len)); if(seg == queue) { seg = NULL; } memp_free(MEMP_TCP_SEG, queue); } else { if(useg == NULL) { pcb->unsent = queue; } else { useg->next = queue; } } if((flags & TCP_SYN) || (flags & TCP_FIN)) { ++len; } pcb->snd_lbb += len; pcb->snd_buf -= len; pcb->snd_queuelen = queuelen; DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen)); #ifdef LWIP_DEBUG if(pcb->snd_queuelen != 0) { ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } #endif /* LWIP_DEBUG */ /* 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_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_PSH); } return ERR_OK; memerr: #ifdef TCP_STATS ++stats.tcp.memerr; #endif /* TCP_STATS */ if(queue != NULL) { tcp_segs_free(queue); } #ifdef LWIP_DEBUG if(pcb->snd_queuelen != 0) { ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } #endif /* LWIP_DEBUG */ DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen)); return ERR_MEM; }
/** * Free a datagram (struct ip6_reassdata) and all its pbufs. * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), * sends an ICMP time exceeded packet. * * @param ipr datagram to free */ static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) { struct ip6_reassdata *prev; u16_t pbufs_freed = 0; u16_t clen; struct pbuf *p; struct ip6_reass_helper *iprh; #if LWIP_ICMP6 iprh = (struct ip6_reass_helper *)ipr->p->payload; if (iprh->start == 0) { /* The first fragment was received, send ICMP time exceeded. */ /* First, de-queue the first pbuf from r->p. */ p = ipr->p; ipr->p = iprh->next_pbuf; /* Restore the part that we've overwritten with our helper structure, or we * might send garbage (and disclose a pointer) in the ICMPv6 reply. */ MEMCPY(p->payload, ipr->orig_hdr, sizeof(iprh)); /* Then, move back to the original ipv6 header (we are now pointing to Fragment header). This cannot fail since we already checked when receiving this fragment. */ if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr))) { LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); } else { /* Reconstruct the zoned source and destination addresses, so that we do * not end up sending the ICMP response over the wrong link. */ ip6_addr_t src_addr, dest_addr; ip6_addr_copy_from_packed(src_addr, IPV6_FRAG_SRC(ipr)); ip6_addr_set_zone(&src_addr, ipr->src_zone); ip6_addr_copy_from_packed(dest_addr, IPV6_FRAG_DEST(ipr)); ip6_addr_set_zone(&dest_addr, ipr->dest_zone); /* Send the actual ICMP response. */ icmp6_time_exceeded_with_addrs(p, ICMP6_TE_FRAG, &src_addr, &dest_addr); } clen = pbuf_clen(p); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); pbufs_freed = (u16_t)(pbufs_freed + clen); pbuf_free(p); } #endif /* LWIP_ICMP6 */ /* First, free all received pbufs. The individual pbufs need to be released separately as they have not yet been chained */ p = ipr->p; while (p != NULL) { struct pbuf *pcur; iprh = (struct ip6_reass_helper *)p->payload; pcur = p; /* get the next pointer before freeing */ p = iprh->next_pbuf; clen = pbuf_clen(pcur); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); pbufs_freed = (u16_t)(pbufs_freed + clen); pbuf_free(pcur); } /* Then, unchain the struct ip6_reassdata from the list and free it. */ if (ipr == reassdatagrams) { reassdatagrams = ipr->next; } else { prev = reassdatagrams; while (prev != NULL) { if (prev->next == ipr) { break; } prev = prev->next; } if (prev != NULL) { prev->next = ipr->next; } } memp_free(MEMP_IP6_REASSDATA, ipr); /* Finally, update number of pbufs in reassembly queue */ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - pbufs_freed); }
/** * 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; }
/** * 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; }
/** * Call the lower part of a netconn_* function * This function is then running in the thread context * of tcpip_thread and has exclusive access to lwIP core code. * * @param apimsg a struct containing the function to call and its parameters * @return ERR_OK if the function was called, another err_t if not */ static err_t tcpip_apimsg(struct api_msg *apimsg) { #ifdef LWIP_DEBUG /* catch functions that don't set err */ apimsg->msg.err = ERR_VAL; #endif #if LWIP_NETCONN_SEM_PER_THREAD apimsg->msg.op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); LWIP_ASSERT("netconn semaphore not initialized", sys_sem_valid(apimsg->msg.op_completed_sem)); #endif #ifdef LWIP_ESP8266 //#if 0 sys_sem_t *op_sem_tmp = NULL; if(apimsg->function == lwip_netconn_do_write) op_sem_tmp = LWIP_API_MSG_SND_SEM(&apimsg->msg); else op_sem_tmp = LWIP_API_MSG_SEM(&apimsg->msg); if (tcpip_send_api_msg(apimsg->function, &apimsg->msg, op_sem_tmp) == ERR_OK) { #else if (tcpip_send_api_msg(apimsg->function, &apimsg->msg, LWIP_API_MSG_SEM(&apimsg->msg)) == ERR_OK) { #endif return apimsg->msg.err; } return ERR_VAL; } #endif /* !LWIP_TCPIP_CORE_LOCKING */ /** * Create a new netconn (of a specific type) that has a callback function. * The corresponding pcb is also created. * * @param t the type of 'connection' to create (@see enum netconn_type) * @param proto the IP protocol for RAW IP pcbs * @param callback a function to call on status changes (RX available, TX'ed) * @return a newly allocated struct netconn or * NULL on memory error */ struct netconn* netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) { struct netconn *conn; API_MSG_VAR_DECLARE(msg); conn = netconn_alloc(t, callback); if (conn != NULL) { err_t err; API_MSG_VAR_ALLOC_DONTFAIL(msg); API_MSG_VAR_REF(msg).msg.msg.n.proto = proto; API_MSG_VAR_REF(msg).msg.conn = conn; TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_newconn, err); API_MSG_VAR_FREE(msg); if (err != ERR_OK) { LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); #if LWIP_TCP LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); #endif /* LWIP_TCP */ #if !LWIP_NETCONN_SEM_PER_THREAD LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); sys_sem_free(&conn->op_completed); #ifdef LWIP_ESP8266 sys_sem_free(&conn->snd_op_completed); #endif #endif /* !LWIP_NETCONN_SEM_PER_THREAD */ sys_mbox_free(&conn->recvmbox); memp_free(MEMP_NETCONN, conn); return NULL; } } return conn; } /** * Close a netconn 'connection' and free its resources. * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate * after this returns. * * @param conn the netconn to delete * @return ERR_OK if the connection was deleted */ err_t netconn_delete(struct netconn *conn) { err_t err; API_MSG_VAR_DECLARE(msg); /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ if (conn == NULL) { return ERR_OK; } API_MSG_VAR_ALLOC(msg); API_MSG_VAR_REF(msg).msg.conn = conn; #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER /* get the time we started, which is later compared to sys_now() + conn->send_timeout */ API_MSG_VAR_REF(msg).msg.msg.sd.time_started = sys_now(); #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ #if LWIP_TCP API_MSG_VAR_REF(msg).msg.msg.sd.polls_left = ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1; #endif /* LWIP_TCP */ #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_delconn, err); API_MSG_VAR_FREE(msg); if (err != ERR_OK) { return err; } netconn_free(conn); return ERR_OK; }
/** * Update (or insert) a IP/MAC address pair in the ARP cache. * * If a pending entry is resolved, any queued packets will be sent * at this point. * * @param ipaddr IP address of the inserted ARP entry. * @param ethaddr Ethernet address of the inserted ARP entry. * @param flags Defines behaviour: * - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified, * only existing ARP entries will be updated. * * @return * - ERR_OK Succesfully updated ARP cache. * - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set. * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. * * @see pbuf_free() */ static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; u8_t k; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 3, ("update_arp_entry()\n")); LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); /* non-unicast address? */ if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find or create ARP entry */ #if LWIP_NETIF_HWADDRHINT i = find_entry(ipaddr, flags, netif); #else /* LWIP_NETIF_HWADDRHINT */ i = find_entry(ipaddr, flags); #endif /* LWIP_NETIF_HWADDRHINT */ /* bail out if no entry could be found */ if (i < 0) return (err_t)i; /* mark it stable */ arp_table[i].state = ETHARP_STATE_STABLE; /* record network interface */ arp_table[i].netif = netif; /* insert in SNMP ARP index tree */ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); /* update address */ k = ETHARP_HWADDR_LEN; while (k > 0) { k--; arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; } /* reset time stamp */ arp_table[i].ctime = 0; #if ARP_QUEUEING /* this is where we will send out queued packets! */ while (arp_table[i].q != NULL) { struct pbuf *p; /* remember remainder of queue */ struct etharp_q_entry *q = arp_table[i].q; /* pop first item off the queue */ arp_table[i].q = q->next; /* get the packet pointer */ p = q->p; /* now queue entry can be freed */ memp_free(MEMP_ARP_QUEUE, q); /* send the queued IP packet */ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); /* free the queued IP packet */ pbuf_free(p); } #endif return ERR_OK; }
/** * 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 assosicated 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) { 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->ext_object_def.access & MIB_ACCESS_READ)) { 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) { 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); LWIP_ASSERT("vb != NULL",vb != NULL); 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 refereced 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); LWIP_ASSERT("vb->value != NULL",vb->value != NULL); 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)) { 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((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 */ struct mib_external_node *en = (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) && (object_def.access & MIB_ACCESS_READ)) { 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); LWIP_ASSERT("vb != NULL",vb != NULL); 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 refereced 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); LWIP_ASSERT("vb->value != NULL",vb->value != NULL); 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; 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); } }
/** * Translates the name of a service location (for example, a host name) and/or * a service name and returns a set of socket addresses and associated * information to be used in creating a socket with which to address the * specified service. * Memory for the result is allocated internally and must be freed by calling * lwip_freeaddrinfo()! * * Due to a limitation in dns_gethostbyname, only the first address of a * host is returned. * Also, service names are not supported (only port numbers)! * * @param nodename descriptive name or address string of the host * (may be NULL -> local address) * @param servname port number as string of NULL * @param hints structure containing input values that set socktype and protocol * @param res pointer to a pointer where to store the result (set to NULL on failure) * @return 0 on success, non-zero on failure */ int lwip_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { err_t err; ip_addr_t addr; struct addrinfo *ai; struct sockaddr_in *sa = NULL; int port_nr = 0; size_t total_size; size_t namelen = 0; if (res == NULL) { return EAI_FAIL; } *res = NULL; if ((nodename == NULL) && (servname == NULL)) { return EAI_NONAME; } if (servname != NULL) { /* service name specified: convert to port number * @todo?: currently, only ASCII integers (port numbers) are supported! */ port_nr = atoi(servname); if ((port_nr <= 0) || (port_nr > 0xffff)) { return EAI_SERVICE; } } if (nodename != NULL) { /* service location specified, try to resolve */ err = netconn_gethostbyname(nodename, &addr); if (err != ERR_OK) { return EAI_FAIL; } } else { /* service location specified, use loopback address */ ip_addr_set_loopback(&addr); } total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in); if (nodename != NULL) { namelen = strlen(nodename); LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); total_size += namelen + 1; } /* If this fails, please report to lwip-devel! :-) */ LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!", total_size <= NETDB_ELEM_SIZE); ai = (struct addrinfo *)memp_malloc(MEMP_NETDB); if (ai == NULL) { goto memerr; } memset(ai, 0, total_size); sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); /* set up sockaddr */ inet_addr_from_ipaddr(&sa->sin_addr, &addr); sa->sin_family = AF_INET; sa->sin_len = sizeof(struct sockaddr_in); sa->sin_port = htons((u16_t)port_nr); /* set up addrinfo */ ai->ai_family = AF_INET; if (hints != NULL) { /* copy socktype & protocol from hints if specified */ ai->ai_socktype = hints->ai_socktype; ai->ai_protocol = hints->ai_protocol; } if (nodename != NULL) { /* copy nodename to canonname if specified */ ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); MEMCPY(ai->ai_canonname, nodename, namelen); ai->ai_canonname[namelen] = 0; } ai->ai_addrlen = sizeof(struct sockaddr_in); ai->ai_addr = (struct sockaddr*)sa; *res = ai; return 0; memerr: if (ai != NULL) { memp_free(MEMP_NETDB, ai); } return EAI_MEMORY; }
/** * 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; } } }
/****************************************************************************** * FunctionName : espconn_tcp_disconnect * Description : disconnect with host * Parameters : arg -- Additional argument to pass to the callback function * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_tcp_disconnect_successful(void *arg) { espconn_msg *pdiscon_cb = arg; sint8 dis_err = 0; espconn_buf *pdis_buf = NULL; espconn_buf *pdis_back = NULL; espconn_kill_oldest_pcb(); if (pdiscon_cb != NULL) { struct espconn *espconn = pdiscon_cb->preverse; dis_err = pdiscon_cb->pcommon.err; if (pdiscon_cb->pespconn != NULL){ struct tcp_pcb *pcb = NULL; if (espconn != NULL){/*Process the server's message block*/ if (pdiscon_cb->pespconn->proto.tcp != NULL && espconn->proto.tcp){ espconn_copy_partial(espconn, pdiscon_cb->pespconn); espconn_printf("server: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->remote_ip[0], espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2], espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port); os_free(pdiscon_cb->pespconn->proto.tcp); pdiscon_cb->pespconn->proto.tcp = NULL; } os_free(pdiscon_cb->pespconn); pdiscon_cb->pespconn = NULL; } else {/*Process the client's message block*/ espconn = pdiscon_cb->pespconn; espconn_printf("client: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->local_ip[0], espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2], espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port); } /*process the current TCP block*/ pcb = espconn_find_current_pcb(pdiscon_cb); if (pcb != NULL){ if (espconn_reuse_disabled(pdiscon_cb)) { struct tcp_pcb *cpcb = NULL; struct tcp_pcb *prev = NULL; u8_t pcb_remove; espconn_printf("espconn_tcp_disconnect_successful %d, %d\n", pcb->state, pcb->local_port); cpcb = tcp_tw_pcbs; while (cpcb != NULL) { pcb_remove = 0; if (cpcb->local_port == pcb->local_port) { ++pcb_remove; } /* If the PCB should be removed, do it. */ if (pcb_remove) { struct tcp_pcb *backup_pcb = NULL; tcp_pcb_purge(cpcb); /* Remove PCB from tcp_tw_pcbs list. */ if (prev != NULL) { LWIP_ASSERT("espconn_tcp_delete: middle cpcb != tcp_tw_pcbs",cpcb != tcp_tw_pcbs); prev->next = cpcb->next; } else { /* This PCB was the first. */ LWIP_ASSERT("espconn_tcp_delete: first cpcb == tcp_tw_pcbs",tcp_tw_pcbs == cpcb); tcp_tw_pcbs = cpcb->next; } backup_pcb = cpcb; cpcb = cpcb->next; memp_free(MEMP_TCP_PCB, backup_pcb); } else { prev = cpcb; cpcb = cpcb->next; } } } else { tcp_arg(pcb, NULL); tcp_err(pcb, NULL); } } } /*to prevent memory leaks, ensure that each allocated is deleted*/ pdis_buf = pdiscon_cb->pcommon.pbuf; while (pdis_buf != NULL) { pdis_back = pdis_buf; pdis_buf = pdis_back->pnext; espconn_pbuf_delete(&pdiscon_cb->pcommon.pbuf, pdis_back); os_free(pdis_back); pdis_back = NULL; } os_bzero(&pktinfo[0], sizeof(struct espconn_packet)); os_memcpy(&pktinfo[0], (void*)&pdiscon_cb->pcommon.packet_info, sizeof(struct espconn_packet)); os_free(pdiscon_cb); pdiscon_cb = NULL; if (espconn->proto.tcp && espconn->proto.tcp->disconnect_callback != NULL) { espconn->proto.tcp->disconnect_callback(espconn); } } else { espconn_printf("espconn_tcp_disconnect err\n"); } }
void snmp_mib_ln_free(struct mib_list_node *ln) { memp_free(MEMP_SNMP_NODE, ln); }
/** * Create a new netconn (of a specific type) that has a callback function. * The corresponding pcb is NOT created! * * @param t the type of 'connection' to create (@see enum netconn_type) * @param proto the IP protocol for RAW IP pcbs * @param callback a function to call on status changes (RX available, TX'ed) * @return a newly allocated struct netconn or * NULL on memory error */ struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback) { struct netconn *conn; int size; conn = (struct netconn *)memp_malloc(MEMP_NETCONN); if (conn == NULL) { return NULL; } conn->last_err = ERR_OK; conn->type = t; conn->pcb.tcp = NULL; #if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) size = DEFAULT_RAW_RECVMBOX_SIZE; #else switch(NETCONNTYPE_GROUP(t)) { #if LWIP_RAW case NETCONN_RAW: size = DEFAULT_RAW_RECVMBOX_SIZE; break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: size = DEFAULT_UDP_RECVMBOX_SIZE; break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: size = DEFAULT_TCP_RECVMBOX_SIZE; break; #endif /* LWIP_TCP */ default: LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); break; } #endif if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { memp_free(MEMP_NETCONN, conn); return NULL; } if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { sys_sem_free(&conn->op_completed); memp_free(MEMP_NETCONN, conn); return NULL; } #if LWIP_TCP sys_mbox_set_invalid(&conn->acceptmbox); #endif conn->state = NETCONN_NONE; #if LWIP_SOCKET /* initialize socket to -1 since 0 is a valid socket */ conn->socket = -1; #endif /* LWIP_SOCKET */ conn->callback = callback; #if LWIP_TCP conn->current_msg = NULL; conn->write_offset = 0; #endif /* LWIP_TCP */ #if LWIP_SO_RCVTIMEO conn->recv_timeout = 0; #endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVBUF conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; conn->recv_avail = 0; #endif /* LWIP_SO_RCVBUF */ conn->flags = 0; return conn; }
void snmp_mib_lrn_free(struct mib_list_rootnode *lrn) { memp_free(MEMP_SNMP_ROOTNODE, lrn); }
/** * Free a callback message allocated by tcpip_callbackmsg_new(). * * @param msg the message to free */ void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) { memp_free(MEMP_TCPIP_MSG_API, msg); }
/** * Closes the connection held by the PCB. * * Listening pcbs are freed and may not be referenced any more. * Connection pcbs are freed if not yet connected and may not be referenced * any more. If a connection is established (at least SYN received or in * a closing state), the connection is closed, and put in a closing state. * The pcb is then automatically freed in tcp_slowtmr(). It is therefore * unsafe to reference it. * * @param pcb the tcp_pcb to close * @return ERR_OK if connection has been closed * another err_t if closing failed and pcb is not freed */ err_t tcp_close(struct tcp_pcb *pcb) { err_t err; #if TCP_DEBUG LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ switch (pcb->state) { /* 若TCP在CLOSE状态,即tcp_new()之后从未使用过 */ case CLOSED: /* Closing a pcb in the CLOSED state might seem erroneous, * however, it is in this state once allocated and as yet unused * and the user needs some way to free it should the need arise. * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) * or for a pcb that has been used and then entered the CLOSED state * is erroneous, but this should never happen as the pcb has in those cases * been freed, and so any remaining handles are bogus. */ err = ERR_OK; /* 从tcp_bound_pcbs链表上删除(如果已经调用tcp_bind()的话) */ TCP_RMV(&tcp_bound_pcbs, pcb); /* 释放TCP控制所占内存 */ memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; break; case LISTEN: err = ERR_OK; /* 从LISTEN链表上删除,并发送可能存在的delayed ACKs */ tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb); /* 释放LISTEN类型的TCP控制块 */ memp_free(MEMP_TCP_PCB_LISTEN, pcb); pcb = NULL; break; case SYN_SENT: err = ERR_OK; tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; snmp_inc_tcpattemptfails(); break; case SYN_RCVD: /* 构造FIN报文,主动关闭 */ err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { snmp_inc_tcpattemptfails(); /* 进入FIN_WAIT_1状态 */ pcb->state = FIN_WAIT_1; } break; case ESTABLISHED: /* 构造FIN报文,主动关闭 */ err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { snmp_inc_tcpestabresets(); /* 进入FIN_WAIT_1状态 */ pcb->state = FIN_WAIT_1; } break; /* 被动关闭,收到对方的FIN后,构造FIN关闭另一个方向传输 */ case CLOSE_WAIT: err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { snmp_inc_tcpestabresets(); /* 进入LAST_ACK状态 */ pcb->state = LAST_ACK; } break; /* 其他状态,用TCP定时器函数实现 */ default: /* Has already been closed, do nothing. */ err = ERR_OK; pcb = NULL; break; } /* 发送TCP控制块中剩余的报文段,包括FIN报文 */ if (pcb != NULL && err == ERR_OK) { /* To ensure all data has been sent when tcp_close returns, we have to make sure tcp_output doesn't fail. Since we don't really have to ensure all data has been sent when tcp_close returns (unsent data is sent from tcp timer functions, also), we don't care for the return value of tcp_output for now. */ /* @todo: When implementing SO_LINGER, this must be changed somehow: If SOF_LINGER is set, the data should be sent when tcp_close returns. */ tcp_output(pcb); } return err; }
/** * Free a datagram (struct ip6_reassdata) and all its pbufs. * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), * sends an ICMP time exceeded packet. * * @param ipr datagram to free */ static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) { struct ip6_reassdata *prev; u16_t pbufs_freed = 0; u8_t clen; struct pbuf *p; struct ip6_reass_helper *iprh; #if LWIP_ICMP6 iprh = (struct ip6_reass_helper *)ipr->p->payload; if (iprh->start == 0) { /* The first fragment was received, send ICMP time exceeded. */ /* First, de-queue the first pbuf from r->p. */ p = ipr->p; ipr->p = iprh->next_pbuf; /* Then, move back to the original header (we are now pointing to Fragment header). */ if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); } else { icmp6_time_exceeded(p, ICMP6_TE_FRAG); } clen = pbuf_clen(p); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); pbufs_freed += clen; pbuf_free(p); } #endif /* LWIP_ICMP6 */ /* First, free all received pbufs. The individual pbufs need to be released separately as they have not yet been chained */ p = ipr->p; while (p != NULL) { struct pbuf *pcur; iprh = (struct ip6_reass_helper *)p->payload; pcur = p; /* get the next pointer before freeing */ p = iprh->next_pbuf; clen = pbuf_clen(pcur); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); pbufs_freed += clen; pbuf_free(pcur); } /* Then, unchain the struct ip6_reassdata from the list and free it. */ if (ipr == reassdatagrams) { reassdatagrams = ipr->next; } else { prev = reassdatagrams; while (prev != NULL) { if (prev->next == ipr) { break; } prev = prev->next; } if (prev != NULL) { prev->next = ipr->next; } } memp_free(MEMP_IP6_REASSDATA, ipr); /* Finally, update number of pbufs in reassembly queue */ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); ip6_reass_pbufcount -= pbufs_freed; }
/** * Closes the TX side of a connection held by the PCB. * For tcp_close(), a RST is sent if the application didn't receive all data * (tcp_recved() not called for all data passed to recv callback). * * Listening pcbs are freed and may not be referenced any more. * Connection pcbs are freed if not yet connected and may not be referenced * any more. If a connection is established (at least SYN received or in * a closing state), the connection is closed, and put in a closing state. * The pcb is then automatically freed in tcp_slowtmr(). It is therefore * unsafe to reference it. * * @param pcb the tcp_pcb to close * @return ERR_OK if connection has been closed * another err_t if closing failed and pcb is not freed */ static err_t tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) { err_t err; if (rst_on_unacked_data && (pcb->state != LISTEN)) { if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { /* Not all data received by application, send RST to tell the remote side about this. */ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); /* don't call tcp_abort here: we must not deallocate the pcb since that might not be expected when calling tcp_close */ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port); tcp_pcb_purge(pcb); /* TODO: to which state do we move now? */ /* move to TIME_WAIT since we close actively */ TCP_RMV(&tcp_active_pcbs, pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); return ERR_OK; } } switch (pcb->state) { case CLOSED: /* Closing a pcb in the CLOSED state might seem erroneous, * however, it is in this state once allocated and as yet unused * and the user needs some way to free it should the need arise. * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) * or for a pcb that has been used and then entered the CLOSED state * is erroneous, but this should never happen as the pcb has in those cases * been freed, and so any remaining handles are bogus. */ err = ERR_OK; if (pcb->local_port != 0) { TCP_RMV(&tcp_bound_pcbs, pcb); } memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; break; case LISTEN: err = ERR_OK; tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); memp_free(MEMP_TCP_PCB_LISTEN, pcb); pcb = NULL; break; case SYN_SENT: err = ERR_OK; tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; snmp_inc_tcpattemptfails(); break; case SYN_RCVD: err = tcp_send_fin(pcb); if (err == ERR_OK) { snmp_inc_tcpattemptfails(); pcb->state = FIN_WAIT_1; } break; case ESTABLISHED: err = tcp_send_fin(pcb); if (err == ERR_OK) { snmp_inc_tcpestabresets(); pcb->state = FIN_WAIT_1; } break; case CLOSE_WAIT: err = tcp_send_fin(pcb); if (err == ERR_OK) { snmp_inc_tcpestabresets(); pcb->state = LAST_ACK; } break; default: /* Has already been closed, do nothing. */ err = ERR_OK; pcb = NULL; break; } if (pcb != NULL && err == ERR_OK) { /* To ensure all data has been sent when tcp_close returns, we have to make sure tcp_output doesn't fail. Since we don't really have to ensure all data has been sent when tcp_close returns (unsent data is sent from tcp timer functions, also), we don't care for the return value of tcp_output for now. */ /* @todo: When implementing SO_LINGER, this must be changed somehow: If SOF_LINGER is set, the data should be sent and acked before close returns. This can only be valid for sequential APIs, not for the raw API. */ tcp_output(pcb); } return err; }
/** Free a struct pbuf_custom_ref */ static void ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) { LWIP_ASSERT("p != NULL", p != NULL); memp_free(MEMP_FRAG_PBUF, p); }
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; flags_t lPCBFlags = pcb -> flags; int iSegCNT = 0; left = len; ptr = arg; if ( len > pcb -> snd_buf ) return ERR_MEM; seqno = pcb -> snd_lbb; queue = NULL; queuelen = pcb -> snd_queuelen; if ( queuelen >= TCP_SND_QUEUELEN ) goto memerr; seg = useg = NULL; seglen = 0; while ( !queue || left > 0 ) { if ( lPCBFlags & TF_EVENSEG ) { ++iSegCNT; seglen = left > pcb -> mss ? pcb -> mss : (((iSegCNT%2) == 1)? ((left + 1) / 2): left); } else seglen = left > pcb -> mss ? pcb -> mss : left; seg = memp_malloc ( MEMP_TCP_SEG ); if ( !seg ) goto memerr; seg -> next = NULL; seg -> p = NULL; if ( !queue ) useg = queue = seg; else { useg -> next = seg; useg = seg; } /* end else */ if (optdata != NULL) { if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { goto memerr; } ++queuelen; seg->dataptr = seg->p->payload; } 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) { mips_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; 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, p); 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; 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. */ mips_memcpy(seg->dataptr, optdata, optlen); } 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); } /* 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)) && useg->len + queue->len <= pcb->mss) { /* Remove TCP header from first segment. */ pbuf_header(queue->p, -TCP_HLEN); pbuf_cat(useg->p, queue->p); useg->len += queue->len; useg->next = queue->next; if (seg == queue) seg = NULL; memp_free(MEMP_TCP_SEG, queue); } else { if (useg == NULL) { pcb->unsent = queue; } else { useg->next = queue; } } if ((flags & TCP_SYN) || (flags & TCP_FIN)) { ++len; } pcb->snd_lbb += len; pcb->snd_buf -= len; 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; }
/** * Closes the connection held by the PCB. * * Listening pcbs are freed and may not be referenced any more. * Connection pcbs are freed if not yet connected and may not be referenced * any more. If a connection is established (at least SYN received or in * a closing state), the connection is closed, and put in a closing state. * The pcb is then automatically freed in tcp_slowtmr(). It is therefore * unsafe to reference it. * * @param pcb the tcp_pcb to close * @return ERR_OK if connection has been closed * another err_t if closing failed and pcb is not freed */ err_t tcp_close(struct tcp_pcb *pcb) { err_t err; #if TCP_DEBUG LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ switch (pcb->state) { case CLOSED: /* Closing a pcb in the CLOSED state might seem erroneous, * however, it is in this state once allocated and as yet unused * and the user needs some way to free it should the need arise. * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) * or for a pcb that has been used and then entered the CLOSED state * is erroneous, but this should never happen as the pcb has in those cases * been freed, and so any remaining handles are bogus. */ err = ERR_OK; TCP_RMV(&tcp_bound_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; break; case LISTEN: err = ERR_OK; tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb); memp_free(MEMP_TCP_PCB_LISTEN, pcb); pcb = NULL; break; case SYN_SENT: err = ERR_OK; tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; snmp_inc_tcpattemptfails(); break; case SYN_RCVD: err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { snmp_inc_tcpattemptfails(); pcb->state = FIN_WAIT_1; } break; case ESTABLISHED: err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { snmp_inc_tcpestabresets(); pcb->state = FIN_WAIT_1; } break; case CLOSE_WAIT: err = tcp_send_ctrl(pcb, TCP_FIN); if (err == ERR_OK) { snmp_inc_tcpestabresets(); pcb->state = LAST_ACK; } break; default: /* Has already been closed, do nothing. */ err = ERR_OK; pcb = NULL; break; } if (pcb != NULL && err == ERR_OK) { /* To ensure all data has been sent when tcp_close returns, we have to make sure tcp_output doesn't fail. Since we don't really have to ensure all data has been sent when tcp_close returns (unsent data is sent from tcp timer functions, also), we don't care for the return value of tcp_output for now. */ /* @todo: When implementing SO_LINGER, this must be changed somehow: If SOF_LINGER is set, the data should be sent when tcp_close returns. */ tcp_output(pcb); } return err; }
struct netbuf * netconn_recv(struct netconn *conn) { struct api_msg *msg; struct netbuf *buf; struct pbuf *p; u16_t len; if (conn == NULL) { return NULL; } if (conn->recvmbox == SYS_MBOX_NULL) { conn->err = ERR_CONN; return NULL; } if (conn->err != ERR_OK) { return NULL; } if (conn->type == NETCONN_TCP) { if (conn->pcb.tcp->state == LISTEN) { conn->err = ERR_CONN; return NULL; } buf = memp_malloc(MEMP_NETBUF); if (buf == NULL) { conn->err = ERR_MEM; return NULL; } sys_mbox_fetch(conn->recvmbox, (void *)&p); if (p != NULL) { len = p->tot_len; conn->recv_avail -= len; } else len = 0; /* Register event with callback */ if (conn->callback) (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, len); /* If we are closed, we indicate that we no longer wish to receive data by setting conn->recvmbox to SYS_MBOX_NULL. */ if (p == NULL) { memp_free(MEMP_NETBUF, buf); sys_mbox_free(conn->recvmbox); conn->recvmbox = SYS_MBOX_NULL; return NULL; } buf->p = p; buf->ptr = p; buf->fromport = 0; buf->fromaddr = NULL; /* Let the stack know that we have taken the data. */ if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { conn->err = ERR_MEM; return buf; } msg->type = API_MSG_RECV; msg->msg.conn = conn; if (buf != NULL) { msg->msg.msg.len = buf->p->tot_len; } else { msg->msg.msg.len = 1; } api_msg_post(msg); sys_mbox_fetch(conn->mbox, NULL); memp_free(MEMP_API_MSG, msg); } else { sys_mbox_fetch(conn->recvmbox, (void *)&buf); conn->recv_avail -= buf->p->tot_len; /* Register event with callback */ if (conn->callback) (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len); } LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err)); return buf; }