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; } /*printf("netconn_recv %p %p\n",conn,conn->recvmbox);*/ if (conn->recvmbox == SYS_MBOX_NULL) { conn->err = ERR_CONN; return NULL; } if (conn->err != ERR_OK) { return NULL; } if (conn->type == NETCONN_TCP) { /* RD1013 do not inspect pcb. use conn->connected instead */ if (conn->connected == 0) { 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->addr.fromaddr = NULL;*/ /* Let the stack know that we have taken the data. */ 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(conn->stack, &msg); sys_mbox_fetch(conn->mbox, NULL); } 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) len %d\n", (void *)buf, conn->err, buf->p->tot_len)); return buf; }
/** * 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; }
/** * Create a one-shot timer (aka timeout). Timeouts are processed in the * following cases: * - while waiting for a message using sys_timeouts_mbox_fetch() * - by calling sys_check_timeouts() (NO_SYS==1 only) * * @param msecs time in milliseconds after that the timer should expire * @param handler callback function to call when msecs have elapsed * @param arg argument to pass to the callback function */ #if LWIP_DEBUG_TIMERNAMES void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) #else /* LWIP_DEBUG_TIMERNAMES */ void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) #endif /* LWIP_DEBUG_TIMERNAMES */ { struct sys_timeo *timeout, *t; #if NO_SYS u32_t now, diff; #endif timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); if (timeout == NULL) { LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); return; } #if NO_SYS now = sys_now(); if (next_timeout == NULL) { diff = 0; timeouts_last_time = now; } else { diff = now - timeouts_last_time; } #endif timeout->next = NULL; timeout->h = handler; timeout->arg = arg; #if NO_SYS timeout->time = msecs + diff; #else timeout->time = msecs; #endif #if LWIP_DEBUG_TIMERNAMES timeout->handler_name = handler_name; LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", (void *)timeout, msecs, handler_name, (void *)arg)); #endif /* LWIP_DEBUG_TIMERNAMES */ if (next_timeout == NULL) { next_timeout = timeout; return; } if (next_timeout->time > msecs) { next_timeout->time -= msecs; timeout->next = next_timeout; next_timeout = timeout; } else { for (t = next_timeout; t != NULL; t = t->next) { timeout->time -= t->time; if (t->next == NULL || t->next->time > timeout->time) { if (t->next != NULL) { t->next->time -= timeout->time; } timeout->next = t->next; t->next = timeout; break; } } } }
/** * 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); goto free_and_return; } #endif if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { goto free_and_return; } if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { sys_sem_free(&conn->op_completed); goto free_and_return; } #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_SNDTIMEO conn->send_timeout = 0; #endif /* LWIP_SO_SNDTIMEO */ #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; free_and_return: memp_free(MEMP_NETCONN, conn); return NULL; }
/** Allocate a new struct pbuf_custom_ref */ static struct pbuf_custom_ref* ICACHE_FLASH_ATTR ip_frag_alloc_pbuf_custom_ref(void) { return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); }
/** * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * The actual memory allocated for the pbuf is determined by the * layer at which the pbuf is allocated and the requested size * (from the size parameter). * * @param layer flag to define header size * @param length size of the pbuf's payload * @param type this parameter decides how and where the pbuf * should be allocated as follows: * * - PBUF_RAM: buffer memory for pbuf is allocated as one large * chunk. This includes protocol headers as well. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * the ROM pbuf. It is assumed that the memory used is really * similar to ROM in that it is immutable and will not be * changed. Memory which is dynamic should generally not * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. * - PBUF_REF: no buffer memory is allocated for the pbuf, even for * protocol headers. It is assumed that the pbuf is only * being used in a single thread. If the pbuf gets queued, * then pbuf_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; u16_t offset; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; break; case PBUF_IP: /* add room for IP layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; break; case PBUF_LINK: /* add room for link layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; break; case PBUF_RAW_TX: /* add room for encapsulating link layer headers (e.g. 802.11) */ offset = PBUF_LINK_ENCAPSULATION_HLEN; break; case PBUF_RAW: /* no offset (e.g. RX buffers or chain successors) */ offset = 0; break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); return NULL; } switch (type) { case PBUF_POOL: /* allocate head of pbuf chain into p */ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); if (p == NULL) { PBUF_POOL_IS_EMPTY(); return NULL; } p->type = type; p->next = NULL; /* make the payload pointer point 'offset' bytes into pbuf data memory */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); /* the total length of the pbuf chain is the requested size */ p->tot_len = length; /* set the length of the first pbuf in the chain */ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); /* set reference count (needed here in case we fail) */ p->ref = 1; /* now allocate the tail of the pbuf chain */ /* remember first pbuf for linkage in next iteration */ r = p; /* remaining length to be allocated */ rem_len = length - p->len; /* any remaining pbufs to be allocated? */ while (rem_len > 0) { q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccessfully */ return NULL; } q->type = type; q->flags = 0; q->next = NULL; /* make previous pbuf point to this pbuf */ r->next = q; /* set total length of this pbuf and next in chain */ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); q->tot_len = (u16_t)rem_len; /* this pbuf length is pool size, unless smaller sized tail */ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); q->ref = 1; /* calculate remaining length to be allocated */ rem_len -= q->len; /* remember this pbuf for linkage in next iteration */ r = q; } /* end of chain */ /*r->next = NULL;*/ break; case PBUF_RAM: /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == NULL) { return NULL; } /* Set up internal structure of the pbuf. */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); p->len = p->tot_len = length; p->next = NULL; p->type = type; LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; /* pbuf references existing (non-volatile static constant) ROM payload? */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ p = (struct pbuf *)memp_malloc(MEMP_PBUF); if (p == NULL) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } /* set reference count */ p->ref = 1; /* set flags */ p->flags = 0; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/** * Receive callback function for UDP netconns. * Posts the packet to conn->recvmbox or deletes it on memory error. * * @see udp.h (struct udp_pcb.recv) for parameters */ static void recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) { struct netbuf *buf; struct netconn *conn; u16_t len; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_udp must have an argument", arg != NULL); conn = (struct netconn *)arg; LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); #if LWIP_SO_RCVBUF SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) || ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { #else /* LWIP_SO_RCVBUF */ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) { #endif /* LWIP_SO_RCVBUF */ pbuf_free(p); return; } buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(p); return; } else { buf->p = p; buf->ptr = p; ip_addr_set(&buf->addr, addr); buf->port = port; #if LWIP_NETBUF_RECVINFO { const struct ip_hdr* iphdr = ip_current_header(); /* get the UDP header - always in the first pbuf, ensured by udp_input */ const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); #if LWIP_CHECKSUM_ON_COPY buf->flags = NETBUF_FLAG_DESTADDR; #endif /* LWIP_CHECKSUM_ON_COPY */ ip_addr_set(&buf->toaddr, ip_current_dest_addr()); buf->toport_chksum = udphdr->dest; } #endif /* LWIP_NETBUF_RECVINFO */ } len = p->tot_len; if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return; } else { #if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } } #endif /* LWIP_UDP */ #if LWIP_TCP /** * Receive callback function for TCP netconns. * Posts the packet to conn->recvmbox, but doesn't delete it on errors. * * @see tcp.h (struct tcp_pcb.recv) for parameters and return value */ static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct netconn *conn; u16_t len; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); conn = (struct netconn *)arg; LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); if (conn == NULL) { return ERR_VAL; } if (!sys_mbox_valid(&conn->recvmbox)) { /* recvmbox already deleted */ if (p != NULL) { tcp_recved(pcb, p->tot_len); pbuf_free(p); } return ERR_OK; } /* Unlike for UDP or RAW pcbs, don't check for available space using recv_avail since that could break the connection (data is already ACKed) */ /* don't overwrite fatal errors! */ NETCONN_SET_SAFE_ERR(conn, err); if (p != NULL) { len = p->tot_len; } else { len = 0; } if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ return ERR_MEM; } else { #if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } return ERR_OK; } /** * Poll callback function for TCP netconns. * Wakes up an application thread that waits for a connection to close * or data to be sent. The application thread then takes the * appropriate action to go on. * * Signals the conn->sem. * netconn_close waits for conn->sem if closing failed. * * @see tcp.h (struct tcp_pcb.poll) for parameters and return value */ static err_t poll_tcp(void *arg, struct tcp_pcb *pcb) { struct netconn *conn = (struct netconn *)arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } /* @todo: implement connect timeout here? */ /* Did a nonblocking write fail before? Then check available write-space. */ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); } } return ERR_OK; } /** * Sent callback function for TCP netconns. * Signals the conn->sem and calls API_EVENT. * netconn_write waits for conn->sem if send buffer is low. * * @see tcp.h (struct tcp_pcb.sent) for parameters and return value */ static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) { struct netconn *conn = (struct netconn *)arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } if (conn) { /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); } } return ERR_OK; } /** * Error callback function for TCP netconns. * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. * The application thread has then to decide what to do. * * @see tcp.h (struct tcp_pcb.err) for parameters */ static void err_tcp(void *arg, err_t err) { struct netconn *conn; enum netconn_state old_state; SYS_ARCH_DECL_PROTECT(lev); conn = (struct netconn *)arg; LWIP_ASSERT("conn != NULL", (conn != NULL)); conn->pcb.tcp = NULL; /* no check since this is always fatal! */ SYS_ARCH_PROTECT(lev); conn->last_err = err; SYS_ARCH_UNPROTECT(lev); /* reset conn->state now before waking up other threads */ old_state = conn->state; conn->state = NETCONN_NONE; /* Notify the user layer about a connection error. Used to signal select. */ API_EVENT(conn, NETCONN_EVT_ERROR, 0); /* Try to release selects pending on 'read' or 'write', too. They will get an error if they actually try to read or write. */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); /* pass NULL-message to recvmbox to wake up pending recv */ if (sys_mbox_valid(&conn->recvmbox)) { /* use trypost to prevent deadlock */ sys_mbox_trypost(&conn->recvmbox, NULL); } /* pass NULL-message to acceptmbox to wake up pending accept */ if (sys_mbox_valid(&conn->acceptmbox)) { /* use trypost to preven deadlock */ sys_mbox_trypost(&conn->acceptmbox, NULL); } if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || (old_state == NETCONN_CONNECT)) { /* calling do_writemore/do_close_internal is not necessary since the pcb has already been deleted! */ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); SET_NONBLOCKING_CONNECT(conn, 0); if (!was_nonblocking_connect) { /* set error return code */ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); conn->current_msg->err = err; conn->current_msg = NULL; /* wake up the waiting task */ sys_sem_signal(&conn->op_completed); } } else { LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); } } /** * Setup a tcp_pcb with the correct callback function pointers * and their arguments. * * @param conn the TCP netconn to setup */ static void setup_tcp(struct netconn *conn) { struct tcp_pcb *pcb; pcb = conn->pcb.tcp; tcp_arg(pcb, conn); tcp_recv(pcb, recv_tcp); tcp_sent(pcb, sent_tcp); tcp_poll(pcb, poll_tcp, 4); tcp_err(pcb, err_tcp); } /** * Accept callback function for TCP netconns. * Allocates a new netconn and posts that to conn->acceptmbox. * * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value */ static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) { struct netconn *newconn; struct netconn *conn = (struct netconn *)arg; LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); if (!sys_mbox_valid(&conn->acceptmbox)) { LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); return ERR_VAL; } /* We have to set the callback here even though * the new socket is unknown. conn->socket is marked as -1. */ newconn = netconn_alloc(conn->type, conn->callback); if (newconn == NULL) { return ERR_MEM; } newconn->pcb.tcp = newpcb; setup_tcp(newconn); /* no protection: when creating the pcb, the netconn is not yet known to the application thread */ newconn->last_err = err; if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { /* When returning != ERR_OK, the pcb is aborted in tcp_process(), so do nothing here! */ /* remove all references to this netconn from the pcb */ struct tcp_pcb* pcb = newconn->pcb.tcp; tcp_arg(pcb, NULL); tcp_recv(pcb, NULL); tcp_sent(pcb, NULL); tcp_poll(pcb, NULL, 4); tcp_err(pcb, NULL); /* remove reference from to the pcb from this netconn */ newconn->pcb.tcp = NULL; /* no need to drain since we know the recvmbox is empty. */ sys_mbox_free(&newconn->recvmbox); sys_mbox_set_invalid(&newconn->recvmbox); netconn_free(newconn); return ERR_MEM; } else { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); } return ERR_OK; } #endif /* LWIP_TCP */ /** * Create a new pcb of a specific type. * Called from do_newconn(). * * @param msg the api_msg_msg describing the connection type * @return msg->conn->err, but the return value is currently ignored */ static void pcb_new(struct api_msg_msg *msg) { LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); /* Allocate a PCB for this connection */ switch(NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: msg->conn->pcb.raw = raw_new(msg->msg.n.proto); if(msg->conn->pcb.raw == NULL) { msg->err = ERR_MEM; break; } raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: msg->conn->pcb.udp = udp_new(); if(msg->conn->pcb.udp == NULL) { msg->err = ERR_MEM; break; } #if LWIP_UDPLITE if (msg->conn->type==NETCONN_UDPLITE) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); } #endif /* LWIP_UDPLITE */ if (msg->conn->type==NETCONN_UDPNOCHKSUM) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); } udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: msg->conn->pcb.tcp = tcp_new(); if(msg->conn->pcb.tcp == NULL) { msg->err = ERR_MEM; break; } setup_tcp(msg->conn); break; #endif /* LWIP_TCP */ default: /* Unsupported netconn type, e.g. protocol disabled */ msg->err = ERR_VAL; break; } }
/** * 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 * * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG */ 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_storage *sa = NULL; int port_nr = 0; size_t total_size; size_t namelen = 0; int ai_family; if (res == NULL) { return EAI_FAIL; } *res = NULL; if ((nodename == NULL) && (servname == NULL)) { return EAI_NONAME; } if (hints != NULL) { ai_family = hints->ai_family; if ((ai_family != AF_UNSPEC) #if LWIP_IPV4 && (ai_family != AF_INET) #endif /* LWIP_IPV4 */ #if LWIP_IPV6 && (ai_family != AF_INET6) #endif /* LWIP_IPV6 */ ) { return EAI_FAMILY; } } else { ai_family = AF_UNSPEC; } if (servname != NULL) { /* service name specified: convert to port number * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */ port_nr = atoi(servname); if ((port_nr <= 0) || (port_nr > 0xffff)) { return EAI_SERVICE; } } if (nodename != NULL) { /* service location specified, try to resolve */ if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) { /* no DNS lookup, just parse for an address string */ if (!ipaddr_aton(nodename, &addr)) { return EAI_NONAME; } #if LWIP_IPV4 && LWIP_IPV6 if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) || (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) { return EAI_NONAME; } #endif /* LWIP_IPV4 && LWIP_IPV6 */ } else { #if LWIP_IPV4 && LWIP_IPV6 /* AF_UNSPEC: prefer IPv4 */ u8_t type = NETCONN_DNS_IPV4_IPV6; if (ai_family == AF_INET) { type = NETCONN_DNS_IPV4; } else if (ai_family == AF_INET6) { type = NETCONN_DNS_IPV6; } #endif /* LWIP_IPV4 && LWIP_IPV6 */ err = netconn_gethostbyname_addrtype(nodename, &addr, type); if (err != ERR_OK) { return EAI_FAIL; } } } else { /* service location specified, use loopback address */ if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) { ip_addr_set_any(ai_family == AF_INET6, &addr); } else { ip_addr_set_loopback(ai_family == AF_INET6, &addr); } } total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage); if (nodename != NULL) { namelen = strlen(nodename); if (namelen > DNS_MAX_NAME_LENGTH) { /* invalid name length */ return EAI_FAIL; } LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size); 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) { return EAI_MEMORY; } memset(ai, 0, total_size); sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo)); if (IP_IS_V6_VAL(addr)) { #if LWIP_IPV6 struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa; /* set up sockaddr */ inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr)); sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(struct sockaddr_in6); sa6->sin6_port = htons((u16_t)port_nr); ai->ai_family = AF_INET6; #endif /* LWIP_IPV6 */ } else { #if LWIP_IPV4 struct sockaddr_in *sa4 = (struct sockaddr_in*)sa; /* set up sockaddr */ inet_addr_from_ipaddr(&sa4->sin_addr, ip_2_ip4(&addr)); sa4->sin_family = AF_INET; sa4->sin_len = sizeof(struct sockaddr_in); sa4->sin_port = htons((u16_t)port_nr); ai->ai_family = AF_INET; #endif /* LWIP_IPV4 */ } /* set up addrinfo */ 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_storage)); MEMCPY(ai->ai_canonname, nodename, namelen); ai->ai_canonname[namelen] = 0; } ai->ai_addrlen = sizeof(struct sockaddr_storage); ai->ai_addr = (struct sockaddr*)sa; *res = ai; return 0; }
/** Allocate a new struct pbuf_custom_ref */ static struct pbuf_custom_ref* ip_frag_alloc_pbuf_custom_ref(void) { return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); }
/** * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * The actual memory allocated for the pbuf is determined by the * layer at which the pbuf is allocated and the requested size * (from the size parameter). * * @param layer flag to define header size * @param length size of the pbuf's payload * @param type this parameter decides how and where the pbuf * should be allocated as follows: * * - PBUF_RAM: buffer memory for pbuf is allocated as one large * chunk. This includes protocol headers as well. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * the ROM pbuf. It is assumed that the memory used is really * similar to ROM in that it is immutable and will not be * changed. Memory which is dynamic should generally not * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. * - PBUF_REF: no buffer memory is allocated for the pbuf, even for * protocol headers. It is assumed that the pbuf is only * being used in a single thread. If the pbuf gets queued, * then pbuf_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; u16_t offset; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ offset = 0; switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset += PBUF_TRANSPORT_HLEN; /* FALLTHROUGH */ case PBUF_IP: /* add room for IP layer header */ offset += PBUF_IP_HLEN; /* FALLTHROUGH */ case PBUF_LINK: /* add room for link layer header */ offset += PBUF_LINK_HLEN; #ifdef PBUF_RSV_FOR_WLAN /* * 1. LINK_HLEN 14Byte will be remove in WLAN layer * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. * 3. encryption needs exra 4 bytes ahead of actual data payload, and require * DAddr and SAddr to be 4-byte aligned. * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... * 5. LCC add 6 bytes more, We don't consider WAPI yet... * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be * matter is ether_hdr is not 4B aligned. * * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned * * 1. lwip * | empty 30B | eth_hdr (14B) | payload ...| * total: 44B ahead payload * 2. net80211 * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| * total: 40B ahead sec_rsv and 44B ahead payload * */ offset += EP_OFFSET; //remove LINK hdr in wlan #endif /* PBUF_RSV_FOR_WLAN */ break; case PBUF_RAW: #ifdef PBUF_RSV_FOR_WLAN /* * RAW pbuf suppose */ offset += EP_OFFSET; //remove LINK hdr in wlan #endif /* PBUF_RAW */ break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); return NULL; } switch (type) { case PBUF_POOL: /* allocate head of pbuf chain into p */ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); if (p == NULL) { PBUF_POOL_IS_EMPTY(); return NULL; } p->type = type; p->next = NULL; /* make the payload pointer point 'offset' bytes into pbuf data memory */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); /* the total length of the pbuf chain is the requested size */ p->tot_len = length; /* set the length of the first pbuf in the chain */ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); /* set reference count (needed here in case we fail) */ p->ref = 1; /* now allocate the tail of the pbuf chain */ /* remember first pbuf for linkage in next iteration */ r = p; /* remaining length to be allocated */ rem_len = length - p->len; /* any remaining pbufs to be allocated? */ while (rem_len > 0) { q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccesfully */ return NULL; } q->type = type; q->flags = 0; q->next = NULL; /* make previous pbuf point to this pbuf */ r->next = q; /* set total length of this pbuf and next in chain */ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); q->tot_len = (u16_t)rem_len; /* this pbuf length is pool size, unless smaller sized tail */ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); q->ref = 1; /* calculate remaining length to be allocated */ rem_len -= q->len; /* remember this pbuf for linkage in next iteration */ r = q; } /* end of chain */ /*r->next = NULL;*/ break; case PBUF_RAM: /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == NULL) { return NULL; } /* Set up internal structure of the pbuf. */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); p->len = p->tot_len = length; p->next = NULL; p->type = type; p->eb = NULL; LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; #ifdef EBUF_LWIP case PBUF_ESF_RX: #endif /* ESF_LWIP */ /* pbuf references existing (non-volatile static constant) ROM payload? */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ p = (struct pbuf *)memp_malloc(MEMP_PBUF); if (p == NULL) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } /* set reference count */ p->ref = 1; /* set flags */ p->flags = 0; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/** * 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; u16_t clen; u8_t valid = 1; struct pbuf *q; IP6_FRAG_STATS_INC(ip6_frag.recv); if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) { /* ip6_frag_hdr must be in the first pbuf, not chained */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } 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); len -= (u16_t)(((u8_t*)p->payload - (const 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(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) && ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(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). */ #if IPV6_FRAG_COPYHEADER MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); #else /* IPV6_FRAG_COPYHEADER */ /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; #endif /* IPV6_FRAG_COPYHEADER */ /* 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. */ #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_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 */ iprh = (struct ip6_reass_helper *)p->payload; 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) { #if IPV6_FRAG_COPYHEADER if (iprh->next_pbuf != NULL) { MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); } #else /* IPV6_FRAG_COPYHEADER */ /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; #endif /* IPV6_FRAG_COPYHEADER */ } /* 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) { struct pbuf* 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_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_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM)); 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; } #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */ u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM)); LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN); MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN); #else iphdr_ptr = ipr->iphdr; #endif /* Adjust datagram length by adding header lengths. */ ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr) + IP6_FRAG_HLEN - IP6_HLEN); /* Set payload length in ip header. */ iphdr_ptr->_plen = lwip_htons(ipr->datagram_len); /* Get the first 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; } 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. This cannot fail since we already checked when receiving this fragment. */ 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: pbuf_free(p); return NULL; }
/** * @ingroup pbuf * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * The actual memory allocated for the pbuf is determined by the * layer at which the pbuf is allocated and the requested size * (from the size parameter). * * @param layer header size * @param length size of the pbuf's payload * @param type this parameter decides how and where the pbuf * should be allocated as follows: * * - PBUF_RAM: buffer memory for pbuf is allocated as one large * chunk. This includes protocol headers as well. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * the ROM pbuf. It is assumed that the memory used is really * similar to ROM in that it is immutable and will not be * changed. Memory which is dynamic should generally not * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. * - PBUF_REF: no buffer memory is allocated for the pbuf, even for * protocol headers. It is assumed that the pbuf is only * being used in a single thread. If the pbuf gets queued, * then pbuf_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p; u16_t offset = (u16_t)layer; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); switch (type) { case PBUF_REF: /* fall through */ case PBUF_ROM: p = pbuf_alloc_reference(NULL, length, type); break; case PBUF_POOL: { struct pbuf *q, *last; u16_t rem_len; /* remaining length */ p = NULL; last = NULL; rem_len = length; do { u16_t qlen; q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ if (p) { pbuf_free(p); } /* bail out unsuccessfully */ return NULL; } qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset))); pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)), rem_len, qlen, type, 0); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); if (p == NULL) { /* allocated head of pbuf chain (into p) */ p = q; } else { /* make previous pbuf point to this pbuf */ last->next = q; } last = q; rem_len = (u16_t)(rem_len - qlen); offset = 0; } while (rem_len > 0); break; } case PBUF_RAM: { u16_t payload_len = (u16_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length)); mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len); /* bug #50040: Check for integer overflow when calculating alloc_len */ if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) || (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) { return NULL; } /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf *)mem_malloc(alloc_len); if (p == NULL) { return NULL; } pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)), length, length, type, 0); LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; } default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/** * Receive callback function for RAW netconns. * Doesn't 'eat' the packet, only references it and sends it to * conn->recvmbox * * @see raw.h (struct raw_pcb.recv) for parameters and return value */ static u8_t recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr) { struct pbuf *q; struct netbuf *buf; struct netconn *conn; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ LWIP_UNUSED_ARG(addr); conn = arg; #if LWIP_SO_RCVBUF SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) && ((recv_avail + (int)(p->tot_len)) <= conn->recv_bufsize)) { #else /* LWIP_SO_RCVBUF */ if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) { #endif /* LWIP_SO_RCVBUF */ /* copy the whole packet into new pbufs */ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if(q != NULL) { if (pbuf_copy(q, p) != ERR_OK) { pbuf_free(q); q = NULL; } } if(q != NULL) { buf = memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(q); return 0; } buf->p = q; buf->ptr = q; buf->addr = &(((struct ip_hdr*)(q->payload))->src); buf->port = pcb->protocol; if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return 0; } else { SYS_ARCH_INC(conn->recv_avail, q->tot_len); /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_len); } } } return 0; /* do not eat the packet */ } #endif /* LWIP_RAW*/ #if LWIP_UDP /** * Receive callback function for UDP netconns. * Posts the packet to conn->recvmbox or deletes it on memory error. * * @see udp.h (struct udp_pcb.recv) for parameters */ static void recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) { struct netbuf *buf; struct netconn *conn; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_udp must have an argument", arg != NULL); conn = arg; LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); #if LWIP_SO_RCVBUF SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL) || ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { #else /* LWIP_SO_RCVBUF */ if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { #endif /* LWIP_SO_RCVBUF */ pbuf_free(p); return; } buf = memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(p); return; } else { buf->p = p; buf->ptr = p; buf->addr = addr; buf->port = port; #if LWIP_NETBUF_RECVINFO { const struct ip_hdr* iphdr = ip_current_header(); /* get the UDP header - always in the first pbuf, ensured by udp_input */ const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); buf->toaddr = (struct ip_addr*)&iphdr->dest; buf->toport = udphdr->dest; } #endif /* LWIP_NETBUF_RECVINFO */ } if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return; } else { SYS_ARCH_INC(conn->recv_avail, p->tot_len); /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len); } } #endif /* LWIP_UDP */ #if LWIP_TCP /** * Receive callback function for TCP netconns. * Posts the packet to conn->recvmbox, but doesn't delete it on errors. * * @see tcp.h (struct tcp_pcb.recv) for parameters and return value */ static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct netconn *conn; u16_t len; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); conn = arg; LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { return ERR_VAL; } conn->err = err; if (p != NULL) { len = p->tot_len; SYS_ARCH_INC(conn->recv_avail, len); } else { len = 0; } if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) { return ERR_MEM; } else { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } return ERR_OK; } /** * Poll callback function for TCP netconns. * Wakes up an application thread that waits for a connection to close * or data to be sent. The application thread then takes the * appropriate action to go on. * * Signals the conn->sem. * netconn_close waits for conn->sem if closing failed. * * @see tcp.h (struct tcp_pcb.poll) for parameters and return value */ static err_t poll_tcp(void *arg, struct tcp_pcb *pcb) { struct netconn *conn = arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } return ERR_OK; } /** * Sent callback function for TCP netconns. * Signals the conn->sem and calls API_EVENT. * netconn_write waits for conn->sem if send buffer is low. * * @see tcp.h (struct tcp_pcb.sent) for parameters and return value */ static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) { struct netconn *conn = arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } if (conn) { if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) { API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); } } return ERR_OK; } /** * Error callback function for TCP netconns. * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. * The application thread has then to decide what to do. * * @see tcp.h (struct tcp_pcb.err) for parameters */ static void err_tcp(void *arg, err_t err) { struct netconn *conn; conn = arg; LWIP_ASSERT("conn != NULL", (conn != NULL)); conn->pcb.tcp = NULL; conn->err = err; if (conn->recvmbox != SYS_MBOX_NULL) { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); sys_mbox_post(conn->recvmbox, NULL); } if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) { conn->state = NETCONN_NONE; sys_sem_signal(conn->op_completed); } if (conn->acceptmbox != SYS_MBOX_NULL) { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); sys_mbox_post(conn->acceptmbox, NULL); } if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) { /* calling do_writemore/do_close_internal is not necessary since the pcb has already been deleted! */ conn->state = NETCONN_NONE; /* wake up the waiting task */ sys_sem_signal(conn->op_completed); } } /** * Setup a tcp_pcb with the correct callback function pointers * and their arguments. * * @param conn the TCP netconn to setup */ static void setup_tcp(struct netconn *conn) { struct tcp_pcb *pcb; pcb = conn->pcb.tcp; tcp_arg(pcb, conn); tcp_recv(pcb, recv_tcp); tcp_sent(pcb, sent_tcp); tcp_poll(pcb, poll_tcp, 4); tcp_err(pcb, err_tcp); } /** * Accept callback function for TCP netconns. * Allocates a new netconn and posts that to conn->acceptmbox. * * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value */ static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) { struct netconn *newconn; struct netconn *conn; #if API_MSG_DEBUG #if TCP_DEBUG tcp_debug_print_state(newpcb->state); #endif /* TCP_DEBUG */ #endif /* API_MSG_DEBUG */ conn = (struct netconn *)arg; LWIP_ERROR("accept_function: invalid conn->acceptmbox", conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;); /* We have to set the callback here even though * the new socket is unknown. conn->socket is marked as -1. */ newconn = netconn_alloc(conn->type, conn->callback); if (newconn == NULL) { return ERR_MEM; } newconn->pcb.tcp = newpcb; setup_tcp(newconn); newconn->err = err; if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { /* When returning != ERR_OK, the connection is aborted in tcp_process(), so do nothing here! */ newconn->pcb.tcp = NULL; netconn_free(newconn); return ERR_MEM; } else { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); } return ERR_OK; } #endif /* LWIP_TCP */ /** * Create a new pcb of a specific type. * Called from do_newconn(). * * @param msg the api_msg_msg describing the connection type * @return msg->conn->err, but the return value is currently ignored */ static err_t pcb_new(struct api_msg_msg *msg) { msg->conn->err = ERR_OK; LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); /* Allocate a PCB for this connection */ switch(NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: msg->conn->pcb.raw = raw_new(msg->msg.n.proto); if(msg->conn->pcb.raw == NULL) { msg->conn->err = ERR_MEM; break; } raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: msg->conn->pcb.udp = udp_new(); if(msg->conn->pcb.udp == NULL) { msg->conn->err = ERR_MEM; break; } #if LWIP_UDPLITE if (msg->conn->type==NETCONN_UDPLITE) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); } #endif /* LWIP_UDPLITE */ if (msg->conn->type==NETCONN_UDPNOCHKSUM) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); } udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: msg->conn->pcb.tcp = tcp_new(); if(msg->conn->pcb.tcp == NULL) { msg->conn->err = ERR_MEM; break; } setup_tcp(msg->conn); break; #endif /* LWIP_TCP */ default: /* Unsupported netconn type, e.g. protocol disabled */ msg->conn->err = ERR_VAL; break; } return msg->conn->err; }
/** * 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; }