Skip to content

Instantly share code, notes, and snippets.

@swanav
Created January 17, 2019 11:22
Show Gist options
  • Save swanav/d1e95fc62aa5c15b65c788d5d8a23e9f to your computer and use it in GitHub Desktop.
Save swanav/d1e95fc62aa5c15b65c788d5d8a23e9f to your computer and use it in GitHub Desktop.
Diff file for esp-lwip vs lwip_nat
This file has been truncated, but you can view the full file.
diff --git a/src/Filelists.mk b/../lwip_nat/src/Filelists.mk
index 7d30bb8f..513539e6 100644
--- a/src/Filelists.mk
+++ b/../lwip_nat/src/Filelists.mk
@@ -42,6 +42,8 @@ COREFILES=$(LWIPDIR)/core/init.c \
$(LWIPDIR)/core/raw.c \
$(LWIPDIR)/core/stats.c \
$(LWIPDIR)/core/sys.c \
+ $(LWIPDIR)/core/altcp.c \
+ $(LWIPDIR)/core/altcp_tcp.c \
$(LWIPDIR)/core/tcp.c \
$(LWIPDIR)/core/tcp_in.c \
$(LWIPDIR)/core/tcp_out.c \
@@ -71,6 +73,7 @@ CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \
APIFILES=$(LWIPDIR)/api/api_lib.c \
$(LWIPDIR)/api/api_msg.c \
$(LWIPDIR)/api/err.c \
+ $(LWIPDIR)/api/if_api.c \
$(LWIPDIR)/api/netbuf.c \
$(LWIPDIR)/api/netdb.c \
$(LWIPDIR)/api/netifapi.c \
@@ -79,6 +82,7 @@ APIFILES=$(LWIPDIR)/api/api_lib.c \
# NETIFFILES: Files implementing various generic network interface functions
NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
+ $(LWIPDIR)/netif/bridgeif.c \
$(LWIPDIR)/netif/slipif.c
# SIXLOWPAN: 6LoWPAN
@@ -136,6 +140,8 @@ SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
$(LWIPDIR)/apps/snmp/snmp_mib2_system.c \
$(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
$(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
+ $(LWIPDIR)/apps/snmp/snmp_snmpv2_framework.c \
+ $(LWIPDIR)/apps/snmp/snmp_snmpv2_usm.c \
$(LWIPDIR)/apps/snmp/snmp_msg.c \
$(LWIPDIR)/apps/snmp/snmpv3.c \
$(LWIPDIR)/apps/snmp/snmp_netconn.c \
@@ -144,9 +150,7 @@ SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
$(LWIPDIR)/apps/snmp/snmp_scalar.c \
$(LWIPDIR)/apps/snmp/snmp_table.c \
$(LWIPDIR)/apps/snmp/snmp_threadsync.c \
- $(LWIPDIR)/apps/snmp/snmp_traps.c \
- $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c \
- $(LWIPDIR)/apps/snmp/snmpv3_dummy.c
+ $(LWIPDIR)/apps/snmp/snmp_traps.c
# HTTPDFILES: HTTP server
HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \
@@ -155,6 +159,9 @@ HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \
# LWIPERFFILES: IPERF server
LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
+# SMTPFILES: SMTP client
+SMTPFILES=$(LWIPDIR)/apps/smtp/smtp.c
+
# SNTPFILES: SNTP client
SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c
@@ -170,12 +177,19 @@ TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
# MQTTFILES: MQTT client files
MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c
+# MBEDTLS_FILES: MBEDTLS related files of lwIP rep
+MBEDTLS_FILES=$(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls.c \
+ $(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls_mem.c \
+ $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c
+
# LWIPAPPFILES: All LWIP APPs
LWIPAPPFILES=$(SNMPFILES) \
$(HTTPDFILES) \
$(LWIPERFFILES) \
+ $(SMTPFILES) \
$(SNTPFILES) \
$(MDNSFILES) \
$(NETBIOSNSFILES) \
$(TFTPFILES) \
- $(MQTTFILES)
+ $(MQTTFILES) \
+ $(MBEDTLS_FILES)
diff --git a/src/api/api_lib.c b/../lwip_nat/src/api/api_lib.c
index 227644a7..f93a9b78 100644
--- a/src/api/api_lib.c
+++ b/../lwip_nat/src/api/api_lib.c
@@ -88,16 +88,12 @@ static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
* @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
*/
-#if ESP_LWIP
-static err_t ESP_IRAM_ATTR
-#else
static err_t
-#endif
netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
{
err_t err;
-#if LWIP_DEBUG
+#ifdef LWIP_DEBUG
/* catch functions that don't set err */
apimsg->err = ERR_VAL;
#endif /* LWIP_DEBUG */
@@ -147,9 +143,6 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
sys_sem_free(&conn->op_completed);
#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
-#if ESP_THREAD_SAFE
- sys_mbox_set_owner(&conn->recvmbox, NULL);
-#endif
sys_mbox_free(&conn->recvmbox);
memp_free(MEMP_NETCONN, conn);
API_MSG_VAR_FREE(msg);
@@ -160,18 +153,6 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
return conn;
}
-#if ESP_THREAD_SAFE
-static inline bool is_created_by_socket(struct netconn *conn)
-{
-#if LWIP_SOCKET
- if (conn && (conn->socket >= 0)) {
- return true;
- }
-#endif
- return false;
-}
-#endif
-
/**
* @ingroup netconn_common
* Close a netconn 'connection' and free its resources.
@@ -211,14 +192,7 @@ netconn_delete(struct netconn *conn)
return err;
}
-#if ESP_THREAD_SAFE
- if (is_created_by_socket(conn) == false) {
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n"));
- netconn_free(conn);
- }
-#else
netconn_free(conn);
-#endif
return ERR_OK;
}
@@ -307,6 +281,32 @@ netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
return err;
}
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific interface and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param if_idx the local interface index to bind the netconn to
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind_if(struct netconn *conn, u8_t if_idx)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_bind_if: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.if_idx = if_idx;
+ err = netconn_apimsg(lwip_netconn_do_bind_if, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
/**
* @ingroup netconn_common
* Connect a netconn to a specific remote IP address and port.
@@ -414,6 +414,7 @@ err_t
netconn_accept(struct netconn *conn, struct netconn **new_conn)
{
#if LWIP_TCP
+ err_t err;
void *accept_ptr;
struct netconn *newconn;
#if TCP_LISTEN_BACKLOG
@@ -424,52 +425,66 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
*new_conn = NULL;
LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
- if (ERR_IS_FATAL(conn->last_err)) {
- /* don't recv on fatal errors: this might block the application task
+ /* NOTE: Although the opengroup spec says a pending error shall be returned to
+ send/recv/getsockopt(SO_ERROR) only, we return it for listening
+ connections also, to handle embedded-system errors */
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
+ /* don't accept if closed: this might block the application task
waiting on acceptmbox forever! */
- return conn->last_err;
+ return ERR_CLSD;
}
if (!sys_mbox_valid(&conn->acceptmbox)) {
return ERR_CLSD;
}
#if TCP_LISTEN_BACKLOG
+ /* need to allocate API message here so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
API_MSG_VAR_ALLOC(msg);
#endif /* TCP_LISTEN_BACKLOG */
+ if (netconn_is_nonblocking(conn)) {
+ if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return ERR_WOULDBLOCK;
+ }
+ } else {
#if LWIP_SO_RCVTIMEO
- if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
#if TCP_LISTEN_BACKLOG
- API_MSG_VAR_FREE(msg);
+ API_MSG_VAR_FREE(msg);
#endif /* TCP_LISTEN_BACKLOG */
- return ERR_TIMEOUT;
- }
+ return ERR_TIMEOUT;
+ }
#else
- sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
+ sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
#endif /* LWIP_SO_RCVTIMEO*/
- newconn = (struct netconn *)accept_ptr;
+ }
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
- if (accept_ptr == &netconn_aborted) {
- /* a connection has been aborted: out of pcbs or out of netconns during accept */
- /* @todo: set netconn error, but this would be fatal and thus block further accepts */
+ if (lwip_netconn_is_err_msg(accept_ptr, &err)) {
+ /* a connection has been aborted: e.g. out of pcbs or out of netconns during accept */
#if TCP_LISTEN_BACKLOG
API_MSG_VAR_FREE(msg);
#endif /* TCP_LISTEN_BACKLOG */
- return ERR_ABRT;
+ return err;
}
- if (newconn == NULL) {
+ if (accept_ptr == NULL) {
/* connection has been aborted */
- /* in this special case, we set the netconn error from application thread, as
- on a ready-to-accept listening netconn, there should not be anything running
- in tcpip_thread */
- NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
#if TCP_LISTEN_BACKLOG
API_MSG_VAR_FREE(msg);
#endif /* TCP_LISTEN_BACKLOG */
return ERR_CLSD;
}
+ newconn = (struct netconn *)accept_ptr;
#if TCP_LISTEN_BACKLOG
/* Let the stack know that we have accepted the connection. */
API_MSG_VAR_REF(msg).conn = newconn;
@@ -491,113 +506,75 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
/**
* @ingroup netconn_common
* Receive data: actual implementation that doesn't care whether pbuf or netbuf
- * is received
+ * is received (this is internal, it's just here for describing common errors)
*
* @param conn the netconn from which to receive data
* @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
+ * ERR_CONN if not connected
+ * ERR_CLSD if TCP connection has been closed
+ * ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
+ * ERR_TIMEOUT if the netconn has a receive timeout and no data was received
*/
-#if ESP_LWIP
-static err_t ESP_IRAM_ATTR
-#else
static err_t
-#endif
-netconn_recv_data(struct netconn *conn, void **new_buf)
+netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
{
void *buf = NULL;
u16_t len;
-#if LWIP_TCP
- API_MSG_VAR_DECLARE(msg);
-#if LWIP_MPU_COMPATIBLE
- msg = NULL;
-#endif
-#endif /* LWIP_TCP */
LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
*new_buf = NULL;
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
-#if LWIP_TCP
-#if (LWIP_UDP || LWIP_RAW)
- if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
-#endif /* (LWIP_UDP || LWIP_RAW) */
- {
- if (!sys_mbox_valid(&conn->recvmbox)) {
- /* This happens when calling this function after receiving FIN */
- return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
+
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ err_t err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
}
+ return ERR_CONN;
}
-#endif /* LWIP_TCP */
- LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
-
- if (ERR_IS_FATAL(conn->last_err)) {
- /* don't recv on fatal errors: this might block the application task
- waiting on recvmbox forever! */
- /* @todo: this does not allow us to fetch data that has been put into recvmbox
- before the fatal error occurred - is that a problem? */
- return conn->last_err;
- }
-#if LWIP_TCP
-#if (LWIP_UDP || LWIP_RAW)
- if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
-#endif /* (LWIP_UDP || LWIP_RAW) */
- {
- API_MSG_VAR_ALLOC(msg);
- }
-#endif /* LWIP_TCP */
+ if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
+ (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
+ if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
+ err_t err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
+ return ERR_CONN;
+ }
+ return ERR_WOULDBLOCK;
+ }
+ } else {
#if LWIP_SO_RCVTIMEO
- if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
-#if LWIP_TCP
-#if (LWIP_UDP || LWIP_RAW)
- if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
-#endif /* (LWIP_UDP || LWIP_RAW) */
- {
- API_MSG_VAR_FREE(msg);
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ return ERR_TIMEOUT;
}
-#endif /* LWIP_TCP */
- return ERR_TIMEOUT;
- }
#else
- sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
#endif /* LWIP_SO_RCVTIMEO*/
+ }
#if LWIP_TCP
#if (LWIP_UDP || LWIP_RAW)
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
#endif /* (LWIP_UDP || LWIP_RAW) */
{
- /* Let the stack know that we have taken the data. */
- /* @todo: Speedup: Don't block and wait for the answer here
- (to prevent multiple thread-switches). */
-#if ESP_AUTO_RECV
- if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
-#endif
- API_MSG_VAR_REF(msg).conn = conn;
- if (buf != NULL) {
- API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len;
- } else {
- API_MSG_VAR_REF(msg).msg.r.len = 1;
- }
-
- /* don't care for the return value of lwip_netconn_do_recv */
- netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
- API_MSG_VAR_FREE(msg);
-#if ESP_AUTO_RECV
- }
-#endif
-
- /* If we are closed, we indicate that we no longer wish to use the socket */
- if (buf == NULL) {
- API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
- if (conn->pcb.ip == NULL) {
- /* race condition: RST during recv */
- return conn->last_err == ERR_OK ? ERR_RST : conn->last_err;
+ err_t err;
+ /* Check if this is an error message or a pbuf */
+ if (lwip_netconn_is_err_msg(buf, &err)) {
+ /* new_buf has been zeroed above already */
+ if (err == ERR_CLSD) {
+ /* connection closed translates to ERR_OK with *new_buf == NULL */
+ return ERR_OK;
}
- /* RX side is closed, so deallocate the recvmbox */
- netconn_close_shutdown(conn, NETCONN_SHUT_RD);
- /* Don' store ERR_CLSD as conn->err since we are only half-closed */
- return ERR_CLSD;
+ return err;
}
len = ((struct pbuf *)buf)->tot_len;
}
@@ -607,12 +584,6 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
#if (LWIP_UDP || LWIP_RAW)
{
-#if ESP_THREAD_SAFE
- if (buf == NULL){
- API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
- return ERR_CLSD;
- }
-#endif
LWIP_ASSERT("buf != NULL", buf != NULL);
len = netbuf_len((struct netbuf*)buf);
}
@@ -631,6 +602,92 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
return ERR_OK;
}
+#if LWIP_TCP
+static err_t
+netconn_tcp_recvd_msg(struct netconn *conn, size_t len, struct api_msg* msg)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ msg->conn = conn;
+ msg->msg.r.len = len;
+
+ return netconn_apimsg(lwip_netconn_do_recv, msg);
+}
+
+err_t
+netconn_tcp_recvd(struct netconn *conn, size_t len)
+{
+ err_t err;
+ API_MSG_VAR_DECLARE(msg);
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+ return err;
+}
+
+static err_t
+netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+ err_t err;
+ struct pbuf *buf;
+#if LWIP_TCP
+ API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+ msg = NULL;
+#endif
+#endif /* LWIP_TCP */
+
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ /* This happens when calling this function after receiving FIN */
+ return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
+ }
+
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ /* need to allocate API message here so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+ API_MSG_VAR_ALLOC(msg);
+ }
+
+ err = netconn_recv_data(conn, (void **)new_buf, apiflags);
+ if (err != ERR_OK) {
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ API_MSG_VAR_FREE(msg);
+ }
+ return err;
+ }
+ buf = *new_buf;
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ /* Let the stack know that we have taken the data. */
+ u16_t len = buf ? buf->tot_len : 1;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ /* @todo: this should really be fixed, e.g. by retrying in poll on error */
+ netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+ }
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ if (conn->pcb.ip == NULL) {
+ /* race condition: RST during recv */
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ return err;
+ }
+ return ERR_RST;
+ }
+ /* RX side is closed, so deallocate the recvmbox */
+ netconn_close_shutdown(conn, NETCONN_SHUT_RD);
+ /* Don' store ERR_CLSD as conn->err since we are only half-closed */
+ return ERR_CLSD;
+ }
+ return err;
+}
+
/**
* @ingroup netconn_tcp
* Receive data (in form of a pbuf) from a TCP netconn
@@ -638,20 +695,76 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
* @param conn the netconn from which to receive data
* @param new_buf pointer where a new pbuf is stored when received data
* @return ERR_OK if data has been received, an error code otherwise (timeout,
- * memory error or another error)
+ * memory error or another error, @see netconn_recv_data)
* ERR_ARG if conn is not a TCP netconn
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
{
- LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data_tcp(conn, new_buf, 0);
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error, @see netconn_recv_data)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
- return netconn_recv_data(conn, (void **)new_buf);
+ return netconn_recv_data_tcp(conn, new_buf, apiflags);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf, 0);
+}
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
+{
+ LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf, apiflags);
}
/**
@@ -663,11 +776,7 @@ netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
netconn_recv(struct netconn *conn, struct netbuf **new_buf)
{
#if LWIP_TCP
@@ -692,7 +801,7 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
return ERR_MEM;
}
- err = netconn_recv_data(conn, (void **)&p);
+ err = netconn_recv_data_tcp(conn, &p, 0);
if (err != ERR_OK) {
memp_free(MEMP_NETBUF, buf);
return err;
@@ -713,46 +822,11 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
{
#if (LWIP_UDP || LWIP_RAW)
- return netconn_recv_data(conn, (void **)new_buf);
+ return netconn_recv_data(conn, (void **)new_buf, 0);
#endif /* (LWIP_UDP || LWIP_RAW) */
}
}
-#if ESP_AUTO_RECV
-/**
- * TCP: update the receive window: by calling this, the application
- * tells the stack that it has processed data and is able to accept
- * new data.
- * ATTENTION: use with care, this is mainly used for sockets!
- * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
- *
- * @param conn the netconn for which to update the receive window
- * @param length amount of data processed (ATTENTION: this must be accurate!)
- */
-void
-netconn_recved(struct netconn *conn, u32_t length)
-{
-#if LWIP_TCP
- if ((conn != NULL) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) &&
- (netconn_get_noautorecved(conn))) {
- API_MSG_VAR_DECLARE(msg);
- /* Let the stack know that we have taken the data. */
- /* TODO: Speedup: Don't block and wait for the answer here
- (to prevent multiple thread-switches). */
- API_MSG_VAR_ALLOC_RETURN_NULL(msg);
- API_MSG_VAR_REF(msg).conn = conn;
- API_MSG_VAR_REF(msg).msg.r.len = length;
- /* don't care for the return value of lwip_netconn_do_recv */
- netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
- API_MSG_VAR_FREE(msg);
- }
-#else /* LWIP_TCP */
- LWIP_UNUSED_ARG(conn);
- LWIP_UNUSED_ARG(length);
-#endif /* LWIP_TCP */
-}
-#endif
-
/**
* @ingroup netconn_udp
* Send data (in form of a netbuf) to a specific remote IP address and port.
@@ -783,11 +857,7 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr,
* @param buf a netbuf containing the data to send
* @return ERR_OK if data was sent, any other err_t on error
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
netconn_send(struct netconn *conn, struct netbuf *buf)
{
API_MSG_VAR_DECLARE(msg);
@@ -820,23 +890,41 @@ netconn_send(struct netconn *conn, struct netbuf *buf)
* @param bytes_written pointer to a location that receives the number of written bytes
* @return ERR_OK if data was sent, any other err_t on error
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
u8_t apiflags, size_t *bytes_written)
+{
+ struct netvector vector;
+ vector.ptr = dataptr;
+ vector.len = size;
+ return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
+}
+
+/**
+ * Send vectorized data atomically over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param vectors array of vectors containing data to send
+ * @param vectorcnt number of vectors in the array
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
+ u8_t apiflags, size_t *bytes_written)
{
API_MSG_VAR_DECLARE(msg);
err_t err;
u8_t dontblock;
+ size_t size;
+ int i;
LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
- if (size == 0) {
- return ERR_OK;
- }
dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
#if LWIP_SO_SNDTIMEO
if (conn->send_timeout != 0) {
@@ -849,12 +937,37 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
return ERR_VAL;
}
+ /* sum up the total size */
+ size = 0;
+ for (i = 0; i < vectorcnt; i++) {
+ size += vectors[i].len;
+ if (size < vectors[i].len) {
+ /* overflow */
+ return ERR_VAL;
+ }
+ }
+ if (size == 0) {
+ return ERR_OK;
+ } else if (size > SSIZE_MAX) {
+ ssize_t limited;
+ /* this is required by the socket layer (cannot send full size_t range) */
+ if (!bytes_written) {
+ return ERR_VAL;
+ }
+ /* limit the amount of data to send */
+ limited = SSIZE_MAX;
+ size = (size_t)limited;
+ }
+
API_MSG_VAR_ALLOC(msg);
/* non-blocking write sends as much */
API_MSG_VAR_REF(msg).conn = conn;
- API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr;
+ API_MSG_VAR_REF(msg).msg.w.vector = vectors;
+ API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
+ API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
API_MSG_VAR_REF(msg).msg.w.len = size;
+ API_MSG_VAR_REF(msg).msg.w.offset = 0;
#if LWIP_SO_SNDTIMEO
if (conn->send_timeout != 0) {
/* get the time we started, which is later compared to
@@ -869,13 +982,14 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
but if it is, this is done inside api_msg.c:do_write(), so we can use the
non-blocking version here. */
err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
- if ((err == ERR_OK) && (bytes_written != NULL)) {
- if (dontblock) {
- /* nonblocking write: maybe the data has been sent partly */
- *bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
- } else {
- /* blocking call succeeded: all data has been sent if it */
- *bytes_written = size;
+ if (err == ERR_OK) {
+ if (bytes_written != NULL) {
+ *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
+ }
+ /* for blocking, check all requested bytes were written, NOTE: send_timeout is
+ treated as dontblock (see dontblock assignment above) */
+ if (!dontblock) {
+ LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
}
}
API_MSG_VAR_FREE(msg);
@@ -934,6 +1048,28 @@ netconn_close(struct netconn *conn)
return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
}
+/**
+ * @ingroup netconn_common
+ * Get and reset pending error on a netconn
+ *
+ * @param conn the netconn to get the error from
+ * @return and pending error or ERR_OK if no error was pending
+ */
+err_t
+netconn_err(struct netconn *conn)
+{
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+ SYS_ARCH_PROTECT(lev);
+ err = conn->pending_err;
+ conn->pending_err = ERR_OK;
+ SYS_ARCH_UNPROTECT(lev);
+ return err;
+}
+
/**
* @ingroup netconn_tcp
* Shut down one or both sides of a TCP netconn (doesn't delete it).
@@ -946,7 +1082,7 @@ netconn_close(struct netconn *conn)
err_t
netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
{
- return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+ return netconn_close_shutdown(conn, (u8_t)((shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)));
}
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
@@ -1054,18 +1190,14 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr)
}
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
- cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
- if (cberr != ERR_OK) {
+ cberr = tcpip_send_msg_wait_sem(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg), API_EXPR_REF(API_VAR_REF(msg).sem));
#if !LWIP_NETCONN_SEM_PER_THREAD
- sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+ sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+ if (cberr != ERR_OK) {
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
return cberr;
}
- sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
-#if !LWIP_NETCONN_SEM_PER_THREAD
- sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
-#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
#if LWIP_MPU_COMPATIBLE
*addr = msg->addr;
diff --git a/src/api/api_msg.c b/../lwip_nat/src/api/api_msg.c
index 6d689111..09e91ec8 100644
--- a/src/api/api_msg.c
+++ b/../lwip_nat/src/api/api_msg.c
@@ -60,10 +60,10 @@
#define NETCONN_TCP_POLL_INTERVAL 2
#define SET_NONBLOCKING_CONNECT(conn, val) do { if (val) { \
- (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+ netconn_set_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); \
} else { \
- (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
-#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+ netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
/* forward declarations */
#if LWIP_TCP
@@ -75,28 +75,58 @@
#define WRITE_DELAYED_PARAM
#endif /* LWIP_TCPIP_CORE_LOCKING */
static err_t lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM);
-
-#if ESP_LWIP
-#define SIG_CLOSE_PARAM , bool sig_close
-#define SIG_CLOSE_TRUE true
-#define SIG_CLOSE_FALSE false
-static err_t lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM SIG_CLOSE_PARAM);
-#else
static err_t lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM);
#endif
-#endif
-
#if LWIP_TCPIP_CORE_LOCKING
-#define TCPIP_APIMSG_ACK(m) NETCONN_SET_SAFE_ERR((m)->conn, (m)->err)
+#define TCPIP_APIMSG_ACK(m)
#else /* LWIP_TCPIP_CORE_LOCKING */
-#define TCPIP_APIMSG_ACK(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#define TCPIP_APIMSG_ACK(m) do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
#endif /* LWIP_TCPIP_CORE_LOCKING */
#if LWIP_TCP
-u8_t netconn_aborted;
+const u8_t netconn_aborted = 0;
+const u8_t netconn_reset = 0;
+const u8_t netconn_closed = 0;
+
+/** Translate an error to a unique void* passed via an mbox */
+static void*
+lwip_netconn_err_to_msg(err_t err)
+{
+ switch(err)
+ {
+ case ERR_ABRT:
+ return LWIP_CONST_CAST(void*, &netconn_aborted);
+ case ERR_RST:
+ return LWIP_CONST_CAST(void*, &netconn_reset);
+ case ERR_CLSD:
+ return LWIP_CONST_CAST(void*, &netconn_closed);
+ default:
+ LWIP_ASSERT("unhandled error", err == ERR_OK);
+ return NULL;
+ }
+}
+
+int
+lwip_netconn_is_err_msg(void *msg, err_t *err)
+{
+ LWIP_ASSERT("err != NULL", err != NULL);
+
+ if (msg == &netconn_aborted) {
+ *err = ERR_ABRT;
+ return 1;
+ } else if (msg == &netconn_reset) {
+ *err = ERR_RST;
+ return 1;
+ } else if (msg == &netconn_closed) {
+ *err = ERR_CLSD;
+ return 1;
+ }
+ return 0;
+}
#endif /* LWIP_TCP */
+
#if LWIP_RAW
/**
* Receive callback function for RAW netconns.
@@ -148,7 +178,6 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
len = q->tot_len;
if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
- ESP_STATS_DROP_INC(esp.rx_rawmbox_post_fail);
netbuf_delete(buf);
return 0;
} else {
@@ -206,18 +235,6 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
return;
}
-#if ESP_LWIP
-#if LWIP_IPV6
- /* This should be eventually moved to a flag on the UDP PCB, and this drop can happen
- more correctly in udp_input(). This will also allow icmp_dest_unreach() to be called. */
- if (conn->flags & NETCONN_FLAG_IPV6_V6ONLY && !ip_current_is_v6()) {
- LWIP_DEBUGF(API_MSG_DEBUG, ("recv_udp: Dropping IPv4 UDP packet (IPv6-only socket)"));
- pbuf_free(p);
- return;
- }
-#endif
-#endif
-
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
if (buf == NULL) {
pbuf_free(p);
@@ -228,12 +245,10 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
ip_addr_set(&buf->addr, addr);
buf->port = port;
#if LWIP_NETBUF_RECVINFO
- {
+ if (conn->flags & NETCONN_FLAG_PKTINFO) {
/* get the UDP header - always in the first pbuf, ensured by udp_input */
const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr();
-#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;
}
@@ -242,7 +257,6 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
len = p->tot_len;
if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
- ESP_STATS_DROP_INC(esp.rx_udpmbox_post_fail);
netbuf_delete(buf);
return;
} else {
@@ -267,10 +281,12 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct netconn *conn;
u16_t len;
+ void *msg;
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);
+ LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
conn = (struct netconn *)arg;
if (conn == NULL) {
@@ -290,19 +306,15 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
using recv_avail since that could break the connection
(data is already ACKed) */
- /* don't overwrite fatal errors! */
- if (err != ERR_OK) {
- NETCONN_SET_SAFE_ERR(conn, err);
- }
-
if (p != NULL) {
+ msg = p;
len = p->tot_len;
} else {
+ msg = LWIP_CONST_CAST(void*, &netconn_closed);
len = 0;
}
- if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
- ESP_STATS_DROP_INC(esp.rx_tcpmbox_post_fail);
+ if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
/* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
return ERR_MEM;
} else {
@@ -331,9 +343,6 @@ static err_t
poll_tcp(void *arg, struct tcp_pcb *pcb)
{
struct netconn *conn = (struct netconn *)arg;
-#if ESP_LWIP
- bool sig_close = false;
-#endif
LWIP_UNUSED_ARG(pcb);
LWIP_ASSERT("conn != NULL", (conn != NULL));
@@ -346,14 +355,7 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
conn->current_msg->msg.sd.polls_left--;
}
#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
-#if ESP_LWIP
- /* Delay the netconn close until no one use 'conn' because close frees 'conn'*/
- if (ERR_OK == lwip_netconn_do_close_internal(conn WRITE_DELAYED, SIG_CLOSE_FALSE)) {
- sig_close = true;
- }
-#else
lwip_netconn_do_close_internal(conn WRITE_DELAYED);
-#endif
}
/* @todo: implement connect timeout here? */
@@ -363,20 +365,11 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
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;
+ netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
}
}
-#if ESP_LWIP
- if (sig_close) {
- sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
- conn->current_msg = NULL;
- sys_sem_signal(op_completed_sem);
- return ERR_ABRT;
- }
-#endif
-
return ERR_OK;
}
@@ -391,9 +384,6 @@ static err_t
sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
{
struct netconn *conn = (struct netconn *)arg;
-#if ESP_LWIP
- bool sig_close = false;
-#endif
LWIP_UNUSED_ARG(pcb);
LWIP_ASSERT("conn != NULL", (conn != NULL));
@@ -402,32 +392,16 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
if (conn->state == NETCONN_WRITE) {
lwip_netconn_do_writemore(conn WRITE_DELAYED);
} else if (conn->state == NETCONN_CLOSE) {
-#if ESP_LWIP
- /* Delay the netconn close until no one use 'conn' because close frees 'conn'*/
- if (ERR_OK == lwip_netconn_do_close_internal(conn WRITE_DELAYED, SIG_CLOSE_FALSE)) {
- sig_close = true;
- }
-#else
lwip_netconn_do_close_internal(conn WRITE_DELAYED);
-#endif
}
/* 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;
+ netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
}
-
-#if ESP_LWIP
- if (sig_close) {
- sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
- conn->current_msg = NULL;
- sys_sem_signal(op_completed_sem);
- return ERR_ABRT;
- }
-#endif
}
return ERR_OK;
@@ -445,26 +419,26 @@ err_tcp(void *arg, err_t err)
{
struct netconn *conn;
enum netconn_state old_state;
+ void *mbox_msg;
+ SYS_ARCH_DECL_PROTECT(lev);
conn = (struct netconn *)arg;
LWIP_ASSERT("conn != NULL", (conn != NULL));
+ SYS_ARCH_PROTECT(lev);
+
+ /* when err is called, the pcb is deallocated, so delete the reference */
conn->pcb.tcp = NULL;
+ /* store pending error */
+ conn->pending_err = err;
+ /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
+ conn->flags |= NETCONN_FLAG_MBOXCLOSED;
/* reset conn->state now before waking up other threads */
old_state = conn->state;
conn->state = NETCONN_NONE;
- if (old_state == NETCONN_CLOSE) {
- /* RST during close: let close return success & dealloc the netconn */
- err = ERR_OK;
- NETCONN_SET_SAFE_ERR(conn, ERR_OK);
- } else {
- /* no check since this is always fatal! */
- SYS_ARCH_SET(conn->last_err, err);
- }
-
- /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */
+ SYS_ARCH_UNPROTECT(lev);
/* Notify the user layer about a connection error. Used to signal select. */
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
@@ -473,27 +447,16 @@ err_tcp(void *arg, err_t err)
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
- /* pass NULL-message to recvmbox to wake up pending recv */
+ mbox_msg = lwip_netconn_err_to_msg(err);
+ /* pass error message to recvmbox to wake up pending recv */
if (sys_mbox_valid(&conn->recvmbox)) {
/* use trypost to prevent deadlock */
-#if ESP_STATS_DROP
- if (sys_mbox_trypost(&conn->recvmbox, NULL) != ERR_OK){
- ESP_STATS_DROP_INC(esp.err_tcp_rxmbox_post_fail);
- }
-#else
- sys_mbox_trypost(&conn->recvmbox, NULL);
-#endif
+ sys_mbox_trypost(&conn->recvmbox, mbox_msg);
}
- /* pass NULL-message to acceptmbox to wake up pending accept */
+ /* pass error message to acceptmbox to wake up pending accept */
if (sys_mbox_valid(&conn->acceptmbox)) {
/* use trypost to preven deadlock */
-#if ESP_STATS_DROP
- if (sys_mbox_trypost(&conn->acceptmbox, NULL) != ERR_OK) {
- ESP_STATS_DROP_INC(esp.err_tcp_rxmbox_post_fail);
- }
-#else
- sys_mbox_trypost(&conn->acceptmbox, NULL);
-#endif
+ sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
}
if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
@@ -507,13 +470,20 @@ err_tcp(void *arg, err_t err)
sys_sem_t* op_completed_sem;
/* set error return code */
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
- conn->current_msg->err = err;
+ if (old_state == NETCONN_CLOSE) {
+ /* let close succeed: the connection is closed after all... */
+ conn->current_msg->err = ERR_OK;
+ } else {
+ /* Write and connect fail */
+ conn->current_msg->err = err;
+ }
op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
conn->current_msg = NULL;
/* wake up the waiting task */
- NETCONN_SET_SAFE_ERR(conn, err);
sys_sem_signal(op_completed_sem);
+ } else {
+ /* @todo: test what happens for error on nonblocking connect */
}
} else {
LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
@@ -561,21 +531,23 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
if (newpcb == NULL) {
/* out-of-pcbs during connect: pass on this error to the application */
- if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+ if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
}
return ERR_VAL;
}
+ LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
+ LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
- LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
/* We have to set the callback here even though
* the new socket is unknown. newconn->socket is marked as -1. */
newconn = netconn_alloc(conn->type, conn->callback);
if (newconn == NULL) {
/* outof netconns: pass on this error to the application */
- if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+ if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
}
@@ -583,15 +555,11 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
}
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;
/* handle backlog counter */
tcp_backlog_delayed(newpcb);
if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
- ESP_STATS_DROP_INC(esp.acceptmbox_post_fail);
/* When returning != ERR_OK, the pcb is aborted in tcp_process(),
so do nothing here! */
/* remove all references to this netconn from the pcb */
@@ -603,9 +571,6 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
tcp_err(pcb, NULL);
/* remove reference from to the pcb from this netconn */
newconn->pcb.tcp = NULL;
-#if ESP_THREAD_SAFE
- sys_mbox_set_owner(&newconn->recvmbox, NULL);
-#endif
/* no need to drain since we know the recvmbox is empty. */
sys_mbox_free(&newconn->recvmbox);
sys_mbox_set_invalid(&newconn->recvmbox);
@@ -624,7 +589,7 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
* Create a new pcb of a specific type.
* Called from lwip_netconn_do_newconn().
*
- * @param msg the api_msg_msg describing the connection type
+ * @param msg the api_msg describing the connection type
*/
static void
pcb_new(struct api_msg *msg)
@@ -689,24 +654,13 @@ pcb_new(struct api_msg *msg)
if (msg->conn->pcb.ip == NULL) {
msg->err = ERR_MEM;
}
-#if ESP_LWIP
-#if LWIP_IPV4 && LWIP_IPV6
- else {
- if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
- /* Convert IPv4 PCB manually to an IPv6 PCB */
- IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip, IPADDR_TYPE_V6);
- IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_V6);
- }
- }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-#endif
}
/**
* Create a new pcb of a specific type inside a netconn.
* Called from netconn_new_with_proto_and_callback.
*
- * @param m the api_msg_msg describing the connection type
+ * @param m the api_msg describing the connection type
*/
void
lwip_netconn_do_newconn(void *m)
@@ -738,13 +692,14 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
{
struct netconn *conn;
int size;
+ u8_t init_flags = 0;
conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
if (conn == NULL) {
return NULL;
}
- conn->last_err = ERR_OK;
+ conn->pending_err = ERR_OK;
conn->type = t;
conn->pcb.tcp = NULL;
@@ -758,6 +713,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
#if LWIP_UDP
case NETCONN_UDP:
size = DEFAULT_UDP_RECVMBOX_SIZE;
+#if LWIP_NETBUF_RECVINFO
+ init_flags |= NETCONN_FLAG_PKTINFO;
+#endif /* LWIP_NETBUF_RECVINFO */
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
@@ -780,15 +738,7 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
}
#endif
-#if ESP_THREAD_SAFE
- sys_mbox_set_owner(&conn->recvmbox, conn);
-#endif
-
#if LWIP_TCP
-#if ESP_THREAD_SAFE
- /* Init acceptmbox to NULL because sys_mbox_set_invalid is implemented as empty macro */
- conn->acceptmbox = NULL;
-#endif
sys_mbox_set_invalid(&conn->acceptmbox);
#endif
conn->state = NETCONN_NONE;
@@ -799,7 +749,6 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
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;
@@ -814,7 +763,7 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
#if LWIP_SO_LINGER
conn->linger = -1;
#endif /* LWIP_SO_LINGER */
- conn->flags = 0;
+ conn->flags = init_flags;
return conn;
free_and_return:
memp_free(MEMP_NETCONN, conn);
@@ -831,21 +780,12 @@ void
netconn_free(struct netconn *conn)
{
LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
-#if !ESP_THREAD_SAFE
LWIP_ASSERT("recvmbox must be deallocated before calling this function",
!sys_mbox_valid(&conn->recvmbox));
-
#if LWIP_TCP
LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
!sys_mbox_valid(&conn->acceptmbox));
#endif /* LWIP_TCP */
-#else /* !ESP_THREAD_SAFE */
- sys_mbox_free(&conn->recvmbox);
-
-#if LWIP_TCP
- sys_mbox_free(&conn->acceptmbox);
-#endif /* LWIP TCP */
-#endif /* !ESP_THREAD_SAFE */
#if !LWIP_NETCONN_SEM_PER_THREAD
sys_sem_free(&conn->op_completed);
@@ -878,7 +818,8 @@ netconn_drain(struct netconn *conn)
while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
#if LWIP_TCP
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
- if (mem != NULL) {
+ err_t err;
+ if (!lwip_netconn_is_err_msg(mem, &err)) {
p = (struct pbuf*)mem;
/* pcb might be set to NULL already by err_tcp() */
if (conn->pcb.tcp != NULL) {
@@ -900,7 +841,8 @@ netconn_drain(struct netconn *conn)
#if LWIP_TCP
if (sys_mbox_valid(&conn->acceptmbox)) {
while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
- if (mem != &netconn_aborted) {
+ err_t err;
+ if (!lwip_netconn_is_err_msg(mem, &err)) {
struct netconn *newconn = (struct netconn *)mem;
/* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
/* pcb might be set to NULL already by err_tcp() */
@@ -928,14 +870,10 @@ netconn_drain(struct netconn *conn)
* @param conn the TCP netconn to close
*/
static err_t
-#if ESP_LWIP
-lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM SIG_CLOSE_PARAM)
-#else
lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
-#endif
{
err_t err;
- u8_t shut, shut_rx, shut_tx, close;
+ u8_t shut, shut_rx, shut_tx, shut_close;
u8_t close_finished = 0;
struct tcp_pcb* tpcb;
#if LWIP_SO_LINGER
@@ -955,20 +893,20 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
/* shutting down both ends is the same as closing
(also if RD or WR side was shut down before already) */
if (shut == NETCONN_SHUT_RDWR) {
- close = 1;
+ shut_close = 1;
} else if (shut_rx &&
((tpcb->state == FIN_WAIT_1) ||
(tpcb->state == FIN_WAIT_2) ||
(tpcb->state == CLOSING))) {
- close = 1;
+ shut_close = 1;
} else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
- close = 1;
+ shut_close = 1;
} else {
- close = 0;
+ shut_close = 0;
}
/* Set back some callback pointers */
- if (close) {
+ if (shut_close) {
tcp_arg(tpcb, NULL);
}
if (tpcb->state == LISTEN) {
@@ -982,13 +920,13 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
if (shut_tx) {
tcp_sent(tpcb, NULL);
}
- if (close) {
+ if (shut_close) {
tcp_poll(tpcb, NULL, 0);
tcp_err(tpcb, NULL);
}
}
/* Try to close the connection */
- if (close) {
+ if (shut_close) {
#if LWIP_SO_LINGER
/* check linger possibilites before calling tcp_close */
err = ERR_OK;
@@ -1057,7 +995,7 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
if (conn->current_msg->msg.sd.polls_left == 0) {
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
close_finished = 1;
- if (close) {
+ if (shut_close) {
/* in this case, we want to RST the connection */
tcp_abort(tpcb);
err = ERR_OK;
@@ -1072,9 +1010,10 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
/* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
conn->current_msg->err = err;
+ conn->current_msg = NULL;
conn->state = NETCONN_NONE;
if (err == ERR_OK) {
- if (close) {
+ if (shut_close) {
/* Set back some callback pointers as conn is going away */
conn->pcb.tcp = NULL;
/* Trigger select() in socket layer. Make sure everybody notices activity
@@ -1088,20 +1027,12 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
}
}
- NETCONN_SET_SAFE_ERR(conn, err);
#if LWIP_TCPIP_CORE_LOCKING
if (delayed)
#endif
{
/* wake up the application task */
-#if ESP_LWIP
- if (sig_close) {
- conn->current_msg = NULL;
- sys_sem_signal(op_completed_sem);
- }
-#else
sys_sem_signal(op_completed_sem);
-#endif
}
return ERR_OK;
}
@@ -1129,7 +1060,7 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
* Delete the pcb inside a netconn.
* Called from netconn_delete.
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_delconn(void *m)
@@ -1139,10 +1070,6 @@ lwip_netconn_do_delconn(void *m)
enum netconn_state state = msg->conn->state;
LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
(state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
-#if ESP_LWIP
- msg->err = ERR_OK;
-#endif
-
#if LWIP_NETCONN_FULLDUPLEX
/* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
if (state != NETCONN_NONE) {
@@ -1154,12 +1081,7 @@ lwip_netconn_do_delconn(void *m)
op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
msg->conn->current_msg->err = ERR_CLSD;
msg->conn->current_msg = NULL;
- msg->conn->write_offset = 0;
msg->conn->state = NETCONN_NONE;
- NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
-#if ESP_LWIP
- msg->err = ERR_INPROGRESS;
-#endif
sys_sem_signal(op_completed_sem);
}
}
@@ -1174,11 +1096,9 @@ lwip_netconn_do_delconn(void *m)
} else
#endif /* LWIP_NETCONN_FULLDUPLEX */
{
-#if !ESP_LWIP
LWIP_ASSERT("blocking connect in progress",
(state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
msg->err = ERR_OK;
-#endif
/* Drain and delete mboxes */
netconn_drain(msg->conn);
@@ -1198,17 +1118,12 @@ lwip_netconn_do_delconn(void *m)
#endif /* LWIP_UDP */
#if LWIP_TCP
case NETCONN_TCP:
- LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
- msg->conn->write_offset == 0);
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
msg->conn->state = NETCONN_CLOSE;
msg->msg.sd.shut = NETCONN_SHUT_RDWR;
msg->conn->current_msg = msg;
#if LWIP_TCPIP_CORE_LOCKING
-#if ESP_LWIP
- if (lwip_netconn_do_close_internal(msg->conn, 0, SIG_CLOSE_TRUE) != ERR_OK) {
-#else
if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
-#endif
LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
UNLOCK_TCPIP_CORE();
sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
@@ -1216,11 +1131,7 @@ lwip_netconn_do_delconn(void *m)
LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
}
#else /* LWIP_TCPIP_CORE_LOCKING */
-#if ESP_LWIP
- lwip_netconn_do_close_internal(msg->conn, SIG_CLOSE_TRUE);
-#else
lwip_netconn_do_close_internal(msg->conn);
-#endif
#endif /* LWIP_TCPIP_CORE_LOCKING */
/* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
the application thread, so we can return at this point! */
@@ -1247,58 +1158,84 @@ lwip_netconn_do_delconn(void *m)
* Bind a pcb contained in a netconn
* Called from netconn_bind.
*
- * @param m the api_msg_msg pointing to the connection and containing
+ * @param m the api_msg pointing to the connection and containing
* the IP address and port to bind to
*/
void
lwip_netconn_do_bind(void *m)
{
struct api_msg *msg = (struct api_msg*)m;
+ err_t err;
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ err = ERR_VAL;
+ break;
+ }
} else {
- msg->err = ERR_VAL;
- if (msg->conn->pcb.tcp != NULL) {
-
-#if ESP_LWIP
-#if LWIP_IPV4 && LWIP_IPV6
- /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
- * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to bind
- */
- if (ip_addr_cmp(API_EXPR_REF(msg->msg.bc.ipaddr), IP6_ADDR_ANY) &&
- (netconn_get_ipv6only(msg->conn) == 0)) {
- /* change PCB type to IPADDR_TYPE_ANY */
- IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip, IPADDR_TYPE_ANY);
- IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_ANY);
-
- /* bind to IPADDR_TYPE_ANY */
- API_EXPR_REF(msg->msg.bc.ipaddr) = IP_ANY_TYPE;
- }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-#endif
+ err = ERR_VAL;
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Bind a pcb contained in a netconn to an interface
+ * Called from netconn_bind_if.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind_if(void *m)
+{
+ struct netif* netif;
+ struct api_msg *msg = (struct api_msg*)m;
+ err_t err;
+
+ netif = netif_get_by_index(msg->msg.bc.if_idx);
- switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+ if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
+ err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
- case NETCONN_RAW:
- msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
- break;
+ case NETCONN_RAW:
+ raw_bind_netif(msg->conn->pcb.raw, netif);
+ break;
#endif /* LWIP_RAW */
#if LWIP_UDP
- case NETCONN_UDP:
- msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
- break;
+ case NETCONN_UDP:
+ udp_bind_netif(msg->conn->pcb.udp, netif);
+ break;
#endif /* LWIP_UDP */
#if LWIP_TCP
- case NETCONN_TCP:
- msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
- break;
+ case NETCONN_TCP:
+ tcp_bind_netif(msg->conn->pcb.tcp, netif);
+ break;
#endif /* LWIP_TCP */
- default:
- break;
- }
+ default:
+ err = ERR_VAL;
+ break;
}
+ } else {
+ err = ERR_VAL;
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
@@ -1342,7 +1279,6 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
(!was_blocking && op_completed_sem == NULL));
conn->current_msg = NULL;
conn->state = NETCONN_NONE;
- NETCONN_SET_SAFE_ERR(conn, ERR_OK);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
if (was_blocking) {
@@ -1356,46 +1292,47 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
* Connect a pcb contained inside a netconn
* Called from netconn_connect.
*
- * @param m the api_msg_msg pointing to the connection and containing
+ * @param m the api_msg pointing to the connection and containing
* the IP address and port to connect to
*/
void
lwip_netconn_do_connect(void *m)
{
struct api_msg *msg = (struct api_msg*)m;
+ err_t err;
if (msg->conn->pcb.tcp == NULL) {
/* This may happen when calling netconn_connect() a second time */
- msg->err = ERR_CLSD;
+ err = ERR_CLSD;
} else {
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
- msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
break;
#endif /* LWIP_RAW */
#if LWIP_UDP
case NETCONN_UDP:
- msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
case NETCONN_TCP:
/* Prevent connect while doing any other action. */
if (msg->conn->state == NETCONN_CONNECT) {
- msg->err = ERR_ALREADY;
+ err = ERR_ALREADY;
} else if (msg->conn->state != NETCONN_NONE) {
- msg->err = ERR_ISCONN;
+ err = ERR_ISCONN;
} else {
setup_tcp(msg->conn);
- msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+ err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
msg->msg.bc.port, lwip_netconn_do_connected);
- if (msg->err == ERR_OK) {
+ if (err == ERR_OK) {
u8_t non_blocking = netconn_is_nonblocking(msg->conn);
msg->conn->state = NETCONN_CONNECT;
SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
if (non_blocking) {
- msg->err = ERR_INPROGRESS;
+ err = ERR_INPROGRESS;
} else {
msg->conn->current_msg = msg;
/* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
@@ -1414,10 +1351,11 @@ lwip_netconn_do_connect(void *m)
break;
#endif /* LWIP_TCP */
default:
- LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+ LWIP_ERROR("Invalid netconn type", 0, do{ err = ERR_VAL; }while(0));
break;
}
}
+ msg->err = err;
/* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
so use TCPIP_APIMSG_ACK() here. */
TCPIP_APIMSG_ACK(msg);
@@ -1428,7 +1366,7 @@ lwip_netconn_do_connect(void *m)
* Only used for UDP netconns.
* Called from netconn_disconnect.
*
- * @param m the api_msg_msg pointing to the connection to disconnect
+ * @param m the api_msg pointing to the connection to disconnect
*/
void
lwip_netconn_do_disconnect(void *m)
@@ -1452,85 +1390,82 @@ lwip_netconn_do_disconnect(void *m)
* Set a TCP pcb contained in a netconn into listen mode
* Called from netconn_listen.
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_listen(void *m)
{
struct api_msg *msg = (struct api_msg*)m;
+ err_t err;
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
- } else {
- msg->err = ERR_CONN;
- if (msg->conn->pcb.tcp != NULL) {
- if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
- if (msg->conn->state == NETCONN_NONE) {
- struct tcp_pcb* lpcb;
- if (msg->conn->pcb.tcp->state != CLOSED) {
- /* connection is not closed, cannot listen */
- msg->err = ERR_VAL;
- } else {
- err_t err;
- u8_t backlog;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+ struct tcp_pcb* lpcb;
+ if (msg->conn->pcb.tcp->state != CLOSED) {
+ /* connection is not closed, cannot listen */
+ err = ERR_VAL;
+ } else {
+ u8_t backlog;
#if TCP_LISTEN_BACKLOG
- backlog = msg->msg.lb.backlog;
+ backlog = msg->msg.lb.backlog;
#else /* TCP_LISTEN_BACKLOG */
- backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+ backlog = TCP_DEFAULT_LISTEN_BACKLOG;
#endif /* TCP_LISTEN_BACKLOG */
#if LWIP_IPV4 && LWIP_IPV6
- /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
- * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
- */
- if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
- (netconn_get_ipv6only(msg->conn) == 0)) {
- /* change PCB type to IPADDR_TYPE_ANY */
- IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip, IPADDR_TYPE_ANY);
- IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
- }
+ /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+ * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+ */
+ if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+ (netconn_get_ipv6only(msg->conn) == 0)) {
+ /* change PCB type to IPADDR_TYPE_ANY */
+ IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip, IPADDR_TYPE_ANY);
+ IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+ }
#endif /* LWIP_IPV4 && LWIP_IPV6 */
- lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
+ lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
- if (lpcb == NULL) {
- /* in this case, the old pcb is still allocated */
- msg->err = err;
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
} else {
- /* delete the recvmbox and allocate the acceptmbox */
- if (sys_mbox_valid(&msg->conn->recvmbox)) {
- /** @todo: should we drain the recvmbox here? */
- sys_mbox_free(&msg->conn->recvmbox);
- sys_mbox_set_invalid(&msg->conn->recvmbox);
- }
- msg->err = ERR_OK;
- if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
- msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
- }
- if (msg->err == ERR_OK) {
-#if ESP_THREAD_SAFE
- sys_mbox_set_owner(&msg->conn->acceptmbox, msg->conn);
-#endif
- msg->conn->state = NETCONN_LISTEN;
- msg->conn->pcb.tcp = lpcb;
- tcp_arg(msg->conn->pcb.tcp, msg->conn);
- tcp_accept(msg->conn->pcb.tcp, accept_function);
- } else {
- /* since the old pcb is already deallocated, free lpcb now */
- tcp_close(lpcb);
- msg->conn->pcb.tcp = NULL;
- }
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
}
}
- } else if (msg->conn->state == NETCONN_LISTEN) {
- /* already listening, allow updating of the backlog */
- msg->err = ERR_OK;
- tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
}
- } else {
- msg->err = ERR_ARG;
+ } else if (msg->conn->state == NETCONN_LISTEN) {
+ /* already listening, allow updating of the backlog */
+ err = ERR_OK;
+ tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+ }
+ else {
+ err = ERR_CONN;
}
+ } else {
+ err = ERR_ARG;
}
+ } else {
+ err = ERR_CONN;
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
#endif /* LWIP_TCP */
@@ -1539,33 +1474,23 @@ lwip_netconn_do_listen(void *m)
* Send some data on a RAW or UDP pcb contained in a netconn
* Called from netconn_send
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_send(void *m)
{
struct api_msg *msg = (struct api_msg*)m;
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
-#if ESP_LWIP
-#if LWIP_IPV4 && LWIP_IPV6
- } else if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) &&
- IP_IS_V4MAPPEDV6(&msg->msg.b->addr)) {
- LWIP_DEBUGF(API_MSG_DEBUG, ("lwip_netconn_do_send: Dropping IPv4 packet on IPv6-only socket"));
- msg->err = ERR_VAL;
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-#endif
- } else {
- msg->err = ERR_CONN;
+ err_t err = netconn_err(msg->conn);
+ if (err == ERR_OK) {
if (msg->conn->pcb.tcp != NULL) {
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
- msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
} else {
- msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
}
break;
#endif
@@ -1573,27 +1498,31 @@ lwip_netconn_do_send(void *m)
case NETCONN_UDP:
#if LWIP_CHECKSUM_ON_COPY
if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
- msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
} else {
- msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
&msg->msg.b->addr, msg->msg.b->port,
msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
}
#else /* LWIP_CHECKSUM_ON_COPY */
if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
- msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
} else {
- msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+ err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
}
#endif /* LWIP_CHECKSUM_ON_COPY */
break;
#endif /* LWIP_UDP */
default:
+ err = ERR_CONN;
break;
}
+ } else {
+ err = ERR_CONN;
}
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
@@ -1602,7 +1531,7 @@ lwip_netconn_do_send(void *m)
* Indicate data has been received from a TCP pcb contained in a netconn
* Called from netconn_recv
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_recv(void *m)
@@ -1612,9 +1541,9 @@ lwip_netconn_do_recv(void *m)
msg->err = ERR_OK;
if (msg->conn->pcb.tcp != NULL) {
if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
- u32_t remaining = msg->msg.r.len;
+ size_t remaining = msg->msg.r.len;
do {
- u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+ u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
tcp_recved(msg->conn->pcb.tcp, recved);
remaining -= recved;
} while (remaining != 0);
@@ -1627,7 +1556,7 @@ lwip_netconn_do_recv(void *m)
/** Indicate that a TCP pcb has been accepted
* Called from netconn_accept
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_accepted(void *m)
@@ -1665,13 +1594,15 @@ lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM)
size_t diff;
u8_t dontblock;
u8_t apiflags;
+ u8_t write_more;
LWIP_ASSERT("conn != NULL", conn != NULL);
LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
- LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
- conn->write_offset < conn->current_msg->msg.w.len);
+ LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
+ conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
+ LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
apiflags = conn->current_msg->msg.w.apiflags;
dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
@@ -1680,75 +1611,96 @@ lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM)
if ((conn->send_timeout != 0) &&
((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
write_finished = 1;
- if (conn->write_offset == 0) {
+ if (conn->current_msg->msg.w.offset == 0) {
/* nothing has been written */
err = ERR_WOULDBLOCK;
- conn->current_msg->msg.w.len = 0;
} else {
/* partial write */
err = ERR_OK;
- conn->current_msg->msg.w.len = conn->write_offset;
- conn->write_offset = 0;
}
} else
#endif /* LWIP_SO_SNDTIMEO */
{
- dataptr = (const u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
- diff = conn->current_msg->msg.w.len - conn->write_offset;
- if (diff > 0xffffUL) { /* max_u16_t */
- len = 0xffff;
- apiflags |= TCP_WRITE_FLAG_MORE;
- } else {
- len = (u16_t)diff;
- }
- available = tcp_sndbuf(conn->pcb.tcp);
- if (available < len) {
- /* don't try to write more than sendbuf */
- len = available;
- if (dontblock) {
- if (!len) {
- err = ERR_WOULDBLOCK;
- goto err_mem;
- }
+ do {
+ dataptr = (const u8_t*)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
+ diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+ apiflags |= TCP_WRITE_FLAG_MORE;
} else {
+ len = (u16_t)diff;
+ }
+ available = tcp_sndbuf(conn->pcb.tcp);
+ if (available < len) {
+ /* don't try to write more than sendbuf */
+ len = available;
+ if (dontblock) {
+ if (!len) {
+ /* set error according to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ goto err_mem;
+ }
+ } else {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ }
+ LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
+ ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
+ /* we should loop around for more sending in the following cases:
+ 1) We couldn't finish the current vector because of 16-bit size limitations.
+ tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
+ 2) We are sending the remainder of the current vector and have more */
+ if ((len == 0xffff && diff > 0xffffUL) ||
+ (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
+ write_more = 1;
apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ write_more = 0;
}
- }
- LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
- err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ if (err == ERR_OK) {
+ conn->current_msg->msg.w.offset += len;
+ conn->current_msg->msg.w.vector_off += len;
+ /* check if current vector is finished */
+ if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
+ conn->current_msg->msg.w.vector_cnt--;
+ /* if we have additional vectors, move on to them */
+ if (conn->current_msg->msg.w.vector_cnt > 0) {
+ conn->current_msg->msg.w.vector++;
+ conn->current_msg->msg.w.vector_off = 0;
+ }
+ }
+ }
+ } while (write_more && err == ERR_OK);
/* if OK or memory error, check available space */
if ((err == ERR_OK) || (err == ERR_MEM)) {
err_mem:
- if (dontblock && (len < conn->current_msg->msg.w.len)) {
+ if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
/* non-blocking write did not write everything: mark the pcb non-writable
and let poll_tcp check writable space to mark the pcb writable again */
- API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
} else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
(tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
/* The queued byte- or pbuf-count exceeds the configured low-water limit,
let select mark this pcb as non-writable. */
- API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
}
}
if (err == ERR_OK) {
err_t out_err;
- conn->write_offset += len;
- if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
- /* return sent length */
- conn->current_msg->msg.w.len = conn->write_offset;
- /* everything was written */
+ if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
+ /* return sent length (caller reads length from msg.w.offset) */
write_finished = 1;
}
out_err = tcp_output(conn->pcb.tcp);
- if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
- /* If tcp_output fails with fatal error or no route is found,
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
don't try writing any more but return the error
to the application thread. */
err = out_err;
write_finished = 1;
- conn->current_msg->msg.w.len = 0;
}
} else if (err == ERR_MEM) {
/* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
@@ -1758,24 +1710,22 @@ err_mem:
/* tcp_write returned ERR_MEM, try tcp_output anyway */
err_t out_err = tcp_output(conn->pcb.tcp);
- if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
- /* If tcp_output fails with fatal error or no route is found,
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
don't try writing any more but return the error
to the application thread. */
err = out_err;
write_finished = 1;
- conn->current_msg->msg.w.len = 0;
} else if (dontblock) {
- /* non-blocking write is done on ERR_MEM */
- err = ERR_WOULDBLOCK;
+ /* non-blocking write is done on ERR_MEM, set error according
+ to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
write_finished = 1;
- conn->current_msg->msg.w.len = 0;
}
} else {
/* On errors != ERR_MEM, we don't try writing any more but return
the error to the application thread. */
write_finished = 1;
- conn->current_msg->msg.w.len = 0;
}
}
if (write_finished) {
@@ -1784,9 +1734,7 @@ err_mem:
sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
conn->current_msg->err = err;
conn->current_msg = NULL;
- conn->write_offset = 0;
conn->state = NETCONN_NONE;
- NETCONN_SET_SAFE_ERR(conn, err);
#if LWIP_TCPIP_CORE_LOCKING
if (delayed)
#endif
@@ -1807,29 +1755,26 @@ err_mem:
* Send some data on a TCP pcb contained in a netconn
* Called from netconn_write
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_write(void *m)
{
struct api_msg *msg = (struct api_msg*)m;
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
- } else {
+ err_t err = netconn_err(msg->conn);
+ if (err == ERR_OK) {
if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
#if LWIP_TCP
if (msg->conn->state != NETCONN_NONE) {
/* netconn is connecting, closing or in blocking write */
- msg->err = ERR_INPROGRESS;
+ err = ERR_INPROGRESS;
} else if (msg->conn->pcb.tcp != NULL) {
msg->conn->state = NETCONN_WRITE;
/* set all the variables used by lwip_netconn_do_writemore */
- LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
- msg->conn->write_offset == 0);
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
msg->conn->current_msg = msg;
- msg->conn->write_offset = 0;
#if LWIP_TCPIP_CORE_LOCKING
if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
@@ -1845,17 +1790,18 @@ lwip_netconn_do_write(void *m)
since lwip_netconn_do_writemore ACKs it! */
return;
} else {
- msg->err = ERR_CONN;
+ err = ERR_CONN;
}
#else /* LWIP_TCP */
- msg->err = ERR_VAL;
+ err = ERR_VAL;
#endif /* LWIP_TCP */
#if (LWIP_UDP || LWIP_RAW)
} else {
- msg->err = ERR_VAL;
+ err = ERR_VAL;
#endif /* (LWIP_UDP || LWIP_RAW) */
}
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
@@ -1863,7 +1809,7 @@ lwip_netconn_do_write(void *m)
* Return a connection's local or remote address
* Called from netconn_getaddr
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_getaddr(void *m)
@@ -1930,7 +1876,7 @@ lwip_netconn_do_getaddr(void *m)
* Called from netconn_close
* In contrast to closing sockets, the netconn is not deallocated.
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_close(void *m)
@@ -1957,10 +1903,8 @@ lwip_netconn_do_close(void *m)
write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
msg->conn->current_msg->err = ERR_CLSD;
msg->conn->current_msg = NULL;
- msg->conn->write_offset = 0;
msg->conn->state = NETCONN_NONE;
state = NETCONN_NONE;
- NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
sys_sem_signal(write_completed_sem);
} else {
LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
@@ -1978,16 +1922,11 @@ lwip_netconn_do_close(void *m)
/* Drain and delete mboxes */
netconn_drain(msg->conn);
}
- LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
- msg->conn->write_offset == 0);
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
msg->conn->state = NETCONN_CLOSE;
msg->conn->current_msg = msg;
#if LWIP_TCPIP_CORE_LOCKING
-#if ESP_LWIP
- if (lwip_netconn_do_close_internal(msg->conn, 0, SIG_CLOSE_TRUE) != ERR_OK) {
-#else
if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
-#endif
LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
UNLOCK_TCPIP_CORE();
sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
@@ -1995,11 +1934,7 @@ lwip_netconn_do_close(void *m)
LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
}
#else /* LWIP_TCPIP_CORE_LOCKING */
-#if ESP_LWIP
- lwip_netconn_do_close_internal(msg->conn, SIG_CLOSE_TRUE);
-#else
lwip_netconn_do_close_internal(msg->conn);
-#endif
#endif /* LWIP_TCPIP_CORE_LOCKING */
/* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
return;
@@ -2017,50 +1952,45 @@ lwip_netconn_do_close(void *m)
* Join multicast groups for UDP netconns.
* Called from netconn_join_leave_group
*
- * @param m the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
lwip_netconn_do_join_leave_group(void *m)
{
struct api_msg *msg = (struct api_msg*)m;
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
- } else {
- if (msg->conn->pcb.tcp != NULL) {
- if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
#if LWIP_UDP
#if LWIP_IPV6 && LWIP_IPV6_MLD
- if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
- msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
- ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
- } else {
- msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
- ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
- }
+ if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
}
- else
+ }
+ else
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
- {
+ {
#if LWIP_IGMP
- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
- msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
- ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
- } else {
- msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
- ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
- }
-#endif /* LWIP_IGMP */
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
}
+#endif /* LWIP_IGMP */
+ }
#endif /* LWIP_UDP */
#if (LWIP_TCP || LWIP_RAW)
- } else {
- msg->err = ERR_VAL;
-#endif /* (LWIP_TCP || LWIP_RAW) */
- }
} else {
- msg->err = ERR_CONN;
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
}
}
TCPIP_APIMSG_ACK(msg);
@@ -2112,11 +2042,21 @@ lwip_netconn_do_gethostbyname(void *arg)
API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+#if LWIP_TCPIP_CORE_LOCKING
+ /* For core locking, only block if we need to wait for answer/timeout */
+ if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
+ UNLOCK_TCPIP_CORE();
+ sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
/* on error or immediate success, wake up the application
* task waiting in netconn_gethostbyname */
sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
}
#endif /* LWIP_DNS */
diff --git a/../lwip_nat/src/api/if_api.c b/../lwip_nat/src/api/if_api.c
new file mode 100644
index 00000000..e1730ae9
--- /dev/null
+++ b/../lwip_nat/src/api/if_api.c
@@ -0,0 +1,102 @@
+/**
+ * @file
+ * Interface Identification APIs from:
+ * RFC 3493: Basic Socket Interface Extensions for IPv6
+ * Section 4: Interface Identification
+ *
+ * @defgroup if_api Interface Identification API
+ * @ingroup socket
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET
+
+#include "lwip/errno.h"
+#include "lwip/if_api.h"
+#include "lwip/netifapi.h"
+#include "lwip/priv/sockets_priv.h"
+
+/**
+ * @ingroup if_api
+ * Maps an interface index to its corresponding name.
+ * @param ifindex interface index
+ * @param ifname shall point to a buffer of at least {IF_NAMESIZE} bytes
+ * @return If ifindex is an interface index, then the function shall return the
+ * value supplied in ifname, which points to a buffer now containing the interface name.
+ * Otherwise, the function shall return a NULL pointer.
+ */
+char *
+lwip_if_indextoname(unsigned int ifindex, char *ifname)
+{
+#if LWIP_NETIF_API
+ if (ifindex <= 0xff) {
+ err_t err = netifapi_netif_index_to_name((u8_t)ifindex, ifname);
+ if (!err && ifname[0] != '\0') {
+ return ifname;
+ }
+ }
+#else /* LWIP_NETIF_API */
+ LWIP_UNUSED_ARG(ifindex);
+ LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+ set_errno(ENXIO);
+ return NULL;
+}
+
+/**
+ * @ingroup if_api
+ * Returs the interface index corresponding to name ifname.
+ * @param ifname Interface name
+ * @return The corresponding index if ifname is the name of an interface;
+ * otherwise, zero.
+ */
+unsigned int
+lwip_if_nametoindex(const char *ifname)
+{
+#if LWIP_NETIF_API
+ err_t err;
+ u8_t idx;
+
+ err = netifapi_netif_name_to_index(ifname, &idx);
+ if (!err) {
+ return idx;
+ }
+#else /* LWIP_NETIF_API */
+ LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+ return 0; /* invalid index */
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/src/api/netbuf.c b/../lwip_nat/src/api/netbuf.c
index 4046ed14..3e87178e 100644
--- a/src/api/netbuf.c
+++ b/../lwip_nat/src/api/netbuf.c
@@ -98,11 +98,7 @@ netbuf_delete(struct netbuf *buf)
* @return pointer to the allocated memory
* NULL if no memory could be allocated
*/
-#if ESP_LWIP
-void * ESP_IRAM_ATTR
-#else
void *
-#endif
netbuf_alloc(struct netbuf *buf, u16_t size)
{
LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
@@ -127,11 +123,7 @@ netbuf_alloc(struct netbuf *buf, u16_t size)
*
* @param buf pointer to the netbuf which contains the packet buffer to free
*/
-#if ESP_LWIP
-void ESP_IRAM_ATTR
-#else
void
-#endif
netbuf_free(struct netbuf *buf)
{
LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
@@ -139,6 +131,10 @@ netbuf_free(struct netbuf *buf)
pbuf_free(buf->p);
}
buf->p = buf->ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = 0;
+ buf->toport_chksum = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
}
/**
diff --git a/src/api/netdb.c b/../lwip_nat/src/api/netdb.c
index c06e80bc..225be257 100644
--- a/src/api/netdb.c
+++ b/../lwip_nat/src/api/netdb.c
@@ -187,7 +187,7 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
}
namelen = strlen(name);
- if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + LWIP_MEM_ALIGN_BUFFER(namelen + 1))) {
/* buf can't hold the data needed + a copy of name */
*h_errnop = ERANGE;
return -1;
@@ -332,11 +332,6 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
type = NETCONN_DNS_IPV4;
} else if (ai_family == AF_INET6) {
type = NETCONN_DNS_IPV6;
-#if ESP_LWIP
- if (hints->ai_flags & AI_V4MAPPED) {
- type = NETCONN_DNS_IPV6_IPV4;
- }
-#endif
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
@@ -347,22 +342,12 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
} else {
/* service location specified, use loopback address */
if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
- ip_addr_set_any(ai_family == AF_INET6, &addr);
+ ip_addr_set_any_val(ai_family == AF_INET6, addr);
} else {
- ip_addr_set_loopback(ai_family == AF_INET6, &addr);
+ ip_addr_set_loopback_val(ai_family == AF_INET6, addr);
}
}
-#if ESP_LWIP
-#if LWIP_IPV4 && LWIP_IPV6
- if (ai_family == AF_INET6 && (hints->ai_flags & AI_V4MAPPED)
- && IP_GET_TYPE(&addr) == IPADDR_TYPE_V4) {
- /* Convert native V4 address to a V4-mapped IPV6 address */
- ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&addr), ip_2_ip4(&addr));
- }
-#endif
-#endif
-
total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
if (nodename != NULL) {
namelen = strlen(nodename);
@@ -391,6 +376,7 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
sa6->sin6_family = AF_INET6;
sa6->sin6_len = sizeof(struct sockaddr_in6);
sa6->sin6_port = lwip_htons((u16_t)port_nr);
+ sa6->sin6_scope_id = ip6_addr_zone(ip_2_ip6(&addr));
ai->ai_family = AF_INET6;
#endif /* LWIP_IPV6 */
} else {
diff --git a/src/api/netifapi.c b/../lwip_nat/src/api/netifapi.c
index fef05a34..cba614c4 100644
--- a/src/api/netifapi.c
+++ b/../lwip_nat/src/api/netifapi.c
@@ -46,6 +46,8 @@
#include "lwip/memp.h"
#include "lwip/priv/tcpip_priv.h"
+#include <string.h> /* strncpy */
+
#define NETIFAPI_VAR_REF(name) API_VAR_REF(name)
#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name)
#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
@@ -95,6 +97,37 @@ netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
}
#endif /* LWIP_IPV4 */
+/**
+* Call netif_name_to_index() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_name_to_index(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+ msg->msg.ifs.index = netif_name_to_index(msg->msg.ifs.name);
+ return ERR_OK;
+}
+
+/**
+* Call netif_index_to_name() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_index_to_name(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+ if (!netif_index_to_name(msg->msg.ifs.index, msg->msg.ifs.name)) {
+ /* return failure via empty name */
+ msg->msg.ifs.name[0] = '\0';
+ }
+ return ERR_OK;
+}
+
/**
* Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
* tcpip_thread context.
@@ -218,4 +251,66 @@ netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
return err;
}
+/**
+* @ingroup netifapi_netif
+* Call netif_name_to_index() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param name the interface name of the netif
+* @param idx output index of the found netif
+*/
+err_t
+netifapi_netif_name_to_index(const char *name, u8_t *idx)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ *idx = 0;
+
+#if LWIP_MPU_COMPATIBLE
+ strncpy(NETIFAPI_VAR_REF(msg).msg.ifs.name, name, NETIF_NAMESIZE - 1);
+ NETIFAPI_VAR_REF(msg).msg.ifs.name[NETIF_NAMESIZE - 1] = '\0';
+#else
+ NETIFAPI_VAR_REF(msg).msg.ifs.name = LWIP_CONST_CAST(char*, name);
+#endif /* LWIP_MPU_COMPATIBLE */
+ err = tcpip_api_call(netifapi_do_name_to_index, &API_VAR_REF(msg).call);
+ if (!err) {
+ *idx = NETIFAPI_VAR_REF(msg).msg.ifs.index;
+ }
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_index_to_name() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param idx the interface index of the netif
+* @param name output name of the found netif, empty '\0' string if netif not found.
+* name should be of at least NETIF_NAMESIZE bytes
+*/
+err_t
+netifapi_netif_index_to_name(u8_t idx, char *name)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ NETIFAPI_VAR_REF(msg).msg.ifs.index = idx;
+#if !LWIP_MPU_COMPATIBLE
+ NETIFAPI_VAR_REF(msg).msg.ifs.name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+ err = tcpip_api_call(netifapi_do_index_to_name, &API_VAR_REF(msg).call);
+#if LWIP_MPU_COMPATIBLE
+ if (!err) {
+ strncpy(name, NETIFAPI_VAR_REF(msg).msg.ifs.name, NETIF_NAMESIZE - 1);
+ name[NETIF_NAMESIZE - 1] = '\0';
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
#endif /* LWIP_NETIF_API */
diff --git a/src/api/sockets.c b/../lwip_nat/src/api/sockets.c
index d367fdb2..a50faf55 100644
--- a/src/api/sockets.c
+++ b/../lwip_nat/src/api/sockets.c
@@ -49,10 +49,10 @@
#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.h"
#include "lwip/api.h"
#include "lwip/sys.h"
#include "lwip/igmp.h"
-#include "lwip/mld6.h"
#include "lwip/inet.h"
#include "lwip/tcp.h"
#include "lwip/raw.h"
@@ -60,7 +60,6 @@
#include "lwip/memp.h"
#include "lwip/pbuf.h"
#include "lwip/priv/tcpip_priv.h"
-#include "lwip/priv/api_msg.h"
#if LWIP_CHECKSUM_ON_COPY
#include "lwip/inet_chksum.h"
#endif
@@ -98,9 +97,12 @@
(sin6)->sin6_port = lwip_htons((port)); \
(sin6)->sin6_flowinfo = 0; \
inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
- (sin6)->sin6_scope_id = 0; }while(0)
+ (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0)
#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+ if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
+ ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
+ } \
(port) = lwip_ntohs((sin6)->sin6_port); }while(0)
#endif /* LWIP_IPV6 */
@@ -115,7 +117,7 @@ static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t*
((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
(((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
- if (IP_IS_V6(ipaddr)) { \
+ if (IP_IS_ANY_TYPE_VAL(*ipaddr) || IP_IS_V6_VAL(*ipaddr)) { \
IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
} else { \
IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
@@ -150,16 +152,16 @@ static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t*
#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
-#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype) do { if ((optlen) < sizeof(opttype)) { done_socket(sock); return EINVAL; }}while(0)
#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
- LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
- if ((sock)->conn == NULL) { return EINVAL; } }while(0)
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+ if ((sock)->conn == NULL) { done_socket(sock); return EINVAL; } }while(0)
#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
- LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
- if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0)
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+ if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { done_socket(sock); return EINVAL; } }while(0)
#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
- if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0)
+ if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { done_socket(sock); return ENOPROTOOPT; } }while(0)
#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name)
@@ -170,6 +172,7 @@ static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t*
name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
if (name == NULL) { \
sock_set_errno(sock, ENOMEM); \
+ done_socket(sock); \
return -1; \
} }while(0)
#else /* LWIP_MPU_COMPATIBLE */
@@ -179,155 +182,16 @@ static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t*
#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
-#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(const int*)(optval))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((long)*(const int*)(optval))
#else
#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
- s32_t loc = (val); \
- ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \
- ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0)
-#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U))
+ u32_t loc = (val); \
+ ((struct timeval *)(optval))->tv_sec = (long)((loc) / 1000U); \
+ ((struct timeval *)(optval))->tv_usec = (long)(((loc) % 1000U) * 1000U); }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000) + (((const struct timeval *)(optval))->tv_usec / 1000))
#endif
-#define NUM_SOCKETS MEMP_NUM_NETCONN
-
-#if !defined IOV_MAX
-#define IOV_MAX 0xFFFF
-#elif IOV_MAX > 0xFFFF
-#error "IOV_MAX larger than supported by LwIP"
-#endif /* IOV_MAX */
-
-/** This is overridable for the rare case where more than 255 threads
- * select on the same socket...
- */
-#ifndef SELWAIT_T
-#define SELWAIT_T u8_t
-#endif
-
-/** Contains all internal pointers and states used for a socket */
-struct lwip_sock {
- /** sockets currently are built on netconns, each socket has one netconn */
- struct netconn *conn;
- /** data that was left from the previous read */
- void *lastdata;
- /** offset in the data that was left from the previous read */
- u16_t lastoffset;
- /** number of times data was received, set by event_callback(),
- tested by the receive and select functions */
- s16_t rcvevent;
- /** number of times data was ACKed (free send buffer), set by event_callback(),
- tested by select */
- u16_t sendevent;
- /** error happened for this socket, set by event_callback(), tested by select */
- u16_t errevent;
- /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */
- u8_t err;
-
-#if ESP_THREAD_SAFE
- /* lock is used to protect state/ref field, however this lock is not a perfect lock, e.g
- * taskA and taskB can access sock X, then taskA freed sock X, before taskB detect
- * this, taskC reuse sock X, then when taskB try to access sock X, problem may happen.
- * A mitigation solution may be, when allocate a socket, alloc the least frequently used
- * socket.
- */
- sys_mutex_t lock;
-
- /* can be LWIP_SOCK_OPEN/LWIP_SOCK_CLOSING/LWIP_SOCK_CLOSED */
- u8_t state;
-
- /* if ref is 0, the sock need/can to be freed */
- s8_t ref;
-
- /* indicate how long the sock is in LWIP_SOCK_CLOSED status */
- u8_t age;
-#endif
-
- /** counter of how many threads are waiting for this socket using select */
- SELWAIT_T select_waiting;
-};
-
-#if ESP_THREAD_SAFE
-
-#define LWIP_SOCK_OPEN 0
-#define LWIP_SOCK_CLOSING 1
-#define LWIP_SOCK_CLOSED 2
-
-#define LWIP_SOCK_LOCK(sock) \
-do{\
- /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("l\n"));*/\
- sys_mutex_lock(&sock->lock);\
- /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("l ok\n"));*/\
-}while(0)
-
-
-#define LWIP_SOCK_UNLOCK(sock) \
-do{\
- sys_mutex_unlock(&sock->lock);\
- /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG1, ("unl\n"));*/\
-}while(0)
-
-#define LWIP_FREE_SOCK(sock) \
-do{\
- if(sock->conn && NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP){\
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free tcp sock\n"));\
- free_socket(sock, 1);\
- } else {\
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free non-tcp sock\n"));\
- free_socket(sock, 0);\
- }\
-}while(0)
-
-#define LWIP_SET_CLOSE_FLAG() \
-do{\
- LWIP_SOCK_LOCK(__sock);\
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("mark sock closing\n"));\
- __sock->state = LWIP_SOCK_CLOSING;\
- LWIP_SOCK_UNLOCK(__sock);\
-}while(0)
-
-#define LWIP_API_LOCK() \
- struct lwip_sock *__sock;\
- int __ret;\
-\
- __sock = get_socket(s);\
- if (!__sock) {\
- return -1;\
- }\
-\
-do{\
- LWIP_SOCK_LOCK(__sock);\
- __sock->ref ++;\
- if (__sock->state != LWIP_SOCK_OPEN) {\
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_LOCK:soc is %d, return\n", __sock->state));\
- __sock->ref --;\
- LWIP_SOCK_UNLOCK(__sock);\
- return -1;\
- }\
-\
- LWIP_SOCK_UNLOCK(__sock);\
-}while(0)
-
-#define LWIP_API_UNLOCK() \
-do{\
- LWIP_SOCK_LOCK(__sock);\
- __sock->ref --;\
- if (__sock->state == LWIP_SOCK_CLOSING) {\
- if (__sock->ref == 0){\
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK:ref 0, free __sock\n"));\
- LWIP_FREE_SOCK(__sock);\
- LWIP_SOCK_UNLOCK(__sock);\
- return __ret;\
- }\
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK: soc state is closing, return\n"));\
- LWIP_SOCK_UNLOCK(__sock);\
- return __ret;\
- }\
-\
- LWIP_SOCK_UNLOCK(__sock);\
- return __ret;\
-}while(0)
-
-#endif
#if LWIP_NETCONN_SEM_PER_THREAD
#define SELECT_SEM_T sys_sem_t*
@@ -337,6 +201,7 @@ do{\
#define SELECT_SEM_PTR(sem) (&(sem))
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+#if LWIP_SOCKET_SELECT
/** Description for a task waiting in select */
struct lwip_select_cb {
/** Pointer to the next waiting task */
@@ -347,13 +212,14 @@ struct lwip_select_cb {
fd_set *readset;
/** writeset passed to select */
fd_set *writeset;
- /** unimplemented: exceptset passed to select */
+ /** exceptset passed to select */
fd_set *exceptset;
/** don't signal the same semaphore twice: set to 1 when signalled */
int sem_signalled;
/** semaphore to wake up a task waiting for select */
SELECT_SEM_T sem;
};
+#endif /* LWIP_SOCKET_SELECT */
/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
* sockaddr_in6 if instantiated.
@@ -376,22 +242,6 @@ union sockaddr_aligned {
/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
a socket is closed */
-#if ESP_LWIP
-struct lwip_socket_multicast_pair {
- /** the socket (+1 to not require initialization) */
- int sa;
- /** the interface address */
- ip_addr_t if_addr;
- /** the group address */
- ip_addr_t multi_addr;
-};
-
-struct lwip_socket_multicast_pair socket_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
-
-static int lwip_socket_register_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr);
-static void lwip_socket_unregister_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr);
-
-#else
struct lwip_socket_multicast_pair {
/** the socket */
struct lwip_sock* sock;
@@ -405,44 +255,51 @@ struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_
static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
-#endif
-
static void lwip_socket_drop_registered_memberships(int s);
#endif /* LWIP_IGMP */
/** The global array of available sockets */
static struct lwip_sock sockets[NUM_SOCKETS];
-#if ESP_THREAD_SAFE
-static bool sockets_init_flag = false;
-#endif
-/** The global list of tasks waiting for select */
-static struct lwip_select_cb *select_cb_list;
+
+#if LWIP_SOCKET_SELECT
+#if LWIP_TCPIP_CORE_LOCKING
+/* protect the select_cb_list using core lock */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev) LOCK_TCPIP_CORE()
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) UNLOCK_TCPIP_CORE()
+#else /* LWIP_TCPIP_CORE_LOCKING */
+/* protect the select_cb_list using SYS_LIGHTWEIGHT_PROT */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev) SYS_ARCH_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
/** This counter is increased from lwip_select when the list is changed
- and checked in event_callback to see if it has changed. */
+ and checked in select_check_waiters to see if it has changed. */
static volatile int select_cb_ctr;
-
-#if LWIP_SOCKET_SET_ERRNO
-#ifndef set_errno
-#define set_errno(err) do { if (err) { errno = (err); } } while(0)
-#endif
-#else /* LWIP_SOCKET_SET_ERRNO */
-#define set_errno(err)
-#endif /* LWIP_SOCKET_SET_ERRNO */
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+#endif /* LWIP_SOCKET_SELECT */
#define sock_set_errno(sk, e) do { \
const int sockerr = (e); \
- sk->err = (u8_t)sockerr; \
set_errno(sockerr); \
} while (0)
/* Forward declaration of some functions */
+#if LWIP_SOCKET_SELECT
static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+#define DEFAULT_SOCKET_EVENTCB event_callback
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
+#else
+#define DEFAULT_SOCKET_EVENTCB NULL
+#endif
#if !LWIP_TCPIP_CORE_LOCKING
static void lwip_getsockopt_callback(void *arg);
static void lwip_setsockopt_callback(void *arg);
#endif
-static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
-static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+static void free_socket(struct lwip_sock *sock, int is_tcp);
#if LWIP_IPV4 && LWIP_IPV6
static void
@@ -462,67 +319,125 @@ sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_
void
lwip_socket_thread_init(void)
{
- netconn_thread_init();
+ netconn_thread_init();
}
/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
void
lwip_socket_thread_cleanup(void)
{
- netconn_thread_cleanup();
+ netconn_thread_cleanup();
}
-/**
- * Map a externally used socket index to the internal socket representation.
- *
- * @param s externally used socket index
- * @return struct lwip_sock for the socket or NULL if not found
+#if LWIP_NETCONN_FULLDUPLEX
+/* Thread-safe increment of sock->fd_used, with overflow check */
+static void
+sock_inc_used(struct lwip_sock *sock)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+
+ SYS_ARCH_PROTECT(lev);
+ ++sock->fd_used;
+ LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
+ * released (and possibly reused) when used from more than one thread
+ * (e.g. read-while-write or close-while-write, etc)
+ * This function is called at the end of functions using (try)get_socket*().
*/
-#if ESP_LWIP
-static struct lwip_sock * ESP_IRAM_ATTR
-#else
-static struct lwip_sock *
-#endif
-get_socket(int s)
+static void
+done_socket(struct lwip_sock *sock)
{
- struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_ASSERT("sock != NULL", sock != NULL);
- s -= LWIP_SOCKET_OFFSET;
+ SYS_ARCH_PROTECT(lev);
+ LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+ if (--sock->fd_used == 0) {
+ if (sock->fd_free_pending) {
+ /* free the socket */
+ sock->fd_used = 1;
+ free_socket(sock, sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP);
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define sock_inc_used(sock)
+#define done_socket(sock)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn_nouse(int fd)
+{
+ int s = fd - LWIP_SOCKET_OFFSET;
if ((s < 0) || (s >= NUM_SOCKETS)) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s + LWIP_SOCKET_OFFSET));
- set_errno(EBADF);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
return NULL;
}
+ return &sockets[s];
+}
- sock = &sockets[s];
+struct lwip_sock*
+lwip_socket_dbg_get_socket(int fd)
+{
+ return tryget_socket_unconn_nouse(fd);
+}
- if (!sock->conn) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s + LWIP_SOCKET_OFFSET));
- set_errno(EBADF);
- return NULL;
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn(int fd)
+{
+ struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
+ if (ret != NULL) {
+ sock_inc_used(ret);
}
-
- return sock;
+ return ret;
}
/**
* Same as get_socket but doesn't set errno
*
- * @param s externally used socket index
+ * @param fd externally used socket index
* @return struct lwip_sock for the socket or NULL if not found
*/
static struct lwip_sock *
-tryget_socket(int s)
+tryget_socket(int fd)
{
- s -= LWIP_SOCKET_OFFSET;
- if ((s < 0) || (s >= NUM_SOCKETS)) {
- return NULL;
+ struct lwip_sock *sock = tryget_socket_unconn(fd);
+ if (sock != NULL) {
+ if (sock->conn) {
+ return sock;
+ }
+ done_socket(sock);
}
- if (!sockets[s].conn) {
+ return NULL;
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param fd externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int fd)
+{
+ struct lwip_sock *sock = tryget_socket(fd);
+ if (!sock) {
+ if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
+ }
+ set_errno(EBADF);
return NULL;
}
- return &sockets[s];
+ return sock;
}
/**
@@ -538,100 +453,38 @@ alloc_socket(struct netconn *newconn, int accepted)
{
int i;
SYS_ARCH_DECL_PROTECT(lev);
-
-#if ESP_THREAD_SAFE
- bool found = false;
- int oldest = -1;
-
- SYS_ARCH_PROTECT(lev);
-
- if (sockets_init_flag == false){
- sockets_init_flag = true;
- memset(sockets, 0, sizeof(sockets));
- }
-
- for (i = 0; i < NUM_SOCKETS; ++i) {
- sockets[i].age ++;
-
- if (found == true){
- continue;
- }
-
- if (!sockets[i].conn && (sockets[i].state == LWIP_SOCK_OPEN)) {
- found = true;
- oldest = i;
- continue;
- }
-
- if (!sockets[i].conn){
- if (oldest == -1 || sockets[i].age > sockets[oldest].age){
- oldest = i;
- }
- }
- }
-
- if ((oldest != -1) && !sockets[oldest].conn) {
- found = true;
- sockets[oldest].conn = newconn;
- }
-
- SYS_ARCH_UNPROTECT(lev);
-
- if (found == true) {
- sockets[oldest].lastdata = NULL;
- sockets[oldest].lastoffset = 0;
- sockets[oldest].rcvevent = 0;
-
- /* TCP sendbuf is empty, but the socket is not yet writable until connected
- * (unless it has been created by accept()). */
- sockets[oldest].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
- sockets[oldest].errevent = 0;
- sockets[oldest].err = 0;
- sockets[oldest].select_waiting = 0;
-
- sockets[oldest].state = LWIP_SOCK_OPEN;
- sockets[oldest].age = 0;
- sockets[oldest].ref = 0;
- if (!sockets[oldest].lock){
- /* one time init and never free */
- if (sys_mutex_new(&sockets[oldest].lock) != ERR_OK){
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new sock lock fail\n"));
- return -1;
- }
- }
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("alloc_socket: alloc %d ok\n", oldest));
-
- return oldest + LWIP_SOCKET_OFFSET;
- }
-
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("alloc_socket: failed\n"));
-
-#else
+ LWIP_UNUSED_ARG(accepted);
/* allocate a new socket identifier */
for (i = 0; i < NUM_SOCKETS; ++i) {
/* Protect socket array */
SYS_ARCH_PROTECT(lev);
- if (!sockets[i].conn && (sockets[i].select_waiting == 0)) {
+ if (!sockets[i].conn) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (sockets[i].fd_used) {
+ SYS_ARCH_UNPROTECT(lev);
+ continue;
+ }
+ sockets[i].fd_used = 1;
+ sockets[i].fd_free_pending = 0;
+#endif
sockets[i].conn = newconn;
/* The socket is not yet known to anyone, so no need to protect
after having marked it as used. */
SYS_ARCH_UNPROTECT(lev);
- sockets[i].lastdata = NULL;
- sockets[i].lastoffset = 0;
+ sockets[i].lastdata.pbuf = NULL;
+#if LWIP_SOCKET_SELECT
+ LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
sockets[i].rcvevent = 0;
/* TCP sendbuf is empty, but the socket is not yet writable until connected
* (unless it has been created by accept()). */
sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
sockets[i].errevent = 0;
- sockets[i].err = 0;
+#endif /* LWIP_SOCKET_SELECT */
return i + LWIP_SOCKET_OFFSET;
}
SYS_ARCH_UNPROTECT(lev);
}
-
-#endif
-
return -1;
}
@@ -644,38 +497,32 @@ alloc_socket(struct netconn *newconn, int accepted)
static void
free_socket(struct lwip_sock *sock, int is_tcp)
{
- void *lastdata;
-
-#if ESP_THREAD_SAFE
+ union lwip_sock_lastdata lastdata;
SYS_ARCH_DECL_PROTECT(lev);
- LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free socket s=%p is_tcp=%d\n", sock, is_tcp));
-#endif
-
- lastdata = sock->lastdata;
- sock->lastdata = NULL;
- sock->lastoffset = 0;
- sock->err = 0;
-#if ESP_THREAD_SAFE
- if (sock->conn){
- netconn_free(sock->conn);
- }
+ /* Protect socket array */
SYS_ARCH_PROTECT(lev);
- sock->age = 0;
- sock->conn = NULL;
- sock->state = LWIP_SOCK_CLOSED;
- SYS_ARCH_UNPROTECT(lev);
+
+#if LWIP_NETCONN_FULLDUPLEX
+ LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+ if (--sock->fd_used > 0) {
+ sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0;
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
#endif
- /* Protect socket array */
- SYS_ARCH_SET(sock->conn, NULL);
+ lastdata = sock->lastdata;
+ sock->lastdata.pbuf = NULL;
+ sock->conn = NULL;
+ SYS_ARCH_UNPROTECT(lev);
/* don't use 'sock' after this line, as another task might have allocated it */
- if (lastdata != NULL) {
+ if (lastdata.pbuf != NULL) {
if (is_tcp) {
- pbuf_free((struct pbuf *)lastdata);
+ pbuf_free(lastdata.pbuf);
} else {
- netbuf_delete((struct netbuf *)lastdata);
+ netbuf_delete(lastdata.netbuf);
}
}
}
@@ -695,6 +542,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
u16_t port = 0;
int newsock;
err_t err;
+ int recvevent;
SYS_ARCH_DECL_PROTECT(lev);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
@@ -703,12 +551,6 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
- if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
- set_errno(EWOULDBLOCK);
- return -1;
- }
-
/* wait for a new connection */
err = netconn_accept(sock->conn, &newconn);
if (err != ERR_OK) {
@@ -720,22 +562,19 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
} else {
sock_set_errno(sock, err_to_errno(err));
}
+ done_socket(sock);
return -1;
}
LWIP_ASSERT("newconn != NULL", newconn != NULL);
-#if ESP_AUTO_RECV
- /* Prevent automatic window updates, we do this on our own! */
- netconn_set_noautorecved(newconn, 1);
-#endif
newsock = alloc_socket(newconn, 1);
if (newsock == -1) {
netconn_delete(newconn);
sock_set_errno(sock, ENFILE);
+ done_socket(sock);
return -1;
}
LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
- LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
/* See event_callback: If data comes in right away after an accept, even
@@ -744,14 +583,23 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
* so nsock->rcvevent is >= 1 here!
*/
SYS_ARCH_PROTECT(lev);
- nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+ recvevent = (s16_t)(-1 - newconn->socket);
newconn->socket = newsock;
SYS_ARCH_UNPROTECT(lev);
+ if (newconn->callback) {
+ LOCK_TCPIP_CORE();
+ while(recvevent > 0) {
+ recvevent--;
+ newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ UNLOCK_TCPIP_CORE();
+ }
+
/* Note that POSIX only requires us to check addr is non-NULL. addrlen must
* not be NULL if addr is valid.
*/
- if (addr != NULL) {
+ if ((addr != NULL) && (addrlen != NULL)) {
union sockaddr_aligned tempaddr;
/* get the IP address and port of the remote host */
err = netconn_peer(newconn, &naddr, &port);
@@ -760,9 +608,9 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
netconn_delete(newconn);
free_socket(nsock, 1);
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
- LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
if (*addrlen > tempaddr.sa.sa_len) {
@@ -778,6 +626,8 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
}
sock_set_errno(sock, 0);
+ done_socket(sock);
+ done_socket(nsock);
return newsock;
}
@@ -797,13 +647,14 @@ lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
/* sockaddr does not match socket type (IPv4/IPv6) */
sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
return -1;
}
/* check size, family and alignment of 'name' */
LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
LWIP_UNUSED_ARG(namelen);
SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
@@ -824,11 +675,13 @@ lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
@@ -848,36 +701,23 @@ lwip_close(int s)
if (sock->conn != NULL) {
is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
-#if ESP_THREAD_SAFE
- LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: is_tcp=%d\n", is_tcp));
-#endif
} else {
- LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
}
-#if ESP_LWIP
-#if (LWIP_IGMP) || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS)
- /* drop all possibly joined IGMP memberships */
- lwip_socket_drop_registered_memberships(s);
-#endif
-#else
#if LWIP_IGMP
/* drop all possibly joined IGMP memberships */
lwip_socket_drop_registered_memberships(s);
#endif /* LWIP_IGMP */
-#endif
-
err = netconn_delete(sock->conn);
if (err != ERR_OK) {
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
-#if !ESP_THREAD_SAFE
free_socket(sock, is_tcp);
-#endif
-
set_errno(0);
return 0;
}
@@ -896,6 +736,7 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
/* sockaddr does not match socket type (IPv4/IPv6) */
sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
return -1;
}
@@ -910,7 +751,7 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
/* check size, family and alignment of 'name' */
LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
@@ -931,11 +772,13 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
@@ -969,92 +812,70 @@ lwip_listen(int s, int backlog)
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
- return -1;
+ } else {
+ sock_set_errno(sock, err_to_errno(err));
}
- sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
-#if ESP_LWIP
-int ESP_IRAM_ATTR
-#else
-int
-#endif
-lwip_recvfrom(int s, void *mem, size_t len, int flags,
- struct sockaddr *from, socklen_t *fromlen)
+#if LWIP_TCP
+/* Helper function to loop over receiving pbufs from netconn
+ * until "len" bytes are received or we're otherwise done.
+ * Keeps sock->lastdata for peeking or partly copying.
+ */
+static ssize_t
+lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
{
- struct lwip_sock *sock;
- void *buf = NULL;
- struct pbuf *p;
- u16_t buflen, copylen;
- int off = 0;
- u8_t done = 0;
- err_t err;
+ u8_t apiflags = NETCONN_NOAUTORCVD;
+ ssize_t recvd = 0;
+ ssize_t recv_left = (len <= SSIZE_MAX) ? (ssize_t)len : SSIZE_MAX;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
- sock = get_socket(s);
- if (!sock) {
- return -1;
+ LWIP_ASSERT("no socket given", sock != NULL);
+ LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
+
+ if (flags & MSG_DONTWAIT) {
+ apiflags |= NETCONN_DONTBLOCK;
}
do {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+ struct pbuf *p;
+ err_t err;
+ u16_t copylen;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void*)sock->lastdata.pbuf));
/* Check if there is data left from the last recv operation. */
- if (sock->lastdata) {
- buf = sock->lastdata;
+ if (sock->lastdata.pbuf) {
+ p = sock->lastdata.pbuf;
} else {
- /* If this is non-blocking call, then check first */
- if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
- (sock->rcvevent <= 0)) {
- if (off > 0) {
-#if ESP_AUTO_RECV
- /* update receive window */
- netconn_recved(sock->conn, (u32_t)off);
-#endif
- /* already received data, return that */
- sock_set_errno(sock, 0);
- return off;
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
-#if ESP_LWIP
- sock_set_errno(sock, EWOULDBLOCK);
-#else
- set_errno(EWOULDBLOCK);
-#endif
- return -1;
- }
-
/* No data was left from the previous operation, so we try to get
some from the network. */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
- } else {
- err = netconn_recv(sock->conn, (struct netbuf **)&buf);
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
- err, buf));
+ err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
+ err, (void*)p));
if (err != ERR_OK) {
- if (off > 0) {
-#if ESP_AUTO_RECV
- /* update receive window */
- netconn_recved(sock->conn, (u32_t)off);
-#endif
+ if (recvd > 0) {
+ /* already received data, return that (this trusts in getting the same error from
+ netconn layer again next time netconn_recv is called) */
if (err == ERR_CLSD) {
/* closed but already received data, ensure select gets the FIN, too */
- event_callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+ if (sock->conn->callback != NULL) {
+ LOCK_TCPIP_CORE();
+ sock->conn->callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+ UNLOCK_TCPIP_CORE();
+ }
}
- /* already received data, return that */
- sock_set_errno(sock, 0);
- return off;
+ goto lwip_recv_tcp_done;
}
/* We should really do some error checking here. */
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
- s, lwip_strerr(err)));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
+ lwip_strerr(err)));
sock_set_errno(sock, err_to_errno(err));
if (err == ERR_CLSD) {
return 0;
@@ -1062,183 +883,302 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
return -1;
}
}
- LWIP_ASSERT("buf != NULL", buf != NULL);
- sock->lastdata = buf;
- }
-
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- p = (struct pbuf *)buf;
- } else {
- p = ((struct netbuf *)buf)->p;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ sock->lastdata.pbuf = p;
}
- buflen = p->tot_len;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
- buflen, len, off, sock->lastoffset));
- buflen -= sock->lastoffset;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
+ p->tot_len, (int)recv_left, (int)recvd));
- if (len > buflen) {
- copylen = buflen;
+ if (recv_left > p->tot_len) {
+ copylen = p->tot_len;
} else {
- copylen = (u16_t)len;
+ copylen = (u16_t)recv_left;
+ }
+ if (recvd + copylen < recvd) {
+ /* overflow */
+ copylen = (u16_t)(SSIZE_MAX - recvd);
}
/* copy the contents of the received buffer into
the supplied memory pointer mem */
- pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
-
- off += copylen;
-
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
- len -= copylen;
- if ((len <= 0) ||
- (p->flags & PBUF_FLAG_PUSH) ||
- (sock->rcvevent <= 0) ||
- ((flags & MSG_PEEK) != 0)) {
- done = 1;
- }
- } else {
- done = 1;
- }
-
- /* Check to see from where the data was.*/
- if (done) {
-#if !SOCKETS_DEBUG
- if (from && fromlen)
-#endif /* !SOCKETS_DEBUG */
- {
- u16_t port;
- ip_addr_t tmpaddr;
- ip_addr_t *fromaddr;
- union sockaddr_aligned saddr;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- fromaddr = &tmpaddr;
- netconn_getaddr(sock->conn, fromaddr, &port, 0);
- } else {
- port = netbuf_fromport((struct netbuf *)buf);
- fromaddr = netbuf_fromaddr((struct netbuf *)buf);
- }
+ pbuf_copy_partial(p, (u8_t*)mem + recvd, copylen, 0);
-#if LWIP_IPV4 && LWIP_IPV6
- /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
- if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) {
- ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
- IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
- }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ recvd += copylen;
- IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
- ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
- LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
-#if SOCKETS_DEBUG
- if (from && fromlen)
-#endif /* SOCKETS_DEBUG */
- {
- if (*fromlen > saddr.sa.sa_len) {
- *fromlen = saddr.sa.sa_len;
- }
- MEMCPY(from, &saddr, *fromlen);
- }
- }
-#if ESP_LWIP
- /*fix the code for setting the UDP PROTO's remote infomation by liuh at 2014.8.27*/
- if (!from || !fromlen) {
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP) {
- u16_t port;
- ip_addr_t *fromaddr;
-
- port = netbuf_fromport((struct netbuf *)buf);
- fromaddr = netbuf_fromaddr((struct netbuf *)buf);
-
- sock->conn->pcb.udp->remote_ip.u_addr.ip4.addr = fromaddr->u_addr.ip4.addr;
- sock->conn->pcb.udp->remote_port = port;
- }
- }
-#endif
- }
+ /* TCP combines multiple pbufs for one recv */
+ LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
+ recv_left -= copylen;
- /* If we don't peek the incoming message... */
+ /* Unless we peek the incoming message... */
if ((flags & MSG_PEEK) == 0) {
- /* If this is a TCP socket, check if there is data left in the
- buffer. If so, it should be saved in the sock structure for next
- time around. */
- if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) {
- sock->lastdata = buf;
- sock->lastoffset += copylen;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+ /* ... check if there is data left in the pbuf */
+ LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
+ if (p->tot_len - copylen > 0) {
+ /* If so, it should be saved in the sock structure for the next recv call.
+ We store the pbuf but hide/free the consumed data: */
+ sock->lastdata.pbuf = pbuf_free_header(p, copylen);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void*)sock->lastdata.pbuf));
} else {
- sock->lastdata = NULL;
- sock->lastoffset = 0;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- pbuf_free((struct pbuf *)buf);
- } else {
- netbuf_delete((struct netbuf *)buf);
- }
- buf = NULL;
+ sock->lastdata.pbuf = NULL;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void*)p));
+ pbuf_free(p);
}
}
- } while (!done);
-
-#if ESP_AUTO_RECV
- if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) &&
- ((flags & MSG_PEEK) == 0)) {
- /* update receive window */
- netconn_recved(sock->conn, (u32_t)off);
+ /* once we have some data to return, only add more if we don't need to wait */
+ apiflags |= NETCONN_DONTBLOCK;
+ /* @todo: do we need to support peeking more than one pbuf? */
+ } while ((recv_left > 0) || (flags & MSG_PEEK));
+lwip_recv_tcp_done:
+ if (recvd > 0) {
+ /* ensure window update after copying all data */
+ netconn_tcp_recvd(sock->conn, (size_t)recvd);
}
-#endif
sock_set_errno(sock, 0);
- return off;
+ return recvd;
}
+#endif
-int
-lwip_read(int s, void *mem, size_t len)
+/* Convert a netbuf's address data to struct sockaddr */
+static int
+lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
+ struct sockaddr *from, socklen_t *fromlen)
{
- return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+ int truncated = 0;
+ union sockaddr_aligned saddr;
+
+ LWIP_UNUSED_ARG(conn);
+
+ LWIP_ASSERT("fromaddr != NULL", fromaddr != NULL);
+ LWIP_ASSERT("from != NULL", from != NULL);
+ LWIP_ASSERT("fromlen != NULL", fromlen != NULL);
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(conn)) && IP_IS_V4(fromaddr)) {
+ ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+ IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+ if (*fromlen < saddr.sa.sa_len) {
+ truncated = 1;
+ } else if (*fromlen > saddr.sa.sa_len) {
+ *fromlen = saddr.sa.sa_len;
+ }
+ MEMCPY(from, &saddr, *fromlen);
+ return truncated;
}
-int
-lwip_recv(int s, void *mem, size_t len, int flags)
+#if LWIP_TCP
+/* Helper function to get a tcp socket's remote address info */
+static int
+lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fromlen, const char *dbg_fn, int dbg_s, ssize_t dbg_ret)
{
- return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+ if (sock == NULL) {
+ return 0;
+ }
+ LWIP_UNUSED_ARG(dbg_fn);
+ LWIP_UNUSED_ARG(dbg_s);
+ LWIP_UNUSED_ARG(dbg_ret);
+
+#if !SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ /* get remote addr/port from tcp_pcb */
+ u16_t port;
+ ip_addr_t tmpaddr;
+ netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%s(%d): addr=", dbg_fn, dbg_s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, tmpaddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, (int)dbg_ret));
+ if (from && fromlen) {
+ return lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
+ }
+ }
+ return 0;
}
+#endif
-int
-lwip_send(int s, const void *data, size_t size, int flags)
+/* Helper function to receive a netbuf from a udp or raw netconn.
+ * Keeps sock->lastdata for peeking.
+ */
+static err_t
+lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
{
- struct lwip_sock *sock;
+ struct netbuf *buf;
+ u8_t apiflags;
err_t err;
- u8_t write_flags;
- size_t written;
+ u16_t buflen, copylen, copied;
+ int i;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
- s, data, size, flags));
+ LWIP_UNUSED_ARG(dbg_s);
+ LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
- sock = get_socket(s);
- if (!sock) {
- return -1;
+ if (flags & MSG_DONTWAIT) {
+ apiflags = NETCONN_DONTBLOCK;
+ } else {
+ apiflags = 0;
}
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
-#if (LWIP_UDP || LWIP_RAW)
- return lwip_sendto(s, data, size, flags, NULL, 0);
-#else /* (LWIP_UDP || LWIP_RAW) */
- sock_set_errno(sock, err_to_errno(ERR_ARG));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: top sock->lastdata=%p\n", (void*)sock->lastdata.netbuf));
+ /* Check if there is data left from the last recv operation. */
+ buf = sock->lastdata.netbuf;
+ if (buf == NULL) {
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
+ err, (void*)buf));
+
+ if (err != ERR_OK) {
+ return err;
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata.netbuf = buf;
+ }
+ buflen = buf->p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw: buflen=%"U16_F"\n", buflen));
+
+ copied = 0;
+ /* copy the pbuf payload into the iovs */
+ for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
+ u16_t len_left = (u16_t)(buflen - copied);
+ if (msg->msg_iov[i].iov_len > len_left) {
+ copylen = len_left;
+ } else {
+ copylen = (u16_t)msg->msg_iov[i].iov_len;
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory buffer */
+ pbuf_copy_partial(buf->p, (u8_t*)msg->msg_iov[i].iov_base, copylen, copied);
+ copied = (u16_t)(copied + copylen);
+ }
+
+ /* Check to see from where the data was.*/
+#if !SOCKETS_DEBUG
+ if (msg->msg_name && msg->msg_namelen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d): addr=", dbg_s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, *netbuf_fromaddr(buf));
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
+ if (msg->msg_name && msg->msg_namelen) {
+ lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
+ (struct sockaddr *)msg->msg_name, &msg->msg_namelen);
+ }
+ }
+
+ /* Initialize flag output */
+ msg->msg_flags = 0;
+
+ if (msg->msg_control){
+ u8_t wrote_msg = 0;
+#if LWIP_NETBUF_RECVINFO
+ /* Check if packet info was recorded */
+ if (buf->flags & NETBUF_FLAG_DESTADDR) {
+ if (IP_IS_V4(&buf->toaddr)) {
+#if LWIP_IPV4
+ if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
+ struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
+ struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
+ chdr->cmsg_level = IPPROTO_IP;
+ chdr->cmsg_type = IP_PKTINFO;
+ chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ pkti->ipi_ifindex = buf->p->if_idx;
+ inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
+ msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+ wrote_msg = 1;
+ } else {
+ msg->msg_flags |= MSG_CTRUNC;
+ }
+#endif /* LWIP_IPV4 */
+ }
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+
+ if (!wrote_msg) {
+ msg->msg_controllen = 0;
+ }
+ }
+
+ /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
+ if ((flags & MSG_PEEK) == 0) {
+ sock->lastdata.netbuf = NULL;
+ netbuf_delete(buf);
+ }
+ if (datagram_len) {
+ *datagram_len = buflen;
+ }
+ return ERR_OK;
+}
+
+ssize_t
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ ssize_t ret;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+ sock = get_socket(s);
+ if (!sock) {
return -1;
-#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+#if LWIP_TCP
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ ret = lwip_recv_tcp(sock, mem, len, flags);
+ lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);
+ done_socket(sock);
+ return ret;
+ } else
+#endif
+ {
+ u16_t datagram_len = 0;
+ struct iovec vec;
+ struct msghdr msg;
+ err_t err;
+ vec.iov_base = mem;
+ vec.iov_len = len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_name = from;
+ msg.msg_namelen = (fromlen ? *fromlen : 0);
+ err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+ ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
+ if (fromlen) {
+ *fromlen = msg.msg_namelen;
+ }
}
- write_flags = NETCONN_COPY |
- ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
- ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
- written = 0;
- err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return ret;
+}
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
- sock_set_errno(sock, err_to_errno(err));
- return (err == ERR_OK ? (int)written : -1);
+ssize_t
+lwip_read(int s, void *mem, size_t len)
+{
+ return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+ssize_t
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+ return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
}
ssize_t
@@ -1248,10 +1188,10 @@ lwip_recvmsg(int s, struct msghdr *message, int flags)
int i;
ssize_t buflen;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void*)message, flags));
LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
- LWIP_ERROR("lwip_recvmsg: unsupported flags", (flags & ~(MSG_PEEK|MSG_DONTWAIT)) == 0,
- set_errno(EOPNOTSUPP); return -1;);
+ LWIP_ERROR("lwip_recvmsg: unsupported flags", ((flags == 0) || (flags == MSG_PEEK)),
+ set_errno(EOPNOTSUPP); return -1;);
if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
set_errno(EMSGSIZE);
@@ -1267,55 +1207,130 @@ lwip_recvmsg(int s, struct msghdr *message, int flags)
buflen = 0;
for (i = 0; i < message->msg_iovlen; i++) {
if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
- ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
- ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
+ ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
+ ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
sock_set_errno(sock, ERR_VAL);
+ done_socket(sock);
return -1;
}
buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
}
- int recv_flags = flags;
- message->msg_flags = 0;
- /* recv the data */
- buflen = 0;
- for (i = 0; i < message->msg_iovlen; i++) {
- /* try to receive into this vector's buffer */
- ssize_t recvd_local = lwip_recvfrom(s, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags, NULL, NULL);
- if (recvd_local > 0) {
- /* sum up received bytes */
- buflen += recvd_local;
- }
- if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
- (flags & MSG_PEEK)) {
- /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
- if (buflen <= 0) {
- /* nothing received at all, propagate the error */
- buflen = recvd_local;
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ int recv_flags = flags;
+ message->msg_flags = 0;
+ /* recv the data */
+ buflen = 0;
+ for (i = 0; i < message->msg_iovlen; i++) {
+ /* try to receive into this vector's buffer */
+ ssize_t recvd_local = lwip_recv_tcp(sock, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags);
+ if (recvd_local > 0) {
+ /* sum up received bytes */
+ buflen += recvd_local;
}
- break;
+ if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
+ (flags & MSG_PEEK)) {
+ /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
+ if (buflen <= 0) {
+ /* nothing received at all, propagate the error */
+ buflen = recvd_local;
+ }
+ break;
+ }
+ /* while MSG_DONTWAIT is not supported for this function, we pass it to
+ lwip_recv_tcp() to prevent waiting for more data */
+ recv_flags |= MSG_DONTWAIT;
+ }
+ if (buflen > 0) {
+ /* reset socket error since we have received something */
+ sock_set_errno(sock, 0);
}
- /* pass MSG_DONTWAIT to lwip_recv_tcp() to prevent waiting for more data */
- recv_flags |= MSG_DONTWAIT;
+ /* " If the socket is connected, the msg_name and msg_namelen members shall be ignored." */
+ done_socket(sock);
+ return buflen;
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_TCP */
}
- if (buflen > 0) {
- /* reset socket error since we have received something */
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+ {
+ u16_t datagram_len = 0;
+ err_t err;
+ err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+ if (datagram_len > buflen) {
+ message->msg_flags |= MSG_TRUNC;
+ }
+
sock_set_errno(sock, 0);
+ done_socket(sock);
+ return (int)datagram_len;
+ }
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t write_flags;
+ size_t written;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+ s, data, size, flags));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ done_socket(sock);
+ return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
}
- return buflen;
+ write_flags = (u8_t)(NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
+ written = 0;
+ err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+ return (err == ERR_OK ? (ssize_t)written : -1);
}
-int
+ssize_t
lwip_sendmsg(int s, const struct msghdr *msg, int flags)
{
struct lwip_sock *sock;
- int i;
#if LWIP_TCP
u8_t write_flags;
size_t written;
#endif
- int size = 0;
err_t err = ERR_OK;
sock = get_socket(s);
@@ -1324,90 +1339,82 @@ lwip_sendmsg(int s, const struct msghdr *msg, int flags)
}
LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
+ sock_set_errno(sock, EMSGSIZE); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: unsupported flags", (flags & ~(MSG_DONTWAIT|MSG_MORE)) == 0,
+ sock_set_errno(sock, EOPNOTSUPP); done_socket(sock); return -1;);
LWIP_UNUSED_ARG(msg->msg_control);
LWIP_UNUSED_ARG(msg->msg_controllen);
LWIP_UNUSED_ARG(msg->msg_flags);
- LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
#if LWIP_TCP
- write_flags = NETCONN_COPY |
+ write_flags = (u8_t)(NETCONN_COPY |
((flags & MSG_MORE) ? NETCONN_MORE : 0) |
- ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
- for (i = 0; i < msg->msg_iovlen; i++) {
- u8_t apiflags = write_flags;
- if (i + 1 < msg->msg_iovlen) {
- apiflags |= NETCONN_MORE;
- }
- written = 0;
- err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written);
- if (err == ERR_OK) {
- size += written;
- /* check that the entire IO vector was accepected, if not return a partial write */
- if (written != msg->msg_iov[i].iov_len)
- break;
- }
- /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */
- else if (err == ERR_WOULDBLOCK && size > 0) {
- err = ERR_OK;
- /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */
- break;
- } else {
- size = -1;
- break;
- }
- }
+ written = 0;
+ err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
sock_set_errno(sock, err_to_errno(err));
- return size;
+ done_socket(sock);
+ /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+ return (err == ERR_OK ? (ssize_t)written : -1);
#else /* LWIP_TCP */
sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
return -1;
#endif /* LWIP_TCP */
}
/* else, UDP and RAW NETCONNs */
#if LWIP_UDP || LWIP_RAW
{
- struct netbuf *chain_buf;
+ struct netbuf chain_buf;
+ int i;
+ ssize_t size = 0;
LWIP_UNUSED_ARG(flags);
LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
/* initialize chain buffer with destination */
- chain_buf = netbuf_new();
- if (!chain_buf) {
- sock_set_errno(sock, err_to_errno(ERR_MEM));
- return -1;
- }
+ memset(&chain_buf, 0, sizeof(struct netbuf));
if (msg->msg_name) {
u16_t remote_port;
- SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf->addr, remote_port);
- netbuf_fromport(chain_buf) = remote_port;
+ SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf.addr, remote_port);
+ netbuf_fromport(&chain_buf) = remote_port;
}
#if LWIP_NETIF_TX_SINGLE_PBUF
for (i = 0; i < msg->msg_iovlen; i++) {
size += msg->msg_iov[i].iov_len;
+ if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ }
+ if (size > 0xFFFF) {
+ /* overflow */
+ goto sendmsg_emsgsize;
}
/* Allocate a new netbuf and copy the data into it. */
- if (netbuf_alloc(chain_buf, (u16_t)size) == NULL) {
+ if (netbuf_alloc(&chain_buf, (u16_t)size) == NULL) {
err = ERR_MEM;
} else {
/* flatten the IO vectors */
size_t offset = 0;
for (i = 0; i < msg->msg_iovlen; i++) {
- MEMCPY(&((u8_t*)chain_buf->p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+ MEMCPY(&((u8_t*)chain_buf.p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
offset += msg->msg_iov[i].iov_len;
}
#if LWIP_CHECKSUM_ON_COPY
{
/* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
- u16_t chksum = ~inet_chksum_pbuf(chain_buf->p);
- netbuf_set_chksum(chain_buf, chksum);
+ u16_t chksum = ~inet_chksum_pbuf(chain_buf.p);
+ netbuf_set_chksum(&chain_buf, chksum);
}
#endif /* LWIP_CHECKSUM_ON_COPY */
err = ERR_OK;
@@ -1416,58 +1423,70 @@ lwip_sendmsg(int s, const struct msghdr *msg, int flags)
/* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
manually to avoid having to allocate, chain, and delete a netbuf for each iov */
for (i = 0; i < msg->msg_iovlen; i++) {
- struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ struct pbuf *p;
+ if (msg->msg_iov[i].iov_len > 0xFFFF) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
if (p == NULL) {
err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
break;
}
p->payload = msg->msg_iov[i].iov_base;
- LWIP_ASSERT("iov_len < u16_t", msg->msg_iov[i].iov_len <= 0xFFFF);
p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
/* netbuf empty, add new pbuf */
- if (chain_buf->p == NULL) {
- chain_buf->p = chain_buf->ptr = p;
+ if (chain_buf.p == NULL) {
+ chain_buf.p = chain_buf.ptr = p;
/* add pbuf to existing pbuf chain */
} else {
- pbuf_cat(chain_buf->p, p);
+ if (chain_buf.p->tot_len + p->len > 0xffff) {
+ /* overflow */
+ pbuf_free(p);
+ goto sendmsg_emsgsize;
+ }
+ pbuf_cat(chain_buf.p, p);
}
}
/* save size of total chain */
if (err == ERR_OK) {
- size = netbuf_len(chain_buf);
+ size = netbuf_len(&chain_buf);
}
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
if (err == ERR_OK) {
#if LWIP_IPV4 && LWIP_IPV6
/* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
- if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf->addr))) {
- unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf->addr), ip_2_ip6(&chain_buf->addr));
- IP_SET_TYPE_VAL(chain_buf->addr, IPADDR_TYPE_V4);
+ if (IP_IS_V6_VAL(chain_buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf.addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf.addr), ip_2_ip6(&chain_buf.addr));
+ IP_SET_TYPE_VAL(chain_buf.addr, IPADDR_TYPE_V4);
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
/* send the data */
- err = netconn_send(sock->conn, chain_buf);
+ err = netconn_send(sock->conn, &chain_buf);
}
/* deallocated the buffer */
- netbuf_delete(chain_buf);
+ netbuf_free(&chain_buf);
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return (err == ERR_OK ? size : -1);
+sendmsg_emsgsize:
+ sock_set_errno(sock, EMSGSIZE);
+ netbuf_free(&chain_buf);
+ done_socket(sock);
+ return -1;
}
#else /* LWIP_UDP || LWIP_RAW */
sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
return -1;
#endif /* LWIP_UDP || LWIP_RAW */
}
-#if ESP_LWIP
-int ESP_IRAM_ATTR
-#else
-int
-#endif
+ssize_t
lwip_sendto(int s, const void *data, size_t size, int flags,
const struct sockaddr *to, socklen_t tolen)
{
@@ -1484,21 +1503,27 @@ lwip_sendto(int s, const void *data, size_t size, int flags,
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
#if LWIP_TCP
+ done_socket(sock);
return lwip_send(s, data, size, flags);
#else /* LWIP_TCP */
LWIP_UNUSED_ARG(flags);
sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
return -1;
#endif /* LWIP_TCP */
}
- /* @todo: split into multiple sendto's? */
- LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+ if (size > LWIP_MIN(0xFFFF, SSIZE_MAX)) {
+ /* cannot fit into one datagram (at least for us) */
+ sock_set_errno(sock, EMSGSIZE);
+ done_socket(sock);
+ return -1;
+ }
short_size = (u16_t)size;
LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
(IS_SOCK_ADDR_LEN_VALID(tolen) &&
IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
LWIP_UNUSED_ARG(tolen);
/* initialize a buffer */
@@ -1517,7 +1542,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags,
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
s, data, short_size, flags));
- ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, buf.addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
/* make the buffer point to the data that should be sent */
@@ -1557,6 +1582,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags,
netbuf_free(&buf);
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return (err == ERR_OK ? short_size : -1);
}
@@ -1572,27 +1598,27 @@ lwip_socket(int domain, int type, int protocol)
switch (type) {
case SOCK_RAW:
conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
- (u8_t)protocol, event_callback);
+ (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
break;
case SOCK_DGRAM:
conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
- event_callback);
+ DEFAULT_SOCKET_EVENTCB);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+#if LWIP_NETBUF_RECVINFO
+ if (conn) {
+ /* netconn layer enables pktinfo by default, sockets default to off */
+ conn->flags &= ~NETCONN_FLAG_PKTINFO;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
break;
case SOCK_STREAM:
- conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback);
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
-#if ESP_AUTO_RECV
- if (conn != NULL) {
- /* Prevent automatic window updates, we do this on our own! */
- netconn_set_noautorecved(conn, 1);
- }
-#endif
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
@@ -1615,18 +1641,19 @@ lwip_socket(int domain, int type, int protocol)
return -1;
}
conn->socket = i;
+ done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
set_errno(0);
return i;
}
-int
+ssize_t
lwip_write(int s, const void *data, size_t size)
{
return lwip_send(s, data, size, 0);
}
-int
+ssize_t
lwip_writev(int s, const struct iovec *iov, int iovcnt)
{
struct msghdr msg;
@@ -1643,6 +1670,7 @@ lwip_writev(int s, const struct iovec *iov, int iovcnt)
return lwip_sendmsg(s, &msg, 0);
}
+#if LWIP_SOCKET_SELECT
/**
* Go through the readset and writeset lists and see which socket of the sockets
* set in the sets has events. On return, readset, writeset and exceptset have
@@ -1681,9 +1709,9 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
}
/* First get the socket's status (protected)... */
SYS_ARCH_PROTECT(lev);
- sock = tryget_socket(i);
+ sock = tryget_socket_unconn(i);
if (sock != NULL) {
- void* lastdata = sock->lastdata;
+ void* lastdata = sock->lastdata.pbuf;
s16_t rcvevent = sock->rcvevent;
u16_t sendevent = sock->sendevent;
u16_t errevent = sock->errevent;
@@ -1708,9 +1736,11 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
nready++;
}
+ done_socket(sock);
} else {
SYS_ARCH_UNPROTECT(lev);
- /* continue on to next FD in list */
+ /* no a valid open socket */
+ return -1;
}
}
/* copy local sets to the ones provided as arguments */
@@ -1722,6 +1752,68 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
return nready;
}
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all of the set sockets in one of the three fdsets passed to select as used.
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_selscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+ if (fdset) {
+ int i;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+ /* if this FD is in the set, lock it (unless already done) */
+ if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn(i);
+ if (sock != NULL) {
+ /* leave the socket used until released by lwip_select_dec_sockets_used */
+ FD_SET(i, used_sockets);
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ }
+}
+
+/* Mark all sockets passed to select as used to prevent them from being freed
+ * from other threads while select is running.
+ * Marked sockets are added to 'used_sockets' to mark them only once an be able
+ * to unmark them correctly.
+ */
+static void
+lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
+{
+ FD_ZERO(used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
+}
+
+/* Let go all sockets that were marked as used when starting select */
+static void
+lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
+{
+ int i;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+ /* if this FD is not in the set, continue */
+ if (FD_ISSET(i, used_sockets)) {
+ struct lwip_sock *sock = tryget_socket_unconn_nouse(i);
+ LWIP_ASSERT("socket gone at the end of select", sock != NULL);
+ if (sock != NULL) {
+ done_socket(sock);
+ }
+ }
+ }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
+#define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
int
lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
struct timeval *timeout)
@@ -1735,18 +1827,35 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
int maxfdp2;
#if LWIP_NETCONN_SEM_PER_THREAD
int waited = 0;
+#endif
+#if LWIP_NETCONN_FULLDUPLEX
+ fd_set used_sockets;
#endif
SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_SOCKET_SELECT_DECL_PROTECT(lev2);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+ if ((maxfdp1 < 0) || (maxfdp1 > (FD_SETSIZE + LWIP_SOCKET_OFFSET))) {
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
+
/* Go through each socket in each list to count number of sockets which
currently match */
nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (nready < 0) {
+ set_errno(EBADF);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ return -1;
+ }
+
/* If we don't have any current events, then suspend if we are supposed to */
if (!nready) {
if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
@@ -1773,12 +1882,13 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
/* failed to create semaphore */
set_errno(ENOMEM);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
return -1;
}
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
/* Protect the select_cb_list */
- SYS_ARCH_PROTECT(lev);
+ LWIP_SOCKET_SELECT_PROTECT(lev2);
/* Put this select_cb on top of list */
select_cb.next = select_cb_list;
@@ -1786,11 +1896,13 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
select_cb_list->prev = &select_cb;
}
select_cb_list = &select_cb;
- /* Increasing this counter tells event_callback that the list has changed. */
+#if !LWIP_TCPIP_CORE_LOCKING
+ /* Increasing this counter tells select_check_waiters that the list has changed. */
select_cb_ctr++;
+#endif
/* Now we can safely unprotect */
- SYS_ARCH_UNPROTECT(lev);
+ LWIP_SOCKET_SELECT_UNPROTECT(lev2);
/* Increase select_waiting for each socket we are interested in */
maxfdp2 = maxfdp1;
@@ -1800,15 +1912,26 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
(exceptset && FD_ISSET(i, exceptset))) {
struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev);
- sock = tryget_socket(i);
+ sock = tryget_socket_unconn(i);
if (sock != NULL) {
sock->select_waiting++;
- LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ if (sock->select_waiting == 0) {
+ /* overflow - too many threads waiting */
+ sock->select_waiting--;
+ done_socket(sock);
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ set_errno(EBUSY);
+ break;
+ }
+ done_socket(sock);
} else {
/* Not a valid socket */
nready = -1;
maxfdp2 = i;
SYS_ARCH_UNPROTECT(lev);
+ set_errno(EBADF);
break;
}
SYS_ARCH_UNPROTECT(lev);
@@ -1825,10 +1948,12 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
/* Wait forever */
msectimeout = 0;
} else {
- msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
- if (msectimeout == 0) {
+ long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+ if (msecs_long <= 0) {
/* Wait 1ms at least (0 means wait forever) */
msectimeout = 1;
+ } else {
+ msectimeout = (u32_t)msecs_long;
}
}
@@ -1846,22 +1971,24 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
(exceptset && FD_ISSET(i, exceptset))) {
struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev);
- sock = tryget_socket(i);
+ sock = tryget_socket_unconn(i);
if (sock != NULL) {
/* for now, handle select_waiting==0... */
LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
if (sock->select_waiting > 0) {
sock->select_waiting--;
}
+ done_socket(sock);
} else {
/* Not a valid socket */
nready = -1;
+ set_errno(EBADF);
}
SYS_ARCH_UNPROTECT(lev);
}
}
/* Take us off the list */
- SYS_ARCH_PROTECT(lev);
+ LWIP_SOCKET_SELECT_PROTECT(lev2);
if (select_cb.next != NULL) {
select_cb.next->prev = select_cb.prev;
}
@@ -1872,9 +1999,11 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
select_cb.prev->next = select_cb.next;
}
- /* Increasing this counter tells event_callback that the list has changed. */
+#if !LWIP_TCPIP_CORE_LOCKING
+ /* Increasing this counter tells select_check_waiters that the list has changed. */
select_cb_ctr++;
- SYS_ARCH_UNPROTECT(lev);
+#endif
+ LWIP_SOCKET_SELECT_UNPROTECT(lev2);
#if LWIP_NETCONN_SEM_PER_THREAD
if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
@@ -1887,7 +2016,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
if (nready < 0) {
/* This happens when a socket got closed while waiting */
- set_errno(EBADF);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
return -1;
}
@@ -1905,6 +2034,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
return_copy_fdsets:
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
set_errno(0);
if (readset) {
*readset = lreadset;
@@ -1921,18 +2051,19 @@ return_copy_fdsets:
/**
* Callback registered in the netconn layer for each socket-netconn.
* Processes recvevent (data available) and wakes up tasks waiting for select.
+ *
+ * @note for LWIP_TCPIP_CORE_LOCKING any caller of this function
+ * must have the core lock held when signaling the following events
+ * as they might cause select_list_cb to be checked:
+ * NETCONN_EVT_RCVPLUS
+ * NETCONN_EVT_SENDPLUS
+ * NETCONN_EVT_ERROR
*/
-#if ESP_LWIP
-static void ESP_IRAM_ATTR
-#else
static void
-#endif
event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
{
- int s;
+ int s, check_waiters;
struct lwip_sock *sock;
- struct lwip_select_cb *scb;
- int last_select_cb_ctr;
SYS_ARCH_DECL_PROTECT(lev);
LWIP_UNUSED_ARG(len);
@@ -1949,6 +2080,8 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
SYS_ARCH_PROTECT(lev);
if (conn->socket < 0) {
if (evt == NETCONN_EVT_RCVPLUS) {
+ /* conn->socket is -1 on initialization
+ lwip_accept adjusts sock->recvevent if conn->socket < -1 */
conn->socket--;
}
SYS_ARCH_UNPROTECT(lev);
@@ -1966,21 +2099,28 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
return;
}
+ check_waiters = 1;
SYS_ARCH_PROTECT(lev);
/* Set event as required */
switch (evt) {
case NETCONN_EVT_RCVPLUS:
sock->rcvevent++;
+ if (sock->rcvevent > 1) {
+ check_waiters = 0;
+ }
break;
case NETCONN_EVT_RCVMINUS:
sock->rcvevent--;
- break;
+ check_waiters = 0;
case NETCONN_EVT_SENDPLUS:
+ if (sock->sendevent) {
+ check_waiters = 0;
+ }
sock->sendevent = 1;
break;
case NETCONN_EVT_SENDMINUS:
sock->sendevent = 0;
- break;
+ check_waiters = 0;
case NETCONN_EVT_ERROR:
sock->errevent = 1;
break;
@@ -1989,48 +2129,79 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
break;
}
- if (sock->select_waiting == 0) {
- /* noone is waiting for this socket, no need to check select_cb_list */
+ if (sock->select_waiting && check_waiters) {
+ /* Save which events are active */
+ int has_recvevent, has_sendevent, has_errevent;
+ has_recvevent = sock->rcvevent > 0;
+ has_sendevent = sock->sendevent != 0;
+ has_errevent = sock->errevent != 0;
+ SYS_ARCH_UNPROTECT(lev);
+ /* Check any select calls waiting on this socket */
+ select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
+ } else {
SYS_ARCH_UNPROTECT(lev);
- return;
}
+ done_socket(sock);
+}
- /* Now decide if anyone is waiting for this socket */
- /* NOTE: This code goes through the select_cb_list list multiple times
- ONLY IF a select was actually waiting. We go through the list the number
- of waiting select calls + 1. This list is expected to be small. */
+/**
+ * Check if any select waiters are waiting on this socket and its events
+ *
+ * @note on synchronization of select_cb_list:
+ * LWIP_TCPIP_CORE_LOCKING: the select_cb_list must only be accessed while holding
+ * the core lock. We do a single pass through the list and signal any waiters.
+ * Core lock should already be held when calling here!!!!
+
+ * !LWIP_TCPIP_CORE_LOCKING: we use SYS_ARCH_PROTECT but unlock on each iteration
+ * of the loop, thus creating a possibility where a thread could modify the
+ * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
+ * detect this change and restart the list walk. The list is expected to be small
+ */
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
+{
+ struct lwip_select_cb *scb;
+#if !LWIP_TCPIP_CORE_LOCKING
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+#endif
- /* At this point, SYS_ARCH is still protected! */
+#if !LWIP_TCPIP_CORE_LOCKING
+ SYS_ARCH_PROTECT(lev);
again:
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
+#endif
for (scb = select_cb_list; scb != NULL; scb = scb->next) {
- /* remember the state of select_cb_list to detect changes */
- last_select_cb_ctr = select_cb_ctr;
if (scb->sem_signalled == 0) {
/* semaphore not signalled yet */
int do_signal = 0;
/* Test this select call for our socket */
- if (sock->rcvevent > 0) {
+ if (has_recvevent) {
if (scb->readset && FD_ISSET(s, scb->readset)) {
do_signal = 1;
}
}
- if (sock->sendevent != 0) {
+ if (has_sendevent) {
if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
do_signal = 1;
}
}
- if (sock->errevent != 0) {
+ if (has_errevent) {
if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
do_signal = 1;
}
}
if (do_signal) {
scb->sem_signalled = 1;
- /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
- lead to the select thread taking itself off the list, invalidating the semaphore. */
+ /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
+ the semaphore, as this might lead to the select thread taking itself off the list,
+ invalidating the semaphore. */
sys_sem_signal(SELECT_SEM_PTR(scb->sem));
}
}
+#if LWIP_TCPIP_CORE_LOCKING
+ }
+#else
/* unlock interrupts with each step */
SYS_ARCH_UNPROTECT(lev);
/* this makes sure interrupt protection time is short */
@@ -2039,9 +2210,13 @@ again:
/* someone has changed select_cb_list, restart at the beginning */
goto again;
}
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
}
SYS_ARCH_UNPROTECT(lev);
+#endif
}
+#endif /* LWIP_SOCKET_SELECT */
/**
* Close one end of a full-duplex connection.
@@ -2063,10 +2238,12 @@ lwip_shutdown(int s, int how)
if (sock->conn != NULL) {
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
+ done_socket(sock);
return -1;
}
} else {
sock_set_errno(sock, ENOTCONN);
+ done_socket(sock);
return -1;
}
@@ -2079,11 +2256,13 @@ lwip_shutdown(int s, int how)
shut_tx = 1;
} else {
sock_set_errno(sock, EINVAL);
+ done_socket(sock);
return -1;
}
err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return (err == ERR_OK ? 0 : -1);
}
@@ -2105,6 +2284,7 @@ lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
err = netconn_getaddr(sock->conn, &naddr, &port, local);
if (err != ERR_OK) {
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
@@ -2129,6 +2309,7 @@ lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
MEMCPY(name, &saddr, *namelen);
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
@@ -2147,9 +2328,10 @@ lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
int
lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
{
- u8_t err;
+ int err;
struct lwip_sock *sock = get_socket(s);
#if !LWIP_TCPIP_CORE_LOCKING
+ err_t cberr;
LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
#endif /* !LWIP_TCPIP_CORE_LOCKING */
@@ -2159,6 +2341,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
if ((NULL == optval) || (NULL == optlen)) {
sock_set_errno(sock, EFAULT);
+ done_socket(sock);
return -1;
}
@@ -2174,6 +2357,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
/* MPU_COMPATIBLE copies the optval data, so check for max size here */
if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
sock_set_errno(sock, ENOBUFS);
+ done_socket(sock);
return -1;
}
#endif /* LWIP_MPU_COMPATIBLE */
@@ -2192,10 +2376,11 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
#else
LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
#endif
- err = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
- if (err != ERR_OK) {
+ cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (cberr != ERR_OK) {
LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
- sock_set_errno(sock, err_to_errno(err));
+ sock_set_errno(sock, err_to_errno(cberr));
+ done_socket(sock);
return -1;
}
sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
@@ -2213,6 +2398,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
#endif /* LWIP_TCPIP_CORE_LOCKING */
sock_set_errno(sock, err);
+ done_socket(sock);
return err ? -1 : 0;
}
@@ -2242,10 +2428,10 @@ lwip_getsockopt_callback(void *arg)
/** lwip_getsockopt_impl: the actual implementation of getsockopt:
* same argument as lwip_getsockopt, either called directly or through callback
*/
-static u8_t
+static int
lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
{
- u8_t err = 0;
+ int err = 0;
struct lwip_sock *sock = tryget_socket(s);
if (!sock) {
return EBADF;
@@ -2261,6 +2447,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
case SO_ACCEPTCONN:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+ done_socket(sock);
return ENOPROTOOPT;
}
if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
@@ -2277,6 +2464,11 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
#if SO_REUSE
case SO_REUSEADDR:
#endif /* SO_REUSE */
+ if ((optname == SO_BROADCAST) &&
+ (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
*(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
@@ -2306,13 +2498,8 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
break;
case SO_ERROR:
- LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int);
- /* only overwrite ERR_OK or temporary errors */
- if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) {
- sock_set_errno(sock, err_to_errno(sock->conn->last_err));
- }
- *(int *)optval = (sock->err == 0xFF ? (int)-1 : (int)sock->err);
- sock->err = 0;
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, *optlen, int);
+ *(int *)optval = err_to_errno(netconn_err(sock->conn));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
s, *(int *)optval));
break;
@@ -2358,6 +2545,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
#if LWIP_UDPLITE
if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
/* this flag is only available for UDP, not for UDP lite */
+ done_socket(sock);
return EAFNOSUPPORT;
}
#endif /* LWIP_UDPLITE */
@@ -2387,10 +2575,11 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
s, *(int *)optval));
break;
-#if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
case IP_MULTICAST_TTL:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ done_socket(sock);
return ENOPROTOOPT;
}
*(u8_t*)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
@@ -2400,6 +2589,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
case IP_MULTICAST_IF:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ done_socket(sock);
return ENOPROTOOPT;
}
inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
@@ -2416,7 +2606,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
s, *(int *)optval));
break;
-#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
s, optname));
@@ -2431,6 +2621,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
/* Special case: all IPPROTO_TCP option take an int */
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
if (sock->conn->pcb.tcp->state == LISTEN) {
+ done_socket(sock);
return EINVAL;
}
switch (optname) {
@@ -2488,42 +2679,6 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
break;
} /* switch (optname) */
break;
-
-#if ESP_LWIP
-#if LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS /* Multicast options, similar to LWIP_IGMP options for IPV4 */
- case IPV6_MULTICAST_IF: /* NB: like IP_MULTICAST_IF, this returns an IP not an index */
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in6_addr);
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- return ENOPROTOOPT;
- }
- inet6_addr_from_ip6addr((struct in6_addr*)optval,
- udp_get_multicast_netif_ip6addr(sock->conn->pcb.udp));
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = 0x%"X32_F"\n",
- s, *(u32_t *)optval));
- break;
- case IPV6_MULTICAST_HOPS:
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- return ENOPROTOOPT;
- }
- *(u8_t*)optval = sock->conn->pcb.udp->mcast_ttl;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IP_MULTICAST_LOOP) = %d\n",
- s, *(int *)optval));
- break;
- case IPV6_MULTICAST_LOOP:
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
- if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
- *(u8_t*)optval = 1;
- } else {
- *(u8_t*)optval = 0;
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IP_MULTICAST_LOOP) = %d\n",
- s, *(int *)optval));
- break;
-
-#endif /* LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS */
-#endif
-
#endif /* LWIP_IPV6 */
#if LWIP_UDP && LWIP_UDPLITE
@@ -2533,6 +2688,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
/* If this is no UDP lite socket, ignore any options. */
if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ done_socket(sock);
return ENOPROTOOPT;
}
switch (optname) {
@@ -2583,15 +2739,17 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
break;
} /* switch (level) */
+ done_socket(sock);
return err;
}
int
lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
{
- u8_t err = 0;
+ int err = 0;
struct lwip_sock *sock = get_socket(s);
#if !LWIP_TCPIP_CORE_LOCKING
+ err_t cberr;
LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
#endif /* !LWIP_TCPIP_CORE_LOCKING */
@@ -2601,6 +2759,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
if (NULL == optval) {
sock_set_errno(sock, EFAULT);
+ done_socket(sock);
return -1;
}
@@ -2616,6 +2775,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
/* MPU_COMPATIBLE copies the optval data, so check for max size here */
if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
sock_set_errno(sock, ENOBUFS);
+ done_socket(sock);
return -1;
}
#endif /* LWIP_MPU_COMPATIBLE */
@@ -2636,10 +2796,11 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
#else
LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
#endif
- err = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
- if (err != ERR_OK) {
+ cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (cberr != ERR_OK) {
LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
- sock_set_errno(sock, err_to_errno(err));
+ sock_set_errno(sock, err_to_errno(cberr));
+ done_socket(sock);
return -1;
}
sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
@@ -2650,6 +2811,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
#endif /* LWIP_TCPIP_CORE_LOCKING */
sock_set_errno(sock, err);
+ done_socket(sock);
return err ? -1 : 0;
}
@@ -2679,10 +2841,10 @@ lwip_setsockopt_callback(void *arg)
/** lwip_setsockopt_impl: the actual implementation of setsockopt:
* same argument as lwip_setsockopt, either called directly or through callback
*/
-static u8_t
+static int
lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
{
- u8_t err = 0;
+ int err = 0;
struct lwip_sock *sock = tryget_socket(s);
if (!sock) {
return EBADF;
@@ -2702,6 +2864,11 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
#if SO_REUSE
case SO_REUSEADDR:
#endif /* SO_REUSE */
+ if ((optname == SO_BROADCAST) &&
+ (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
if (*(const int*)optval) {
ip_set_option(sock->conn->pcb.ip, optname);
@@ -2717,15 +2884,31 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
#if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
- netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
- break;
+ {
+ long ms_long;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+ if (ms_long < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ netconn_set_sendtimeout(sock->conn, ms_long);
+ break;
+ }
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
- netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
- break;
+ {
+ long ms_long;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+ if (ms_long < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ netconn_set_recvtimeout(sock->conn, (u32_t)ms_long);
+ break;
+ }
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
case SO_RCVBUF:
@@ -2741,6 +2924,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
if (linger->l_onoff) {
int lingersec = linger->l_linger;
if (lingersec < 0) {
+ done_socket(sock);
return EINVAL;
}
if (lingersec > 0xFFFF) {
@@ -2759,16 +2943,56 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
#if LWIP_UDPLITE
if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
/* this flag is only available for UDP, not for UDP lite */
+ done_socket(sock);
return EAFNOSUPPORT;
}
#endif /* LWIP_UDPLITE */
if (*(const int*)optval) {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+ udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
} else {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+ udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
}
break;
#endif /* LWIP_UDP */
+ case SO_BINDTODEVICE:
+ {
+ const struct ifreq *iface;
+ struct netif* n = NULL;
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct ifreq);
+
+ iface = (const struct ifreq*)optval;
+ if (iface->ifr_name[0] != 0) {
+ n = netif_find(iface->ifr_name);
+ if (n == NULL) {
+ done_socket(sock);
+ return ENODEV;
+ }
+ }
+
+ switch (NETCONNTYPE_GROUP(netconn_type(sock->conn)))
+ {
+#if LWIP_TCP
+ case NETCONN_TCP:
+ tcp_bind_netif(sock->conn->pcb.tcp, n);
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+ udp_bind_netif(sock->conn->pcb.udp, n);
+ break;
+#endif
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_bind_netif(sock->conn->pcb.raw, n);
+ break;
+#endif
+ default:
+ LWIP_ASSERT("Unhandled netconn type in SO_BINDTODEVICE", 0);
+ break;
+ }
+ }
+ break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
s, optname));
@@ -2792,7 +3016,17 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
s, sock->conn->pcb.ip->tos));
break;
-#if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_NETBUF_RECVINFO
+ case IP_PKTINFO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+ if (*(const int*)optval) {
+ sock->conn->flags |= NETCONN_FLAG_PKTINFO;
+ } else {
+ sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
+ }
+ break;
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
case IP_MULTICAST_TTL:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval));
@@ -2808,12 +3042,12 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
case IP_MULTICAST_LOOP:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
if (*(const u8_t*)optval) {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+ udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
} else {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+ udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
}
break;
-#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
#if LWIP_IGMP
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
@@ -2822,39 +3056,21 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
/* @todo: assign membership to this socket so that it is dropped when closing the socket */
err_t igmp_err;
const struct ip_mreq *imr = (const struct ip_mreq *)optval;
-#if ESP_LWIP
- ip_addr_t if_addr;
- ip_addr_t multi_addr;
-#else
ip4_addr_t if_addr;
ip4_addr_t multi_addr;
-#endif
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
-#if ESP_LWIP
- inet_addr_to_ip4addr(ip_2_ip4(&if_addr), &imr->imr_interface);
- inet_addr_to_ip4addr(ip_2_ip4(&multi_addr), &imr->imr_multiaddr);
-#else
inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
-#endif
if (optname == IP_ADD_MEMBERSHIP) {
if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
/* cannot track membership (out of memory) */
err = ENOMEM;
igmp_err = ERR_OK;
} else {
-#if ESP_LWIP
- igmp_err = igmp_joingroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
-#else
igmp_err = igmp_joingroup(&if_addr, &multi_addr);
-#endif
}
} else {
-#if ESP_LWIP
- igmp_err = igmp_leavegroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
-#else
igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
-#endif
lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
}
if (igmp_err != ERR_OK) {
@@ -2877,6 +3093,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
/* Special case: all IPPROTO_TCP option take an int */
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
if (sock->conn->pcb.tcp->state == LISTEN) {
+ done_socket(sock);
return EINVAL;
}
switch (optname) {
@@ -2926,11 +3143,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
case IPPROTO_IPV6:
switch (optname) {
case IPV6_V6ONLY:
-#if ESP_LWIP
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
-#else
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
-#endif
if (*(const int*)optval) {
netconn_set_ipv6only(sock->conn, 1);
} else {
@@ -2939,59 +3152,6 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
break;
-
-#if ESP_LWIP
-#if LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS /* Multicast options, similar to LWIP_IGMP options for IPV4 */
- case IPV6_MULTICAST_IF: /* NB: like IP_MULTICAST_IF, this takes an IP not an index */
- {
- ip6_addr_t if_addr;
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in6_addr, NETCONN_UDP);
- inet6_addr_to_ip6addr(&if_addr, (const struct in6_addr*)optval);
- udp_set_multicast_netif_ip6addr(sock->conn->pcb.udp, &if_addr);
- }
- break;
- case IPV6_MULTICAST_HOPS:
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
- sock->conn->pcb.udp->mcast_ttl = (u8_t)(*(const u8_t*)optval);
- break;
- case IPV6_MULTICAST_LOOP:
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
- if (*(const u8_t*)optval) {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
- } else {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
- }
- break;
- case IPV6_ADD_MEMBERSHIP:
- case IPV6_DROP_MEMBERSHIP:
- {
- err_t mld_err;
- const struct ip6_mreq *imr = (const struct ip6_mreq *)optval;
- ip_addr_t if_addr;
- ip_addr_t multi_addr;
- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip6_mreq, NETCONN_UDP);
- inet6_addr_to_ip6addr(ip_2_ip6(&if_addr), &imr->ipv6mr_interface);
- inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &imr->ipv6mr_multiaddr);
- if (optname == IPV6_ADD_MEMBERSHIP) {
- if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
- /* cannot track membership (out of memory) */
- err = ENOMEM;
- mld_err = ERR_OK;
- } else {
- mld_err = mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
- }
- } else {
- mld_err = mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
- lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
- }
- if (mld_err != ERR_OK) {
- err = EADDRNOTAVAIL;
- }
- }
- break;
-#endif /* LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS */
-#endif
-
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
s, optname));
@@ -3008,6 +3168,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
/* If this is no UDP lite socket, ignore any options. */
if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ done_socket(sock);
return ENOPROTOOPT;
}
switch (optname) {
@@ -3047,6 +3208,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
/* It should not be possible to disable the checksum generation with ICMPv6
* as per RFC 3542 chapter 3.1 */
if(sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
+ done_socket(sock);
return EINVAL;
}
@@ -3055,6 +3217,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
sock->conn->pcb.raw->chksum_reqd = 0;
} else if (*(const int *)optval & 1) {
/* Per RFC3542, odd offsets are not allowed */
+ done_socket(sock);
return EINVAL;
} else {
sock->conn->pcb.raw->chksum_reqd = 1;
@@ -3078,6 +3241,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
break;
} /* switch (level) */
+ done_socket(sock);
return err;
}
@@ -3087,7 +3251,6 @@ lwip_ioctl(int s, long cmd, void *argp)
struct lwip_sock *sock = get_socket(s);
u8_t val;
#if LWIP_SO_RCVBUF
- u16_t buflen = 0;
int recv_avail;
#endif /* LWIP_SO_RCVBUF */
@@ -3100,30 +3263,26 @@ lwip_ioctl(int s, long cmd, void *argp)
case FIONREAD:
if (!argp) {
sock_set_errno(sock, EINVAL);
+ done_socket(sock);
return -1;
}
#if LWIP_FIONREAD_LINUXMODE
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
- struct pbuf *p;
- if (sock->lastdata) {
- p = ((struct netbuf *)sock->lastdata)->p;
- *((int*)argp) = p->tot_len - sock->lastoffset;
+ struct netbuf *nb;
+ if (sock->lastdata.netbuf) {
+ nb = sock->lastdata.netbuf;
+ *((int*)argp) = nb->p->tot_len;
} else {
struct netbuf *rxbuf;
- err_t err;
- if (sock->rcvevent <= 0) {
+ err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
+ if (err != ERR_OK) {
*((int*)argp) = 0;
} else {
- err = netconn_recv(sock->conn, &rxbuf);
- if (err != ERR_OK) {
- *((int*)argp) = 0;
- } else {
- sock->lastdata = rxbuf;
- sock->lastoffset = 0;
- *((int*)argp) = rxbuf->p->tot_len;
- }
+ sock->lastdata.netbuf = rxbuf;
+ *((int*)argp) = rxbuf->p->tot_len;
}
}
+ done_socket(sock);
return 0;
}
#endif /* LWIP_FIONREAD_LINUXMODE */
@@ -3134,22 +3293,20 @@ lwip_ioctl(int s, long cmd, void *argp)
if (recv_avail < 0) {
recv_avail = 0;
}
- *((int*)argp) = recv_avail;
/* Check if there is data left from the last recv operation. /maq 041215 */
- if (sock->lastdata) {
- struct pbuf *p = (struct pbuf *)sock->lastdata;
+ if (sock->lastdata.netbuf) {
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
- p = ((struct netbuf *)p)->p;
+ recv_avail += sock->lastdata.pbuf->tot_len;
+ } else {
+ recv_avail += sock->lastdata.netbuf->p->tot_len;
}
- buflen = p->tot_len;
- buflen -= sock->lastoffset;
-
- *((int*)argp) += buflen;
}
+ *((int*)argp) = recv_avail;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
#else /* LWIP_SO_RCVBUF */
break;
@@ -3158,12 +3315,13 @@ lwip_ioctl(int s, long cmd, void *argp)
case (long)FIONBIO:
val = 0;
- if (argp && *(u32_t*)argp) {
+ if (argp && *(int*)argp) {
val = 1;
}
netconn_set_nonblocking(sock->conn, val);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
default:
@@ -3171,18 +3329,21 @@ lwip_ioctl(int s, long cmd, void *argp)
} /* switch (cmd) */
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ done_socket(sock);
return -1;
}
/** A minimal implementation of fcntl.
* Currently only the commands F_GETFL and F_SETFL are implemented.
- * Only the flag O_NONBLOCK is implemented.
+ * The flag O_NONBLOCK and access modes are supported for F_GETFL, only
+ * the flag O_NONBLOCK is implemented for F_SETFL.
*/
int
lwip_fcntl(int s, int cmd, int val)
{
struct lwip_sock *sock = get_socket(s);
int ret = -1;
+ int op_mode = 0;
if (!sock) {
return -1;
@@ -3192,8 +3353,42 @@ lwip_fcntl(int s, int cmd, int val)
case F_GETFL:
ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
sock_set_errno(sock, 0);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+#else
+ SYS_ARCH_DECL_PROTECT(lev);
+ /* the proper thing to do here would be to get into the tcpip_thread,
+ but locking should be OK as well since we only *read* some flags */
+ SYS_ARCH_PROTECT(lev);
+#endif
+#if LWIP_TCP
+ if (sock->conn->pcb.tcp) {
+ if(!(sock->conn->pcb.tcp->flags & TF_RXCLOSED)) {
+ op_mode |= O_RDONLY;
+ }
+ if (!(sock->conn->pcb.tcp->flags & TF_FIN)) {
+ op_mode |= O_WRONLY;
+ }
+ }
+#endif
+#if LWIP_TCPIP_CORE_LOCKING
+ UNLOCK_TCPIP_CORE();
+#else
+ SYS_ARCH_UNPROTECT(lev);
+#endif
+ } else {
+ op_mode |= O_RDWR;
+ }
+
+ /* ensure O_RDWR for (O_RDONLY|O_WRONLY) != O_RDWR cases */
+ ret |= (op_mode == (O_RDONLY|O_WRONLY)) ? O_RDWR : op_mode;
+
break;
case F_SETFL:
+ /* Bits corresponding to the file access mode and the file creation flags [..] that are set in arg shall be ignored */
+ val &= ~(O_RDONLY|O_WRONLY|O_RDWR);
if ((val & ~O_NONBLOCK) == 0) {
/* only O_NONBLOCK, all other bits are zero */
netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
@@ -3208,91 +3403,81 @@ lwip_fcntl(int s, int cmd, int val)
sock_set_errno(sock, ENOSYS); /* not yet implemented */
break;
}
+ done_socket(sock);
return ret;
}
-#if ESP_LWIP
-#if LWIP_IGMP || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS)
-/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
- *
- * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
- *
- * @return 1 on success, 0 on failure
- */
-static int
-lwip_socket_register_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr)
+const char *
+lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
- /* s+1 is stored in the array to prevent having to initialize the array
- (default initialization is to 0) */
- int sa = s + 1;
- int i;
-
- for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
- if (socket_multicast_memberships[i].sa == 0) {
- socket_multicast_memberships[i].sa = sa;
- ip_addr_copy(socket_multicast_memberships[i].if_addr, *if_addr);
- ip_addr_copy(socket_multicast_memberships[i].multi_addr, *multi_addr);
- return 1;
- }
+ const char *ret = NULL;
+ int size_int = (int)size;
+ if (size_int < 0) {
+ set_errno(ENOSPC);
+ return NULL;
}
- return 0;
-}
-
-/** Unregister a previously registered membership. This prevents dropping the membership
- * on socket close.
- *
- * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
- */
-static void
-lwip_socket_unregister_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr)
-{
- /* s+1 is stored in the array to prevent having to initialize the array
- (default initialization is to 0) */
- int sa = s + 1;
- int i;
-
- for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
- if ((socket_multicast_memberships[i].sa == sa) &&
- ip_addr_cmp(&socket_multicast_memberships[i].if_addr, if_addr) &&
- ip_addr_cmp(&socket_multicast_memberships[i].multi_addr, multi_addr)) {
- socket_multicast_memberships[i].sa = 0;
- ip_addr_set_zero(&socket_multicast_memberships[i].if_addr);
- ip_addr_set_zero(&socket_multicast_memberships[i].multi_addr);
- return;
- }
+ switch (af) {
+#if LWIP_IPV4
+ case AF_INET:
+ ret = ip4addr_ntoa_r((const ip4_addr_t*)src, dst, size_int);
+ if (ret == NULL) {
+ set_errno(ENOSPC);
+ }
+ break;
+#endif
+#if LWIP_IPV6
+ case AF_INET6:
+ ret = ip6addr_ntoa_r((const ip6_addr_t*)src, dst, size_int);
+ if (ret == NULL) {
+ set_errno(ENOSPC);
+ }
+ break;
+#endif
+ default:
+ set_errno(EAFNOSUPPORT);
+ break;
}
+ return ret;
}
-/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
- *
- * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
- */
-static void lwip_socket_drop_registered_memberships(int s)
+int
+lwip_inet_pton(int af, const char *src, void *dst)
{
- /* s+1 is stored in the array to prevent having to initialize the array
- (default initialization is to 0) */
- int sa = s + 1;
- int i;
- struct lwip_sock *sock = get_socket(s);
-
- LWIP_ASSERT("socket has no netconn", sock->conn != NULL);
-
- for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
- if (socket_multicast_memberships[i].sa == sa) {
- socket_multicast_memberships[i].sa = 0;
- netconn_join_leave_group(sock->conn,
- &socket_multicast_memberships[i].multi_addr,
- &socket_multicast_memberships[i].if_addr,
- NETCONN_LEAVE);
- ip_addr_set_zero(&socket_multicast_memberships[i].if_addr);
- ip_addr_set_zero(&socket_multicast_memberships[i].multi_addr);
+ int err;
+ switch (af) {
+#if LWIP_IPV4
+ case AF_INET:
+ err = ip4addr_aton(src, (ip4_addr_t*)dst);
+ break;
+#endif
+#if LWIP_IPV6
+ case AF_INET6:
+ {
+ /* convert into temporary variable since ip6_addr_t might be larger
+ than in6_addr when scopes are enabled */
+ ip6_addr_t addr;
+ err = ip6addr_aton(src, &addr);
+ if (err) {
+ memcpy(dst, &addr.addr, sizeof(addr.addr));
+ }
+ break;
}
+#endif
+ default:
+ err = -1;
+ set_errno(EAFNOSUPPORT);
+ break;
}
+ return err;
}
-#endif /* LWIP_IGMP || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS) */
-#else
#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
static int
lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
{
@@ -3308,9 +3493,11 @@ lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr
socket_ipv4_multicast_memberships[i].sock = sock;
ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+ done_socket(sock);
return 1;
}
}
+ done_socket(sock);
return 0;
}
@@ -3336,12 +3523,12 @@ lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_ad
socket_ipv4_multicast_memberships[i].sock = NULL;
ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
- return;
+ break;
}
}
+ done_socket(sock);
}
-
/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
*
* ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
@@ -3368,186 +3555,7 @@ lwip_socket_drop_registered_memberships(int s)
netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
}
}
-}
-#endif /* LWIP_IGMP */
-#endif /* ESP_LWIP */
-
-#if ESP_THREAD_SAFE
-
-int ESP_IRAM_ATTR
-lwip_sendto_r(int s, const void *data, size_t size, int flags,
- const struct sockaddr *to, socklen_t tolen)
-{
- LWIP_API_LOCK();
- __ret = lwip_sendto(s, data, size, flags, to, tolen);
- LWIP_API_UNLOCK();
+ done_socket(sock);
}
-
-int
-lwip_send_r(int s, const void *data, size_t size, int flags)
-{
- LWIP_API_LOCK();
- __ret = lwip_send(s, data, size, flags);
- LWIP_API_UNLOCK();
-}
-
-int ESP_IRAM_ATTR
-lwip_recvfrom_r(int s, void *mem, size_t len, int flags,
- struct sockaddr *from, socklen_t *fromlen)
-{
- LWIP_API_LOCK();
- __ret = lwip_recvfrom(s, mem, len, flags, from, fromlen);
- LWIP_API_UNLOCK();
-}
-
-int ESP_IRAM_ATTR
-lwip_recv_r(int s, void *mem, size_t len, int flags)
-{
- return lwip_recvfrom_r(s, mem, len, flags, NULL, NULL);
-}
-
-int
-lwip_read_r(int s, void *mem, size_t len)
-{
- return lwip_recvfrom_r(s, mem, len, 0, NULL, NULL);
-}
-
-int
-lwip_sendmsg_r(int s, const struct msghdr *msg, int flags)
-{
- LWIP_API_LOCK();
- __ret = lwip_sendmsg(s, msg, flags);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_write_r(int s, const void *data, size_t size)
-{
- return lwip_send_r(s, data, size, 0);
-}
-
-int
-lwip_writev_r(int s, const struct iovec *iov, int iovcnt)
-{
- struct msghdr msg;
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
- Blame the opengroup standard for this inconsistency. */
- msg.msg_iov = (struct iovec *)(size_t)iov;
- msg.msg_iovlen = iovcnt;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
- return lwip_sendmsg_r(s, &msg, 0);
-}
-
-int
-lwip_connect_r(int s, const struct sockaddr *name, socklen_t namelen)
-{
- LWIP_API_LOCK();
- __ret = lwip_connect(s, name, namelen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_recvmsg_r(int s, struct msghdr *msg, int flags)
-{
- LWIP_API_LOCK();
- __ret = lwip_recvmsg(s, msg, flags);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_listen_r(int s, int backlog)
-{
- LWIP_API_LOCK();
- __ret = lwip_listen(s, backlog);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_bind_r(int s, const struct sockaddr *name, socklen_t namelen)
-{
- LWIP_API_LOCK();
- __ret = lwip_bind(s, name, namelen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_accept_r(int s, struct sockaddr *addr, socklen_t *addrlen)
-{
- LWIP_API_LOCK();
- __ret = lwip_accept(s, addr, addrlen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_ioctl_r(int s, long cmd, void *argp)
-{
- LWIP_API_LOCK();
- __ret = lwip_ioctl(s, cmd, argp);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_fcntl_r(int s, int cmd, int val)
-{
- LWIP_API_LOCK();
- __ret = lwip_fcntl(s, cmd, val);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_setsockopt_r(int s, int level, int optname, const void *optval, socklen_t optlen)
-{
- LWIP_API_LOCK();
- __ret = lwip_setsockopt(s, level, optname, optval, optlen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_getsockopt_r(int s, int level, int optname, void *optval, socklen_t *optlen)
-{
- LWIP_API_LOCK();
- __ret = lwip_getsockopt(s, level, optname, optval, optlen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_getpeername_r(int s, struct sockaddr *name, socklen_t *namelen)
-{
- LWIP_API_LOCK();
- __ret = lwip_getpeername(s, name, namelen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_getsockname_r(int s, struct sockaddr *name, socklen_t *namelen)
-{
- LWIP_API_LOCK();
- __ret = lwip_getsockname(s, name, namelen);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_close_r(int s)
-{
- LWIP_API_LOCK();
- LWIP_SET_CLOSE_FLAG();
- __ret = lwip_close(s);
- LWIP_API_UNLOCK();
-}
-
-int
-lwip_shutdown_r(int s, int how)
-{
- LWIP_API_LOCK();
- __ret = lwip_shutdown(s, how);
- LWIP_API_UNLOCK();
-}
-
-#endif
-
+#endif /* LWIP_IGMP */
#endif /* LWIP_SOCKET */
diff --git a/src/api/tcpip.c b/../lwip_nat/src/api/tcpip.c
index 40a81e23..55d7fc57 100644
--- a/src/api/tcpip.c
+++ b/../lwip_nat/src/api/tcpip.c
@@ -41,7 +41,6 @@
#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
#include "lwip/priv/tcpip_priv.h"
-#include "lwip/priv/api_msg.h"
#include "lwip/sys.h"
#include "lwip/memp.h"
#include "lwip/mem.h"
@@ -60,9 +59,6 @@
static tcpip_init_done_fn tcpip_init_done;
static void *tcpip_init_done_arg;
static sys_mbox_t mbox;
-#if ESP_LWIP
-sys_thread_t g_lwip_task = NULL;
-#endif
#if LWIP_TCPIP_CORE_LOCKING
/** The global semaphore to lock the stack. */
@@ -77,6 +73,8 @@ sys_mutex_t lock_tcpip_core;
#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
#endif /* LWIP_TIMERS */
+static void tcpip_thread_handle_msg(struct tcpip_msg *msg);
+
/**
* 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
@@ -87,11 +85,7 @@ sys_mutex_t lock_tcpip_core;
*
* @param arg unused argument
*/
-#if ESP_LWIP
-static void ESP_IRAM_ATTR
-#else
static void
-#endif
tcpip_thread(void *arg)
{
struct tcpip_msg *msg;
@@ -113,64 +107,88 @@ tcpip_thread(void *arg)
LWIP_ASSERT("tcpip_thread: invalid message", 0);
continue;
}
- switch (msg->type) {
+ tcpip_thread_handle_msg(msg);
+ }
+}
+
+/* Handle a single tcpip_msg
+ * This is in its own function for access by tests only.
+ */
+static void
+tcpip_thread_handle_msg(struct tcpip_msg *msg)
+{
+ switch (msg->type) {
#if !LWIP_TCPIP_CORE_LOCKING
- case TCPIP_MSG_API:
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
- msg->msg.api_msg.function(msg->msg.api_msg.msg);
- break;
- case TCPIP_MSG_API_CALL:
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
- msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
- sys_sem_signal(msg->msg.api_call.sem);
- break;
+ case TCPIP_MSG_API:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+ msg->msg.api_msg.function(msg->msg.api_msg.msg);
+ break;
+ case TCPIP_MSG_API_CALL:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
+ msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
+ sys_sem_signal(msg->msg.api_call.sem);
+ break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
- case TCPIP_MSG_INPKT:
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
-#if ESP_LWIP
- if(msg->msg.inp.p != NULL && msg->msg.inp.netif != NULL) {
-#endif
- msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
-#if ESP_LWIP
- }
-#endif
- memp_free(MEMP_TCPIP_MSG_INPKT, msg);
- break;
+ case TCPIP_MSG_INPKT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+ msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
- 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;
+ 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 && LWIP_TIMERS */
- 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:
+ 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;
+ }
+}
- 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;
+#ifdef TCPIP_THREAD_TEST
+/** Work on queued items in single-threaded test mode */
+int
+tcpip_thread_poll_one(void)
+{
+ int ret = 0;
+ struct tcpip_msg *msg;
- default:
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
- LWIP_ASSERT("tcpip_thread: invalid message", 0);
- break;
+ /* wait for a message, timeouts are processed while waiting */
+ if (sys_arch_mbox_tryfetch(&mbox, (void **)&msg) != SYS_ARCH_TIMEOUT) {
+ LOCK_TCPIP_CORE();
+ if (msg != NULL) {
+ tcpip_thread_handle_msg(msg);
+ ret = 1;
}
+ UNLOCK_TCPIP_CORE();
}
+ return ret;
}
+#endif
/**
* Pass a received packet to tcpip_thread for input processing
@@ -179,11 +197,7 @@ tcpip_thread(void *arg)
* @param inp the network interface on which the packet was received
* @param input_fn input function to call
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
{
#if LWIP_TCPIP_CORE_LOCKING_INPUT
@@ -196,13 +210,7 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
struct tcpip_msg *msg;
-#if ESP_LWIP
- if (!sys_mbox_valid_val(mbox)) {
- return ERR_VAL;
- }
-#else
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
-#endif
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
if (msg == NULL) {
@@ -214,7 +222,6 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
msg->msg.inp.netif = inp;
msg->msg.inp.input_fn = input_fn;
if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
- ESP_STATS_DROP_INC(esp.tcpip_inpkt_post_fail);
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
return ERR_MEM;
}
@@ -233,11 +240,7 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
* NETIF_FLAG_ETHERNET flags)
* @param inp the network interface on which the packet was received
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
tcpip_input(struct pbuf *p, struct netif *inp)
{
#if LWIP_ETHERNET
@@ -249,18 +252,22 @@ tcpip_input(struct pbuf *p, struct netif *inp)
}
/**
+ * @ingroup lwip_os
* Call a specific function in the thread context of
* tcpip_thread for easy access synchronization.
* A function called in that way may access lwIP core code
* without fearing concurrent access.
+ * Blocks until the request is posted.
+ * Must not be called from interrupt context!
*
* @param function the function to call
* @param ctx parameter passed to f
- * @param block 1 to block until the request is posted, 0 to non-blocking mode
* @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_try_callback
*/
err_t
-tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+tcpip_callback(tcpip_callback_fn function, void *ctx)
{
struct tcpip_msg *msg;
@@ -274,13 +281,46 @@ tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
msg->type = TCPIP_MSG_CALLBACK;
msg->msg.cb.function = function;
msg->msg.cb.ctx = ctx;
- if (block) {
- sys_mbox_post(&mbox, msg);
- } else {
- if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
- memp_free(MEMP_TCPIP_MSG_API, msg);
- return ERR_MEM;
- }
+
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup lwip_os
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ * Does NOT block when the request cannot be posted because the
+ * mbox is full, but returns ERR_MEM instead.
+ * Can be called from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_callback
+ */
+err_t
+tcpip_try_callback(tcpip_callback_fn function, void *ctx)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ return ERR_MEM;
}
return ERR_OK;
}
@@ -354,11 +394,7 @@ tcpip_untimeout(sys_timeout_handler h, void *arg)
* @param sem semaphore to wait on
* @return ERR_OK if the function was called, another err_t if not
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
{
#if LWIP_TCPIP_CORE_LOCKING
@@ -507,15 +543,7 @@ tcpip_init(tcpip_init_done_fn initfunc, void *arg)
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
-#if ESP_LWIP
- g_lwip_task = sys_thread_new(TCPIP_THREAD_NAME
- , tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
-
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_task_hdlxxx : %x, prio:%d,stack:%d\n",
- (u32_t)g_lwip_task,TCPIP_THREAD_PRIO,TCPIP_THREAD_STACKSIZE));
-#else
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
-#endif
}
/**
@@ -540,7 +568,7 @@ pbuf_free_int(void *p)
err_t
pbuf_free_callback(struct pbuf *p)
{
- return tcpip_callback_with_block(pbuf_free_int, p, 0);
+ return tcpip_try_callback(pbuf_free_int, p);
}
/**
@@ -550,22 +578,10 @@ pbuf_free_callback(struct pbuf *p)
* @param m the heap memory to free
* @return ERR_OK if callback could be enqueued, an err_t if not
*/
-
-#if ESP_LWIP
-static void mem_free_local(void *arg)
-{
- mem_free(arg);
-}
-err_t mem_free_callback(void *m)
-{
- return tcpip_callback_with_block(mem_free_local, m, 0);
-}
-#else
err_t
mem_free_callback(void *m)
{
- return tcpip_callback_with_block(mem_free, m, 0);
+ return tcpip_try_callback(mem_free, m);
}
-#endif
#endif /* !NO_SYS */
diff --git a/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls.c b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls.c
new file mode 100644
index 00000000..ebca8855
--- /dev/null
+++ b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls.c
@@ -0,0 +1,990 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file provides a TLS layer using mbedTLS
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ * Watch out:
+ * - 'sent' is always called with len==0 to the upper layer. This is because keeping
+ * track of the ratio of application data and TLS overhead would be too much.
+ *
+ * Mandatory security-related configuration:
+ * - define ALTCP_MBEDTLS_RNG_FN to a custom GOOD rng function returning 0 on success:
+ * int my_rng_fn(void *ctx, unsigned char *buffer , size_t len)
+ * - define ALTCP_MBEDTLS_ENTROPY_PTR and ALTCP_MBEDTLS_ENTROPY_LEN to something providing
+ * GOOD custom entropy
+ *
+ * Missing things / @todo:
+ * - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
+ * the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
+ * connection; altcp_recved() from inner connection does nothing)
+ * - Client connections starting with 'connect()' are not handled yet...
+ * - some unhandled things are caught by LWIP_ASSERTs...
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/priv/altcp_priv.h"
+
+#include "altcp_tls_mbedtls_structs.h"
+#include "altcp_tls_mbedtls_mem.h"
+
+/* @todo: which includes are really needed? */
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/certs.h"
+#include "mbedtls/x509.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/net.h"
+#include "mbedtls/error.h"
+#include "mbedtls/debug.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/memory_buffer_alloc.h"
+#include "mbedtls/ssl_cache.h"
+
+#include "mbedtls/ssl_internal.h" /* to call mbedtls_flush_output after ERR_MEM */
+
+#include <string.h>
+
+#ifndef ALTCP_MBEDTLS_ENTROPY_PTR
+#define ALTCP_MBEDTLS_ENTROPY_PTR NULL
+#endif
+#ifndef ALTCP_MBEDTLS_ENTROPY_LEN
+#define ALTCP_MBEDTLS_ENTROPY_LEN 0
+#endif
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_mbedtls_functions;
+
+/** Our global mbedTLS configuration (server-specific, not connection-specific) */
+struct altcp_tls_config
+{
+ mbedtls_ssl_config conf;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+ /** Inter-connection cache for fast connection startup */
+ struct mbedtls_ssl_cache_context cache;
+#endif
+};
+
+static err_t altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err);
+static err_t altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn);
+static void altcp_mbedtls_dealloc(struct altcp_pcb *conn);
+static err_t altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static err_t altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static int altcp_mbedtls_bio_send(void* ctx, const unsigned char* dataptr, size_t size);
+
+
+/* callback functions from inner/lower connection: */
+
+/** Accept callback from lower connection (i.e. TCP)
+ * Allocate one of our structures, assign it to the new connection's 'state' and
+ * call the new connection's 'accepted' callback. If that succeeds, we wait
+ * to receive connection setup handshake bytes from the client.
+ */
+static err_t
+altcp_mbedtls_lower_accept(void *arg, struct altcp_pcb *accepted_conn, err_t err)
+{
+ struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+ if (listen_conn && listen_conn->state && listen_conn->accept) {
+ err_t setup_err;
+ altcp_mbedtls_state_t *listen_state = (altcp_mbedtls_state_t *)listen_conn->state;
+ /* create a new altcp_conn to pass to the next 'accept' callback */
+ struct altcp_pcb *new_conn = altcp_alloc();
+ if (new_conn == NULL) {
+ return ERR_MEM;
+ }
+ setup_err = altcp_mbedtls_setup(listen_state->conf, new_conn, accepted_conn);
+ if (setup_err != ERR_OK) {
+ altcp_free(new_conn);
+ return setup_err;
+ }
+ return listen_conn->accept(listen_conn->arg, new_conn, err);
+ }
+ return ERR_ARG;
+}
+
+/** Connected callback from lower connection (i.e. TCP).
+ * Not really implemented/tested yet...
+ */
+static err_t
+altcp_mbedtls_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn && conn->state) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ /* upper connected is called when handshake is done */
+ if (err != ERR_OK) {
+ if (conn->connected) {
+ if (conn->connected(conn->arg, conn, err) == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+ }
+ }
+ return altcp_mbedtls_lower_recv_process(conn, (altcp_mbedtls_state_t*)conn->state);
+ }
+ return ERR_VAL;
+}
+
+/* Call recved for possibly more than an u16_t */
+static void
+altcp_mbedtls_lower_recved(struct altcp_pcb *inner_conn, int recvd_cnt)
+{
+ while (recvd_cnt > 0) {
+ u16_t recvd_part = (u16_t)LWIP_MIN(recvd_cnt, 0xFFFF);
+ altcp_recved(inner_conn, recvd_part);
+ recvd_cnt -= recvd_part;
+ }
+}
+
+/** Recv callback from lower connection (i.e. TCP)
+ * This one mainly differs between connection setup/handshake (data is fed into mbedTLS only)
+ * and application phase (data is decoded by mbedTLS and passed on to the application).
+ */
+static err_t
+altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
+{
+ altcp_mbedtls_state_t *state;
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+
+ LWIP_ASSERT("no err expected", err == ERR_OK);
+ LWIP_UNUSED_ARG(err);
+
+ if (!conn) {
+ /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state) {
+ /* already closed */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+
+ /* handle NULL pbuf (connection closed) */
+ if (p == NULL) {
+ /* remote host sent FIN, remember this (SSL state is destroyed
+ when both sides are closed only!) */
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED;
+ if ((state->flags & (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE|ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) ==
+ (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE|ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) {
+ /* need to notify upper layer (e.g. 'accept' called or 'connect' succeeded) */
+ if ((state->rx != NULL) || (state->rx_app != NULL)) {
+ /* this is a normal close (FIN) but we have unprocessed data, so delay the FIN */
+ altcp_mbedtls_handle_rx_appldata(conn, state);
+ return ERR_OK;
+ }
+ if (conn->recv) {
+ err_t local_err = conn->recv(conn->arg, conn, NULL, ERR_OK);
+ if (local_err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ } else {
+ /* before connection setup is done: call 'err' */
+ if (conn->err) {
+ conn->err(conn->arg, ERR_CLSD);
+ }
+ }
+ altcp_close(conn);
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+ if (conn->state && ((state->flags & ALTCP_MBEDTLS_FLAGS_CLOSED) == ALTCP_MBEDTLS_FLAGS_CLOSED)) {
+ altcp_mbedtls_dealloc(conn);
+ }
+ return ERR_OK;
+ }
+
+ /* If we come here, the connection is in good state (handshake phase or application data phase).
+ Queue up the pbuf for processing as handshake data or application data. */
+ if (state->rx == NULL) {
+ state->rx = p;
+ } else {
+ LWIP_ASSERT("rx pbuf overflow", (int)p->tot_len + (int)p->len <= 0xFFFF);
+ pbuf_cat(state->rx, p);
+ }
+ return altcp_mbedtls_lower_recv_process(conn, state);
+}
+
+static err_t
+altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* handle connection setup (handshake not done) */
+ int ret = mbedtls_ssl_handshake(&state->ssl_context);
+ /* try to send data... */
+ altcp_output(conn->inner_conn);
+ if (state->bio_bytes_read) {
+ /* acknowledge all bytes read */
+ altcp_mbedtls_lower_recved(conn->inner_conn, state->bio_bytes_read);
+ state->bio_bytes_read = 0;
+ }
+
+ if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ /* handshake not done, wait for more recv calls */
+ LWIP_ASSERT("in this state, the rx chain should be empty", state->rx == NULL);
+ return ERR_OK;
+ }
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_handshake failed: %d", ret));
+ /* handshake failed, connection has to be closed */
+ conn->recv(conn->arg, conn, NULL, ERR_OK);
+ if (altcp_close(conn->inner_conn) != ERR_OK) {
+ altcp_abort(conn->inner_conn);
+ }
+ return ERR_OK;
+ }
+ /* If we come here, handshake succeeded. */
+ LWIP_ASSERT("rx pbufs left at end of handshake", state->rx == NULL);
+ LWIP_ASSERT("state", state->bio_bytes_read == 0);
+ LWIP_ASSERT("state", state->bio_bytes_appl == 0);
+ state->flags |= ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE;
+ /* issue "connect" callback" to upper connection (this can only happen for active open) */
+ if (conn->connected) {
+ conn->connected(conn->arg, conn, ERR_OK);
+ }
+ return ERR_OK;
+ } else {
+ /* handle application data */
+ return altcp_mbedtls_handle_rx_appldata(conn, state);
+ }
+}
+
+/* Pass queued decoded rx data to application */
+static err_t
+altcp_mbedtls_pass_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ err_t err;
+ struct pbuf *buf;
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("state != NULL", state != NULL);
+ buf = state->rx_app;
+ if (buf) {
+ if (conn->recv) {
+ u16_t tot_len = state->rx_app->tot_len;
+ /* this needs to be increased first because the 'recved' call may come nested */
+ state->rx_passed_unrecved += tot_len;
+ state->flags |= ALTCP_MBEDTLS_FLAGS_UPPER_CALLED;
+ err = conn->recv(conn->arg, conn, state->rx_app, ERR_OK);
+ if (err != ERR_OK) {
+ /* not received, leave the pbuf(s) queued (and decrease 'unrecved' again) */
+ state->rx_passed_unrecved -= tot_len;
+ LWIP_ASSERT("state->rx_passed_unrecved >= 0", state->rx_passed_unrecved >= 0);
+ if (state->rx_passed_unrecved < 0) {
+ state->rx_passed_unrecved = 0;
+ }
+ return err;
+ }
+ } else {
+ pbuf_free(buf);
+ }
+ state->rx_app = NULL;
+ } else if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED|ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+ ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+ if (conn->recv) {
+ err = conn->recv(conn->arg, conn, NULL, ERR_OK);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+/* Helper function that processes rx application data stored in rx pbuf chain */
+static err_t
+altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ int ret;
+ LWIP_ASSERT("state != NULL", state != NULL);
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* handshake not done yet */
+ return ERR_VAL;
+ }
+ do {
+ /* allocate a full-sized unchained PBUF_POOL: this is for RX! */
+ struct pbuf *buf = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL);
+ if (buf == NULL) {
+ /* We're short on pbufs, try again later from 'poll' or 'recv' callbacks.
+ @todo: close on excessive allocation failures or leave this up to upper conn? */
+ return ERR_OK;
+ }
+
+ /* decrypt application data, this pulls encrypted RX data off state->rx pbuf chain */
+ ret = mbedtls_ssl_read(&state->ssl_context, (unsigned char *)buf->payload, PBUF_POOL_BUFSIZE);
+ if (ret < 0) {
+ if (ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT) {
+ /* client is initiating a new connection using the same source port -> close connection or make handshake */
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("new connection on same source port"));
+ LWIP_ASSERT("TODO: new connection on same source port, close this connection", 0);
+ } else if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) {
+ if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was closed gracefully"));
+ } else if (ret == MBEDTLS_ERR_NET_CONN_RESET) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was reset by peer"));
+ }
+ pbuf_free(buf);
+ return ERR_OK;
+ } else {
+ pbuf_free(buf);
+ return ERR_OK;
+ }
+ pbuf_free(buf);
+ altcp_abort(conn);
+ return ERR_ABRT;
+ } else {
+ err_t err;
+ if (ret) {
+ LWIP_ASSERT("bogus receive length", ret <= PBUF_POOL_BUFSIZE);
+ /* trim pool pbuf to actually decoded length */
+ pbuf_realloc(buf, (u16_t)ret);
+
+ state->bio_bytes_appl += ret;
+ if (mbedtls_ssl_get_bytes_avail(&state->ssl_context) == 0) {
+ /* Record is done, now we know the share between application and protocol bytes
+ and can adjust the RX window by the protocol bytes.
+ The rest is 'recved' by the application calling our 'recved' fn. */
+ int overhead_bytes;
+ LWIP_ASSERT("bogus byte counts", state->bio_bytes_read > state->bio_bytes_appl);
+ overhead_bytes = state->bio_bytes_read - state->bio_bytes_appl;
+ altcp_mbedtls_lower_recved(conn->inner_conn, overhead_bytes);
+ state->bio_bytes_read = 0;
+ state->bio_bytes_appl = 0;
+ }
+
+ if (state->rx_app == NULL) {
+ state->rx_app = buf;
+ } else {
+ pbuf_cat(state->rx_app, buf);
+ }
+ } else {
+ pbuf_free(buf);
+ buf = NULL;
+ }
+ err = altcp_mbedtls_pass_rx_data(conn, state);
+ if (err != ERR_OK) {
+ if (err == ERR_ABRT) {
+ /* recv callback needs to return this as the pcb is deallocated */
+ return ERR_ABRT;
+ }
+ /* we hide all other errors as we retry feeding the pbuf to the app later */
+ return ERR_OK;
+ }
+ }
+ } while (ret > 0);
+ return ERR_OK;
+}
+
+/** Receive callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function mainly copies data from pbufs and frees the pbufs after copying.
+ */
+static int
+altcp_mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)ctx;
+ altcp_mbedtls_state_t *state;
+ struct pbuf* p;
+ u16_t ret;
+ u16_t copy_len;
+ err_t err;
+
+ if ((conn == NULL) || (conn->state == NULL)) {
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ p = state->rx;
+
+ /* @todo: return MBEDTLS_ERR_NET_CONN_RESET/MBEDTLS_ERR_NET_RECV_FAILED? */
+
+ if ((p == NULL) || ((p->len == 0) && (p->next == NULL))) {
+ if (p) {
+ pbuf_free(p);
+ }
+ state->rx = NULL;
+ if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED|ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+ ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+ /* close queued but not passed up yet */
+ return 0;
+ }
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ /* limit number of bytes to copy to fit into an s16_t for pbuf_header */
+ copy_len = (u16_t)LWIP_MIN(len, 0x7FFF);
+ /* limit number of bytes again to copy from first pbuf in a chain only */
+ copy_len = LWIP_MIN(copy_len, p->len);
+ /* copy the data */
+ ret = pbuf_copy_partial(p, buf, copy_len, 0);
+ LWIP_ASSERT("ret == copy_len", ret == copy_len);
+ /* hide the copied bytes from the pbuf */
+ err = pbuf_header(p, -(s16_t)ret);
+ LWIP_ASSERT("error", err == ERR_OK);
+ if (p->len == 0) {
+ /* the first pbuf has been fully read, free it */
+ state->rx = p->next;
+ p->next = NULL;
+ pbuf_free(p);
+ }
+
+ state->bio_bytes_read += (int)ret;
+ return ret;
+}
+
+/** Sent callback from lower connection (i.e. TCP)
+ * This only informs the upper layer to try to send more, not about
+ * the number of ACKed bytes.
+ */
+static err_t
+altcp_mbedtls_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ LWIP_UNUSED_ARG(len);
+ if (conn) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: do something here? */
+ return ERR_OK;
+ }
+ /* try to send more if we failed before */
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ /* call upper sent with len==0 if the application already sent data */
+ if ((state->flags & ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT) && conn->sent) {
+ return conn->sent(conn->arg, conn, 0);
+ }
+ }
+ return ERR_OK;
+}
+
+/** Poll callback from lower connection (i.e. TCP)
+ * Just pass this on to the application.
+ * @todo: retry sending?
+ */
+static err_t
+altcp_mbedtls_lower_poll(void *arg, struct altcp_pcb *inner_conn)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ /* check if there's unreceived rx data */
+ if (conn->state) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ /* try to send more if we failed before */
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ if (altcp_mbedtls_handle_rx_appldata(conn, state) == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_mbedtls_lower_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ /* @todo: deallocate/close this connection? */
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ }
+}
+
+/* setup functions */
+static void
+altcp_mbedtls_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ altcp_arg(inner_conn, conn);
+ altcp_recv(inner_conn, altcp_mbedtls_lower_recv);
+ altcp_sent(inner_conn, altcp_mbedtls_lower_sent);
+ altcp_err(inner_conn, altcp_mbedtls_lower_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static err_t
+altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ int ret;
+ struct altcp_tls_config *config = (struct altcp_tls_config *)conf;
+ altcp_mbedtls_state_t *state;
+ if (!conf) {
+ return ERR_ARG;
+ }
+ /* allocate mbedtls context */
+ state = altcp_mbedtls_alloc(conf);
+ if (state == NULL) {
+ return ERR_MEM;
+ }
+ /* initialize mbedtls context: */
+ mbedtls_ssl_init(&state->ssl_context);
+ ret = mbedtls_ssl_setup(&state->ssl_context, &config->conf);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_setup failed"));
+ /* @todo: convert 'ret' to err_t */
+ altcp_mbedtls_free(conf, state);
+ return ERR_MEM;
+ }
+ /* tell mbedtls about our I/O functions */
+ mbedtls_ssl_set_bio(&state->ssl_context, conn, altcp_mbedtls_bio_send, altcp_mbedtls_bio_recv, NULL);
+
+ altcp_mbedtls_setup_callbacks(conn, inner_conn);
+ conn->inner_conn = inner_conn;
+ conn->fns = &altcp_mbedtls_functions;
+ conn->state = state;
+ return ERR_OK;
+}
+
+struct altcp_pcb *
+altcp_tls_new(struct altcp_tls_config* config, struct altcp_pcb *inner_pcb)
+{
+ struct altcp_pcb *ret;
+ if (inner_pcb == NULL) {
+ return NULL;
+ }
+ ret = altcp_alloc();
+ if (ret != NULL) {
+ if (altcp_mbedtls_setup(config, ret, inner_pcb) != ERR_OK) {
+ altcp_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+static void
+altcp_mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
+{
+ LWIP_UNUSED_ARG(str);
+ LWIP_UNUSED_ARG(level);
+ LWIP_UNUSED_ARG(file);
+ LWIP_UNUSED_ARG(line);
+ LWIP_UNUSED_ARG(ctx);
+ /* @todo: output debug string :-) */
+}
+#endif
+
+#ifndef ALTCP_MBEDTLS_RNG_FN
+/** ATTENTION: It is *really* important to *NOT* use this dummy RNG in production code!!!! */
+static int
+dummy_rng(void *ctx, unsigned char *buffer , size_t len)
+{
+ static size_t ctr;
+ size_t i;
+ LWIP_UNUSED_ARG(ctx);
+ for (i = 0; i < len; i++) {
+ buffer[i] = (unsigned char)++ctr;
+ }
+ return 0;
+}
+#define ALTCP_MBEDTLS_RNG_FN dummy_rng
+#endif /* ALTCP_MBEDTLS_RNG_FN */
+
+/** Create new TLS configuration
+ * ATTENTION: Server certificate and private key have to be added outside this function!
+ */
+static struct altcp_tls_config *
+altcp_tls_create_config(int is_server)
+{
+ int ret;
+ struct altcp_tls_config *conf;
+
+ altcp_mbedtls_mem_init();
+
+ conf = (struct altcp_tls_config *)altcp_mbedtls_alloc_config(sizeof(struct altcp_tls_config));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ mbedtls_ssl_config_init(&conf->conf);
+ mbedtls_entropy_init(&conf->entropy);
+ mbedtls_ctr_drbg_init(&conf->ctr_drbg);
+
+ /* Seed the RNG */
+ ret = mbedtls_ctr_drbg_seed(&conf->ctr_drbg, ALTCP_MBEDTLS_RNG_FN, &conf->entropy, ALTCP_MBEDTLS_ENTROPY_PTR, ALTCP_MBEDTLS_ENTROPY_LEN);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ctr_drbg_seed failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ /* Setup ssl context (@todo: what's different for a client here? -> might better be done on listen/connect) */
+ ret = mbedtls_ssl_config_defaults(&conf->conf, is_server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_config_defaults failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+ mbedtls_ssl_conf_authmode(&conf->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+
+ mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &conf->ctr_drbg);
+#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+ mbedtls_ssl_conf_dbg(&conf->conf, altcp_mbedtls_debug, stdout);
+#endif
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+ mbedtls_ssl_conf_session_cache(&conf->conf, &conf->cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set);
+ mbedtls_ssl_cache_set_timeout(&conf->cache, 30);
+ mbedtls_ssl_cache_set_max_entries(&conf->cache, 30);
+#endif
+
+ return conf;
+}
+
+/** Create new TLS configuration
+ * This is a suboptimal version that gets the encrypted private key and its password,
+ * as well as the server certificate.
+ */
+struct altcp_tls_config *
+altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
+ const u8_t *privkey_pass, size_t privkey_pass_len,
+ const u8_t *cert, size_t cert_len)
+{
+ int ret;
+ static mbedtls_x509_crt srvcert;
+ static mbedtls_pk_context pkey;
+ struct altcp_tls_config *conf = altcp_tls_create_config(1);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ mbedtls_x509_crt_init(&srvcert);
+ mbedtls_pk_init(&pkey);
+
+ /* Load the certificates and private key */
+ ret = mbedtls_x509_crt_parse(&srvcert, cert, cert_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ ret = mbedtls_pk_parse_key(&pkey, (const unsigned char *) privkey, privkey_len, privkey_pass, privkey_pass_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_public_key failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&conf->conf, srvcert.next, NULL);
+ ret = mbedtls_ssl_conf_own_cert(&conf->conf, &srvcert, &pkey);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+ return conf;
+}
+
+struct altcp_tls_config *
+altcp_tls_create_config_client(const u8_t *cert, size_t cert_len)
+{
+ int ret;
+ static mbedtls_x509_crt acc_cert;
+ struct altcp_tls_config *conf = altcp_tls_create_config(0);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ mbedtls_x509_crt_init(&acc_cert);
+
+ /* Load the certificates */
+ ret = mbedtls_x509_crt_parse(&acc_cert, cert, cert_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&conf->conf, &acc_cert, NULL);
+ return conf;
+}
+
+/* "virtual" functions */
+static void
+altcp_mbedtls_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ altcp_poll(conn->inner_conn, altcp_mbedtls_lower_poll, interval);
+ }
+}
+
+static void
+altcp_mbedtls_recved(struct altcp_pcb *conn, u16_t len)
+{
+ u16_t lower_recved;
+ altcp_mbedtls_state_t *state;
+ if (conn == NULL) {
+ return;
+ }
+ state = (altcp_mbedtls_state_t*)conn->state;
+ if (state == NULL) {
+ return;
+ }
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ return;
+ }
+ lower_recved = len;
+ if (lower_recved > state->rx_passed_unrecved) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("bogus recved count (len > state->rx_passed_unrecved / %d / %d)",
+ len, state->rx_passed_unrecved));
+ lower_recved = (u16_t)state->rx_passed_unrecved;
+ }
+ state->rx_passed_unrecved -= lower_recved;
+
+ altcp_recved(conn->inner_conn, lower_recved);
+}
+
+static err_t
+altcp_mbedtls_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ conn->connected = connected;
+ return altcp_connect(conn->inner_conn, ipaddr, port, altcp_mbedtls_lower_connected);
+}
+
+static struct altcp_pcb *
+altcp_mbedtls_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ struct altcp_pcb *lpcb;
+ if (conn == NULL) {
+ return NULL;
+ }
+ lpcb = altcp_listen_with_backlog_and_err(conn->inner_conn, backlog, err);
+ if (lpcb != NULL) {
+ conn->inner_conn = lpcb;
+ altcp_accept(lpcb, altcp_mbedtls_lower_accept);
+ return conn;
+ }
+ return NULL;
+}
+
+static void
+altcp_mbedtls_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ altcp_abort(conn->inner_conn);
+ }
+}
+
+static err_t
+altcp_mbedtls_close(struct altcp_pcb *conn)
+{
+ altcp_mbedtls_state_t *state;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ state = (altcp_mbedtls_state_t*)conn->state;
+ if (state != NULL) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_TX_CLOSED;
+ if (state->flags & ALTCP_MBEDTLS_FLAGS_RX_CLOSED) {
+ altcp_mbedtls_dealloc(conn);
+ }
+ }
+ return altcp_close(conn->inner_conn);
+}
+
+/** Write data to a TLS connection. Calls into mbedTLS, which in turn calls into
+ * @ref altcp_mbedtls_bio_send() to send the encrypted data
+ */
+static err_t
+altcp_mbedtls_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ int ret;
+ altcp_mbedtls_state_t *state;
+
+ LWIP_UNUSED_ARG(apiflags);
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ state = (altcp_mbedtls_state_t*)conn->state;
+ if (state == NULL) {
+ /* @todo: which error? */
+ return ERR_CLSD;
+ }
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: which error? */
+ return ERR_VAL;
+ }
+
+ /* HACK: if thre is something left to send, try to flush it and only
+ allow sending more if this succeeded (this is a hack because neither
+ returning 0 nor MBEDTLS_ERR_SSL_WANT_WRITE worked for me) */
+ if (state->ssl_context.out_left) {
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ if (state->ssl_context.out_left) {
+ return ERR_MEM;
+ }
+ }
+ ret = mbedtls_ssl_write(&state->ssl_context, (const unsigned char *)dataptr, len);
+ /* try to send data... */
+ altcp_output(conn->inner_conn);
+ if (ret >= 0) {
+ if(ret == len) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT;
+ return ERR_OK;
+ } else {
+ /* @todo/@fixme: assumption: either everything sent or error */
+ LWIP_ASSERT("ret <= 0", 0);
+ return ERR_MEM;
+ }
+ } else {
+ if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ /* @todo: convert error to err_t */
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("unhandled error", 0);
+ return ERR_VAL;
+ }
+}
+
+/** Send callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function is either called during handshake or when sending application
+ * data via @ref altcp_mbedtls_write (or altcp_write)
+ */
+static int
+altcp_mbedtls_bio_send(void* ctx, const unsigned char* dataptr, size_t size)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *) ctx;
+ int written = 0;
+ size_t size_left = size;
+ u8_t apiflags = TCP_WRITE_FLAG_COPY;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ if ((conn == NULL) || (conn->inner_conn == NULL)) {
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+
+ while (size_left) {
+ u16_t write_len = (u16_t)LWIP_MIN(size_left, 0xFFFF);
+ err_t err = altcp_write(conn->inner_conn, (const void *)dataptr, write_len, apiflags);
+ if (err == ERR_OK) {
+ written += write_len;
+ size_left -= write_len;
+ } else if (err == ERR_MEM) {
+ if (written) {
+ return written;
+ }
+ return 0; /* MBEDTLS_ERR_SSL_WANT_WRITE; */
+ } else {
+ LWIP_ASSERT("tls_write, tcp_write: err != ERR MEM", 0);
+ /* @todo: return MBEDTLS_ERR_NET_CONN_RESET or MBEDTLS_ERR_NET_SEND_FAILED */
+ return MBEDTLS_ERR_NET_SEND_FAILED;
+ }
+ }
+ return written;
+}
+
+static u16_t
+altcp_mbedtls_mss(struct altcp_pcb *conn)
+{
+ if (conn == NULL) {
+ return 0;
+ }
+ /* @todo: LWIP_MIN(mss, mbedtls_ssl_get_max_frag_len()) ? */
+ return altcp_mss(conn->inner_conn);
+}
+
+static void
+altcp_mbedtls_dealloc(struct altcp_pcb *conn)
+{
+ /* clean up and free tls state */
+ if (conn) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t*)conn->state;
+ if (state) {
+ mbedtls_ssl_free(&state->ssl_context);
+ state->flags = 0;
+ altcp_mbedtls_free(state->conf, state);
+ }
+ conn->state = NULL;
+ }
+}
+
+const struct altcp_functions altcp_mbedtls_functions = {
+ altcp_mbedtls_set_poll,
+ altcp_mbedtls_recved,
+ altcp_default_bind,
+ altcp_mbedtls_connect,
+ altcp_mbedtls_listen,
+ altcp_mbedtls_abort,
+ altcp_mbedtls_close,
+ altcp_default_shutdown,
+ altcp_mbedtls_write,
+ altcp_default_output,
+ altcp_mbedtls_mss,
+ altcp_default_sndbuf,
+ altcp_default_sndqueuelen,
+ altcp_default_nagle_disable,
+ altcp_default_nagle_enable,
+ altcp_default_nagle_disabled,
+ altcp_default_setprio,
+ altcp_mbedtls_dealloc,
+ altcp_default_get_tcp_addrinfo,
+ altcp_default_get_ip,
+ altcp_default_get_port
+#ifdef LWIP_DEBUG
+ ,altcp_default_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
new file mode 100644
index 00000000..6df1a7c2
--- /dev/null
+++ b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
@@ -0,0 +1,212 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management functions for a TLS layer using mbedTLS.
+ *
+ * ATTENTION: For production usage, you might want to override this file with
+ * your own implementation since this implementation simply uses the
+ * lwIP heap without caring for fragmentation or leaving heap for
+ * other parts of lwIP!
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ * Missing things / @todo:
+ * - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
+ * the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
+ * connection; altcp_recved() from inner connection does nothing)
+ * - TX data is marked as 'sent' (i.e. acknowledged; sent callback is called) right
+ * after enqueueing for transmission, not when actually ACKed be the remote host.
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_mem.h"
+#include "altcp_tls_mbedtls_structs.h"
+#include "lwip/mem.h"
+
+#include "mbedtls/platform.h"
+
+#include <string.h>
+
+#ifndef ALTCP_MBEDTLS_MEM_DEBUG
+#define ALTCP_MBEDTLS_MEM_DEBUG LWIP_DBG_OFF
+#endif
+
+#if defined(MBEDTLS_PLATFORM_MEMORY) && \
+ (!defined(MBEDTLS_PLATFORM_FREE_MACRO) || \
+ defined(MBEDTLS_PLATFORM_CALLOC_MACRO))
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 1
+#else
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 0
+#endif
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+
+#ifndef ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS 0
+#endif
+
+/* This is an example/debug implementation of alloc/free functions only */
+typedef struct altcp_mbedtls_malloc_helper_s {
+ size_t c;
+ size_t len;
+} altcp_mbedtls_malloc_helper_t;
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+typedef struct altcp_mbedtls_malloc_stats_s {
+ size_t allocedBytes;
+ size_t allocCnt;
+ size_t maxBytes;
+ size_t totalBytes;
+} altcp_mbedtls_malloc_stats_t;
+altcp_mbedtls_malloc_stats_t altcp_mbedtls_malloc_stats;
+volatile int altcp_mbedtls_malloc_clear_stats;
+#endif
+
+static void *
+tls_malloc(size_t c, size_t len)
+{
+ altcp_mbedtls_malloc_helper_t* hlpr;
+ void* ret;
+ size_t alloc_size;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ if (altcp_mbedtls_malloc_clear_stats) {
+ if (altcp_mbedtls_malloc_clear_stats) {
+ altcp_mbedtls_malloc_clear_stats = 0;
+ memset(&altcp_mbedtls_malloc_stats, 0, sizeof(altcp_mbedtls_malloc_stats));
+ }
+ }
+#endif
+ alloc_size = sizeof(altcp_mbedtls_malloc_helper_t) + (c*len);
+ /* check for maximum allocation size, mainly to prevent mem_size_t overflow */
+ if (alloc_size > MEM_SIZE) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls allocation too big: %c * %d bytes vs MEM_SIZE=%d",
+ (int)c, (int)len, (int)MEM_SIZE));
+ return NULL;
+ }
+ hlpr = (altcp_mbedtls_malloc_helper_t*)mem_malloc((mem_size_t)alloc_size);
+ if (hlpr == NULL) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls alloc callback failed for %c * %d bytes", (int)c, (int)len));
+ return NULL;
+ }
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ altcp_mbedtls_malloc_stats.allocCnt++;
+ altcp_mbedtls_malloc_stats.allocedBytes += c*len;
+ if (altcp_mbedtls_malloc_stats.allocedBytes > altcp_mbedtls_malloc_stats.maxBytes) {
+ altcp_mbedtls_malloc_stats.maxBytes = altcp_mbedtls_malloc_stats.allocedBytes;
+ }
+ altcp_mbedtls_malloc_stats.totalBytes += c*len;
+#endif
+ hlpr->c = c;
+ hlpr->len = len;
+ ret = hlpr + 1;
+ /* zeroing the allocated chunk is required by mbedTLS! */
+ memset(ret, 0, c*len);
+ return ret;
+}
+
+static void
+tls_free(void * ptr)
+{
+ altcp_mbedtls_malloc_helper_t *hlpr;
+ if (ptr == NULL) {
+ /* this obviously happened in mbedtls... */
+ return;
+ }
+ hlpr = ((altcp_mbedtls_malloc_helper_t *)ptr)-1;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ if (!altcp_mbedtls_malloc_clear_stats) {
+ altcp_mbedtls_malloc_stats.allocedBytes -= hlpr->c*hlpr->len;
+ }
+#endif
+ mem_free(hlpr);
+}
+#endif /* ALTCP_MBEDTLS_PLATFORM_ALLOC*/
+
+void
+altcp_mbedtls_mem_init(void)
+{
+ /* not much to do here when using the heap */
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+ /* set mbedtls allocation methods */
+ mbedtls_platform_set_calloc_free(&tls_malloc, &tls_free);
+#endif
+}
+
+altcp_mbedtls_state_t *
+altcp_mbedtls_alloc(void *conf)
+{
+ altcp_mbedtls_state_t *ret = (altcp_mbedtls_state_t *)mem_calloc(1, sizeof(altcp_mbedtls_state_t));
+ if (ret != NULL) {
+ ret->conf = conf;
+ }
+ return ret;
+}
+
+void
+altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state)
+{
+ LWIP_UNUSED_ARG(conf);
+ LWIP_ASSERT("state != NULL", state != NULL);
+ mem_free(state);
+}
+
+void *
+altcp_mbedtls_alloc_config(size_t size)
+{
+ void *ret;
+ size_t checked_size = (mem_size_t)size;
+ if (size != checked_size) {
+ /* allocation too big (mem_size_t overflow) */
+ return NULL;
+ }
+ ret = (altcp_mbedtls_state_t *)mem_calloc(1, (mem_size_t)size);
+ return ret;
+}
+
+void
+altcp_mbedtls_free_config(void *item)
+{
+ LWIP_ASSERT("item != NULL", item != NULL);
+ mem_free(item);
+}
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h
new file mode 100644
index 00000000..9ccde64a
--- /dev/null
+++ b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management function prototypes for a TLS layer using mbedTLS.
+ *
+ * Memory management contains:
+ * - allocating/freeing altcp_mbedtls_state_t
+ * - allocating/freeing memory used in the mbedTLS library
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+#define LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_structs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void altcp_mbedtls_mem_init(void);
+altcp_mbedtls_state_t *altcp_mbedtls_alloc(void *conf);
+void altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state);
+void *altcp_mbedtls_alloc_config(size_t size);
+void altcp_mbedtls_free_config(void *item);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_TLS_H */
diff --git a/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h
new file mode 100644
index 00000000..f215fc63
--- /dev/null
+++ b/../lwip_nat/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h
@@ -0,0 +1,85 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains structure definitions for a TLS layer using mbedTLS.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+#define LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/pbuf.h"
+
+#include "mbedtls/ssl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE 0x01
+#define ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT 0x02
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED 0x04
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSED 0x08
+#define ALTCP_MBEDTLS_FLAGS_TX_CLOSED 0x10
+#define ALTCP_MBEDTLS_FLAGS_CLOSED (ALTCP_MBEDTLS_FLAGS_RX_CLOSED|ALTCP_MBEDTLS_FLAGS_TX_CLOSED)
+#define ALTCP_MBEDTLS_FLAGS_UPPER_CALLED 0x20
+
+typedef struct altcp_mbedtls_state_s {
+ void *conf;
+ mbedtls_ssl_context ssl_context;
+ /* chain of rx pbufs (before decryption) */
+ struct pbuf* rx;
+ struct pbuf* rx_app;
+ u8_t flags;
+ int rx_passed_unrecved;
+ int bio_bytes_read;
+ int bio_bytes_appl;
+} altcp_mbedtls_state_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H */
diff --git a/src/apps/httpd/fs.c b/../lwip_nat/src/apps/httpd/fs.c
index 35b5e310..1bd370e3 100644
--- a/src/apps/httpd/fs.c
+++ b/../lwip_nat/src/apps/httpd/fs.c
@@ -33,15 +33,10 @@
#include "lwip/apps/httpd_opts.h"
#include "lwip/def.h"
#include "lwip/apps/fs.h"
-#include "fsdata.h"
#include <string.h>
-#if HTTPD_USE_CUSTOM_FSDATA
-#include "fsdata_custom.c"
-#else /* HTTPD_USE_CUSTOM_FSDATA */
-#include "fsdata.c"
-#endif /* HTTPD_USE_CUSTOM_FSDATA */
+#include HTTPD_FSDATA_FILE
/*-----------------------------------------------------------------------------------*/
diff --git a/src/apps/httpd/fsdata.c b/../lwip_nat/src/apps/httpd/fsdata.c
index 6170ce63..ab9da7e1 100644
--- a/src/apps/httpd/fsdata.c
+++ b/../lwip_nat/src/apps/httpd/fsdata.c
@@ -1,13 +1,33 @@
#include "lwip/apps/fs.h"
#include "lwip/def.h"
-#include "fsdata.h"
#define file_NULL (struct fsdata_file *) NULL
+#ifndef FS_FILE_FLAGS_HEADER_INCLUDED
+#define FS_FILE_FLAGS_HEADER_INCLUDED 1
+#endif
+#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT
+#define FS_FILE_FLAGS_HEADER_PERSISTENT 0
+#endif
+/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */
+#ifndef FSDATA_FILE_ALIGNMENT
+#define FSDATA_FILE_ALIGNMENT 0
+#endif
+#ifndef FSDATA_ALIGN_PRE
+#define FSDATA_ALIGN_PRE
+#endif
+#ifndef FSDATA_ALIGN_POST
+#define FSDATA_ALIGN_POST
+#endif
+#if FSDATA_FILE_ALIGNMENT==2
+#include "fsdata_alignment.h"
+#endif
+#if FSDATA_FILE_ALIGNMENT==1
static const unsigned int dummy_align__img_sics_gif = 0;
-static const unsigned char data__img_sics_gif[] = {
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__img_sics_gif[] FSDATA_ALIGN_POST = {
/* /img/sics.gif (14 chars) */
0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00,
@@ -16,16 +36,21 @@ static const unsigned char data__img_sics_gif[] = {
" (17 bytes) */
0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
0x0a,
-/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
-" (63 bytes) */
-0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
-0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
-0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
-0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
-/* "Content-type: image/gif
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 724
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x37,0x32,0x34,0x0d,0x0a,
+/* "Content-Type: image/gif
" (27 bytes) */
-0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d,
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d,
0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a,
/* raw file data (724 bytes) */
0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39,
@@ -75,8 +100,10 @@ static const unsigned char data__img_sics_gif[] = {
0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10,
0x41,0x00,0x00,0x3b,};
+#if FSDATA_FILE_ALIGNMENT==1
static const unsigned int dummy_align__404_html = 1;
-static const unsigned char data__404_html[] = {
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__404_html[] FSDATA_ALIGN_POST = {
/* /404.html (10 chars) */
0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00,
@@ -85,16 +112,21 @@ static const unsigned char data__404_html[] = {
" (29 bytes) */
0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c,
0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a,
-/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
-" (63 bytes) */
-0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
-0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
-0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
-0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
-/* "Content-type: text/html
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 565
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x35,0x36,0x35,0x0d,0x0a,
+/* "Content-Type: text/html
" (27 bytes) */
-0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
/* raw file data (565 bytes) */
0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
@@ -134,8 +166,10 @@ static const unsigned char data__404_html[] = {
0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
0x6d,0x6c,0x3e,0x0d,0x0a,};
+#if FSDATA_FILE_ALIGNMENT==1
static const unsigned int dummy_align__index_html = 2;
-static const unsigned char data__index_html[] = {
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__index_html[] FSDATA_ALIGN_POST = {
/* /index.html (12 chars) */
0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00,
@@ -144,16 +178,21 @@ static const unsigned char data__index_html[] = {
" (17 bytes) */
0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
0x0a,
-/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
-" (63 bytes) */
-0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
-0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
-0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
-0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
-/* "Content-type: text/html
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 1751
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x31,0x37,0x35,0x31,0x0d,0x0a,
+/* "Content-Type: text/html
" (27 bytes) */
-0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
/* raw file data (1751 bytes) */
0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
@@ -274,7 +313,7 @@ file_NULL,
data__img_sics_gif,
data__img_sics_gif + 16,
sizeof(data__img_sics_gif) - 16,
-1,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
}};
const struct fsdata_file file__404_html[] = { {
@@ -282,7 +321,7 @@ file__img_sics_gif,
data__404_html,
data__404_html + 12,
sizeof(data__404_html) - 12,
-1,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
}};
const struct fsdata_file file__index_html[] = { {
@@ -290,7 +329,7 @@ file__404_html,
data__index_html,
data__index_html + 12,
sizeof(data__index_html) - 12,
-1,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
}};
#define FS_ROOT file__index_html
diff --git a/src/apps/httpd/fsdata.h b/../lwip_nat/src/apps/httpd/fsdata.h
index ac4548c7..55b0824b 100644
--- a/src/apps/httpd/fsdata.h
+++ b/../lwip_nat/src/apps/httpd/fsdata.h
@@ -35,16 +35,7 @@
#include "lwip/apps/httpd_opts.h"
#include "lwip/apps/fs.h"
-struct fsdata_file {
- const struct fsdata_file *next;
- const unsigned char *name;
- const unsigned char *data;
- int len;
- u8_t flags;
-#if HTTPD_PRECALCULATED_CHECKSUM
- u16_t chksum_count;
- const struct fsdata_chksum *chksum;
-#endif /* HTTPD_PRECALCULATED_CHECKSUM */
-};
+/* THIS FILE IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE */
+/* content was moved to fs.h to simplify #include structure */
#endif /* LWIP_FSDATA_H */
diff --git a/src/apps/httpd/httpd.c b/../lwip_nat/src/apps/httpd/httpd.c
index 43195d7c..f8d2233f 100644
--- a/src/apps/httpd/httpd.c
+++ b/../lwip_nat/src/apps/httpd/httpd.c
@@ -95,8 +95,15 @@
#include "lwip/apps/fs.h"
#include "httpd_structs.h"
#include "lwip/def.h"
-#include "lwip/ip.h"
-#include "lwip/tcp.h"
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#if HTTPD_ENABLE_HTTPS
+#include "lwip/altcp_tls.h"
+#endif
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
#include <string.h> /* memset */
#include <stdlib.h> /* atoi */
@@ -113,26 +120,21 @@
#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
#endif
-/** These defines check whether tcp_write has to copy data or not */
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL)
+#else
+#define HTTP_IS_DYNAMIC_FILE(hs) 0
+#endif
+
+/* This defines checks whether tcp_write has to copy data or not */
-/** This was TI's check whether to let TCP copy data or not
- * \#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY)
- */
#ifndef HTTP_IS_DATA_VOLATILE
-#if LWIP_HTTPD_SSI
-/* Copy for SSI files, no copy for non-SSI files */
-#define HTTP_IS_DATA_VOLATILE(hs) ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0)
-#else /* LWIP_HTTPD_SSI */
-/** Default: don't copy if the data is sent from file-system directly */
-#define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \
- (const char*)hs->handle->data + hs->handle->len - hs->left)) \
- ? 0 : TCP_WRITE_FLAG_COPY)
-#endif /* LWIP_HTTPD_SSI */
+/** tcp_write does not have to copy data when sent from rom-file-system directly */
+#define HTTP_IS_DATA_VOLATILE(hs) (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0)
#endif
-
-/** Default: headers are sent from ROM */
+/** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */
#ifndef HTTP_IS_HDR_VOLATILE
-#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0
+#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0
#endif
/* Return values for http_send_*() */
@@ -235,7 +237,7 @@ struct http_state {
struct fs_file *handle;
const char *file; /* Pointer to first unsent byte in buf. */
- struct tcp_pcb *pcb;
+ struct altcp_pcb *pcb;
#if LWIP_HTTPD_SUPPORT_REQUESTLIST
struct pbuf *req;
#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
@@ -294,12 +296,12 @@ LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(
#endif /* LWIP_HTTPD_SSI */
#endif /* HTTPD_USE_MEM_POOL */
-static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs);
-static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
+static err_t http_close_conn(struct altcp_pcb *pcb, struct http_state *hs);
+static err_t http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char* params);
-static err_t http_poll(void *arg, struct tcp_pcb *pcb);
-static u8_t http_check_eof(struct tcp_pcb *pcb, struct http_state *hs);
+static err_t http_poll(void *arg, struct altcp_pcb *pcb);
+static u8_t http_check_eof(struct altcp_pcb *pcb, struct http_state *hs);
#if LWIP_HTTPD_FS_ASYNC_READ
static void http_continue(void *connection);
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
@@ -508,7 +510,7 @@ http_state_free(struct http_state *hs)
/** Call tcp_write() in a loop trying smaller and smaller length
*
- * @param pcb tcp_pcb to send
+ * @param pcb altcp_pcb to send
* @param ptr Data to send
* @param length Length of data to send (in/out: on return, contains the
* amount of data sent)
@@ -516,7 +518,7 @@ http_state_free(struct http_state *hs)
* @return the return value of tcp_write
*/
static err_t
-http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
+http_write(struct altcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
{
u16_t len, max_len;
err_t err;
@@ -526,7 +528,7 @@ http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
return ERR_OK;
}
/* We cannot send more data than space available in the send buffer. */
- max_len = tcp_sndbuf(pcb);
+ max_len = altcp_sndbuf(pcb);
if (max_len < len) {
len = max_len;
}
@@ -538,11 +540,11 @@ http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
}
#endif /* HTTPD_MAX_WRITE_LEN */
do {
- LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len));
- err = tcp_write(pcb, ptr, len, apiflags);
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len));
+ err = altcp_write(pcb, ptr, len, apiflags);
if (err == ERR_MEM) {
- if ((tcp_sndbuf(pcb) == 0) ||
- (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
+ if ((altcp_sndbuf(pcb) == 0) ||
+ (altcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
/* no need to try smaller sizes */
len = 1;
} else {
@@ -565,7 +567,7 @@ http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
/* ensure nagle is normally enabled (only disabled for persistent connections
when all data has been enqueued but the connection stays open for the next
request */
- tcp_nagle_enable(pcb);
+ altcp_nagle_enable(pcb);
#endif
return err;
@@ -579,7 +581,7 @@ http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
* @param hs connection state to free
*/
static err_t
-http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
+http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
{
err_t err;
LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb));
@@ -599,24 +601,24 @@ http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_
#endif /* LWIP_HTTPD_SUPPORT_POST*/
- tcp_arg(pcb, NULL);
- tcp_recv(pcb, NULL);
- tcp_err(pcb, NULL);
- tcp_poll(pcb, NULL, 0);
- tcp_sent(pcb, NULL);
+ altcp_arg(pcb, NULL);
+ altcp_recv(pcb, NULL);
+ altcp_err(pcb, NULL);
+ altcp_poll(pcb, NULL, 0);
+ altcp_sent(pcb, NULL);
if (hs != NULL) {
http_state_free(hs);
}
if (abort_conn) {
- tcp_abort(pcb);
+ altcp_abort(pcb);
return ERR_OK;
}
- err = tcp_close(pcb);
+ err = altcp_close(pcb);
if (err != ERR_OK) {
LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb));
/* error closing, try again later in poll */
- tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
}
return err;
}
@@ -629,7 +631,7 @@ http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_
* @param hs connection state to free
*/
static err_t
-http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
+http_close_conn(struct altcp_pcb *pcb, struct http_state *hs)
{
return http_close_or_abort_conn(pcb, hs, 0);
}
@@ -638,7 +640,7 @@ http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
* close the file (Connection: keep-alive)
*/
static void
-http_eof(struct tcp_pcb *pcb, struct http_state *hs)
+http_eof(struct altcp_pcb *pcb, struct http_state *hs)
{
/* HTTP/1.1 persistent connection? (Not supported for SSI) */
#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
@@ -652,7 +654,7 @@ http_eof(struct tcp_pcb *pcb, struct http_state *hs)
hs->keepalive = 1;
http_add_connection(hs);
/* ensure nagle doesn't interfere with sending all data as fast as possible: */
- tcp_nagle_disable(pcb);
+ altcp_nagle_disable(pcb);
} else
#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
{
@@ -942,7 +944,7 @@ get_http_headers(struct http_state *hs, const char *uri)
hs->handle->len);
len = strlen(hs->hdr_content_len);
if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
- SMEMCPY(&hs->hdr_content_len[len], CRLF "\0", 3);
+ SMEMCPY(&hs->hdr_content_len[len], CRLF, 3);
hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
} else {
add_content_len = 0;
@@ -973,7 +975,7 @@ get_http_headers(struct http_state *hs, const char *uri)
* so don't send HTTP body yet
*/
static u8_t
-http_send_headers(struct tcp_pcb *pcb, struct http_state *hs)
+http_send_headers(struct altcp_pcb *pcb, struct http_state *hs)
{
err_t err;
u16_t len;
@@ -981,7 +983,7 @@ http_send_headers(struct tcp_pcb *pcb, struct http_state *hs)
u16_t hdrlen, sendlen;
/* How much data can we send? */
- len = tcp_sndbuf(pcb);
+ len = altcp_sndbuf(pcb);
sendlen = len;
while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
@@ -1059,7 +1061,7 @@ http_send_headers(struct tcp_pcb *pcb, struct http_state *hs)
* 1 if the file is not finished and data has been read
*/
static u8_t
-http_check_eof(struct tcp_pcb *pcb, struct http_state *hs)
+http_check_eof(struct altcp_pcb *pcb, struct http_state *hs)
{
int bytes_left;
#if LWIP_HTTPD_DYNAMIC_FILE_READ
@@ -1089,7 +1091,7 @@ http_check_eof(struct tcp_pcb *pcb, struct http_state *hs)
count = LWIP_MIN(hs->buf_len, bytes_left);
} else {
/* We don't have a send buffer so allocate one now */
- count = tcp_sndbuf(pcb);
+ count = altcp_sndbuf(pcb);
if(bytes_left < count) {
count = bytes_left;
}
@@ -1158,7 +1160,7 @@ http_check_eof(struct tcp_pcb *pcb, struct http_state *hs)
* - 0: no data has been written (no need to call tcp_output)
*/
static u8_t
-http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs)
+http_send_data_nonssi(struct altcp_pcb *pcb, struct http_state *hs)
{
err_t err;
u16_t len;
@@ -1185,7 +1187,7 @@ http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs)
* - 0: no data has been written (no need to call tcp_output)
*/
static u8_t
-http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
+http_send_data_ssi(struct altcp_pcb *pcb, struct http_state *hs)
{
err_t err = ERR_OK;
u16_t len;
@@ -1199,7 +1201,7 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
* have to split the insert string between two tcp_write operations. */
/* How much data could we send? */
- len = tcp_sndbuf(pcb);
+ len = altcp_sndbuf(pcb);
/* Do we have remaining data to send before parsing more? */
if(ssi->parsed > hs->file) {
@@ -1213,7 +1215,7 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
}
/* If the send buffer is full, return now. */
- if(tcp_sndbuf(pcb) == 0) {
+ if(altcp_sndbuf(pcb) == 0) {
return data_to_send;
}
}
@@ -1222,7 +1224,7 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
/* We have sent all the data that was already parsed so continue parsing
* the buffer contents looking for SSI tags. */
- while((ssi->parse_left) && (err == ERR_OK)) {
+ while(((ssi->tag_state == TAG_SENDING) || ssi->parse_left) && (err == ERR_OK)) {
if (len == 0) {
return data_to_send;
}
@@ -1382,7 +1384,7 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
#if !LWIP_HTTPD_SSI_INCLUDE_TAG
if(ssi->tag_started <= hs->file) {
/* pretend to have sent the tag, too */
- len += ssi->tag_end - ssi->tag_started;
+ len += (u16_t)(ssi->tag_end - ssi->tag_started);
}
#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
hs->file += len;
@@ -1428,7 +1430,7 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
#if !LWIP_HTTPD_SSI_INCLUDE_TAG
if(ssi->tag_started <= hs->file) {
/* pretend to have sent the tag, too */
- len += ssi->tag_end - ssi->tag_started;
+ len += (u16_t)(ssi->tag_end - ssi->tag_started);
}
#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
hs->file += len;
@@ -1489,7 +1491,19 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
* file data to send so send it now. In TAG_SENDING state, we've already
* handled this so skip the send if that's the case. */
if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
- len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) {
+ /* If we found tag on the edge of the read buffer: just throw away the first part
+ (we have copied/saved everything required for parsing on later). */
+ len = (u16_t)(ssi->tag_started - hs->file);
+ hs->left -= (ssi->parsed - ssi->tag_started);
+ ssi->parsed = ssi->tag_started;
+ ssi->tag_started = hs->buf;
+ } else
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ {
+ len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+ }
err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
if (err == ERR_OK) {
@@ -1509,7 +1523,7 @@ http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
* @param hs connection state
*/
static u8_t
-http_send(struct tcp_pcb *pcb, struct http_state *hs)
+http_send(struct altcp_pcb *pcb, struct http_state *hs)
{
u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
@@ -1849,7 +1863,7 @@ void httpd_post_data_recved(void *connection, u16_t recved_len)
}
if (hs->pcb != NULL) {
if (len != 0) {
- tcp_recved(hs->pcb, len);
+ altcp_recved(hs->pcb, len);
}
if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
/* finished handling POST */
@@ -1878,7 +1892,7 @@ http_continue(void *connection)
if (http_send(hs->pcb, hs)) {
/* If we wrote anything to be sent, go ahead and send it now. */
LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
- tcp_output(hs->pcb);
+ altcp_output(hs->pcb);
}
}
}
@@ -1890,13 +1904,13 @@ http_continue(void *connection)
*
* @param inp the received pbuf
* @param hs the connection state
- * @param pcb the tcp_pcb which received this packet
+ * @param pcb the altcp_pcb which received this packet
* @return ERR_OK if request was OK and hs has been initialized correctly
* ERR_INPROGRESS if request was OK so far but not fully received
* another err_t otherwise
*/
static err_t
-http_parse_request(struct pbuf *inp, struct http_state *hs, struct tcp_pcb *pcb)
+http_parse_request(struct pbuf *inp, struct http_state *hs, struct altcp_pcb *pcb)
{
char *data;
char *crlf;
@@ -2255,7 +2269,7 @@ http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const cha
} else
#endif /* LWIP_HTTPD_CUSTOM_FILES */
{
- hs->left = file->len;
+ hs->left = (u32_t)file->len;
}
hs->retries = 0;
#if LWIP_HTTPD_TIMING
@@ -2271,7 +2285,7 @@ http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const cha
search for the end of the header. */
char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left);
if (file_start != NULL) {
- size_t diff = file_start + 4 - hs->file;
+ int diff = file_start + 4 - hs->file;
hs->file += diff;
hs->left -= (u32_t)diff;
}
@@ -2353,7 +2367,7 @@ http_err(void *arg, err_t err)
* This means that more data can be sent.
*/
static err_t
-http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+http_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
{
struct http_state *hs = (struct http_state *)arg;
@@ -2380,11 +2394,11 @@ http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
* This could be increased, but we don't want to waste resources for bad connections.
*/
static err_t
-http_poll(void *arg, struct tcp_pcb *pcb)
+http_poll(void *arg, struct altcp_pcb *pcb)
{
struct http_state *hs = (struct http_state *)arg;
LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
- (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state)));
+ (void*)pcb, (void*)hs, tcp_debug_state_str(altcp_dbg_get_tcp_state(pcb))));
if (hs == NULL) {
err_t closed;
@@ -2394,7 +2408,7 @@ http_poll(void *arg, struct tcp_pcb *pcb)
LWIP_UNUSED_ARG(closed);
#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
if (closed == ERR_MEM) {
- tcp_abort(pcb);
+ altcp_abort(pcb);
return ERR_ABRT;
}
#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
@@ -2415,7 +2429,7 @@ http_poll(void *arg, struct tcp_pcb *pcb)
if(http_send(pcb, hs)) {
/* If we wrote anything to be sent, go ahead and send it now. */
LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
- tcp_output(pcb);
+ altcp_output(pcb);
}
}
}
@@ -2428,7 +2442,7 @@ http_poll(void *arg, struct tcp_pcb *pcb)
* For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
*/
static err_t
-http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct http_state *hs = (struct http_state *)arg;
LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb,
@@ -2438,7 +2452,7 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
/* error or closed by other side? */
if (p != NULL) {
/* Inform TCP that we have taken the data. */
- tcp_recved(pcb, p->tot_len);
+ altcp_recved(pcb, p->tot_len);
pbuf_free(p);
}
if (hs == NULL) {
@@ -2456,7 +2470,7 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
{
/* Inform TCP that we have taken the data. */
- tcp_recved(pcb, p->tot_len);
+ altcp_recved(pcb, p->tot_len);
}
#if LWIP_HTTPD_SUPPORT_POST
@@ -2513,7 +2527,7 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
* A new incoming connection has been accepted.
*/
static err_t
-http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
+http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
{
struct http_state *hs;
LWIP_UNUSED_ARG(err);
@@ -2525,7 +2539,7 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
}
/* Set priority */
- tcp_setprio(pcb, HTTPD_TCP_PRIO);
+ altcp_setprio(pcb, HTTPD_TCP_PRIO);
/* Allocate memory for the structure that holds the state of the
connection - initialized by that function. */
@@ -2538,17 +2552,34 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
/* Tell TCP that this is the structure we wish to be passed for our
callbacks. */
- tcp_arg(pcb, hs);
+ altcp_arg(pcb, hs);
/* Set up the various callback functions */
- tcp_recv(pcb, http_recv);
- tcp_err(pcb, http_err);
- tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
- tcp_sent(pcb, http_sent);
+ altcp_recv(pcb, http_recv);
+ altcp_err(pcb, http_err);
+ altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ altcp_sent(pcb, http_sent);
return ERR_OK;
}
+static void
+httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
+{
+ err_t err;
+
+ if (pcb) {
+ altcp_setprio(pcb, HTTPD_TCP_PRIO);
+ /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
+ err = altcp_bind(pcb, IP_ANY_TYPE, port);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
+ pcb = altcp_listen(pcb);
+ LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
+ altcp_accept(pcb, http_accept);
+ }
+}
+
/**
* @ingroup httpd
* Initialize the httpd: set up a listening PCB and bind it to the defined port
@@ -2556,8 +2587,7 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
void
httpd_init(void)
{
- struct tcp_pcb *pcb;
- err_t err;
+ struct altcp_pcb *pcb;
#if HTTPD_USE_MEM_POOL
LWIP_MEMPOOL_INIT(HTTPD_STATE);
@@ -2567,17 +2597,32 @@ httpd_init(void)
#endif
LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
- pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
+ pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
- tcp_setprio(pcb, HTTPD_TCP_PRIO);
- /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
- err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT);
- LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
- LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
- pcb = tcp_listen(pcb);
- LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
- tcp_accept(pcb, http_accept);
+ httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
+}
+
+#if HTTPD_ENABLE_HTTPS
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port.
+ * Also set up TLS connection handling (HTTPS).
+ */
+void
+httpd_inits(struct altcp_tls_config *conf)
+{
+#if LWIP_ALTCP_TLS
+ struct altcp_pcb *pcb_tls;
+ struct altcp_pcb *pcb_tcp = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("httpd_init: tcp_new failed", pcb_tcp != NULL);
+ pcb_tls = altcp_tls_new(conf, pcb_tcp);
+ LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL);
+ httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS);
+#else /* LWIP_ALTCP_TLS */
+ LWIP_UNUSED_ARG(conf);
+#endif /* LWIP_ALTCP_TLS */
}
+#endif /* HTTPD_ENABLE_HTTPS */
#if LWIP_HTTPD_SSI
/**
diff --git a/src/apps/httpd/httpd_structs.h b/../lwip_nat/src/apps/httpd/httpd_structs.h
index fbd135a8..4b829072 100644
--- a/src/apps/httpd/httpd_structs.h
+++ b/../lwip_nat/src/apps/httpd/httpd_structs.h
@@ -54,24 +54,30 @@ static const char * const g_psHTTPHeaderStrings[] =
#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */
#endif
+#define HTTP_CONTENT_TYPE(contenttype) "Content-Type: "contenttype"\r\n\r\n"
+#define HTTP_CONTENT_TYPE_ENCODING(contenttype, encoding) "Content-Type: "contenttype"\r\nContent-Encoding: "encoding"\r\n\r\n"
-#define HTTP_HDR_HTML "Content-type: text/html\r\n\r\n"
-#define HTTP_HDR_SSI "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n"
-#define HTTP_HDR_GIF "Content-type: image/gif\r\n\r\n"
-#define HTTP_HDR_PNG "Content-type: image/png\r\n\r\n"
-#define HTTP_HDR_JPG "Content-type: image/jpeg\r\n\r\n"
-#define HTTP_HDR_BMP "Content-type: image/bmp\r\n\r\n"
-#define HTTP_HDR_ICO "Content-type: image/x-icon\r\n\r\n"
-#define HTTP_HDR_APP "Content-type: application/octet-stream\r\n\r\n"
-#define HTTP_HDR_JS "Content-type: application/javascript\r\n\r\n"
-#define HTTP_HDR_RA "Content-type: application/javascript\r\n\r\n"
-#define HTTP_HDR_CSS "Content-type: text/css\r\n\r\n"
-#define HTTP_HDR_SWF "Content-type: application/x-shockwave-flash\r\n\r\n"
-#define HTTP_HDR_XML "Content-type: text/xml\r\n\r\n"
-#define HTTP_HDR_PDF "Content-type: application/pdf\r\n\r\n"
-#define HTTP_HDR_JSON "Content-type: application/json\r\n\r\n"
+#define HTTP_HDR_HTML HTTP_CONTENT_TYPE("text/html")
+#define HTTP_HDR_SSI HTTP_CONTENT_TYPE("text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache")
+#define HTTP_HDR_GIF HTTP_CONTENT_TYPE("image/gif")
+#define HTTP_HDR_PNG HTTP_CONTENT_TYPE("image/png")
+#define HTTP_HDR_JPG HTTP_CONTENT_TYPE("image/jpeg")
+#define HTTP_HDR_BMP HTTP_CONTENT_TYPE("image/bmp")
+#define HTTP_HDR_ICO HTTP_CONTENT_TYPE("image/x-icon")
+#define HTTP_HDR_APP HTTP_CONTENT_TYPE("application/octet-stream")
+#define HTTP_HDR_JS HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_RA HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_CSS HTTP_CONTENT_TYPE("text/css")
+#define HTTP_HDR_SWF HTTP_CONTENT_TYPE("application/x-shockwave-flash")
+#define HTTP_HDR_XML HTTP_CONTENT_TYPE("text/xml")
+#define HTTP_HDR_PDF HTTP_CONTENT_TYPE("application/pdf")
+#define HTTP_HDR_JSON HTTP_CONTENT_TYPE("application/json")
+#define HTTP_HDR_CSV HTTP_CONTENT_TYPE("text/csv")
+#define HTTP_HDR_TSV HTTP_CONTENT_TYPE("text/tsv")
+#define HTTP_HDR_SVG HTTP_CONTENT_TYPE("image/svg+xml")
+#define HTTP_HDR_SVGZ HTTP_CONTENT_TYPE_ENCODING("image/svg+xml", "gzip")
-#define HTTP_HDR_DEFAULT_TYPE "Content-type: text/plain\r\n\r\n"
+#define HTTP_HDR_DEFAULT_TYPE HTTP_CONTENT_TYPE("text/plain")
/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES
* and http://www.iana.org/assignments/media-types for registered content types
@@ -97,7 +103,13 @@ static const tHTTPHeader g_psHTTPHeaders[] =
{ "xml", HTTP_HDR_XML},
{ "xsl", HTTP_HDR_XML},
{ "pdf", HTTP_HDR_PDF},
- { "json", HTTP_HDR_JSON}
+ { "json", HTTP_HDR_JSON},
+#ifdef HTTPD_ADDITIONAL_CONTENT_TYPES
+ /* If you need to add content types not listed here:
+ * #define HTTPD_ADDITIONAL_CONTENT_TYPES {"ct1", HTTP_CONTENT_TYPE("text/ct1")}, {"exe", HTTP_CONTENT_TYPE("application/exe")}
+ */
+ , HTTPD_ADDITIONAL_CONTENT_TYPES
+#endif
};
#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader))
diff --git a/src/apps/httpd/makefsdata/makefsdata.c b/../lwip_nat/src/apps/httpd/makefsdata/makefsdata.c
index 934e7219..d9bfe4ad 100644
--- a/src/apps/httpd/makefsdata/makefsdata.c
+++ b/../lwip_nat/src/apps/httpd/makefsdata/makefsdata.c
@@ -206,6 +206,8 @@ int main(int argc, char *argv[])
printf(" by Jim Pettinato - circa 2003 " NEWLINE);
printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE);
+ LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
+
strcpy(path, "fs");
for (i = 1; i < argc; i++) {
if (argv[i] == NULL) {
@@ -306,8 +308,7 @@ int main(int argc, char *argv[])
CHDIR(path);
fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
- fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE);
- fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE);
+ fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE NEWLINE NEWLINE);
fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
/* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
@@ -501,6 +502,7 @@ u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed,
buf = (u8_t*)malloc(fsize);
LWIP_ASSERT("buf != NULL", buf != NULL);
r = fread(buf, 1, fsize, inFile);
+ LWIP_ASSERT("r == fsize", r == fsize);
*file_size = fsize;
*is_compressed = 0;
#if MAKEFS_SUPPORT_DEFLATE
@@ -990,10 +992,10 @@ int file_write_http_header(FILE *data_file, const char *filename, int file_size,
/* ATTENTION: headers are done now (double-CRLF has been written!) */
if (precalcChksum) {
+ LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
hdr_len += cur_len;
- LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff);
LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
*http_hdr_len = (u16_t)hdr_len;
diff --git a/src/apps/mdns/mdns.c b/../lwip_nat/src/apps/mdns/mdns.c
index 14334fc8..7c3f0238 100644
--- a/src/apps/mdns/mdns.c
+++ b/../lwip_nat/src/apps/mdns/mdns.c
@@ -18,7 +18,6 @@
* - Checking that source address of unicast requests are on the same network
* - Limiting multicast responses to 1 per second per resource record
* - Fragmenting replies if required
- * - Subscribe to netif address/link change events and act on them (currently needs to be done manually)
* - Handling multi-packet known answers
* - Individual known answer detection for all local IPv6 addresses
* - Dynamic size of outgoing packet
@@ -102,6 +101,7 @@ static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
static u8_t mdns_netif_client_id;
static struct udp_pcb *mdns_pcb;
+NETIF_DECLARE_EXT_CALLBACK(netif_callback)
#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
@@ -261,15 +261,8 @@ struct mdns_answer {
u16_t rd_offset;
};
-/**
- * Add a label part to a domain
- * @param domain The domain to add a label to
- * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
- * @param len The length of the label
- * @return ERR_OK on success, an err_t otherwise if label too long
- */
-err_t
-mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
+static err_t
+mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
{
if (len > MDNS_LABEL_MAXLEN) {
return ERR_VAL;
@@ -283,6 +276,23 @@ mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
}
domain->name[domain->length] = len;
domain->length++;
+ return ERR_OK;
+}
+
+/**
+ * Add a label part to a domain
+ * @param domain The domain to add a label to
+ * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
+ * @param len The length of the label
+ * @return ERR_OK on success, an err_t otherwise if label too long
+ */
+err_t
+mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
+{
+ err_t err = mdns_domain_add_label_base(domain, len);
+ if (err != ERR_OK) {
+ return err;
+ }
if (len) {
MEMCPY(&domain->name[domain->length], label, len);
domain->length += len;
@@ -290,6 +300,27 @@ mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
return ERR_OK;
}
+/**
+ * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
+ */
+static err_t
+mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
+{
+ err_t err = mdns_domain_add_label_base(domain, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ if (len) {
+ if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
+ /* take back the ++ done before */
+ domain->length--;
+ return ERR_ARG;
+ }
+ domain->length += len;
+ }
+ return ERR_OK;
+}
+
/**
* Internal readname function with max 6 levels of recursion following jumps
* while decompressing name
@@ -333,22 +364,16 @@ mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, uns
/* normal label */
if (c <= MDNS_LABEL_MAXLEN) {
- u8_t label[MDNS_LABEL_MAXLEN];
err_t res;
if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
return MDNS_READNAME_ERROR;
}
- if (c != 0) {
- if (pbuf_copy_partial(p, label, c, offset) != c) {
- return MDNS_READNAME_ERROR;
- }
- offset += c;
- }
- res = mdns_domain_add_label(domain, (char *) label, c);
+ res = mdns_domain_add_label_pbuf(domain, p, offset, c);
if (res != ERR_OK) {
return MDNS_READNAME_ERROR;
}
+ offset += c;
} else {
/* bad length byte */
return MDNS_READNAME_ERROR;
@@ -503,7 +528,7 @@ mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
}
memset(domain, 0, sizeof(struct mdns_domain));
ptr = (const u8_t *) addr;
- for (i = sizeof(ip6_addr_t) - 1; i >= 0; i--) {
+ for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
char buf;
u8_t byte = ptr[i];
int j;
@@ -1138,7 +1163,7 @@ mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct net
struct mdns_domain host;
mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
- return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_t), NULL);
+ return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_p_t), NULL);
}
/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
@@ -1603,7 +1628,7 @@ mdns_handle_question(struct mdns_packet *pkt)
#endif
} else if (match & REPLY_HOST_AAAA) {
#if LWIP_IPV6
- if (ans.rd_length == sizeof(ip6_addr_t) &&
+ if (ans.rd_length == sizeof(ip6_addr_p_t) &&
/* TODO this clears all AAAA responses if first addr is set as known */
pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
@@ -1803,36 +1828,12 @@ dealloc:
pbuf_free(p);
}
-/**
- * @ingroup mdns
- * Initiate MDNS responder. Will open UDP sockets on port 5353
- */
-void
-mdns_resp_init(void)
-{
- err_t res;
-
- mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
- LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
-#if LWIP_MULTICAST_TX_OPTIONS
- udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
-#else
- mdns_pcb->ttl = MDNS_TTL;
-#endif
- res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT);
- LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
- LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
- udp_recv(mdns_pcb, mdns_recv, NULL);
-
- mdns_netif_client_id = netif_alloc_client_data_id();
-}
-
/**
* @ingroup mdns
* Announce IP settings have changed on netif.
* Call this in your callback registered by netif_set_status_callback().
- * This function may go away in the future when netif supports registering
- * multiple callback functions.
+ * No need to call this function when LWIP_NETIF_EXT_STATUS_CALLBACK==1,
+ * this handled automatically for you.
* @param netif The network interface where settings have changed.
*/
void
@@ -1846,13 +1847,51 @@ mdns_resp_netif_settings_changed(struct netif *netif)
/* Announce on IPv6 and IPv4 */
#if LWIP_IPV6
- mdns_announce(netif, IP6_ADDR_ANY);
+ mdns_announce(netif, IP6_ADDR_ANY);
#endif
#if LWIP_IPV4
- mdns_announce(netif, IP4_ADDR_ANY);
+ mdns_announce(netif, IP4_ADDR_ANY);
#endif
}
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+static void
+mdns_netif_ext_status_callback(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
+{
+ LWIP_UNUSED_ARG(args);
+
+ /* MDNS enabled on netif? */
+ if (NETIF_TO_HOST(netif) == NULL) {
+ return;
+ }
+
+ switch (reason)
+ {
+ case LWIP_NSC_STATUS_CHANGED:
+ if (args->status_changed.state != 0) {
+ mdns_resp_netif_settings_changed(netif);
+ }
+ /* TODO: send goodbye message */
+ break;
+ case LWIP_NSC_LINK_CHANGED:
+ if (args->link_changed.state != 0) {
+ mdns_resp_netif_settings_changed(netif);
+ }
+ break;
+ case LWIP_NSC_IPV4_ADDRESS_CHANGED: /* fall through */
+ case LWIP_NSC_IPV4_GATEWAY_CHANGED: /* fall through */
+ case LWIP_NSC_IPV4_NETMASK_CHANGED: /* fall through */
+ case LWIP_NSC_IPV4_SETTINGS_CHANGED: /* fall through */
+ case LWIP_NSC_IPV6_SET: /* fall through */
+ case LWIP_NSC_IPV6_ADDR_STATE_CHANGED:
+ mdns_resp_netif_settings_changed(netif);
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
/**
* @ingroup mdns
* Activate MDNS responder for a network interface and send announce packets.
@@ -1873,12 +1912,11 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
- mdns = (struct mdns_host *) mem_malloc(sizeof(struct mdns_host));
+ mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
-
+
netif_set_client_data(netif, mdns_netif_client_id, mdns);
- memset(mdns, 0, sizeof(struct mdns_host));
MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
mdns->dns_ttl = dns_ttl;
@@ -1955,13 +1993,13 @@ mdns_resp_remove_netif(struct netif *netif)
* @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
* allow dynamic replies.
* @param txt_data Userdata pointer for txt_fn
- * @return ERR_OK if the service was added to the netif, an err_t otherwise
+ * @return service_id if the service was added to the netif, an err_t otherwise
*/
-err_t
+s8_t
mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
{
- int i;
- int slot = -1;
+ s8_t i;
+ s8_t slot = -1;
struct mdns_service *srv;
struct mdns_host* mdns;
@@ -1981,11 +2019,9 @@ mdns_resp_add_service(struct netif *netif, const char *name, const char *service
}
LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
- srv = (struct mdns_service*)mem_malloc(sizeof(struct mdns_service));
+ srv = (struct mdns_service*)mem_calloc(1, sizeof(struct mdns_service));
LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
- memset(srv, 0, sizeof(struct mdns_service));
-
MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
srv->txt_fn = txt_fn;
@@ -2003,7 +2039,30 @@ mdns_resp_add_service(struct netif *netif, const char *name, const char *service
#if LWIP_IPV4
mdns_announce(netif, IP4_ADDR_ANY);
#endif
+ return slot;
+}
+
+/**
+ * @ingroup mdns
+ * Delete a service on the selected network interface.
+ * @param netif The network interface on which service should be removed
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @return ERR_OK if the service was removed from the netif, an err_t otherwise
+ */
+err_t
+mdns_resp_del_service(struct netif *netif, s8_t slot)
+{
+ struct mdns_host* mdns;
+ struct mdns_service *srv;
+ LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+ srv = mdns->services[slot];
+ mdns->services[slot] = NULL;
+ mem_free(srv);
return ERR_OK;
}
@@ -2025,4 +2084,31 @@ mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_
return mdns_domain_add_label(&service->txtdata, txt, txt_len);
}
+/**
+ * @ingroup mdns
+ * Initiate MDNS responder. Will open UDP sockets on port 5353
+ */
+void
+mdns_resp_init(void)
+{
+ err_t res;
+
+ mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
+#if LWIP_MULTICAST_TX_OPTIONS
+ udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
+#else
+ mdns_pcb->ttl = MDNS_TTL;
+#endif
+ res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT);
+ LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
+ udp_recv(mdns_pcb, mdns_recv, NULL);
+
+ mdns_netif_client_id = netif_alloc_client_data_id();
+
+ /* register for netif events when started on first netif */
+ netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
+}
+
#endif /* LWIP_MDNS_RESPONDER */
diff --git a/src/apps/mqtt/mqtt.c b/../lwip_nat/src/apps/mqtt/mqtt.c
index 899e2cbf..1adcab0b 100644
--- a/src/apps/mqtt/mqtt.c
+++ b/../lwip_nat/src/apps/mqtt/mqtt.c
@@ -48,12 +48,15 @@
*
*/
#include "lwip/apps/mqtt.h"
+#include "lwip/apps/mqtt_priv.h"
#include "lwip/timeouts.h"
#include "lwip/ip_addr.h"
#include "lwip/mem.h"
#include "lwip/err.h"
#include "lwip/pbuf.h"
-#include "lwip/tcp.h"
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
#include <string.h>
#if LWIP_TCP && LWIP_CALLBACK_API
@@ -71,7 +74,7 @@
#define MQTT_DEBUG_WARN_STATE (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
#define MQTT_DEBUG_SERIOUS (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
-static void mqtt_cyclic_timer(void *arg);
+
/**
* MQTT client connection states
@@ -119,6 +122,8 @@ enum mqtt_connect_flag {
};
+static void mqtt_cyclic_timer(void *arg);
+
#if defined(LWIP_DEBUG)
static const char * const mqtt_message_type_str[15] =
{
@@ -175,26 +180,51 @@ msg_generate_packet_id(mqtt_client_t *client)
/*--------------------------------------------------------------------------------------------------------------------- */
/* Output ring buffer */
+/** Add single item to ring buffer */
+static void
+mqtt_ringbuf_put(struct mqtt_ringbuf_t *rb, u8_t item)
+{
+ rb->buf[rb->put] = item;
+ rb->put++;
+ if (rb->put >= MQTT_OUTPUT_RINGBUF_SIZE) {
+ rb->put = 0;
+ }
+}
+
+/** Return pointer to ring buffer get position */
+static u8_t*
+mqtt_ringbuf_get_ptr(struct mqtt_ringbuf_t *rb)
+{
+ return &rb->buf[rb->get];
+}
-#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1)
+static void
+mqtt_ringbuf_advance_get_idx(struct mqtt_ringbuf_t *rb, u16_t len)
+{
+ LWIP_ASSERT("mqtt_ringbuf_advance_get_idx: len < MQTT_OUTPUT_RINGBUF_SIZE", len < MQTT_OUTPUT_RINGBUF_SIZE);
-/** Add single item to ring buffer */
-#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item)
+ rb->get += len;
+ if (rb->get >= MQTT_OUTPUT_RINGBUF_SIZE) {
+ rb->get = rb->get - MQTT_OUTPUT_RINGBUF_SIZE;
+ }
+}
/** Return number of bytes in ring buffer */
-#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get))
+static u16_t
+mqtt_ringbuf_len(struct mqtt_ringbuf_t *rb)
+{
+ u32_t len = rb->put - rb->get;
+ if (len > 0xFFFF) {
+ len += MQTT_OUTPUT_RINGBUF_SIZE;
+ }
+ return (u16_t)len;
+}
/** Return number of bytes free in ring buffer */
#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
/** Return number of bytes possible to read without wrapping around */
-#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - ((rb)->get & MQTT_RINGBUF_IDX_MASK)))
-
-/** Return pointer to ring buffer get position */
-#define mqtt_ringbuf_get_ptr(rb) (&(rb)->buf[(rb)->get & MQTT_RINGBUF_IDX_MASK])
-
-#define mqtt_ringbuf_advance_get_idx(rb, len) ((rb)->get += (len))
-
+#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - (rb)->get))
/**
* Try send as many bytes as possible from output ring buffer
@@ -202,12 +232,12 @@ msg_generate_packet_id(mqtt_client_t *client)
* @param tpcb TCP connection handle
*/
static void
-mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb)
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct altcp_pcb *tpcb)
{
err_t err;
u8_t wrap = 0;
u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
- u16_t send_len = tcp_sndbuf(tpcb);
+ u16_t send_len = altcp_sndbuf(tpcb);
LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
if (send_len == 0 || ringbuf_lin_len == 0) {
@@ -215,7 +245,7 @@ mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb)
}
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
- send_len, ringbuf_lin_len, ((rb)->get & MQTT_RINGBUF_IDX_MASK), ((rb)->put & MQTT_RINGBUF_IDX_MASK)));
+ send_len, ringbuf_lin_len, rb->get, rb->put));
if (send_len > ringbuf_lin_len) {
/* Space in TCP output buffer is larger than available in ring buffer linear portion */
@@ -223,18 +253,18 @@ mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb)
/* Wrap around if more data in ring buffer after linear portion */
wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
}
- err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
+ err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
if ((err == ERR_OK) && wrap) {
mqtt_ringbuf_advance_get_idx(rb, send_len);
/* Use the lesser one of ring buffer linear length and TCP send buffer size */
- send_len = LWIP_MIN(tcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
- err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
+ send_len = LWIP_MIN(altcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+ err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
}
if (err == ERR_OK) {
mqtt_ringbuf_advance_get_idx(rb, send_len);
/* Flush */
- tcp_output(tpcb);
+ altcp_output(tpcb);
} else {
LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
}
@@ -453,18 +483,18 @@ mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t leng
* Append fixed header
* @param rb Output ring buffer
* @param msg_type see enum mqtt_message_type
- * @param dup MQTT DUP flag
- * @param qos MQTT QoS field
- * @param retain MQTT retain flag
+ * @param fdup MQTT DUP flag
+ * @param fqos MQTT QoS field
+ * @param fretain MQTT retain flag
* @param r_length Remaining length after fixed header
*/
static void
-mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t dup,
- u8_t qos, u8_t retain, u16_t r_length)
+mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t fdup,
+ u8_t fqos, u8_t fretain, u16_t r_length)
{
/* Start with control byte */
- mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1)));
+ mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((fdup & 1) << 3) | ((fqos & 3) << 1) | (fretain & 1)));
/* Encode remaining length field */
do {
mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
@@ -510,12 +540,12 @@ mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
/* Bring down TCP connection if not already done */
if (client->conn != NULL) {
err_t res;
- tcp_recv(client->conn, NULL);
- tcp_err(client->conn, NULL);
- tcp_sent(client->conn, NULL);
- res = tcp_close(client->conn);
+ altcp_recv(client->conn, NULL);
+ altcp_err(client->conn, NULL);
+ altcp_sent(client->conn, NULL);
+ res = altcp_close(client->conn);
if (res != ERR_OK) {
- tcp_abort(client->conn);
+ altcp_abort(client->conn);
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res)));
}
client->conn = NULL;
@@ -638,7 +668,7 @@ mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
* @param remaining_length Remaining length of complete message
*/
static mqtt_connection_status_t
- mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
+mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
{
mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
@@ -648,6 +678,9 @@ static mqtt_connection_status_t
u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
u16_t pkt_id = 0;
+ LWIP_ERROR("buffer length mismatch", fixed_hdr_idx + length <= MQTT_VAR_HEADER_BUFFER_LEN,
+ return MQTT_CONNECT_DISCONNECTED);
+
if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
if (client->conn_state == MQTT_CONNECTING) {
/* Get result code from CONNACK */
@@ -675,8 +708,8 @@ static mqtt_connection_status_t
if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
/* Should have topic and pkt id*/
- uint8_t *topic;
- uint16_t after_topic;
+ u8_t *topic;
+ u16_t after_topic;
u8_t bkp;
u16_t topic_len = var_hdr_payload[0];
topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
@@ -857,7 +890,7 @@ mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
* @return ERR_OK or err passed into callback
*/
static err_t
-mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+mqtt_tcp_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
{
mqtt_client_t *client = (mqtt_client_t *)arg;
LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
@@ -875,7 +908,7 @@ mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
}
/* Tell remote that data has been received */
- tcp_recved(pcb, p->tot_len);
+ altcp_recved(pcb, p->tot_len);
res = mqtt_parse_incoming(client, p);
pbuf_free(p);
@@ -901,7 +934,7 @@ mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
* @return ERR_OK
*/
static err_t
-mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
+mqtt_tcp_sent_cb(void *arg, struct altcp_pcb *tpcb, u16_t len)
{
mqtt_client_t *client = (mqtt_client_t *)arg;
@@ -952,7 +985,7 @@ mqtt_tcp_err_cb(void *arg, err_t err)
* @return err ERR_OK
*/
static err_t
-mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
+mqtt_tcp_poll_cb(void *arg, struct altcp_pcb *tpcb)
{
mqtt_client_t *client = (mqtt_client_t *)arg;
if (client->conn_state == MQTT_CONNECTED) {
@@ -969,7 +1002,7 @@ mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
* @return ERR_OK
*/
static err_t
-mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
+mqtt_tcp_connect_cb(void *arg, struct altcp_pcb *tpcb, err_t err)
{
mqtt_client_t* client = (mqtt_client_t *)arg;
@@ -982,9 +1015,9 @@ mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
client->msg_idx = 0;
/* Setup TCP callbacks */
- tcp_recv(tpcb, mqtt_tcp_recv_cb);
- tcp_sent(tpcb, mqtt_tcp_sent_cb);
- tcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
+ altcp_recv(tpcb, mqtt_tcp_recv_cb);
+ altcp_sent(tpcb, mqtt_tcp_sent_cb);
+ altcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n"));
/* Enter MQTT connect state */
@@ -1178,11 +1211,7 @@ mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb
mqtt_client_t *
mqtt_client_new(void)
{
- mqtt_client_t *client = (mqtt_client_t *)mem_malloc(sizeof(mqtt_client_t));
- if (client != NULL) {
- memset(client, 0, sizeof(mqtt_client_t));
- }
- return client;
+ return (mqtt_client_t *)mem_calloc(1, sizeof(mqtt_client_t));
}
@@ -1207,6 +1236,7 @@ mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port,
/* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
+ u8_t client_user_len = 0, client_pass_len = 0;
LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
@@ -1243,6 +1273,26 @@ mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port,
LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
remaining_length = (u16_t)len;
}
+ if (client_info->client_user != NULL) {
+ flags |= MQTT_CONNECT_FLAG_USERNAME;
+ len = strlen(client_info->client_user);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL);
+ client_user_len = (u8_t)len;
+ len = remaining_length + 2 + client_user_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
+ if (client_info->client_pass != NULL) {
+ flags |= MQTT_CONNECT_FLAG_PASSWORD;
+ len = strlen(client_info->client_pass);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL);
+ client_pass_len = (u8_t)len;
+ len = remaining_length + 2 + client_pass_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
/* Don't complicate things, always connect using clean session */
flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
@@ -1258,15 +1308,25 @@ mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port,
return ERR_MEM;
}
- client->conn = tcp_new();
+ client->conn = altcp_tcp_new();
if (client->conn == NULL) {
return ERR_MEM;
}
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ if (client_info->tls_config) {
+ struct altcp_pcb *pcb_tls = altcp_tls_new(client_info->tls_config, client->conn);
+ if (pcb_tls == NULL) {
+ altcp_close(client->conn);
+ return ERR_MEM;
+ }
+ client->conn = pcb_tls;
+ }
+#endif
/* Set arg pointer for callbacks */
- tcp_arg(client->conn, client);
+ altcp_arg(client->conn, client);
/* Any local address, pick random local port number */
- err = tcp_bind(client->conn, IP_ADDR_ANY, 0);
+ err = altcp_bind(client->conn, IP_ADDR_ANY, 0);
if (err != ERR_OK) {
LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
goto tcp_fail;
@@ -1274,13 +1334,13 @@ mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port,
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
/* Connect to server */
- err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
+ err = altcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
if (err != ERR_OK) {
LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
goto tcp_fail;
}
/* Set error callback */
- tcp_err(client->conn, mqtt_tcp_err_cb);
+ altcp_err(client->conn, mqtt_tcp_err_cb);
client->conn_state = TCP_CONNECTING;
/* Append fixed header */
@@ -1300,10 +1360,18 @@ mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port,
mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
}
+ /* Append user name if given */
+ if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
+ mqtt_output_append_string(&client->output, client_info->client_user, client_user_len);
+ }
+ /* Append password if given */
+ if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
+ mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len);
+ }
return ERR_OK;
tcp_fail:
- tcp_abort(client->conn);
+ altcp_abort(client->conn);
client->conn = NULL;
return err;
}
diff --git a/../lwip_nat/src/apps/smtp/smtp.c b/../lwip_nat/src/apps/smtp/smtp.c
new file mode 100644
index 00000000..199b07a6
--- /dev/null
+++ b/../lwip_nat/src/apps/smtp/smtp.c
@@ -0,0 +1,1540 @@
+/**
+ * @file
+ * SMTP client module
+ *
+ * Author: Simon Goldschmidt
+ *
+ * @defgroup smtp SMTP client
+ * @ingroup apps
+ *
+ * This is simple SMTP client for raw API.
+ * It is a minimal implementation of SMTP as specified in RFC 5321.
+ *
+ * Example usage:
+@code{.c}
+ void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err)
+ {
+ printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg,
+ smtp_result, srv_err, err);
+ }
+ static void my_smtp_test(void)
+ {
+ smtp_set_server_addr("mymailserver.org");
+ -> set both username and password as NULL if no auth needed
+ smtp_set_auth("username", "password");
+ smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn,
+ some_argument);
+ }
+@endcode
+
+ * When using from any other thread than the tcpip_thread (for NO_SYS==0), use
+ * smtp_send_mail_int()!
+ *
+ * SMTP_BODYDH usage:
+@code{.c}
+ int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh)
+ {
+ if(bdh->state >= 10) {
+ return BDH_DONE;
+ }
+ sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state);
+ bdh->length = strlen(bdh->buffer);
+ ++bdh->state;
+ return BDH_WORKING;
+ }
+
+ smtp_send_mail_bodycback("sender", "recipient", "subject",
+ my_smtp_bodydh_fn, my_smtp_result_fn, some_argument);
+@endcode
+ *
+ * @todo:
+ * - attachments (the main difficulty here is streaming base64-encoding to
+ * prevent having to allocate a buffer for the whole encoded file at once)
+ * - test with more mail servers...
+ *
+ */
+
+#include "lwip/apps/smtp.h"
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+#include "lwip/sys.h"
+#include "lwip/sockets.h"
+#include "lwip/altcp.h"
+#include "lwip/dns.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+
+#include <string.h> /* strnlen, memcpy */
+#include <stdlib.h>
+
+/** TCP poll interval. Unit is 0.5 sec. */
+#define SMTP_POLL_INTERVAL 4
+/** TCP poll timeout while sending message body, reset after every
+ * successful write. 3 minutes */
+#define SMTP_TIMEOUT_DATABLOCK ( 3 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while waiting for confirmation after sending the body.
+ * 10 minutes */
+#define SMTP_TIMEOUT_DATATERM (10 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while not sending the body.
+ * This is somewhat lower than the RFC states (5 minutes for initial, MAIL
+ * and RCPT) but still OK for us here.
+ * 2 minutes */
+#define SMTP_TIMEOUT ( 2 * 60 * SMTP_POLL_INTERVAL / 2)
+
+/* the various debug levels for this file */
+#define SMTP_DEBUG_TRACE (SMTP_DEBUG | LWIP_DBG_TRACE)
+#define SMTP_DEBUG_STATE (SMTP_DEBUG | LWIP_DBG_STATE)
+#define SMTP_DEBUG_WARN (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SMTP_DEBUG_WARN_STATE (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SMTP_DEBUG_SERIOUS (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+
+#define SMTP_RX_BUF_LEN 255
+#define SMTP_TX_BUF_LEN 255
+#define SMTP_CRLF "\r\n"
+#define SMTP_CRLF_LEN 2
+
+#define SMTP_RESP_220 "220"
+#define SMTP_RESP_235 "235"
+#define SMTP_RESP_250 "250"
+#define SMTP_RESP_334 "334"
+#define SMTP_RESP_354 "354"
+#define SMTP_RESP_LOGIN_UNAME "VXNlcm5hbWU6"
+#define SMTP_RESP_LOGIN_PASS "UGFzc3dvcmQ6"
+
+#define SMTP_KEYWORD_AUTH_SP "AUTH "
+#define SMTP_KEYWORD_AUTH_EQ "AUTH="
+#define SMTP_KEYWORD_AUTH_LEN 5
+#define SMTP_AUTH_PARAM_PLAIN "PLAIN"
+#define SMTP_AUTH_PARAM_LOGIN "LOGIN"
+
+#define SMTP_CMD_EHLO_1 "EHLO ["
+#define SMTP_CMD_EHLO_1_LEN 6
+#define SMTP_CMD_EHLO_2 "]\r\n"
+#define SMTP_CMD_EHLO_2_LEN 3
+#define SMTP_CMD_AUTHPLAIN_1 "AUTH PLAIN "
+#define SMTP_CMD_AUTHPLAIN_1_LEN 11
+#define SMTP_CMD_AUTHPLAIN_2 "\r\n"
+#define SMTP_CMD_AUTHPLAIN_2_LEN 2
+#define SMTP_CMD_AUTHLOGIN "AUTH LOGIN\r\n"
+#define SMTP_CMD_AUTHLOGIN_LEN 12
+#define SMTP_CMD_MAIL_1 "MAIL FROM: <"
+#define SMTP_CMD_MAIL_1_LEN 12
+#define SMTP_CMD_MAIL_2 ">\r\n"
+#define SMTP_CMD_MAIL_2_LEN 3
+#define SMTP_CMD_RCPT_1 "RCPT TO: <"
+#define SMTP_CMD_RCPT_1_LEN 10
+#define SMTP_CMD_RCPT_2 ">\r\n"
+#define SMTP_CMD_RCPT_2_LEN 3
+#define SMTP_CMD_DATA "DATA\r\n"
+#define SMTP_CMD_DATA_LEN 6
+#define SMTP_CMD_HEADER_1 "From: <"
+#define SMTP_CMD_HEADER_1_LEN 7
+#define SMTP_CMD_HEADER_2 ">\r\nTo: <"
+#define SMTP_CMD_HEADER_2_LEN 8
+#define SMTP_CMD_HEADER_3 ">\r\nSubject: "
+#define SMTP_CMD_HEADER_3_LEN 12
+#define SMTP_CMD_HEADER_4 "\r\n\r\n"
+#define SMTP_CMD_HEADER_4_LEN 4
+#define SMTP_CMD_BODY_FINISHED "\r\n.\r\n"
+#define SMTP_CMD_BODY_FINISHED_LEN 5
+#define SMTP_CMD_QUIT "QUIT\r\n"
+#define SMTP_CMD_QUIT_LEN 6
+
+#if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX
+#define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);)
+#else /* SMTP_STAT_TX_BUF_MAX */
+#define SMTP_TX_BUF_MAX(len)
+#endif /* SMTP_STAT_TX_BUF_MAX */
+
+#if SMTP_COPY_AUTHDATA
+#define SMTP_USERNAME(session) (session)->username
+#define SMTP_PASS(session) (session)->pass
+#define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session) (session)->auth_plain_len
+#else /* SMTP_COPY_AUTHDATA */
+#define SMTP_USERNAME(session) smtp_username
+#define SMTP_PASS(session) smtp_pass
+#define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session) smtp_auth_plain_len
+#endif /* SMTP_COPY_AUTHDATA */
+
+#if SMTP_BODYDH
+#ifndef SMTP_BODYDH_MALLOC
+#define SMTP_BODYDH_MALLOC(size) mem_malloc(size)
+#define SMTP_BODYDH_FREE(ptr) mem_free(ptr)
+#endif
+
+/* Some internal state return values */
+#define BDHALLDATASENT 2
+#define BDHSOMEDATASENT 1
+
+enum bdh_handler_state {
+ BDH_SENDING, /* Serving the user function generating body content */
+ BDH_STOP /* User function stopped, closing */
+};
+#endif
+
+/** State for SMTP client state machine */
+enum smtp_session_state {
+ SMTP_NULL,
+ SMTP_HELO,
+ SMTP_AUTH_PLAIN,
+ SMTP_AUTH_LOGIN_UNAME,
+ SMTP_AUTH_LOGIN_PASS,
+ SMTP_AUTH_LOGIN,
+ SMTP_MAIL,
+ SMTP_RCPT,
+ SMTP_DATA,
+ SMTP_BODY,
+ SMTP_QUIT,
+ SMTP_CLOSED
+};
+
+#ifdef LWIP_DEBUG
+/** State-to-string table for debugging */
+static const char *smtp_state_str[] = {
+ "SMTP_NULL",
+ "SMTP_HELO",
+ "SMTP_AUTH_PLAIN",
+ "SMTP_AUTH_LOGIN_UNAME",
+ "SMTP_AUTH_LOGIN_PASS",
+ "SMTP_AUTH_LOGIN",
+ "SMTP_MAIL",
+ "SMTP_RCPT",
+ "SMTP_DATA",
+ "SMTP_BODY",
+ "SMTP_QUIT",
+ "SMTP_CLOSED",
+};
+
+static const char *smtp_result_strs[] = {
+ "SMTP_RESULT_OK",
+ "SMTP_RESULT_ERR_UNKNOWN",
+ "SMTP_RESULT_ERR_CONNECT",
+ "SMTP_RESULT_ERR_HOSTNAME",
+ "SMTP_RESULT_ERR_CLOSED",
+ "SMTP_RESULT_ERR_TIMEOUT",
+ "SMTP_RESULT_ERR_SVR_RESP",
+ "SMTP_RESULT_ERR_MEM"
+};
+#endif /* LWIP_DEBUG */
+
+#if SMTP_BODYDH
+struct smtp_bodydh_state {
+ smtp_bodycback_fn callback_fn; /* The function to call (again) */
+ u16_t state;
+ struct smtp_bodydh exposed; /* the user function structure */
+};
+#endif /* SMTP_BODYDH */
+
+/** struct keeping the body and state of an smtp session */
+struct smtp_session {
+ /** keeping the state of the smtp session */
+ enum smtp_session_state state;
+ /** timeout handling, if this reaches 0, the connection is closed */
+ u16_t timer;
+ /** helper buffer for transmit, not used for sending body */
+ char tx_buf[SMTP_TX_BUF_LEN + 1];
+ struct pbuf* p;
+ /** source email address */
+ const char* from;
+ /** size of the sourceemail address */
+ u16_t from_len;
+ /** target email address */
+ const char* to;
+ /** size of the target email address */
+ u16_t to_len;
+ /** subject of the email */
+ const char *subject;
+ /** length of the subject string */
+ u16_t subject_len;
+ /** this is the body of the mail to be sent */
+ const char* body;
+ /** this is the length of the body to be sent */
+ u16_t body_len;
+ /** amount of data from body already sent */
+ u16_t body_sent;
+ /** callback function to call when closed */
+ smtp_result_fn callback_fn;
+ /** argument for callback function */
+ void *callback_arg;
+#if SMTP_COPY_AUTHDATA
+ /** Username to use for this request */
+ char *username;
+ /** Password to use for this request */
+ char *pass;
+ /** Username and password combined as necessary for PLAIN authentication */
+ char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+ /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+ size_t auth_plain_len;
+#endif /* SMTP_COPY_AUTHDATA */
+#if SMTP_BODYDH
+ struct smtp_bodydh_state *bodydh;
+#endif /* SMTP_BODYDH */
+};
+
+/** IP address or DNS name of the server to use for next SMTP request */
+static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1];
+/** TCP port of the server to use for next SMTP request */
+static u16_t smtp_server_port = SMTP_DEFAULT_PORT;
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** If this is set, mail is sent using SMTPS */
+static struct altcp_tls_config *smtp_server_tls_config;
+#endif
+/** Username to use for the next SMTP request */
+static char *smtp_username;
+/** Password to use for the next SMTP request */
+static char *smtp_pass;
+/** Username and password combined as necessary for PLAIN authentication */
+static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+/** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+static size_t smtp_auth_plain_len;
+
+static err_t smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed);
+static err_t smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
+static void smtp_tcp_err(void *arg, err_t err);
+static err_t smtp_tcp_poll(void *arg, struct altcp_pcb *pcb);
+static err_t smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len);
+static err_t smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err);
+#if LWIP_DNS
+static void smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg);
+#endif /* LWIP_DNS */
+static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len);
+static enum smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len);
+static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb);
+static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p);
+#if SMTP_BODYDH
+static void smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb);
+#endif /* SMTP_BODYDH */
+
+
+#ifdef LWIP_DEBUG
+/** Convert an smtp result to a string */
+const char*
+smtp_result_str(u8_t smtp_result)
+{
+ if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) {
+ return "UNKNOWN";
+ }
+ return smtp_result_strs[smtp_result];
+}
+
+/** Null-terminates the payload of p for printing out messages.
+ * WARNING: use this only if p is not needed any more as the last byte of
+ * payload is deleted!
+ */
+static const char*
+smtp_pbuf_str(struct pbuf* p)
+{
+ if ((p == NULL) || (p->len == 0)) {
+ return "";
+ }
+ ((char*)p->payload)[p->len] = 0;
+ return (const char*)p->payload;
+}
+#endif /* LWIP_DEBUG */
+
+/** @ingroup smtp
+ * Set IP address or DNS name for next SMTP connection
+ *
+ * @param server IP address (in ASCII representation) or DNS name of the server
+ */
+err_t
+smtp_set_server_addr(const char* server)
+{
+ size_t len = 0;
+ if (server != NULL) {
+ /* strnlen: returns length WITHOUT terminating 0 byte OR
+ * SMTP_MAX_SERVERNAME_LEN+1 when string is too long */
+ len = strnlen(server, SMTP_MAX_SERVERNAME_LEN+1);
+ }
+ if (len > SMTP_MAX_SERVERNAME_LEN) {
+ return ERR_MEM;
+ }
+ if (len != 0) {
+ MEMCPY(smtp_server, server, len);
+ }
+ smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
+ return ERR_OK;
+}
+
+/** @ingroup smtp
+ * Set TCP port for next SMTP connection
+ *
+ * @param port TCP port
+ */
+void
+smtp_set_server_port(u16_t port)
+{
+ smtp_server_port = port;
+}
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** @ingroup smtp
+ * Set TLS configuration for next SMTP connection
+ *
+ * @param tls_config TLS configuration
+ */
+void
+smtp_set_tls_config(struct altcp_tls_config *tls_config)
+{
+ smtp_server_tls_config = tls_config;
+}
+#endif
+
+/** @ingroup smtp
+ * Set authentication parameters for next SMTP connection
+ *
+ * @param username login name as passed to the server
+ * @param pass password passed to the server together with username
+ */
+err_t
+smtp_set_auth(const char* username, const char* pass)
+{
+ size_t uname_len = 0;
+ size_t pass_len = 0;
+
+ memset(smtp_auth_plain, 0xfa, 64);
+ if (username != NULL) {
+ uname_len = strlen(username);
+ if (uname_len > SMTP_MAX_USERNAME_LEN) {
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
+ (int)uname_len, SMTP_MAX_USERNAME_LEN));
+ return ERR_ARG;
+ }
+ }
+ if (pass != NULL) {
+#if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
+ pass_len = strlen(pass);
+ if (pass_len > SMTP_MAX_PASS_LEN) {
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
+ (int)uname_len, SMTP_MAX_USERNAME_LEN));
+ return ERR_ARG;
+ }
+#else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
+#endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+ }
+ *smtp_auth_plain = 0;
+ if (username != NULL) {
+ smtp_username = smtp_auth_plain + 1;
+ strcpy(smtp_username, username);
+ }
+ if (pass != NULL) {
+ smtp_pass = smtp_auth_plain + uname_len + 2;
+ strcpy(smtp_pass, pass);
+ }
+ smtp_auth_plain_len = uname_len + pass_len + 2;
+
+ return ERR_OK;
+}
+
+#if SMTP_BODYDH
+static void smtp_free_struct(struct smtp_session *s)
+{
+ if (s->bodydh != NULL) {
+ SMTP_BODYDH_FREE(s->bodydh);
+ }
+ SMTP_STATE_FREE(s);
+}
+#else /* SMTP_BODYDH */
+#define smtp_free_struct(x) SMTP_STATE_FREE(x)
+#endif /* SMTP_BODYDH */
+
+static struct altcp_pcb*
+smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
+{
+ struct altcp_pcb* pcb;
+ LWIP_UNUSED_ARG(remote_ip);
+
+ pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
+ if (pcb != NULL) {
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ if (smtp_server_tls_config) {
+ struct altcp_pcb *pcb_tls = altcp_tls_new(smtp_server_tls_config, pcb);
+ if (pcb_tls == NULL) {
+ altcp_close(pcb);
+ return NULL;
+ }
+ pcb = pcb_tls;
+ }
+#endif
+ altcp_arg(pcb, s);
+ altcp_recv(pcb, smtp_tcp_recv);
+ altcp_err(pcb, smtp_tcp_err);
+ altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
+ altcp_sent(pcb, smtp_tcp_sent);
+ }
+ return pcb;
+}
+
+/** The actual mail-sending function, called by smtp_send_mail and
+ * smtp_send_mail_static after setting up the struct smtp_session.
+ */
+static err_t
+smtp_send_mail_alloced(struct smtp_session *s)
+{
+ err_t err;
+ struct altcp_pcb* pcb = NULL;
+ ip_addr_t addr;
+
+ LWIP_ASSERT("no smtp_session supplied", s != NULL);
+
+#if SMTP_CHECK_DATA
+ /* check that body conforms to RFC:
+ * - convert all single-CR or -LF in body to CRLF
+ * - only 7-bit ASCII is allowed
+ */
+ if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+#if SMTP_BODYDH
+ if (s->bodydh == NULL)
+#endif /* SMTP_BODYDH */
+ {
+ if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ }
+#endif /* SMTP_CHECK_DATA */
+
+#if SMTP_COPY_AUTHDATA
+ /* copy auth data, ensuring the first byte is always zero */
+ MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
+ s->auth_plain_len = smtp_auth_plain_len;
+ /* default username and pass is empty string */
+ s->username = s->auth_plain;
+ s->pass = s->auth_plain;
+ if (smtp_username != NULL) {
+ s->username += smtp_username - smtp_auth_plain;
+ }
+ if (smtp_pass != NULL) {
+ s->pass += smtp_pass - smtp_auth_plain;
+ }
+#endif /* SMTP_COPY_AUTHDATA */
+
+ s->state = SMTP_NULL;
+ s->timer = SMTP_TIMEOUT;
+
+#if LWIP_DNS
+ err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
+#else /* LWIP_DNS */
+ err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
+#endif /* LWIP_DNS */
+ if (err == ERR_OK) {
+ pcb = smtp_setup_pcb(s, &addr);
+ if (pcb == NULL) {
+ err = ERR_MEM;
+ goto leave;
+ }
+ err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ goto deallocate_and_leave;
+ }
+ } else if (err != ERR_INPROGRESS) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
+ goto deallocate_and_leave;
+ }
+ return ERR_OK;
+
+deallocate_and_leave:
+ if (pcb != NULL) {
+ altcp_arg(pcb, NULL);
+ altcp_close(pcb);
+ }
+leave:
+ smtp_free_struct(s);
+ /* no need to call the callback here since we return != ERR_OK */
+ return err;
+}
+
+/** @ingroup smtp
+ * Send an email via the currently selected server, username and password.
+ *
+ * @param from source email address (must be NULL-terminated)
+ * @param to target email address (must be NULL-terminated)
+ * @param subject email subject (must be NULL-terminated)
+ * @param body email body (must be NULL-terminated)
+ * @param callback_fn callback function
+ * @param callback_arg user argument to callback_fn
+ * @returns - ERR_OK if structures were allocated and no error occured starting the connection
+ * (this does not mean the email has been successfully sent!)
+ * - another err_t on error.
+ */
+err_t
+smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
+ smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t from_len = strlen(from);
+ size_t to_len = strlen(to);
+ size_t subject_len = strlen(subject);
+ size_t body_len = strlen(body);
+ size_t mem_len = sizeof(struct smtp_session);
+ char *sfrom, *sto, *ssubject, *sbody;
+
+ mem_len += from_len + to_len + subject_len + body_len + 4;
+ if (mem_len > 0xffff) {
+ /* too long! */
+ return ERR_MEM;
+ }
+
+ /* Allocate memory to keep this email's session state */
+ s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ /* initialize the structure */
+ memset(s, 0, mem_len);
+ s->from = sfrom = (char*)s + sizeof(struct smtp_session);
+ s->from_len = (u16_t)from_len;
+ s->to = sto = sfrom + from_len + 1;
+ s->to_len = (u16_t)to_len;
+ s->subject = ssubject = sto + to_len + 1;
+ s->subject_len = (u16_t)subject_len;
+ s->body = sbody = ssubject + subject_len + 1;
+ s->body_len = (u16_t)body_len;
+ /* copy source and target email address */
+ /* cast to size_t is a hack to cast away constness */
+ MEMCPY(sfrom, from, from_len + 1);
+ MEMCPY(sto, to, to_len + 1);
+ MEMCPY(ssubject, subject, subject_len + 1);
+ MEMCPY(sbody, body, body_len + 1);
+
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+/** @ingroup smtp
+ * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
+ * an internal buffer to save memory.
+ * WARNING: the above data must stay untouched until the callback function is
+ * called (unless the function returns != ERR_OK)
+ */
+err_t
+smtp_send_mail_static(const char *from, const char* to, const char* subject,
+ const char* body, smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t len;
+
+ s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(struct smtp_session));
+ /* initialize the structure */
+ s->from = from;
+ len = strlen(from);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->from_len = (u16_t)len;
+ s->to = to;
+ len = strlen(to);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->to_len = (u16_t)len;
+ s->subject = subject;
+ len = strlen(subject);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->subject_len = (u16_t)len;
+ s->body = body;
+ len = strlen(body);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->body_len = (u16_t)len;
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+
+/** @ingroup smtp
+ * Same as smtp_send_mail but takes a struct smtp_send_request as single
+ * parameter which contains all the other parameters.
+ * To be used with tcpip_callback to send mail from interrupt context or from
+ * another thread.
+ *
+ * WARNING: server and authentication must stay untouched until this function has run!
+ *
+ * Usage example:
+ * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
+ * - fill the members of the struct as if calling smtp_send_mail
+ * - specify a callback_function
+ * - set callback_arg to the structure itself
+ * - call this function
+ * - wait for the callback function to be called
+ * - in the callback function, deallocate the structure (passed as arg)
+ */
+void
+smtp_send_mail_int(void *arg)
+{
+ struct smtp_send_request *req = (struct smtp_send_request*)arg;
+ err_t err;
+
+ LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
+
+ if (req->static_data) {
+ err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
+ req->callback_fn, req->callback_arg);
+ } else {
+ err = smtp_send_mail(req->from, req->to, req->subject, req->body,
+ req->callback_fn, req->callback_arg);
+ }
+ if ((err != ERR_OK) && (req->callback_fn != NULL)) {
+ req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
+ }
+}
+
+#if SMTP_CHECK_DATA
+/** Verify that a given string conforms to the SMTP rules
+ * (7-bit only, no single CR or LF,
+ * @todo: no line consisting of a single dot only)
+ */
+static err_t
+smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
+{
+ size_t i;
+ u8_t last_was_cr = 0;
+ for (i = 0; i < data_len; i++) {
+ char current = data[i];
+ if ((current & 0x80) != 0) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
+ return ERR_ARG;
+ }
+ if (current == '\r') {
+ if (!linebreaks_allowed) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
+ return ERR_ARG;
+ }
+ if (last_was_cr) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
+ return ERR_ARG;
+ }
+ last_was_cr = 1;
+ } else {
+ if (current == '\n') {
+ if (!last_was_cr) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
+ return ERR_ARG;
+ }
+ }
+ last_was_cr = 0;
+ }
+ }
+ return ERR_OK;
+}
+#endif /* SMTP_CHECK_DATA */
+
+/** Frees the smtp_session and calls the callback function */
+static void
+smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
+{
+ smtp_result_fn fn = s->callback_fn;
+ void *arg = s->callback_arg;
+ if (s->p != NULL) {
+ pbuf_free(s->p);
+ }
+ smtp_free_struct(s);
+ if (fn != NULL) {
+ fn(arg, result, srv_err, err);
+ }
+}
+
+/** Try to close a pcb and free the arg if successful */
+static void
+smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
+ u16_t srv_err, err_t err)
+{
+ if (pcb != NULL) {
+ altcp_arg(pcb, NULL);
+ if (altcp_close(pcb) == ERR_OK) {
+ if (s != NULL) {
+ smtp_free(s, result, srv_err, err);
+ }
+ } else {
+ /* close failed, set back arg */
+ altcp_arg(pcb, s);
+ }
+ } else {
+ if (s != NULL) {
+ smtp_free(s, result, srv_err, err);
+ }
+ }
+}
+
+/** Raw API TCP err callback: pcb is already deallocated */
+static void
+smtp_tcp_err(void *arg, err_t err)
+{
+ LWIP_UNUSED_ARG(err);
+ if (arg != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
+ smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
+ }
+}
+
+/** Raw API TCP poll callback */
+static err_t
+smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+ if (arg != NULL) {
+ struct smtp_session *s = (struct smtp_session*)arg;
+ if (s->timer != 0) {
+ s->timer--;
+ }
+ }
+ smtp_process(arg, pcb, NULL);
+ return ERR_OK;
+}
+
+/** Raw API TCP sent callback */
+static err_t
+smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+ LWIP_UNUSED_ARG(len);
+
+ smtp_process(arg, pcb, NULL);
+
+ return ERR_OK;
+}
+
+/** Raw API TCP recv callback */
+static err_t
+smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(err);
+ if (p != NULL) {
+ altcp_recved(pcb, p->tot_len);
+ smtp_process(arg, pcb, p);
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
+ smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
+ }
+ return ERR_OK;
+}
+
+static err_t
+smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
+ } else {
+ /* shouldn't happen, but we still check 'err', only to be sure */
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
+ smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
+ }
+ return ERR_OK;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
+ */
+static void
+smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+ struct smtp_session *s = (struct smtp_session*)arg;
+ struct altcp_pcb *pcb;
+ err_t err;
+ u8_t result;
+
+ LWIP_UNUSED_ARG(hostname);
+
+ if (ipaddr != NULL) {
+ pcb = smtp_setup_pcb(s, ipaddr);
+ if (pcb != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
+ err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
+ if (err == ERR_OK) {
+ return;
+ }
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ result = SMTP_RESULT_ERR_CONNECT;
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
+ result = SMTP_RESULT_ERR_MEM;
+ err = ERR_MEM;
+ }
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
+ hostname));
+ pcb = NULL;
+ result = SMTP_RESULT_ERR_HOSTNAME;
+ err = ERR_ARG;
+ }
+ smtp_close(s, pcb, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+
+/** Table 6-bit-index-to-ASCII used for base64-encoding */
+static const char base64_table[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '+', '/'
+};
+
+/** Base64 encoding */
+static size_t
+smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
+{
+ size_t i;
+ s8_t j;
+ size_t target_idx = 0;
+ size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
+ size_t source_len_b64 = source_len + longer;
+ size_t len = (((source_len_b64) * 4) / 3);
+ u8_t x = 5;
+ u8_t current = 0;
+ LWIP_UNUSED_ARG(target_len);
+
+ LWIP_ASSERT("target_len is too short", target_len >= len);
+
+ for (i = 0; i < source_len_b64; i++) {
+ u8_t b = (i < source_len ? (u8_t)source[i] : 0);
+ for (j = 7; j >= 0; j--, x--) {
+ if ((b & (1 << j)) != 0) {
+ current = (u8_t)(current | (1U << x));
+ }
+ if (x == 0) {
+ target[target_idx++] = base64_table[current];
+ x = 6;
+ current = 0;
+ }
+ }
+ }
+ for (i = len - longer; i < len; i++) {
+ target[i] = '=';
+ }
+ return len;
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Parse pbuf to see if it contains the beginning of an answer.
+ * If so, it returns the contained response code as number between 1 and 999.
+ * If not, zero is returned.
+ *
+ * @param s smtp session struct
+ */
+static u16_t
+smtp_is_response(struct smtp_session *s)
+{
+ char digits[4];
+ long num;
+
+ if (s->p == NULL) {
+ return 0;
+ }
+ /* copy three digits and convert them to int */
+ if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
+ /* pbuf was too short */
+ return 0;
+ }
+ digits[3] = 0;
+ num = strtol(digits, NULL, 10);
+ if ((num <= 0) || (num >= 1000)) {
+ /* failed to find response code at start of line */
+ return 0;
+ }
+ return (u16_t)num;
+}
+
+/** Parse pbuf to see if it contains a fully received answer.
+ * If one is found, ERR_OK is returned.
+ * If none is found, ERR_VAL is returned.
+ *
+ * A fully received answer is a 3-digit number followed by a space,
+ * some string and a CRLF as line ending.
+ *
+ * @param s smtp session struct
+ */
+static err_t
+smtp_is_response_finished(struct smtp_session *s)
+{
+ u8_t sp;
+ u16_t crlf;
+ u16_t offset;
+
+ if (s->p == NULL) {
+ return ERR_VAL;
+ }
+ offset = 0;
+again:
+ /* We could check the response number here, but we trust the
+ * protocol definition which says the client can rely on it being
+ * the same on every line. */
+
+ /* find CRLF */
+ if (offset > 0xFFFF - 4) {
+ /* would overflow */
+ return ERR_VAL;
+ }
+ crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
+ if (crlf == 0xFFFF) {
+ /* no CRLF found */
+ return ERR_VAL;
+ }
+ sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
+ if (sp == '-') {
+ /* no space after response code -> try next line */
+ offset = (u16_t)(crlf + 2);
+ if (offset < crlf) {
+ /* overflow */
+ return ERR_VAL;
+ }
+ goto again;
+ } else if (sp == ' ') {
+ /* CRLF found after response code + space -> valid response */
+ return ERR_OK;
+ }
+ /* sp contains invalid character */
+ return ERR_VAL;
+}
+
+/** Prepare HELO/EHLO message */
+static enum smtp_session_state
+smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
+{
+ size_t ipa_len;
+ const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
+ LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
+ ipa_len = strlen(ipa);
+ LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
+
+ *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
+ LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+
+ SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
+ MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
+ SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
+ return SMTP_HELO;
+}
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+/** Parse last server response (in rx_buf) for supported authentication method,
+ * create data to send out (to tx_buf), set tx_data_len correctly
+ * and return the next state.
+ */
+static enum smtp_session_state
+smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ /* check response for supported authentication method */
+ u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
+ if (auth == 0xFFFF) {
+ auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
+ }
+ if (auth != 0xFFFF) {
+ u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
+ if ((crlf != 0xFFFF) && (crlf > auth)) {
+ /* use tx_buf temporarily */
+ u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
+ if (copied != 0) {
+ char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
+ s->tx_buf[copied] = 0;
+#if SMTP_SUPPORT_AUTH_PLAIN
+ /* favour PLAIN over LOGIN since it involves less requests */
+ if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
+ size_t auth_len;
+ /* server supports AUTH PLAIN */
+ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
+
+ /* add base64-encoded string "\0username\0password" */
+ auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
+ SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
+ SMTP_AUTH_PLAIN_LEN(s));
+ LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
+ SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
+ SMTP_CMD_AUTHPLAIN_2_LEN);
+ return SMTP_AUTH_PLAIN;
+ } else
+#endif /* SMTP_SUPPORT_AUTH_PLAIN */
+ {
+#if SMTP_SUPPORT_AUTH_LOGIN
+ if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
+ /* server supports AUTH LOGIN */
+ *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
+ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
+ return SMTP_AUTH_LOGIN_UNAME;
+ }
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+ }
+ }
+ }
+ }
+ /* server didnt's send correct keywords for AUTH, try sending directly */
+ return smtp_prepare_mail(s, tx_buf_len);
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+#if SMTP_SUPPORT_AUTH_LOGIN
+/** Send base64-encoded username */
+static enum smtp_session_state
+smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+ SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
+ /* @todo: support base64-encoded longer than 64k */
+ LWIP_ASSERT("string too long", base64_len <= 0xffff);
+ LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+ *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+ SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+ s->tx_buf[*tx_buf_len] = 0;
+ return SMTP_AUTH_LOGIN_PASS;
+}
+
+/** Send base64-encoded password */
+static enum smtp_session_state
+smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+ SMTP_PASS(s), strlen(SMTP_PASS(s)));
+ /* @todo: support base64-encoded longer than 64k */
+ LWIP_ASSERT("string too long", base64_len <= 0xffff);
+ LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+ *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+ SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+ s->tx_buf[*tx_buf_len] = 0;
+ return SMTP_AUTH_LOGIN;
+}
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Prepare MAIL message */
+static enum smtp_session_state
+smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
+ target += SMTP_CMD_MAIL_1_LEN;
+ MEMCPY(target, s->from, s->from_len);
+ target += s->from_len;
+ SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
+ return SMTP_MAIL;
+}
+
+/** Prepare RCPT message */
+static enum smtp_session_state
+smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
+ target += SMTP_CMD_RCPT_1_LEN;
+ MEMCPY(target, s->to, s->to_len);
+ target += s->to_len;
+ SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
+ return SMTP_RCPT;
+}
+
+/** Prepare header of body */
+static enum smtp_session_state
+smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
+ SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
+ s->subject_len;
+ LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
+ *tx_buf_len = (u16_t)len;
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
+ target += SMTP_CMD_HEADER_1_LEN;
+ MEMCPY(target, s->from, s->from_len);
+ target += s->from_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
+ target += SMTP_CMD_HEADER_2_LEN;
+ MEMCPY(target, s->to, s->to_len);
+ target += s->to_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
+ target += SMTP_CMD_HEADER_3_LEN;
+ MEMCPY(target, s->subject, s->subject_len);
+ target += s->subject_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
+
+ return SMTP_BODY;
+}
+
+/** Prepare QUIT message */
+static enum smtp_session_state
+smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ *tx_buf_len = SMTP_CMD_QUIT_LEN;
+ s->tx_buf[*tx_buf_len] = 0;
+ SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
+ LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+ return SMTP_CLOSED;
+}
+
+/** If in state SMTP_BODY, try to send more body data */
+static void
+smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+ err_t err;
+
+ if (s->state == SMTP_BODY) {
+#if SMTP_BODYDH
+ if (s->bodydh) {
+ smtp_send_body_data_handler(s, pcb);
+ } else
+#endif /* SMTP_BODYDH */
+ {
+ u16_t send_len = (u16_t)(s->body_len - s->body_sent);
+ if (send_len > 0) {
+ u16_t snd_buf = altcp_sndbuf(pcb);
+ if (send_len > snd_buf) {
+ send_len = snd_buf;
+ }
+ if (send_len > 0) {
+ /* try to send something out */
+ err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ s->timer = SMTP_TIMEOUT_DATABLOCK;
+ s->body_sent = (u16_t)(s->body_sent + send_len);
+ if (s->body_sent < s->body_len) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
+ s->body_sent, s->body_len));
+ }
+ }
+ }
+ }
+ }
+ if (s->body_sent == s->body_len) {
+ /* the whole body has been written, write last line */
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
+ s->body_len));
+ err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
+ if (err == ERR_OK) {
+ s->timer = SMTP_TIMEOUT_DATATERM;
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
+ smtp_state_str[SMTP_QUIT]));
+ /* last line written, change state, wait for confirmation */
+ s->state = SMTP_QUIT;
+ }
+ }
+ }
+}
+
+/** State machine-like implementation of an SMTP client.
+ */
+static void
+smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
+{
+ struct smtp_session* s = (struct smtp_session*)arg;
+ u16_t response_code = 0;
+ u16_t tx_buf_len = 0;
+ enum smtp_session_state next_state;
+
+ if (arg == NULL) {
+ /* already closed SMTP connection */
+ if (p != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
+ p->tot_len, smtp_pbuf_str(p)));
+ pbuf_free(p);
+ }
+ return;
+ }
+
+ next_state = s->state;
+
+ if (p != NULL) {
+ /* received data */
+ if (s->p == NULL) {
+ s->p = p;
+ } else {
+ pbuf_cat(s->p, p);
+ }
+ } else {
+ /* idle timer, close connection if timed out */
+ if (s->timer == 0) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
+ smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
+ return;
+ }
+ if (s->state == SMTP_BODY) {
+ smtp_send_body(s, pcb);
+ return;
+ }
+ }
+ response_code = smtp_is_response(s);
+ if (response_code) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
+ if (smtp_is_response_finished(s) != ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
+ /* wait for next packet to complete the respone */
+ return;
+ }
+ } else {
+ if (s->p != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
+ smtp_pbuf_str(s->p)));
+ pbuf_free(s->p);
+ s->p = NULL;
+ }
+ return;
+ }
+
+ switch(s->state)
+ {
+ case(SMTP_NULL):
+ /* wait for 220 */
+ if (response_code == 220) {
+ /* then send EHLO */
+ next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
+ }
+ break;
+ case(SMTP_HELO):
+ /* wait for 250 */
+ if (response_code == 250) {
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+ /* then send AUTH or MAIL */
+ next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_AUTH_LOGIN):
+ case(SMTP_AUTH_PLAIN):
+ /* wait for 235 */
+ if (response_code == 235) {
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+ /* send MAIL */
+ next_state = smtp_prepare_mail(s, &tx_buf_len);
+ }
+ break;
+#if SMTP_SUPPORT_AUTH_LOGIN
+ case(SMTP_AUTH_LOGIN_UNAME):
+ /* wait for 334 Username */
+ if (response_code == 334) {
+ if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
+ /* send username */
+ next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
+ }
+ }
+ break;
+ case(SMTP_AUTH_LOGIN_PASS):
+ /* wait for 334 Password */
+ if (response_code == 334) {
+ if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
+ /* send username */
+ next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
+ }
+ }
+ break;
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+ case(SMTP_MAIL):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send RCPT */
+ next_state = smtp_prepare_rcpt(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_RCPT):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send DATA */
+ SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
+ tx_buf_len = SMTP_CMD_DATA_LEN;
+ next_state = SMTP_DATA;
+ }
+ break;
+ case(SMTP_DATA):
+ /* wait for 354 */
+ if (response_code == 354) {
+ /* send email header */
+ next_state = smtp_prepare_header(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_BODY):
+ /* nothing to be done here, handled somewhere else */
+ break;
+ case(SMTP_QUIT):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send QUIT */
+ next_state = smtp_prepare_quit(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_CLOSED):
+ /* nothing to do, wait for connection closed from server */
+ return;
+ default:
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
+ smtp_state_str[s->state]));
+ break;
+ }
+ if (s->state == next_state) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
+ smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+ /* close connection */
+ smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
+ return;
+ }
+ if (tx_buf_len > 0) {
+ SMTP_TX_BUF_MAX(tx_buf_len);
+ if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
+ smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
+ smtp_state_str[s->state], tx_buf_len, s->tx_buf));
+ s->timer = SMTP_TIMEOUT;
+ pbuf_free(s->p);
+ s->p = NULL;
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
+ smtp_state_str[s->state], smtp_state_str[next_state]));
+ s->state = next_state;
+ if (next_state == SMTP_BODY) {
+ /* try to stream-send body data right now */
+ smtp_send_body(s, pcb);
+ } else if (next_state == SMTP_CLOSED) {
+ /* sent out all data, delete structure */
+ altcp_arg(pcb, NULL);
+ smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
+ }
+ }
+ }
+}
+
+#if SMTP_BODYDH
+/** Elementary sub-function to send data
+ *
+ * @returns: BDHALLDATASENT all data has been written
+ * BDHSOMEDATASENT some data has been written
+ * 0 no data has been written
+ */
+static int
+smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
+{
+ err_t err;
+ u16_t len = *howmany;
+
+ len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
+ err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ *from += len;
+ if ((*howmany -= len) > 0) {
+ return BDHSOMEDATASENT;
+ }
+ return BDHALLDATASENT;
+ }
+ return 0;
+}
+
+/** Same as smtp_send_mail_static, but uses a callback function to send body data
+ */
+err_t
+smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
+ smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t len;
+
+ s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(struct smtp_session));
+ s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
+ if (s->bodydh == NULL) {
+ SMTP_STATE_FREE(s);
+ return ERR_MEM;
+ }
+ memset(s->bodydh, 0, sizeof(struct smtp_bodydh));
+ /* initialize the structure */
+ s->from = from;
+ len = strlen(from);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->from_len = (u16_t)len;
+ s->to = to;
+ len = strlen(to);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->to_len = (u16_t)len;
+ s->subject = subject;
+ len = strlen(subject);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->subject_len = (u16_t)len;
+ s->body = NULL;
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+ s->bodydh->callback_fn = bodycback_fn;
+ s->bodydh->state = BDH_SENDING;
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+static void
+smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+ struct smtp_bodydh_state *bdh = s->bodydh;
+ int res = 0, ret;
+ LWIP_ASSERT("s != NULL", s != NULL);
+ LWIP_ASSERT("bodydh != NULL", bdh != NULL);
+
+ /* resume any leftovers from prior memory constraints */
+ if (s->body_len) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
+ if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
+ != BDHALLDATASENT) {
+ s->body_sent = s->body_len - 1;
+ return;
+ }
+ }
+ ret = res;
+ /* all data on buffer has been queued, resume execution */
+ if (bdh->state == BDH_SENDING) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
+ do {
+ ret |= res; /* remember if we once queued something to send */
+ bdh->exposed.length = 0;
+ if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
+ bdh->state = BDH_STOP;
+ }
+ s->body = bdh->exposed.buffer;
+ s->body_len = bdh->exposed.length;
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
+ } while (s->body_len &&
+ ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
+ && (bdh->state != BDH_STOP));
+ }
+ if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
+ s->body_sent = s->body_len;
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
+ s->body_sent = s->body_len - 1;
+ }
+}
+#endif /* SMTP_BODYDH */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/src/apps/snmp/snmp_asn1.c b/../lwip_nat/src/apps/snmp/snmp_asn1.c
index f35b7604..0b8ec4a1 100644
--- a/src/apps/snmp/snmp_asn1.c
+++ b/../lwip_nat/src/apps/snmp/snmp_asn1.c
@@ -169,48 +169,6 @@ snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u3
return ERR_OK;
}
-
-/**
- * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
- * @param value is the host order u32_t value to be encoded
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_u64t_cnt()
- */
-err_t
-snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
-{
- if (octets_needed > 9) {
- return ERR_ARG;
- }
- if (octets_needed == 9) {
- /* not enough bits in 'value' add leading 0x00 */
- PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
- octets_needed--;
- }
-
- while (octets_needed > 4) {
- octets_needed--;
- PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
- }
-
- /* skip to low u32 */
- value++;
-
- while (octets_needed > 1) {
- octets_needed--;
- PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
- }
-
- /* always write at least one octet (also in case of value == 0) */
- PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
-
- return ERR_OK;
-}
-
/**
* Encodes s32_t integer into a pbuf chained ASN1 msg.
*
@@ -329,31 +287,6 @@ snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
}
}
-/**
- * Returns octet count for an u64_t.
- *
- * @param value value to be encoded
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-void
-snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
-{
- /* check if high u32 is 0 */
- if (*value == 0x00) {
- /* only low u32 is important */
- value++;
- snmp_asn1_enc_u32t_cnt(*value, octets_needed);
- } else {
- /* low u32 does not matter for length determination */
- snmp_asn1_enc_u32t_cnt(*value, octets_needed);
- *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
- }
-}
-
/**
* Returns octet count for an s32_t.
*
@@ -442,6 +375,9 @@ snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tl
tlv->value_len = data;
} else if (data > 0x80) { /* long form */
u8_t length_bytes = data - 0x80;
+ if (length_bytes > pbuf_stream->length) {
+ return ERR_VAL;
+ }
tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
tlv->value_len = 0;
@@ -509,60 +445,6 @@ snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value
return ERR_VAL;
}
-/**
- * Decodes large positive integer (counter64) into 2x u32_t.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-err_t
-snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
-{
- u8_t data;
-
- if (len <= 4) {
- /* high u32 is 0 */
- *value = 0;
- /* directly skip to low u32 */
- value++;
- }
-
- if ((len > 0) && (len <= 9)) {
- PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-
- /* expecting sign bit to be zero, only unsigned please! */
- if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
- *value = data;
- len--;
-
- while (len > 0) {
- PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-
- if (len == 4) {
- /* skip to low u32 */
- value++;
- *value = 0;
- } else {
- *value <<= 8;
- }
-
- *value |= data;
- len--;
- }
-
- return ERR_OK;
- }
- }
-
- return ERR_VAL;
-}
-
/**
* Decodes integer into s32_t.
*
@@ -576,51 +458,26 @@ snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value
err_t
snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
{
-#if BYTE_ORDER == LITTLE_ENDIAN
- u8_t *lsb_ptr = (u8_t*)value;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
-#endif
- u8_t sign;
u8_t data;
if ((len > 0) && (len < 5)) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
- len--;
if (data & 0x80) {
/* negative, start from -1 */
*value = -1;
- sign = 1;
- *lsb_ptr &= data;
+ *value = (*value << 8) | data;
} else {
/* positive, start from 0 */
- *value = 0;
- sign = 0;
- *lsb_ptr |= data;
+ *value = data;
}
-
- /* OR/AND octets with value */
+ len--;
+ /* shift in the remaining value */
while (len > 0) {
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ *value = (*value << 8) | data;
len--;
-
-#if BYTE_ORDER == LITTLE_ENDIAN
- *value <<= 8;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- *value >>= 8;
-#endif
-
- if (sign) {
- *lsb_ptr |= 255;
- *lsb_ptr &= data;
- } else {
- *lsb_ptr |= data;
- }
}
-
return ERR_OK;
}
@@ -746,4 +603,102 @@ snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u1
return ERR_OK;
}
+#if LWIP_HAVE_INT64
+/**
+ * Returns octet count for an u64_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+void
+snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
+{
+ /* check if high u32 is 0 */
+ if ((value >> 32) == 0) {
+ /* only low u32 is important */
+ snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
+ } else {
+ /* low u32 does not matter for length determination */
+ snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
+ *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+ }
+}
+
+/**
+ * Decodes large positive integer (counter64) into 2x u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return 64 bit integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+err_t
+snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
+{
+ u8_t data;
+
+ if ((len > 0) && (len <= 9)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ *value <<= 8;
+ *value |= data;
+ len--;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u64t_cnt()
+ */
+err_t
+snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u64_t value)
+{
+ if (octets_needed > 9) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 9) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* always write at least one octet (also in case of value == 0) */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value)));
+
+ return ERR_OK;
+}
+#endif
+
#endif /* LWIP_SNMP */
diff --git a/src/apps/snmp/snmp_asn1.h b/../lwip_nat/src/apps/snmp/snmp_asn1.h
index ec50d8c9..e5b9e3fc 100644
--- a/src/apps/snmp/snmp_asn1.h
+++ b/../lwip_nat/src/apps/snmp/snmp_asn1.h
@@ -57,13 +57,16 @@ extern "C" {
#define SNMP_ASN1_DATATYPE_MASK 0x1F
#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
-/* context specific (SNMP) tags (from SNMP spec. RFC1157) */
+/* context specific (SNMP) tags (from SNMP spec. RFC1157 and RFC1905) */
#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0
#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2
#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3
#define SNMP_ASN1_CONTEXT_PDU_TRAP 4
#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
+#define SNMP_ASN1_CONTEXT_PDU_INFORM_REQ 6
+#define SNMP_ASN1_CONTEXT_PDU_V2_TRAP 7
+#define SNMP_ASN1_CONTEXT_PDU_REPORT 8
#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0
#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2
@@ -81,7 +84,6 @@ struct snmp_asn1_tlv
err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
-err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len);
err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len);
@@ -90,15 +92,19 @@ err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_t
void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
-void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed);
void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len);
err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value);
err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value);
-err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value);
err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len);
+#if LWIP_HAVE_INT64
+err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value);
+void snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed);
+err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u64_t value);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/apps/snmp/snmp_core.c b/../lwip_nat/src/apps/snmp/snmp_core.c
index c0418336..0fb2d990 100644
--- a/src/apps/snmp/snmp_core.c
+++ b/../lwip_nat/src/apps/snmp/snmp_core.c
@@ -34,17 +34,16 @@
*/
/**
- * @defgroup snmp SNMPv2c agent
+ * @defgroup snmp SNMPv2c/v3 agent
* @ingroup apps
- * SNMPv2c compatible agent\n
+ * SNMPv2c and SNMPv3 compatible agent\n
* There is also a MIB compiler and a MIB viewer in lwIP contrib repository
* (lwip-contrib/apps/LwipMibCompiler).\n
* The agent implements the most important MIB2 MIBs including IPv6 support
* (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
- * whithout IPv6 statistics (TODO).\n
+ * without IPv6 statistics (TODO).\n
* Rewritten by Martin Hentschel <info@cl-soft.de> and
* Dirk Ziegelmeier <dziegel@gmx.de>\n
- * Work on SNMPv3 has started, but is not finished.\n
*
* 0 Agent Capabilities
* ====================
@@ -52,6 +51,7 @@
* Features:
* ---------
* - SNMPv2c support.
+ * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option).
* - Low RAM usage - no memory pools, stack only.
* - MIB2 implementation is separated from SNMP stack.
* - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
@@ -91,6 +91,14 @@
* management protocols CMIP (Common Management Information Protocol)
* and CMOT (CMip Over Tcp).
*
+ * SNMPv3
+ * ------
+ * When SNMPv3 is used, several functions from snmpv3.h must be implemented
+ * by the user. This is mainly user management and persistence handling.
+ * The sample provided in lwip-contrib is insecure, don't use it in production
+ * systems, especially the missing persistence for engine boots variable
+ * simplifies replay attacks.
+ *
* MIB II
* ------
* The standard lwIP stack management information base.
@@ -185,6 +193,9 @@
#if (!LWIP_UDP && LWIP_SNMP)
#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
#endif
+#if SNMP_MAX_OBJ_ID_LEN > 255
+ #error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t"
+#endif
struct snmp_statistics snmp_stats;
static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
@@ -193,11 +204,16 @@ static const struct snmp_obj_id* snmp_device_enterprise_oid = &snmp_devi
const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
-
-#if SNMP_LWIP_MIB2
+#if SNMP_LWIP_MIB2 && LWIP_SNMP_V3
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp_snmpv2_usm.h"
+static const struct snmp_mib* const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib };
+static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
+#elif SNMP_LWIP_MIB2
#include "lwip/apps/snmp_mib2.h"
static const struct snmp_mib* const default_mibs[] = { &mib2 };
-static u8_t snmp_num_mibs = 1;
+static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
#else
static const struct snmp_mib* const default_mibs[] = { NULL };
static u8_t snmp_num_mibs = 0;
@@ -496,10 +512,10 @@ snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
u8_t
snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
{
- u8_t idx = 0;
+ u8_t idx;
/* InetAddressType + InetAddress */
- idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip);
+ idx = snmp_oid_to_ip(&oid[0], oid_len, ip);
if (idx == 0) {
return 0;
}
@@ -588,7 +604,7 @@ snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
if (oid_len > 0) {
MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
- target->len += oid_len;
+ target->len = (u8_t)(target->len + oid_len);
}
}
@@ -655,21 +671,7 @@ snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_le
u8_t
netif_to_num(const struct netif *netif)
{
- u8_t result = 0;
- struct netif *netif_iterator = netif_list;
-
- while (netif_iterator != NULL) {
- result++;
-
- if (netif_iterator == netif) {
- return result;
- }
-
- netif_iterator = netif_iterator->next;
- }
-
- LWIP_ASSERT("netif not found in netif_list", 0);
- return 0;
+ return netif_get_index(netif);
}
static const struct snmp_mib*
@@ -1121,7 +1123,7 @@ snmp_next_oid_init(struct snmp_next_oid_state *state,
this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
u8_t
-snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len)
+snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len)
{
if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
@@ -1141,7 +1143,7 @@ snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, cons
/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
u8_t
-snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference)
+snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void* reference)
{
/* do not overwrite a fail result */
if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
diff --git a/src/apps/snmp/snmp_core_priv.h b/../lwip_nat/src/apps/snmp/snmp_core_priv.h
index 5552177d..51704fcc 100644
--- a/src/apps/snmp/snmp_core_priv.h
+++ b/../lwip_nat/src/apps/snmp/snmp_core_priv.h
@@ -55,6 +55,13 @@ extern "C" {
*/
#define SNMP_ERR_TOOBIG 1
#define SNMP_ERR_AUTHORIZATIONERROR 16
+
+#define SNMP_ERR_UNKNOWN_ENGINEID 30
+#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
+#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
+#define SNMP_ERR_NOTINTIMEWINDOW 33
+#define SNMP_ERR_DECRYIPTION_ERROR 34
+
#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
diff --git a/src/apps/snmp/snmp_mib2_interfaces.c b/../lwip_nat/src/apps/snmp/snmp_mib2_interfaces.c
index 979b5073..f5b8619f 100644
--- a/src/apps/snmp/snmp_mib2_interfaces.c
+++ b/../lwip_nat/src/apps/snmp/snmp_mib2_interfaces.c
@@ -65,10 +65,9 @@ interfaces_get_value(struct snmp_node_instance* instance, void* value)
s32_t *sint_ptr = (s32_t*)value;
s32_t num_netifs = 0;
- struct netif *netif = netif_list;
- while (netif != NULL) {
+ struct netif *netif;
+ NETIF_FOREACH(netif) {
num_netifs++;
- netif = netif->next;
}
*sint_ptr = num_netifs;
@@ -109,14 +108,12 @@ interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8
ifIndex = row_oid[0];
/* find netif with index */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
if (netif_to_num(netif) == ifIndex) {
/* store netif pointer for subsequent operations (get/test/set) */
cell_instance->reference.ptr = netif;
return SNMP_ERR_NOERROR;
}
- netif = netif->next;
}
/* not found */
@@ -136,15 +133,12 @@ interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id*
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
/* iterate over all possible OIDs to find the next one */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
test_oid[0] = netif_to_num(netif);
/* check generated OID: is it a candidate for the next one? */
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
-
- netif = netif->next;
}
/* did we find a next one? */
diff --git a/src/apps/snmp/snmp_mib2_ip.c b/../lwip_nat/src/apps/snmp/snmp_mib2_ip.c
index 4f05180a..671aa2a2 100644
--- a/src/apps/snmp/snmp_mib2_ip.c
+++ b/../lwip_nat/src/apps/snmp/snmp_mib2_ip.c
@@ -262,14 +262,11 @@ ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_
snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
/* find netif with requested ip */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
/* fill in object properties */
return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
}
-
- netif = netif->next;
}
/* not found */
@@ -287,15 +284,12 @@ ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_o
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
/* iterate over all possible OIDs to find the next one */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
/* check generated OID: is it a candidate for the next one? */
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
-
- netif = netif->next;
}
/* did we find a next one? */
@@ -419,8 +413,7 @@ ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row
}
/* find netif with requested route */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
ip4_addr_t dst;
ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
@@ -428,8 +421,6 @@ ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row
/* fill in object properties */
return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
}
-
- netif = netif->next;
}
/* not found */
@@ -454,8 +445,7 @@ ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_
}
/* iterate over all possible OIDs to find the next one */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
ip4_addr_t dst;
ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
@@ -464,8 +454,6 @@ ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_
snmp_ip4_to_oid(&dst, &test_oid[0]);
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
}
-
- netif = netif->next;
}
/* did we find a next one? */
diff --git a/src/apps/snmp/snmp_mib2_tcp.c b/../lwip_nat/src/apps/snmp/snmp_mib2_tcp.c
index 21f69656..47b42a3b 100644
--- a/src/apps/snmp/snmp_mib2_tcp.c
+++ b/../lwip_nat/src/apps/snmp/snmp_mib2_tcp.c
@@ -122,12 +122,22 @@ tcp_get_value(struct snmp_node_instance* instance, void* value)
case 15: /* tcpOutRsts */
*uint_ptr = STATS_GET(mib2.tcpoutrsts);
return sizeof(*uint_ptr);
+#if LWIP_HAVE_INT64
case 17: /* tcpHCInSegs */
- memset(value, 0, 2*sizeof(u32_t)); /* not supported */
- return 2*sizeof(u32_t);
+ {
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.tcpinsegs);
+ *((u64_t*)value) = val64;
+ }
+ return sizeof(u64_t);
case 18: /* tcpHCOutSegs */
- memset(value, 0, 2*sizeof(u32_t)); /* not supported */
- return 2*sizeof(u32_t);
+ {
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.tcpoutsegs);
+ *((u64_t*)value) = val64;
+ }
+ return sizeof(u64_t);
+#endif
default:
LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
break;
@@ -512,8 +522,10 @@ static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE
static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+#if LWIP_HAVE_INT64
static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+#endif
#if LWIP_IPV4
static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
@@ -561,8 +573,10 @@ CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
#endif /* LWIP_IPV4 */
CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
+#if LWIP_HAVE_INT64
CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
+#endif
CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
@@ -585,8 +599,10 @@ static const struct snmp_node* const tcp_nodes[] = {
&SYNC_NODE_NAME(tcp_InErrs).node.node,
&SYNC_NODE_NAME(tcp_OutRsts).node.node,
&SYNC_NODE_NAME(tcp_HCInSegs).node.node,
+#if LWIP_HAVE_INT64
&SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
&SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
+#endif
&SYNC_NODE_NAME(tcp_ListenerTable).node.node
};
diff --git a/src/apps/snmp/snmp_mib2_udp.c b/../lwip_nat/src/apps/snmp/snmp_mib2_udp.c
index 6a983df2..b1dc26df 100644
--- a/src/apps/snmp/snmp_mib2_udp.c
+++ b/../lwip_nat/src/apps/snmp/snmp_mib2_udp.c
@@ -75,12 +75,22 @@ udp_get_value(struct snmp_node_instance* instance, void* value)
case 4: /* udpOutDatagrams */
*uint_ptr = STATS_GET(mib2.udpoutdatagrams);
return sizeof(*uint_ptr);
+#if LWIP_HAVE_INT64
case 8: /* udpHCInDatagrams */
- memset(value, 0, 2*sizeof(u32_t)); /* not supported */
- return 2*sizeof(u32_t);
+ {
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.udpindatagrams);
+ *((u64_t*)value) = val64;
+ }
+ return sizeof(u64_t);
case 9: /* udpHCOutDatagrams */
- memset(value, 0, 2*sizeof(u32_t)); /* not supported */
- return 2*sizeof(u32_t);
+ {
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.udpoutdatagrams);
+ *((u64_t*)value) = val64;
+ }
+ return sizeof(u64_t);
+#endif
default:
LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
break;
@@ -310,8 +320,10 @@ static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NOD
static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+#if LWIP_HAVE_INT64
static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+#endif
#if LWIP_IPV4
static const struct snmp_table_simple_col_def udp_Table_columns[] = {
@@ -337,8 +349,10 @@ CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
CREATE_LWIP_SYNC_NODE(5, udp_Table)
#endif /* LWIP_IPV4 */
CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
+#if LWIP_HAVE_INT64
CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
+#endif
static const struct snmp_node* const udp_nodes[] = {
&SYNC_NODE_NAME(udp_inDatagrams).node.node,
@@ -348,9 +362,12 @@ static const struct snmp_node* const udp_nodes[] = {
#if LWIP_IPV4
&SYNC_NODE_NAME(udp_Table).node.node,
#endif /* LWIP_IPV4 */
- &SYNC_NODE_NAME(udp_endpointTable).node.node,
+ &SYNC_NODE_NAME(udp_endpointTable).node.node
+#if LWIP_HAVE_INT64
+ ,
&SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
&SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
+#endif
};
const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
diff --git a/src/apps/snmp/snmp_msg.c b/../lwip_nat/src/apps/snmp/snmp_msg.c
index 0cb7ca99..f57c032c 100644
--- a/src/apps/snmp/snmp_msg.c
+++ b/../lwip_nat/src/apps/snmp/snmp_msg.c
@@ -48,13 +48,21 @@
#if LWIP_SNMP_V3
#include "lwip/apps/snmpv3.h"
#include "snmpv3_priv.h"
-#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
-#include LWIP_SNMPV3_INCLUDE_ENGINE
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
#endif
#endif
#include <string.h>
+#define SNMP_V3_AUTH_FLAG 0x01
+#define SNMP_V3_PRIV_FLAG 0x02
+
+/* Security levels */
+#define SNMP_V3_NOAUTHNOPRIV 0x00
+#define SNMP_V3_AUTHNOPRIV SNMP_V3_AUTH_FLAG
+#define SNMP_V3_AUTHPRIV (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
+
/* public (non-static) constants */
/** SNMP community string */
const char *snmp_community = SNMP_COMMUNITY;
@@ -66,6 +74,94 @@ const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
snmp_write_callback_fct snmp_write_callback = NULL;
void* snmp_write_callback_arg = NULL;
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+
+static u8_t v1_enabled = 1;
+static u8_t v2c_enabled = 1;
+static u8_t v3_enabled = 1;
+
+static u8_t
+snmp_version_enabled(u8_t version)
+{
+ LWIP_ASSERT("Invalid SNMP version", (version == SNMP_VERSION_1) || (version == SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+ || (version == SNMP_VERSION_3)
+#endif
+ );
+
+ if (version == SNMP_VERSION_1) {
+ return v1_enabled;
+ }
+ else if (version == SNMP_VERSION_2c) {
+ return v2c_enabled;
+ }
+#if LWIP_SNMP_V3
+ else { /* version == SNMP_VERSION_3 */
+ return v3_enabled;
+ }
+#endif
+}
+
+u8_t
+snmp_v1_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_1);
+}
+
+u8_t
+snmp_v2c_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_2c);
+}
+
+u8_t
+snmp_v3_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_3);
+}
+
+static void
+snmp_version_enable(u8_t version, u8_t enable)
+{
+ LWIP_ASSERT("Invalid SNMP version", (version == SNMP_VERSION_1) || (version == SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+ || (version == SNMP_VERSION_3)
+#endif
+ );
+
+ if (version == SNMP_VERSION_1) {
+ v1_enabled = enable;
+ }
+ else if (version == SNMP_VERSION_2c) {
+ v2c_enabled = enable;
+ }
+#if LWIP_SNMP_V3
+ else { /* version == SNMP_VERSION_3 */
+ v3_enabled = enable;
+ }
+#endif
+}
+
+void
+snmp_v1_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_1, enable);
+}
+
+void
+snmp_v2c_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_2c, enable);
+}
+
+void
+snmp_v3_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_3, enable);
+}
+
+#endif
+
/**
* @ingroup snmp_core
* Returns current SNMP community string.
@@ -206,6 +302,72 @@ snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t por
err = snmp_process_set_request(&request);
}
}
+#if LWIP_SNMP_V3
+ else {
+ struct snmp_varbind vb;
+
+ vb.next = NULL;
+ vb.prev = NULL;
+ vb.type = SNMP_ASN1_TYPE_COUNTER32;
+ vb.value_len = sizeof(u32_t);
+
+ switch (request.error_status) {
+ case SNMP_ERR_AUTHORIZATIONERROR:
+ {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.wrongdigests;
+ }
+ break;
+ case SNMP_ERR_UNKNOWN_ENGINEID:
+ {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unknownengineids;
+ }
+ break;
+ case SNMP_ERR_UNKNOWN_SECURITYNAME:
+ {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unknownusernames;
+ }
+ break;
+ case SNMP_ERR_UNSUPPORTED_SECLEVEL:
+ {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unsupportedseclevels;
+ }
+ break;
+ case SNMP_ERR_NOTINTIMEWINDOW:
+ {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.notintimewindows;
+ }
+ break;
+ case SNMP_ERR_DECRYIPTION_ERROR:
+ {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.decryptionerrors;
+ }
+ break;
+ default:
+ /* Unknown or unhandled error_status */
+ err = ERR_ARG;
+ }
+
+ if (err == ERR_OK) {
+ snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
+ request.error_status = SNMP_ERR_NOERROR;
+ }
+
+ request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
+ request.request_id = request.msg_id;
+ }
+#endif
if (err == ERR_OK) {
err = snmp_complete_outbound_frame(&request);
@@ -236,10 +398,12 @@ snmp_msg_getnext_validate_node_inst(struct snmp_node_instance* node_instance, vo
return SNMP_ERR_NOSUCHINSTANCE;
}
+#if LWIP_HAVE_INT64
if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request*)validate_arg)->version == SNMP_VERSION_1)) {
/* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */
return SNMP_ERR_NOSUCHINSTANCE;
}
+#endif
return SNMP_ERR_NOERROR;
}
@@ -615,6 +779,10 @@ snmp_parse_inbound_frame(struct snmp_request *request)
s32_t parent_tlv_value_len;
s32_t s32_value;
err_t err;
+#if LWIP_SNMP_V3
+ snmpv3_auth_algo_t auth;
+ snmpv3_priv_algo_t priv;
+#endif
IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
@@ -630,10 +798,15 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
- if ((s32_value != SNMP_VERSION_1) &&
+
+ if (((s32_value != SNMP_VERSION_1) &&
(s32_value != SNMP_VERSION_2c)
#if LWIP_SNMP_V3
&& (s32_value != SNMP_VERSION_3)
+#endif
+ )
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+ || (!snmp_version_enabled(s32_value))
#endif
)
{
@@ -650,8 +823,9 @@ snmp_parse_inbound_frame(struct snmp_request *request)
/* SNMPv3 doesn't use communities */
/* @todo: Differentiate read/write access */
- strcpy((char*)request->community, snmp_community);
- request->community_strlen = (u16_t)strlen(snmp_community);
+ strncpy((char*)request->community, snmp_community, SNMP_MAX_COMMUNITY_STR_LEN);
+ request->community[SNMP_MAX_COMMUNITY_STR_LEN] = 0; /* ensure NULL termination (strncpy does NOT guarantee it!) */
+ request->community_strlen = (u16_t)strnlen((char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN);
/* RFC3414 globalData */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@@ -736,7 +910,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
- /* @todo: Implement time window checking */
/* msgUserName */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@@ -747,8 +920,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
&u16_value, SNMP_V3_MAX_USER_LENGTH));
request->msg_user_name_len = (u8_t)u16_value;
- /* @todo: Implement unknown user error response */
- IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL));
/* msgAuthenticationParameters */
memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
@@ -760,66 +931,182 @@ snmp_parse_inbound_frame(struct snmp_request *request)
inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
/* Read auth parameters */
- IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ /* IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH); */
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
&u16_value, tlv.value_len));
+ request->msg_authentication_parameters_len = (u8_t)u16_value;
+ /* msgPrivacyParameters */
+ memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
+ &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+ request->msg_privacy_parameters_len = (u8_t)u16_value;
+
+ /* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames)
+ * 1) securityParameters was correctly serialized if we reach here.
+ * 2) securityParameters are already cached.
+ * 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
+ b) https://tools.ietf.org/html/rfc3414#section-7
+ */
+ {
+ const char *eid;
+ u8_t eid_len;
+
+ snmpv3_get_engine_id(&eid, &eid_len);
+
+ if ((request->msg_authoritative_engine_id_len == 0) ||
+ (request->msg_authoritative_engine_id_len != eid_len) ||
+ (memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
+ snmp_stats.unknownengineids++;
+ request->msg_flags = 0; /* noauthnopriv */
+ request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
+ return ERR_OK;
+ }
+ }
+
+ /* 4) verify username */
+ if(snmpv3_get_user((char*)request->msg_user_name, &auth, NULL, &priv, NULL)) {
+ snmp_stats.unknownusernames++;
+ request->msg_flags = 0; /* noauthnopriv */
+ request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
+ return ERR_OK;
+ }
+
+ /* 5) verify security level */
+ switch(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
+ case SNMP_V3_NOAUTHNOPRIV:
+ if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+#if LWIP_SNMP_V3_CRYPTO
+ case SNMP_V3_AUTHNOPRIV:
+ if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+ case SNMP_V3_AUTHPRIV:
+ if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+#endif
+ default:
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+
+ /* 6) if securitylevel specifies authentication, authenticate message. */
#if LWIP_SNMP_V3_CRYPTO
if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
u8_t key[20];
- u8_t algo;
u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
struct snmp_pbuf_stream auth_stream;
+ if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
+ snmp_stats.wrongdigests++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+ return ERR_OK;
+ }
+
/* Rewind stream */
- IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
- IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset));
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+ IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
/* Set auth parameters to zero for verification */
- IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len));
+ IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_len));
/* Verify authentication */
IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
- IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
- IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac));
- /* @todo: Implement error response */
- IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
- }
-#else
- /* Ungraceful exit if we encounter cryptography and don't support it.
- * @todo: Implement error response
- */
- IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)));
-#endif
+ IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &auth, key, NULL, NULL));
+ IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, auth, hmac));
- /* msgPrivacyParameters */
- memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
- IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
- IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
- parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
- IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ if(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
+ snmp_stats.wrongdigests++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+ return ERR_OK;
+ }
- IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
- &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+ /* 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime */
+ {
+ s32_t boots = snmpv3_get_engine_boots_internal();
+ if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ }
+ {
+ s32_t time = snmpv3_get_engine_time_internal();
+ if (request->msg_authoritative_engine_time > time) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ else if (time > 150) {
+ if (request->msg_authoritative_engine_time < time - 150) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ }
+ }
+ }
+#endif
+ /* 8) if securitylevel specifies privacy, decrypt message. */
#if LWIP_SNMP_V3_CRYPTO
- /* Decrypt message */
if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ /* Decrypt message */
+
u8_t key[20];
- u8_t algo;
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
- IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
- IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+ IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &priv, key));
+ if(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
- request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT));
+ request->msg_authoritative_engine_time, priv, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
+ snmp_stats.decryptionerrors++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
+ return ERR_OK;
+ }
}
#endif
+ /* 9) calculate max size of scoped pdu?
+ * 10) securityname for user is retrieved from usertable?
+ * 11) security data is cached?
+ * 12)
+ */
/* Scoped PDU
* Encryption context
@@ -838,6 +1125,7 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
&u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
request->context_engine_id_len = (u8_t)u16_value;
+ /* TODO: do we need to verify this contextengineid too? */
/* contextName */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@@ -848,25 +1136,26 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
&u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
request->context_name_len = (u8_t)u16_value;
+ /* TODO: do we need to verify this contextname too? */
} else
#endif
{
- /* decode community */
- IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
- IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
- parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
- IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ /* decode community */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
- err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
- if (err == ERR_MEM) {
- /* community string does not fit in our buffer -> its too long -> its invalid */
- request->community_strlen = 0;
- snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
- } else {
- IF_PARSE_ASSERT(err == ERR_OK);
- }
- /* add zero terminator */
- request->community[request->community_strlen] = 0;
+ err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
+ if (err == ERR_MEM) {
+ /* community string does not fit in our buffer -> its too long -> its invalid */
+ request->community_strlen = 0;
+ snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
+ } else {
+ IF_PARSE_ASSERT(err == ERR_OK);
+ }
+ /* add zero terminator */
+ request->community[request->community_strlen] = 0;
}
/* decode PDU type (next container level) */
@@ -900,9 +1189,9 @@ snmp_parse_inbound_frame(struct snmp_request *request)
/* unsupported input PDU for this agent (no parse error) */
LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
return ERR_ARG;
- break;
}
request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+ request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
/* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
if (request->community_strlen == 0) {
@@ -1152,7 +1441,7 @@ snmp_prepare_outbound_frame(struct snmp_request *request)
/* 'PDU' sequence */
request->outbound_pdu_offset = pbuf_stream->offset;
- SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0);
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3, 0);
OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
/* request ID */
@@ -1227,12 +1516,14 @@ snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len)
}
snmp_asn1_enc_oid_cnt((u32_t*) varbind->value, varbind->value_len >> 2, &len->value_value_len);
break;
+#if LWIP_HAVE_INT64
case SNMP_ASN1_TYPE_COUNTER64:
- if (varbind->value_len != (2 * sizeof (u32_t))) {
+ if (varbind->value_len != sizeof(u64_t)) {
return ERR_VAL;
}
- snmp_asn1_enc_u64t_cnt((u32_t*) varbind->value, &len->value_value_len);
+ snmp_asn1_enc_u64t_cnt(*(u64_t*)varbind->value, &len->value_value_len);
break;
+#endif
default:
/* unsupported type */
return ERR_VAL;
@@ -1303,9 +1594,11 @@ snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_v
case SNMP_ASN1_TYPE_OBJECT_ID:
OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t*) varbind->value, varbind->value_len / sizeof (u32_t)));
break;
+#if LWIP_HAVE_INT64
case SNMP_ASN1_TYPE_COUNTER64:
- OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, (u32_t*) varbind->value));
+ OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, *(u64_t*) varbind->value));
break;
+#endif
default:
LWIP_ASSERT("Unknown variable type", 0);
break;
@@ -1384,7 +1677,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
struct snmp_pbuf_stream inbound_stream;
OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) );
OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) );
- snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0);
+ OF_BUILD_EXEC( snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0) );
}
frame_size = request->outbound_pbuf_stream.offset;
@@ -1395,7 +1688,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
u8_t i;
outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07;
for (i = 0; i < outbound_padding; i++) {
- snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0);
+ OF_BUILD_EXEC( snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0) );
}
}
#endif
@@ -1433,7 +1726,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
#endif
/* complete missing length in 'PDU' sequence */
- SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3,
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3,
frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
@@ -1502,7 +1795,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
/* Encrypt response */
if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
u8_t key[20];
- u8_t algo;
+ snmpv3_priv_algo_t algo;
/* complete missing length in PDU sequence */
OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
@@ -1520,7 +1813,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) {
u8_t key[20];
- u8_t algo;
+ snmpv3_auth_algo_t algo;
u8_t hmac[20];
OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
@@ -1613,13 +1906,13 @@ snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct s
switch (varbind->type) {
case SNMP_ASN1_TYPE_INTEGER:
VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value));
- varbind->value_len = sizeof(s32_t*);
+ varbind->value_len = sizeof(s32_t);
break;
case SNMP_ASN1_TYPE_COUNTER:
case SNMP_ASN1_TYPE_GAUGE:
case SNMP_ASN1_TYPE_TIMETICKS:
VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
- varbind->value_len = sizeof(u32_t*);
+ varbind->value_len = sizeof(u32_t);
break;
case SNMP_ASN1_TYPE_OCTET_STRING:
case SNMP_ASN1_TYPE_OPAQUE:
@@ -1649,10 +1942,12 @@ snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct s
VB_PARSE_ASSERT(0);
}
break;
+#if LWIP_HAVE_INT64
case SNMP_ASN1_TYPE_COUNTER64:
- VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
- varbind->value_len = 2 * sizeof(u32_t*);
+ VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u64_t*)varbind->value));
+ varbind->value_len = sizeof(u64_t);
break;
+#endif
default:
VB_PARSE_ASSERT(0);
break;
diff --git a/src/apps/snmp/snmp_msg.h b/../lwip_nat/src/apps/snmp/snmp_msg.h
index 2d01ef36..60beaafe 100644
--- a/src/apps/snmp/snmp_msg.h
+++ b/../lwip_nat/src/apps/snmp/snmp_msg.h
@@ -116,6 +116,9 @@ struct snmp_request
/* max-repetitions (getBulkRequest (SNMPv2c)) */
s32_t max_repetitions;
+ /* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
+ u8_t request_out_type;
+
#if LWIP_SNMP_V3
s32_t msg_id;
s32_t msg_max_size;
@@ -128,7 +131,9 @@ struct snmp_request
u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH];
u8_t msg_user_name_len;
u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
+ u8_t msg_authentication_parameters_len;
u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+ u8_t msg_privacy_parameters_len;
u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
u8_t context_engine_id_len;
u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
diff --git a/../lwip_nat/src/apps/snmp/snmp_snmpv2_framework.c b/../lwip_nat/src/apps/snmp/snmp_snmpv2_framework.c
new file mode 100644
index 00000000..16269de3
--- /dev/null
+++ b/../lwip_nat/src/apps/snmp/snmp_snmpv2_framework.c
@@ -0,0 +1,90 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/sys.h"
+
+#include <string.h>
+
+const struct snmp_obj_id usmNoAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 1 } };
+const struct snmp_obj_id usmHMACMD5AuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 2 } };
+const struct snmp_obj_id usmHMACSHAAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 3 } };
+/* .4 sha-224
+ * .5 sha-256
+ * .6 sha-384
+ * .7 sha-512
+ */
+
+const struct snmp_obj_id usmNoPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 1 } };
+const struct snmp_obj_id usmDESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 2 } };
+/* .3 3des-ede */
+const struct snmp_obj_id usmAESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 4 } };
+/* .5 unknown
+ * .6 unknown
+ * .7 unknown
+ */
+
+/* TODO: where should this value come from? */
+#define SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE 1500
+
+/* --- snmpFrameworkMIBObjects 1.3.6.1.6.3.10.2 ----------------------------------------------------- */
+static s16_t snmpengine_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ const char *engineid;
+ u8_t engineid_len;
+
+ switch (node->oid) {
+ case 1: /* snmpEngineID */
+ snmpv3_get_engine_id(&engineid, &engineid_len);
+ MEMCPY(value, engineid, engineid_len);
+ return engineid_len;
+ case 2: /* snmpEngineBoots */
+ *(s32_t *)value = snmpv3_get_engine_boots_internal();
+ return sizeof(s32_t);
+ case 3: /* snmpEngineTime */
+ *(s32_t *)value = snmpv3_get_engine_time_internal();
+ return sizeof(s32_t);
+ case 4: /* snmpEngineMaxMessageSize */
+ *(s32_t *)value = SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE;
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmpengine_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+}
+
+static const struct snmp_scalar_array_node_def snmpengine_scalars_nodes[] = {
+ {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineID */
+ {2, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineBoots */
+ {3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineTime */
+ {4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineMaxMessageSize */
+};
+static const struct snmp_scalar_array_node snmpengine_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, snmpengine_scalars_nodes, snmpengine_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const snmpframeworkmibobjects_subnodes[] = {
+ &snmpengine_scalars.node.node
+};
+static const struct snmp_tree_node snmpframeworkmibobjects_treenode = SNMP_CREATE_TREE_NODE(2, snmpframeworkmibobjects_subnodes);
+
+/* --- snmpFrameworkMIB ----------------------------------------------------- */
+static const struct snmp_node *const snmpframeworkmib_subnodes[] = {
+ &snmpframeworkmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpframeworkmib_root = SNMP_CREATE_TREE_NODE(10, snmpframeworkmib_subnodes);
+static const u32_t snmpframeworkmib_base_oid[] = {1,3,6,1,6,3,10};
+const struct snmp_mib snmpframeworkmib = {snmpframeworkmib_base_oid, LWIP_ARRAYSIZE(snmpframeworkmib_base_oid), &snmpframeworkmib_root.node};
+
+/* --- snmpFrameworkMIB ----------------------------------------------------- */
+#endif /* LWIP_SNMP */
diff --git a/../lwip_nat/src/apps/snmp/snmp_snmpv2_usm.c b/../lwip_nat/src/apps/snmp/snmp_snmpv2_usm.c
new file mode 100644
index 00000000..3f8880d9
--- /dev/null
+++ b/../lwip_nat/src/apps/snmp/snmp_snmpv2_usm.c
@@ -0,0 +1,416 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "lwip/apps/snmp_snmpv2_usm.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+
+#include <string.h>
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_oid_range usmUserTable_oid_ranges[] = {
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff }
+};
+
+static void snmp_engineid_to_oid(const char *engineid, u32_t *oid, u32_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ oid[i] = engineid[i];
+ }
+}
+
+static void snmp_oid_to_name(char *name, const u32_t *oid, size_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ name[i] = (char)oid[i];
+ }
+}
+
+static void snmp_name_to_oid(const char *name, u32_t *oid, size_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ oid[i] = name[i];
+ }
+}
+
+static const struct snmp_obj_id *snmp_auth_algo_to_oid(snmpv3_auth_algo_t algo)
+{
+ if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+ return &usmHMACMD5AuthProtocol;
+ }
+ else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+ return &usmHMACMD5AuthProtocol;
+ }
+
+ return &usmNoAuthProtocol;
+}
+
+static const struct snmp_obj_id *snmp_priv_algo_to_oid(snmpv3_priv_algo_t algo)
+{
+ if (algo == SNMP_V3_PRIV_ALGO_DES) {
+ return &usmDESPrivProtocol;
+ }
+ else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+ return &usmAESPrivProtocol;
+ }
+
+ return &usmNoPrivProtocol;
+}
+
+char username[32];
+
+static snmp_err_t usmusertable_get_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+ const char *engineid;
+ u8_t eid_len;
+
+ u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+ u8_t name_len;
+ u8_t engineid_len;
+
+ u8_t name_start;
+ u8_t engineid_start;
+
+ LWIP_UNUSED_ARG(column);
+
+ snmpv3_get_engine_id(&engineid, &eid_len);
+
+ engineid_len = (u8_t)row_oid[0];
+ engineid_start = 1;
+
+ if (engineid_len != eid_len) {
+ /* EngineID length does not match! */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (engineid_len > row_oid_len) {
+ /* row OID doesn't contain enough data according to engineid_len.*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(&row_oid[engineid_start], engineid_len, usmUserTable_oid_ranges, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+
+ /* Verify EngineID */
+ if (snmp_oid_equal(&row_oid[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ name_len = (u8_t)row_oid[engineid_start + engineid_len];
+ name_start = engineid_start + engineid_len + 1;
+
+ if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+ /* specified name is too long */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (1 + engineid_len + 1 + name_len != row_oid_len) {
+ /* Length of EngineID and name does not match row oid length. (+2 for length fields)*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(&row_oid[name_start], name_len, usmUserTable_oid_ranges, name_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* Verify if user exists */
+ memset(username, 0, sizeof(username));
+ snmp_oid_to_name(username, &row_oid[name_start], name_len);
+ if (snmpv3_get_user(username, NULL, NULL, NULL, NULL) != ERR_OK) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* Save name in reference pointer to make it easier to handle later on */
+ cell_instance->reference.ptr = username;
+ cell_instance->reference_len = name_len;
+
+ /* user was found */
+ return SNMP_ERR_NOERROR;
+}
+
+/*
+ * valid oid options
+ * <oid>
+ * <oid>.<EngineID length>
+ * <oid>.<EngineID length>.<partial EngineID>
+ * <oid>.<EngineID length>.<EngineID>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<partial UserName>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<UserName>
+ *
+ */
+static snmp_err_t usmusertable_get_next_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+ const char *engineid;
+ u8_t eid_len;
+
+ u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+ u8_t name_len;
+ u8_t engineid_len;
+
+ u8_t name_start;
+ u8_t engineid_start = 1;
+ u8_t i;
+
+ struct snmp_next_oid_state state;
+
+ u32_t result_temp[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+ LWIP_UNUSED_ARG(column);
+
+ snmpv3_get_engine_id(&engineid, &eid_len);
+
+ /* If EngineID might be given */
+ if (row_oid->len > 0) {
+ engineid_len = (u8_t)row_oid->id[0];
+ engineid_start = 1;
+
+ if (engineid_len != eid_len) {
+ /* EngineID length does not match! */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (engineid_len > row_oid->len) {
+ /* Verify partial EngineID */
+ snmp_engineid_to_oid(engineid, engineid_oid, row_oid->len - 1);
+ if (!snmp_oid_equal(&row_oid->id[engineid_start], row_oid->len - 1, engineid_oid, row_oid->len - 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+ else {
+ /* Verify complete EngineID */
+ snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+ if (!snmp_oid_equal(&row_oid->id[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+
+ /* At this point, the given EngineID (partially) matches the local EngineID.*/
+
+ /* If name might also be given */
+ if (row_oid->len > engineid_start + engineid_len) {
+ name_len = (u8_t)row_oid->id[engineid_start + engineid_len];
+ name_start = engineid_start + engineid_len + 1;
+
+ if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+ /* specified name is too long, max length is 32 according to mib file.*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (row_oid->len < engineid_len + name_len + 2) {
+ /* Partial name given according to oid.*/
+ u8_t tmplen = row_oid->len - engineid_len - 2;
+ if (!snmp_oid_in_range(&row_oid->id[name_start], tmplen, usmUserTable_oid_ranges, tmplen)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+ else {
+ /* Full name given according to oid. Also test for too much data.*/
+ u8_t tmplen = row_oid->len - engineid_len - 2;
+ if (!snmp_oid_in_range(&row_oid->id[name_start], name_len, usmUserTable_oid_ranges, tmplen)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+
+ /* At this point the EngineID and (partial) UserName match the local EngineID and UserName.*/
+ }
+ }
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(usmUserTable_oid_ranges));
+
+ for (i = 0; i < snmpv3_get_amount_of_users(); i++) {
+ u32_t test_oid[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+ test_oid[0] = eid_len;
+ snmp_engineid_to_oid(engineid, &test_oid[1], eid_len);
+
+ snmpv3_get_username(username, i);
+
+ test_oid[1 + eid_len] = strlen(username);
+ snmp_name_to_oid(username, &test_oid[2 + eid_len], strlen(username));
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, (u8_t)(1 + eid_len + 1 + strlen(username)), LWIP_PTR_NUMERIC_CAST(void*, i));
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* store username for subsequent operations (get/test/set) */
+ memset(username, 0, sizeof(username));
+ snmpv3_get_username(username, LWIP_PTR_NUMERIC_CAST(u8_t, state.reference));
+ cell_instance->reference.ptr = username;
+ cell_instance->reference_len = strlen(username);
+ return SNMP_ERR_NOERROR;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t usmusertable_get_value(struct snmp_node_instance *cell_instance, void *value)
+{
+ snmpv3_user_storagetype_t storage_type;
+
+ switch (SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)) {
+ case 3: /* usmUserSecurityName */
+ MEMCPY(value, cell_instance->reference.ptr, cell_instance->reference_len);
+ return (s16_t)cell_instance->reference_len;
+ case 4: /* usmUserCloneFrom */
+ MEMCPY(value, snmp_zero_dot_zero.id, snmp_zero_dot_zero.len * sizeof(u32_t));
+ return snmp_zero_dot_zero.len * sizeof(u32_t);
+ case 5: /* usmUserAuthProtocol */
+ {
+ const struct snmp_obj_id *auth_algo;
+ snmpv3_auth_algo_t auth_algo_val;
+ snmpv3_get_user((const char*)cell_instance->reference.ptr, &auth_algo_val, NULL, NULL, NULL);
+ auth_algo = snmp_auth_algo_to_oid(auth_algo_val);
+ MEMCPY(value, auth_algo->id, auth_algo->len * sizeof(u32_t));
+ return auth_algo->len * sizeof(u32_t);
+ }
+ case 6: /* usmUserAuthKeyChange */
+ return 0;
+ case 7: /* usmUserOwnAuthKeyChange */
+ return 0;
+ case 8: /* usmUserPrivProtocol */
+ {
+ const struct snmp_obj_id *priv_algo;
+ snmpv3_priv_algo_t priv_algo_val;
+ snmpv3_get_user((const char*)cell_instance->reference.ptr, NULL, NULL, &priv_algo_val, NULL);
+ priv_algo = snmp_priv_algo_to_oid(priv_algo_val);
+ MEMCPY(value, priv_algo->id, priv_algo->len * sizeof(u32_t));
+ return priv_algo->len * sizeof(u32_t);
+ }
+ case 9: /* usmUserPrivKeyChange */
+ return 0;
+ case 10: /* usmUserOwnPrivKeyChange */
+ return 0;
+ case 11: /* usmUserPublic */
+ /* TODO: Implement usmUserPublic */
+ return 0;
+ case 12: /* usmUserStorageType */
+ snmpv3_get_user_storagetype((const char*)cell_instance->reference.ptr, &storage_type);
+ *(s32_t*)value = storage_type;
+ return sizeof(s32_t);
+ case 13: /* usmUserStatus */
+ *(s32_t*)value = 1; /* active */
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("usmusertable_get_value(): unknown id: %"S32_F"\n", SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)));
+ return 0;
+ }
+}
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static s16_t usmstats_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ switch (node->oid) {
+ case 1: /* usmStatsUnsupportedSecLevels */
+ *uint_ptr = snmp_stats.unsupportedseclevels;
+ break;
+ case 2: /* usmStatsNotInTimeWindows */
+ *uint_ptr = snmp_stats.notintimewindows;
+ break;
+ case 3: /* usmStatsUnknownUserNames */
+ *uint_ptr = snmp_stats.unknownusernames;
+ break;
+ case 4: /* usmStatsUnknownEngineIDs */
+ *uint_ptr = snmp_stats.unknownengineids;
+ break;
+ case 5: /* usmStatsWrongDigests */
+ *uint_ptr = snmp_stats.wrongdigests;
+ break;
+ case 6: /* usmStatsDecryptionErrors */
+ *uint_ptr = snmp_stats.decryptionerrors;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("usmstats_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ return sizeof(*uint_ptr);
+}
+
+/* --- snmpUsmMIB ----------------------------------------------------- */
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_table_col_def usmusertable_columns[] = {
+ {3, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserSecurityName */
+ {4, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserCloneFrom */
+ {5, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthProtocol */
+ {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthKeyChange */
+ {7, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnAuthKeyChange */
+ {8, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivProtocol */
+ {9, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivKeyChange */
+ {10, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnPrivKeyChange */
+ {11, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPublic */
+ {12, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStorageType */
+ {13, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStatus */
+};
+static const struct snmp_table_node usmusertable = SNMP_TABLE_CREATE(2, usmusertable_columns, usmusertable_get_instance, usmusertable_get_next_instance, usmusertable_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmuser_subnodes[] = {
+ &usmusertable.node.node
+};
+static const struct snmp_tree_node usmuser_treenode = SNMP_CREATE_TREE_NODE(2, usmuser_subnodes);
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static const struct snmp_scalar_array_node_def usmstats_scalars_nodes[] = {
+ {1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnsupportedSecLevels */
+ {2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsNotInTimeWindows */
+ {3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownUserNames */
+ {4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownEngineIDs */
+ {5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsWrongDigests */
+ {6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsDecryptionErrors */
+};
+static const struct snmp_scalar_array_node usmstats_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, usmstats_scalars_nodes, usmstats_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmmibobjects_subnodes[] = {
+ &usmstats_scalars.node.node,
+ &usmuser_treenode.node
+};
+static const struct snmp_tree_node usmmibobjects_treenode = SNMP_CREATE_TREE_NODE(1, usmmibobjects_subnodes);
+
+/* --- snmpUsmMIB ----------------------------------------------------- */
+static const struct snmp_node *const snmpusmmib_subnodes[] = {
+ &usmmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpusmmib_root = SNMP_CREATE_TREE_NODE(15, snmpusmmib_subnodes);
+static const u32_t snmpusmmib_base_oid[] = {1,3,6,1,6,3,15};
+const struct snmp_mib snmpusmmib = {snmpusmmib_base_oid, LWIP_ARRAYSIZE(snmpusmmib_base_oid), &snmpusmmib_root.node};
+
+#endif /* LWIP_SNMP */
diff --git a/src/apps/snmp/snmp_traps.c b/../lwip_nat/src/apps/snmp/snmp_traps.c
index 0d2df649..60201867 100644
--- a/src/apps/snmp/snmp_traps.c
+++ b/../lwip_nat/src/apps/snmp/snmp_traps.c
@@ -78,8 +78,14 @@ struct snmp_msg_trap
static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
-static void snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
-static void snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
+static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+
+#define BUILD_EXEC(code) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \
+ return ERR_ARG; \
+ }
/** Agent community string for sending traps */
extern const char *snmp_community_trap;
@@ -202,7 +208,7 @@ snmp_send_trap(const struct snmp_obj_id* eoid, s32_t generic_trap, s32_t specifi
struct snmp_pbuf_stream pbuf_stream;
snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
- /* pass 1, encode packet ino the pbuf(s) */
+ /* pass 1, encode packet into the pbuf(s) */
snmp_trap_header_enc(&trap_msg, &pbuf_stream);
snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
@@ -357,7 +363,7 @@ snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
return tot_len;
}
-static void
+static err_t
snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
{
struct snmp_asn1_tlv tlv;
@@ -366,80 +372,84 @@ snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_
varbind = varbinds;
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
while (varbind != NULL) {
- snmp_append_outbound_varbind(pbuf_stream, varbind);
+ BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
varbind = varbind->next;
}
+
+ return ERR_OK;
}
/**
* Encodes trap header from head to tail.
*/
-static void
+static err_t
snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
{
struct snmp_asn1_tlv tlv;
/* 'Message' sequence */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
/* version */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) );
/* community */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen) );
/* 'PDU' sequence */
SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
/* object ID */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) );
/* IP addr */
if (IP_IS_V6_VAL(trap->sip)) {
#if LWIP_IPV6
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr));
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) );
#endif
} else {
#if LWIP_IPV4
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr));
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) );
#endif
}
/* trap length */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) );
/* specific trap */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) );
/* timestamp */
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
- snmp_ans1_enc_tlv(pbuf_stream, &tlv);
- snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) );
+
+ return ERR_OK;
}
#endif /* LWIP_SNMP */
diff --git a/src/apps/snmp/snmpv3.c b/../lwip_nat/src/apps/snmp/snmpv3.c
index 69fb3a0a..79ad7c73 100644
--- a/src/apps/snmp/snmpv3.c
+++ b/../lwip_nat/src/apps/snmp/snmpv3.c
@@ -58,7 +58,7 @@ snmpv3_engine_id_changed(void)
* (re-)initialized itself since snmpEngineID
* was last configured.
*/
-u32_t
+s32_t
snmpv3_get_engine_boots_internal(void)
{
if (snmpv3_get_engine_boots() == 0 ||
@@ -75,7 +75,7 @@ snmpv3_get_engine_boots_internal(void)
* Once the timer reaches 2147483647 it gets reset to zero and the
* engine boot ups get incremented.
*/
-u32_t
+s32_t
snmpv3_get_engine_time_internal(void)
{
if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
@@ -124,7 +124,7 @@ snmpv3_build_priv_param(u8_t* priv_param)
}
#else /* Based on RFC3414 */
static u32_t ctr;
- u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS();
+ u32_t boots = snmpv3_get_engine_boots_internal();
SMEMCPY(&priv_param[0], &boots, 4);
SMEMCPY(&priv_param[4], &ctr, 4);
ctr++;
diff --git a/src/apps/snmp/snmpv3_dummy.c b/src/apps/snmp/snmpv3_dummy.c
deleted file mode 100644
index bdfe8449..00000000
--- a/src/apps/snmp/snmpv3_dummy.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * @file
- * Dummy SNMPv3 functions.
- */
-
-/*
- * Copyright (c) 2016 Elias Oenal.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Elias Oenal <lwip@eliasoenal.com>
- * Dirk Ziegelmeier <dirk@ziegelmeier.net>
- */
-
-#include "lwip/apps/snmpv3.h"
-#include "snmpv3_priv.h"
-#include <string.h>
-#include "lwip/err.h"
-
-#if LWIP_SNMP && LWIP_SNMP_V3
-
-/**
- * @param username is a pointer to a string.
- * @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found.
- * @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
- * @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found.
- * @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
- */
-err_t
-snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key)
-{
- const char* engine_id;
- u8_t engine_id_len;
-
- if(strlen(username) == 0) {
- return ERR_OK;
- }
-
- if(memcmp(username, "lwip", 4) != 0) {
- return ERR_VAL;
- }
-
- snmpv3_get_engine_id(&engine_id, &engine_id_len);
-
- if(auth_key != NULL) {
- snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
- (const u8_t*)engine_id, engine_id_len,
- auth_key);
- *auth_algo = SNMP_V3_AUTH_ALGO_SHA;
- }
- if(priv_key != NULL) {
- snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
- (const u8_t*)engine_id, engine_id_len,
- priv_key);
- *priv_algo = SNMP_V3_PRIV_ALGO_DES;
- }
- return ERR_OK;
-}
-
-/**
- * Get engine ID from persistence
- * @param id
- * @param len
- */
-void
-snmpv3_get_engine_id(const char **id, u8_t *len)
-{
- *id = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02";
- *len = 12;
-}
-
-/**
- * Store engine ID in persistence
- * @param id
- * @param len
- */
-err_t
-snmpv3_set_engine_id(const char *id, u8_t len)
-{
- LWIP_UNUSED_ARG(id);
- LWIP_UNUSED_ARG(len);
- return ERR_OK;
-}
-
-/**
- * Get engine boots from persistence. Must be increased on each boot.
- * @return
- */
-u32_t
-snmpv3_get_engine_boots(void)
-{
- return 0;
-}
-
-/**
- * Store engine boots in persistence
- * @param boots
- */
-void
-snmpv3_set_engine_boots(u32_t boots)
-{
- LWIP_UNUSED_ARG(boots);
-}
-
-/**
- * RFC3414 2.2.2.
- * Once the timer reaches 2147483647 it gets reset to zero and the
- * engine boot ups get incremented.
- */
-u32_t
-snmpv3_get_engine_time(void)
-{
- return 0;
-}
-
-/**
- * Reset current engine time to 0
- */
-void
-snmpv3_reset_engine_time(void)
-{
-}
-
-#endif /* LWIP_SNMP && LWIP_SNMP_V3 */
diff --git a/src/apps/snmp/snmpv3_mbedtls.c b/../lwip_nat/src/apps/snmp/snmpv3_mbedtls.c
index 0b1eefb8..ec4ba38b 100644
--- a/src/apps/snmp/snmpv3_mbedtls.c
+++ b/../lwip_nat/src/apps/snmp/snmpv3_mbedtls.c
@@ -50,7 +50,7 @@
err_t
snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length,
- const u8_t* key, u8_t algo, u8_t* hmac_out)
+ const u8_t* key, snmpv3_auth_algo_t algo, u8_t* hmac_out)
{
u32_t i;
u8_t key_len;
@@ -107,7 +107,7 @@ free_md:
err_t
snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
const u8_t* key, const u8_t* priv_param, const u32_t engine_boots,
- const u32_t engine_time, u8_t algo, u8_t mode)
+ const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode)
{
size_t i;
mbedtls_cipher_context_t ctx;
@@ -154,29 +154,36 @@ snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
out_len = LWIP_ARRAYSIZE(out_bytes) ;
for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
- snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
+ if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) {
+ goto error;
+ }
}
- if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
+ if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
goto error;
}
- snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
+ if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+ goto error;
+ }
}
out_len = LWIP_ARRAYSIZE(out_bytes);
- if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+ if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+ goto error;
+ }
+
+ if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
goto error;
}
- snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
} else if (algo == SNMP_V3_PRIV_ALGO_AES) {
u8_t iv_local[16];
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
- if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+ if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
return ERR_ARG;
}
- if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+ if (mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
goto error;
}
@@ -202,11 +209,15 @@ snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
u8_t out_byte;
size_t out_len = sizeof(out_byte);
- snmp_pbuf_stream_read(&read_stream, &in_byte);
- if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+ if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) {
+ goto error;
+ }
+ if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+ goto error;
+ }
+ if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) {
goto error;
}
- snmp_pbuf_stream_write(&write_stream, out_byte);
}
} else {
return ERR_ARG;
@@ -226,7 +237,7 @@ error:
void
snmpv3_password_to_key_md5(
const u8_t *password, /* IN */
- u8_t passwordlen, /* IN */
+ size_t passwordlen, /* IN */
const u8_t *engineID, /* IN - pointer to snmpEngineID */
u8_t engineLength,/* IN - length of snmpEngineID */
u8_t *key) /* OUT - pointer to caller 16-octet buffer */
@@ -279,7 +290,7 @@ snmpv3_password_to_key_md5(
void
snmpv3_password_to_key_sha(
const u8_t *password, /* IN */
- u8_t passwordlen, /* IN */
+ size_t passwordlen, /* IN */
const u8_t *engineID, /* IN - pointer to snmpEngineID */
u8_t engineLength,/* IN - length of snmpEngineID */
u8_t *key) /* OUT - pointer to caller 20-octet buffer */
diff --git a/src/apps/snmp/snmpv3_priv.h b/../lwip_nat/src/apps/snmp/snmpv3_priv.h
index b87666da..ab729d72 100644
--- a/src/apps/snmp/snmpv3_priv.h
+++ b/../lwip_nat/src/apps/snmp/snmpv3_priv.h
@@ -39,6 +39,7 @@
#if LWIP_SNMP && LWIP_SNMP_V3
+#include "lwip/apps/snmpv3.h"
#include "snmp_pbuf_stream.h"
/* According to RFC 3411 */
@@ -48,18 +49,21 @@
#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12
#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8
-#define SNMP_V3_AUTH_FLAG 0x01
-#define SNMP_V3_PRIV_FLAG 0x02
-
#define SNMP_V3_MD5_LEN 16
#define SNMP_V3_SHA_LEN 20
-u32_t snmpv3_get_engine_boots_internal(void);
-u32_t snmpv3_get_engine_time_internal(void);
-err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out);
+typedef enum
+{
+ SNMP_V3_PRIV_MODE_DECRYPT = 0,
+ SNMP_V3_PRIV_MODE_ENCRYPT = 1
+} snmpv3_priv_mode_t;
+
+s32_t snmpv3_get_engine_boots_internal(void);
+err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, snmpv3_auth_algo_t algo, u8_t* hmac_out);
err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key,
- const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode);
+ const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode);
err_t snmpv3_build_priv_param(u8_t* priv_param);
+void snmpv3_enginetime_timer(void *arg);
#endif
diff --git a/src/apps/sntp/sntp.c b/../lwip_nat/src/apps/sntp/sntp.c
index 71b2abed..e51cf628 100644
--- a/src/apps/sntp/sntp.c
+++ b/../lwip_nat/src/apps/sntp/sntp.c
@@ -42,11 +42,10 @@
* This is simple "SNTP" client for the lwIP raw API.
* It is a minimal implementation of SNTPv4 as specified in RFC 4330.
*
- * For a list of some public NTP servers, see this link :
+ * For a list of some public NTP servers, see this link:
* http://support.ntp.org/bin/view/Servers/NTPPoolServers
*
* @todo:
- * - set/change servers at runtime
* - complete SNTP_CHECK_RESPONSE checks 3 and 4
*/
@@ -76,17 +75,6 @@
#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
#endif
-/* Configure behaviour depending on microsecond or second precision */
-#ifdef SNTP_SET_SYSTEM_TIME_US
-#define SNTP_CALC_TIME_US 1
-#define SNTP_RECEIVE_TIME_SIZE 2
-#else
-#define SNTP_SET_SYSTEM_TIME_US(sec, us)
-#define SNTP_CALC_TIME_US 0
-#define SNTP_RECEIVE_TIME_SIZE 1
-#endif
-
-
/* the various debug levels for this file */
#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE)
#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE)
@@ -101,10 +89,10 @@
#define SNTP_OFFSET_LI_VN_MODE 0
#define SNTP_LI_MASK 0xC0
-#define SNTP_LI_NO_WARNING 0x00
-#define SNTP_LI_LAST_MINUTE_61_SEC 0x01
-#define SNTP_LI_LAST_MINUTE_59_SEC 0x02
-#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */
+#define SNTP_LI_NO_WARNING (0x00 << 6)
+#define SNTP_LI_LAST_MINUTE_61_SEC (0x01 << 6)
+#define SNTP_LI_LAST_MINUTE_59_SEC (0x02 << 6)
+#define SNTP_LI_ALARM_CONDITION (0x03 << 6) /* (clock not synchronized) */
#define SNTP_VERSION_MASK 0x38
#define SNTP_VERSION (4/* NTP Version 4*/<<3)
@@ -121,18 +109,89 @@
#define SNTP_OFFSET_RECEIVE_TIME 32
#define SNTP_OFFSET_TRANSMIT_TIME 40
-/* number of seconds between 1900 and 1970 (MSB=1)*/
-#define DIFF_SEC_1900_1970 (2208988800UL)
-/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
-#define DIFF_SEC_1970_2036 (2085978496UL)
+/* Number of seconds between 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */
+#define DIFF_SEC_1970_2036 ((s32_t)2085978496L)
+
+/** Convert NTP timestamp fraction to microseconds.
+ */
+#ifndef SNTP_FRAC_TO_US
+# if LWIP_HAVE_INT64
+# define SNTP_FRAC_TO_US(f) ((u32_t)(((u64_t)(f) * 1000000UL) >> 32))
+# else
+# define SNTP_FRAC_TO_US(f) ((u32_t)(f) / 4295)
+# endif
+#endif /* !SNTP_FRAC_TO_US */
+
+/* Configure behaviour depending on native, microsecond or second precision.
+ * Treat NTP timestamps as signed two's-complement integers. This way,
+ * timestamps that have the MSB set simply become negative offsets from
+ * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from
+ * 1968 to 2104.
+ */
+#ifndef SNTP_SET_SYSTEM_TIME_NTP
+# ifdef SNTP_SET_SYSTEM_TIME_US
+# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+ SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f))
+# else
+# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+ SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036))
+# endif
+#endif /* !SNTP_SET_SYSTEM_TIME_NTP */
+
+/* Get the system time either natively as NTP timestamp or convert from
+ * Unix time in seconds and microseconds. Take care to avoid overflow if the
+ * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to
+ * avoid special values like 0, and to mask round-off errors that would
+ * otherwise break round-trip conversion identity.
+ */
+#ifndef SNTP_GET_SYSTEM_TIME_NTP
+# define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \
+ u32_t sec_, usec_; \
+ SNTP_GET_SYSTEM_TIME(sec_, usec_); \
+ (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \
+ (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \
+ } while (0)
+#endif /* !SNTP_GET_SYSTEM_TIME_NTP */
+
+/* Start offset of the timestamps to extract from the SNTP packet */
+#define SNTP_OFFSET_TIMESTAMPS \
+ (SNTP_OFFSET_TRANSMIT_TIME + 8 - sizeof(struct sntp_timestamps))
+
+/* Round-trip delay arithmetic helpers */
+#if SNTP_COMP_ROUNDTRIP
+# if !LWIP_HAVE_INT64
+# error "SNTP round-trip delay compensation requires 64-bit arithmetic"
+# endif
+# define SNTP_SEC_FRAC_TO_S64(s, f) \
+ ((s64_t)(((u64_t)(s) << 32) | (u32_t)(f)))
+# define SNTP_TIMESTAMP_TO_S64(t) \
+ SNTP_SEC_FRAC_TO_S64(lwip_ntohl((t).sec), lwip_ntohl((t).frac))
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+/**
+ * 64-bit NTP timestamp, in network byte order.
+ */
+struct sntp_time {
+ u32_t sec;
+ u32_t frac;
+};
+
+/**
+ * Timestamps to be extracted from the NTP header.
+ */
+struct sntp_timestamps {
+#if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2
+ struct sntp_time orig;
+ struct sntp_time recv;
+#endif
+ struct sntp_time xmit;
+};
/**
* SNTP packet format (without optional fields)
* Timestamps are coded as 64 bits:
- * - 32 bits seconds since Jan 01, 1970, 00:00
- * - 32 bits seconds fraction (0-padded)
- * For future use, if the MSB in the seconds part is set, seconds are based
- * on Feb 07, 2036, 06:28:16.
+ * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1)
+ * - unsigned 32 bits seconds fraction (2^32 = 1 second)
*/
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
@@ -199,38 +258,69 @@ static ip_addr_t sntp_last_server_address;
#if SNTP_CHECK_RESPONSE >= 2
/** Saves the last timestamp sent (which is sent back by the server)
- * to compare against in response */
-static u32_t sntp_last_timestamp_sent[2];
+ * to compare against in response. Stored in network byte order. */
+static struct sntp_time sntp_last_timestamp_sent;
#endif /* SNTP_CHECK_RESPONSE >= 2 */
+#if defined(LWIP_DEBUG) && !defined(sntp_format_time)
+/* Debug print helper. */
+static const char *
+sntp_format_time(s32_t sec)
+{
+ time_t ut;
+ ut = (time_t)((time_t)sec + (time_t)DIFF_SEC_1970_2036);
+ return ctime(&ut);
+}
+#endif /* LWIP_DEBUG && !sntp_format_time */
+
/**
* SNTP processing of received timestamp
*/
static void
-sntp_process(u32_t *receive_timestamp)
+sntp_process(const struct sntp_timestamps *timestamps)
{
- /* convert SNTP time (1900-based) to unix GMT time (1970-based)
- * if MSB is 0, SNTP time is 2036-based!
- */
- u32_t rx_secs = lwip_ntohl(receive_timestamp[0]);
- int is_1900_based = ((rx_secs & 0x80000000) != 0);
- u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036);
- time_t tim = t;
-
-#if SNTP_CALC_TIME_US
- u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295;
- SNTP_SET_SYSTEM_TIME_US(t, us);
- /* display local time from GMT time */
- LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us));
-
-#else /* SNTP_CALC_TIME_US */
-
- /* change system time and/or the update the RTC clock */
- SNTP_SET_SYSTEM_TIME(t);
- /* display local time from GMT time */
- LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim)));
-#endif /* SNTP_CALC_TIME_US */
- LWIP_UNUSED_ARG(tim);
+ s32_t sec;
+ u32_t frac;
+
+ sec = (s32_t)lwip_ntohl(timestamps->xmit.sec);
+ frac = lwip_ntohl(timestamps->xmit.frac);
+
+#if SNTP_COMP_ROUNDTRIP
+# if SNTP_CHECK_RESPONSE >= 2
+ if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0)
+# endif
+ {
+ s32_t dest_sec;
+ u32_t dest_frac;
+ u32_t step_sec;
+
+ /* Get the destination time stamp, i.e. the current system time */
+ SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac);
+
+ step_sec = (dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec)
+ : ((u32_t)dest_sec - (u32_t)sec);
+ /* In order to avoid overflows, skip the compensation if the clock step
+ * is larger than about 34 years. */
+ if ((step_sec >> 30) == 0) {
+ s64_t t1, t2, t3, t4;
+
+ t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac);
+ t3 = SNTP_SEC_FRAC_TO_S64(sec, frac);
+ t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig);
+ t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv);
+ /* Clock offset calculation according to RFC 4330 */
+ t4 += ((t2 - t1) + (t3 - t4)) / 2;
+
+ sec = (s32_t)((u64_t)t4 >> 32);
+ frac = (u32_t)((u64_t)t4);
+ }
+ }
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+ SNTP_SET_SYSTEM_TIME_NTP(sec, frac);
+ LWIP_UNUSED_ARG(frac); /* might be unused if only seconds are set */
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n",
+ sntp_format_time(sec), SNTP_FRAC_TO_US(frac)));
}
/**
@@ -242,18 +332,23 @@ sntp_initialize_request(struct sntp_msg *req)
memset(req, 0, SNTP_MSG_LEN);
req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
-#if SNTP_CHECK_RESPONSE >= 2
+#if SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP
{
- u32_t sntp_time_sec, sntp_time_us;
- /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
- SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us);
- sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970);
- req->transmit_timestamp[0] = sntp_last_timestamp_sent[0];
- /* we send/save us instead of fraction to be faster... */
- sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us);
- req->transmit_timestamp[1] = sntp_last_timestamp_sent[1];
+ s32_t secs;
+ u32_t sec, frac;
+ /* Get the transmit timestamp */
+ SNTP_GET_SYSTEM_TIME_NTP(secs, frac);
+ sec = lwip_htonl((u32_t)secs);
+ frac = lwip_htonl(frac);
+
+# if SNTP_CHECK_RESPONSE >= 2
+ sntp_last_timestamp_sent.sec = sec;
+ sntp_last_timestamp_sent.frac = frac;
+# endif
+ req->transmit_timestamp[0] = sec;
+ req->transmit_timestamp[1] = frac;
}
-#endif /* SNTP_CHECK_RESPONSE >= 2 */
+#endif /* SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP */
}
/**
@@ -334,18 +429,14 @@ sntp_try_next_server(void* arg)
static void
sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
+ struct sntp_timestamps timestamps;
u8_t mode;
u8_t stratum;
- u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE];
err_t err;
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
- /* packet received: stop retry timeout */
- sys_untimeout(sntp_try_next_server, NULL);
- sys_untimeout(sntp_request, NULL);
-
err = ERR_ARG;
#if SNTP_CHECK_RESPONSE >= 1
/* check server address and port */
@@ -358,32 +449,30 @@ sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr,
{
/* process the response */
if (p->tot_len == SNTP_MSG_LEN) {
- pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE);
- mode &= SNTP_MODE_MASK;
+ mode = pbuf_get_at(p, SNTP_OFFSET_LI_VN_MODE) & SNTP_MODE_MASK;
/* if this is a SNTP response... */
- if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
+ if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
- pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM);
+ stratum = pbuf_get_at(p, SNTP_OFFSET_STRATUM);
+
if (stratum == SNTP_STRATUM_KOD) {
/* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
err = SNTP_ERR_KOD;
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
} else {
+ pbuf_copy_partial(p, &timestamps, sizeof(timestamps), SNTP_OFFSET_TIMESTAMPS);
#if SNTP_CHECK_RESPONSE >= 2
/* check originate_timetamp against sntp_last_timestamp_sent */
- u32_t originate_timestamp[2];
- pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME);
- if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) ||
- (originate_timestamp[1] != sntp_last_timestamp_sent[1]))
- {
- LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n"));
+ if (timestamps.orig.sec != sntp_last_timestamp_sent.sec ||
+ timestamps.orig.frac != sntp_last_timestamp_sent.frac) {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN,
+ ("sntp_recv: Invalid originate timestamp in response\n"));
} else
#endif /* SNTP_CHECK_RESPONSE >= 2 */
/* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
{
/* correct answer */
err = ERR_OK;
- pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME);
}
}
} else {
@@ -401,12 +490,18 @@ sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr,
err = ERR_TIMEOUT;
}
#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
pbuf_free(p);
+
if (err == ERR_OK) {
- sntp_process(receive_timestamp);
+ /* correct packet received: process it it */
+ sntp_process(&timestamps);
/* Set up timeout for next request (only if poll response was received)*/
if (sntp_opmode == SNTP_OPMODE_POLL) {
+ sys_untimeout(sntp_try_next_server, NULL);
+ sys_untimeout(sntp_request, NULL);
+
/* Correct response, reset retry timeout */
SNTP_RESET_RETRY_TIMEOUT();
@@ -414,17 +509,14 @@ sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr,
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
(u32_t)SNTP_UPDATE_DELAY));
}
- } else if (err != ERR_TIMEOUT) {
- /* Errors are only processed in case of an explicit poll response */
+ } else if (err == SNTP_ERR_KOD) {
+ /* KOD errors are only processed in case of an explicit poll response */
if (sntp_opmode == SNTP_OPMODE_POLL) {
- if (err == SNTP_ERR_KOD) {
- /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
- sntp_try_next_server(NULL);
- } else {
- /* another error, try the same server again */
- sntp_retry(NULL);
- }
+ /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+ sntp_try_next_server(NULL);
}
+ } else {
+ /* ignore any broken packet, poll mode: retry after timeout to avoid flooding */
}
}
diff --git a/src/apps/tftp/tftp_server.c b/../lwip_nat/src/apps/tftp/tftp_server.c
index 243b0924..5009f741 100644
--- a/src/apps/tftp/tftp_server.c
+++ b/../lwip_nat/src/apps/tftp/tftp_server.c
@@ -1,4 +1,4 @@
-/****************************************************************//**
+/**
*
* @file tftp_server.c
*
@@ -10,7 +10,7 @@
* Copyright (c) Deltatee Enterprises Ltd. 2013
* All rights reserved.
*
- ********************************************************************/
+ */
/*
* Redistribution and use in source and binary forms, with or without
@@ -226,8 +226,8 @@ recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16
case PP_HTONS(TFTP_WRQ):
{
const char tftp_null = 0;
- char filename[TFTP_MAX_FILENAME_LEN];
- char mode[TFTP_MAX_MODE_LEN];
+ char filename[TFTP_MAX_FILENAME_LEN+1];
+ char mode[TFTP_MAX_MODE_LEN+1];
u16_t filename_end_offset;
u16_t mode_end_offset;
@@ -240,11 +240,11 @@ recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16
/* find \0 in pbuf -> end of filename string */
filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
- if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
+ if((u16_t)(filename_end_offset-1) > sizeof(filename)) {
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
break;
}
- pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
+ pbuf_copy_partial(p, filename, filename_end_offset-1, 2);
/* find \0 in pbuf -> end of mode string */
mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
diff --git a/../lwip_nat/src/core/altcp.c b/../lwip_nat/src/core/altcp.c
new file mode 100644
index 00000000..4e931f9e
--- /dev/null
+++ b/../lwip_nat/src/core/altcp.c
@@ -0,0 +1,544 @@
+/**
+ * @file
+ * @defgroup altcp Application layered TCP
+ * @ingroup callbackstyle_api
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the common functions for altcp to work.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+extern const struct altcp_functions altcp_tcp_functions;
+
+struct altcp_pcb *
+altcp_alloc(void)
+{
+ struct altcp_pcb *ret = (struct altcp_pcb *)memp_malloc(MEMP_ALTCP_PCB);
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(struct altcp_pcb));
+ }
+ return ret;
+}
+
+void
+altcp_free(struct altcp_pcb *conn)
+{
+ if (conn) {
+ memp_free(MEMP_ALTCP_PCB, conn);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_arg()
+ */
+void
+altcp_arg(struct altcp_pcb *conn, void *arg)
+{
+ if (conn) {
+ conn->arg = arg;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_accept()
+ */
+void
+altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept)
+{
+ if (conn != NULL) {
+ conn->accept = accept;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_recv()
+ */
+void
+altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv)
+{
+ if (conn) {
+ conn->recv = recv;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sent()
+ */
+void
+altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent)
+{
+ if (conn) {
+ conn->sent = sent;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_poll()
+ */
+void
+altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval)
+{
+ if (conn) {
+ conn->poll = poll;
+ conn->pollinterval = interval;
+ if (conn->fns && conn->fns->set_poll) {
+ conn->fns->set_poll(conn, interval);
+ }
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_err()
+ */
+void
+altcp_err(struct altcp_pcb *conn, altcp_err_fn err)
+{
+ if (conn) {
+ conn->err = err;
+ }
+}
+
+/* Generic functions calling the "virtual" ones */
+
+/**
+ * @ingroup altcp
+ * @see tcp_recved()
+ */
+void
+altcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn && conn->fns && conn->fns->recved) {
+ conn->fns->recved(conn, len);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_bind()
+ */
+err_t
+altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ if (conn && conn->fns && conn->fns->bind) {
+ return conn->fns->bind(conn, ipaddr, port);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_connect()
+ */
+err_t
+altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ if (conn && conn->fns && conn->fns->connect) {
+ return conn->fns->connect(conn, ipaddr, port, connected);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_listen_with_backlog_and_err()
+ */
+struct altcp_pcb *
+altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ if (conn && conn->fns && conn->fns->listen) {
+ return conn->fns->listen(conn, backlog, err);
+ }
+ return NULL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_abort()
+ */
+void
+altcp_abort(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->abort) {
+ conn->fns->abort(conn);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_close()
+ */
+err_t
+altcp_close(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->close) {
+ return conn->fns->close(conn);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_shutdown()
+ */
+err_t
+altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ if (conn && conn->fns && conn->fns->shutdown) {
+ return conn->fns->shutdown(conn, shut_rx, shut_tx);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_write()
+ */
+err_t
+altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ if (conn && conn->fns && conn->fns->write) {
+ return conn->fns->write(conn, dataptr, len, apiflags);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_output()
+ */
+err_t
+altcp_output(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->output) {
+ return conn->fns->output(conn);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_mss()
+ */
+u16_t
+altcp_mss(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->mss) {
+ return conn->fns->mss(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndbuf()
+ */
+u16_t
+altcp_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->sndbuf) {
+ return conn->fns->sndbuf(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndqueuelen()
+ */
+u16_t
+altcp_sndqueuelen(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->sndqueuelen) {
+ return conn->fns->sndqueuelen(conn);
+ }
+ return 0;
+}
+
+void
+altcp_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_disable) {
+ conn->fns->nagle_disable(conn);
+ }
+}
+
+void
+altcp_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_enable) {
+ conn->fns->nagle_enable(conn);
+ }
+}
+
+int
+altcp_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_disabled) {
+ return conn->fns->nagle_disabled(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_setprio()
+ */
+void
+altcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn && conn->fns && conn->fns->setprio) {
+ conn->fns->setprio(conn, prio);
+ }
+}
+
+err_t
+altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn && conn->fns && conn->fns->addrinfo) {
+ return conn->fns->addrinfo(conn, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->fns && conn->fns->getip) {
+ return conn->fns->getip(conn, local);
+ }
+ return NULL;
+}
+
+u16_t
+altcp_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->fns && conn->fns->getport) {
+ return conn->fns->getport(conn, local);
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->dbg_get_tcp_state) {
+ return conn->fns->dbg_get_tcp_state(conn);
+ }
+ return CLOSED;
+}
+#endif
+
+/* Default implementations for the "virtual" functions */
+
+void
+altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn && conn->inner_conn) {
+ altcp_poll(conn->inner_conn, conn->poll, interval);
+ }
+}
+
+void
+altcp_default_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn && conn->inner_conn) {
+ altcp_recved(conn->inner_conn, len);
+ }
+}
+
+err_t
+altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_bind(conn->inner_conn, ipaddr, port);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_shutdown(conn->inner_conn, shut_rx, shut_tx);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_write(conn->inner_conn, dataptr, len, apiflags);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_output(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_output(conn->inner_conn);
+ }
+ return ERR_VAL;
+}
+
+u16_t
+altcp_default_mss(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_mss(conn->inner_conn);
+ }
+ return 0;
+}
+
+u16_t
+altcp_default_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_sndbuf(conn->inner_conn);
+ }
+ return 0;
+}
+
+u16_t
+altcp_default_sndqueuelen(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_sndqueuelen(conn->inner_conn);
+ }
+ return 0;
+}
+
+void
+altcp_default_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ altcp_nagle_disable(conn->inner_conn);
+ }
+}
+
+void
+altcp_default_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ altcp_nagle_enable(conn->inner_conn);
+ }
+}
+
+int
+altcp_default_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_nagle_disabled(conn->inner_conn);
+ }
+ return 0;
+}
+
+void
+altcp_default_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn && conn->inner_conn) {
+ altcp_setprio(conn->inner_conn, prio);
+ }
+}
+
+void
+altcp_default_dealloc(struct altcp_pcb *conn)
+{
+ LWIP_UNUSED_ARG(conn);
+ /* nothing to do */
+}
+
+err_t
+altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_tcp_addrinfo(conn->inner_conn, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_default_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_ip(conn->inner_conn, local);
+ }
+ return NULL;
+}
+
+u16_t
+altcp_default_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_port(conn->inner_conn, local);
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_dbg_get_tcp_state(conn->inner_conn);
+ }
+ return CLOSED;
+}
+#endif
+
+
+#endif /* LWIP_ALTCP */
diff --git a/../lwip_nat/src/core/altcp_tcp.c b/../lwip_nat/src/core/altcp_tcp.c
new file mode 100644
index 00000000..b330e17c
--- /dev/null
+++ b/../lwip_nat/src/core/altcp_tcp.c
@@ -0,0 +1,486 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the base implementation calling into tcp.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+#define ALTCP_TCP_ASSERT_CONN(conn) LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL)
+#define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
+ LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
+ ALTCP_TCP_ASSERT_CONN(conn); } while(0)
+
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_tcp_functions;
+
+static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
+
+/* callback functions for TCP */
+static err_t
+altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
+{
+ struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+ if (listen_conn && listen_conn->accept) {
+ /* create a new altcp_conn to pass to the next 'accept' callback */
+ struct altcp_pcb *new_conn = altcp_alloc();
+ if (new_conn == NULL) {
+ return ERR_MEM;
+ }
+ altcp_tcp_setup(new_conn, new_tpcb);
+ return listen_conn->accept(listen_conn->arg, new_conn, err);
+ }
+ return ERR_ARG;
+}
+
+static err_t
+altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->connected) {
+ return conn->connected(conn->arg, conn, err);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->recv) {
+ return conn->recv(conn->arg, conn, p, err);
+ }
+ }
+ if (p != NULL) {
+ /* prevent memory leaks */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->sent) {
+ return conn->sent(conn->arg, conn, len);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_tcp_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ conn->state = NULL;
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ }
+}
+
+/* setup functions */
+static void
+altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+ tcp_arg(tpcb, conn);
+ tcp_recv(tpcb, altcp_tcp_recv);
+ tcp_sent(tpcb, altcp_tcp_sent);
+ tcp_err(tpcb, altcp_tcp_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static void
+altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+ altcp_tcp_setup_callbacks(conn, tpcb);
+ conn->state = tpcb;
+ conn->fns = &altcp_tcp_functions;
+}
+
+struct altcp_pcb *
+altcp_tcp_new_ip_type(u8_t ip_type)
+{
+ /* FIXME: pool alloc */
+ struct altcp_pcb *ret = altcp_alloc();
+ if (ret != NULL) {
+ struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
+ if (tpcb != NULL) {
+ altcp_tcp_setup(ret, tpcb);
+ } else {
+ /* tcp_pcb allocation failed -> free the altcp_pcb too */
+ altcp_free(ret);
+ ret = NULL;
+ }
+ }
+ return ret;
+}
+
+
+/* "virtual" functions calling into tcp */
+static void
+altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_poll(pcb, altcp_tcp_poll, interval);
+ }
+}
+
+static void
+altcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_recved(pcb, len);
+ }
+}
+
+static err_t
+altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_bind(pcb, ipaddr, port);
+}
+
+static err_t
+altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ conn->connected = connected;
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
+}
+
+static struct altcp_pcb *
+altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ struct tcp_pcb *pcb;
+ struct tcp_pcb *lpcb;
+ if (conn == NULL) {
+ return NULL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
+ if (lpcb != NULL) {
+ conn->state = lpcb;
+ tcp_accept(lpcb, altcp_tcp_accept);
+ return conn;
+ }
+ return NULL;
+}
+
+static void
+altcp_tcp_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_abort(pcb);
+ }
+}
+
+static err_t
+altcp_tcp_close(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_close(pcb);
+}
+
+static err_t
+altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_shutdown(pcb, shut_rx, shut_tx);
+}
+
+static err_t
+altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_write(pcb, dataptr, len, apiflags);
+}
+
+static err_t
+altcp_tcp_output(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_output(pcb);
+}
+
+static u16_t
+altcp_tcp_mss(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_mss(pcb);
+}
+
+static u16_t
+altcp_tcp_sndbuf(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_sndbuf(pcb);
+}
+
+static u16_t
+altcp_tcp_sndqueuelen(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_sndqueuelen(pcb);
+}
+
+static void
+altcp_tcp_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_nagle_disable(pcb);
+ }
+}
+
+static void
+altcp_tcp_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_nagle_enable(pcb);
+ }
+}
+
+static int
+altcp_tcp_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ return tcp_nagle_disabled(pcb);
+ }
+ return 0;
+}
+
+static void
+altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_setprio(pcb, prio);
+ }
+}
+
+static void
+altcp_tcp_dealloc(struct altcp_pcb *conn)
+{
+ LWIP_UNUSED_ARG(conn);
+ ALTCP_TCP_ASSERT_CONN(conn);
+ /* no private state to clean up */
+}
+
+static err_t
+altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+static ip_addr_t *
+altcp_tcp_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ if (local) {
+ return &pcb->local_ip;
+ } else {
+ return &pcb->remote_ip;
+ }
+ }
+ }
+ return NULL;
+}
+
+static u16_t
+altcp_tcp_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ if (local) {
+ return pcb->local_port;
+ } else {
+ return pcb->remote_port;
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+static enum tcp_state
+altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ return pcb->state;
+ }
+ }
+ return CLOSED;
+}
+#endif
+const struct altcp_functions altcp_tcp_functions = {
+ altcp_tcp_set_poll,
+ altcp_tcp_recved,
+ altcp_tcp_bind,
+ altcp_tcp_connect,
+ altcp_tcp_listen,
+ altcp_tcp_abort,
+ altcp_tcp_close,
+ altcp_tcp_shutdown,
+ altcp_tcp_write,
+ altcp_tcp_output,
+ altcp_tcp_mss,
+ altcp_tcp_sndbuf,
+ altcp_tcp_sndqueuelen,
+ altcp_tcp_nagle_disable,
+ altcp_tcp_nagle_enable,
+ altcp_tcp_nagle_disabled,
+ altcp_tcp_setprio,
+ altcp_tcp_dealloc,
+ altcp_tcp_get_tcp_addrinfo,
+ altcp_tcp_get_ip,
+ altcp_tcp_get_port
+#ifdef LWIP_DEBUG
+ ,altcp_tcp_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP */
diff --git a/src/core/def.c b/../lwip_nat/src/core/def.c
index 8125313f..d3004d4d 100644
--- a/src/core/def.c
+++ b/../lwip_nat/src/core/def.c
@@ -75,7 +75,7 @@
u16_t
lwip_htons(u16_t n)
{
- return (u16_t)PP_HTONS(n);
+ return PP_HTONS(n);
}
#endif /* lwip_htons */
@@ -89,7 +89,7 @@ lwip_htons(u16_t n)
u32_t
lwip_htonl(u32_t n)
{
- return (u32_t)PP_HTONL(n);
+ return PP_HTONL(n);
}
#endif /* lwip_htonl */
@@ -197,26 +197,47 @@ lwip_strnicmp(const char* str1, const char* str2, size_t len)
void
lwip_itoa(char* result, size_t bufsize, int number)
{
- const int base = 10;
- char* ptr = result, *ptr1 = result, tmp_char;
- int tmp_value;
- LWIP_UNUSED_ARG(bufsize);
+ char *res = result;
+ char *tmp = result;
+ size_t res_left = bufsize;
+ size_t result_len;
+ int n = (number > 0) ? number : -number;
+
+ /* handle invalid bufsize */
+ if (bufsize < 2) {
+ if (bufsize == 1) {
+ *result = 0;
+ }
+ return;
+ }
- do {
- tmp_value = number;
- number /= base;
- *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - number * base)];
- } while(number);
-
- /* Apply negative sign */
- if (tmp_value < 0) {
- *ptr++ = '-';
+ /* ensure output string is zero terminated */
+ result[bufsize-1] = 0;
+ result_len = 1;
+ /* create the string in a temporary buffer since we don't know how long
+ it will get */
+ tmp = &result[bufsize-2];
+ while ((n != 0) && (result_len < (bufsize - 1))) {
+ char val = (char)('0' + (n % 10));
+ *tmp = val;
+ tmp--;
+ n = n / 10;
+ result_len++;
+ }
+
+ /* output sign first */
+ if (number < 0) {
+ *res = '-';
+ res++;
+ res_left--;
}
- *ptr-- = '\0';
- while(ptr1 < ptr) {
- tmp_char = *ptr;
- *ptr--= *ptr1;
- *ptr1++ = tmp_char;
+ if (result_len > res_left) {
+ /* buffer is too small */
+ result[0] = '.';
+ result[1] = 0;
+ return;
}
+ /* copy from temporary buffer to output buffer */
+ memmove(res, tmp+1, result_len);
}
#endif
diff --git a/src/core/dns.c b/../lwip_nat/src/core/dns.c
index f80d9240..de482bae 100644
--- a/src/core/dns.c
+++ b/../lwip_nat/src/core/dns.c
@@ -317,24 +317,12 @@ const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
void
dns_init(void)
{
-#if ESP_DNS
-#ifdef FALLBACK_DNS_SERVER_ADDRESS
- /* initialize fallback dns DNS server address */
- ip_addr_t dnsserver;
- FALLBACK_DNS_SERVER_ADDRESS(&dnsserver);
- dnsserver.type = IPADDR_TYPE_V4;
- dns_setserver(DNS_FALLBACK_SERVER_INDEX, &dnsserver);
-#endif /* FALLBACK_DNS_SERVER_ADDRESS */
-
-#else
-
#ifdef DNS_SERVER_ADDRESS
/* initialize default DNS server address */
ip_addr_t dnsserver;
DNS_SERVER_ADDRESS(&dnsserver);
dns_setserver(0, &dnsserver);
#endif /* DNS_SERVER_ADDRESS */
-#endif /* ESP_DNS */
LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
@@ -384,22 +372,6 @@ dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
}
}
-#if ESP_DNS
-void
-dns_clear_servers(bool keep_fallback)
-{
- u8_t numdns = 0;
-
- for (numdns = 0; numdns < DNS_MAX_SERVERS; numdns ++) {
- if (keep_fallback && numdns == DNS_FALLBACK_SERVER_INDEX) {
- continue;
- }
-
- dns_setserver(numdns, NULL);
- }
-}
-#endif
-
/**
* @ingroup dns
* Obtain one of the currently configured DNS server.
@@ -408,17 +380,6 @@ dns_clear_servers(bool keep_fallback)
* @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
* server has not been configured.
*/
-#if ESP_DNS
-ip_addr_t
-dns_getserver(u8_t numdns)
-{
- if (numdns < DNS_MAX_SERVERS) {
- return dns_servers[numdns];
- } else {
- return *IP_ADDR_ANY;
- }
-}
-#else
const ip_addr_t*
dns_getserver(u8_t numdns)
{
@@ -428,8 +389,6 @@ dns_getserver(u8_t numdns)
return IP_ADDR_ANY;
}
}
-#endif
-
/**
* The DNS resolver client timer - handle retries and timeouts and should
@@ -646,8 +605,6 @@ static err_t
dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
{
u8_t i;
-#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
-#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
#if DNS_LOCAL_HOSTLIST
if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
return ERR_OK;
@@ -665,7 +622,7 @@ dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addr
(lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) &&
LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
- ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+ ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr);
LWIP_DEBUGF(DNS_DEBUG, ("\n"));
if (addr) {
ip_addr_copy(*addr, dns_table[i].ipaddr);
@@ -696,7 +653,7 @@ dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
do {
n = pbuf_try_get_at(p, response_offset++);
- if (n < 0) {
+ if ((n < 0) || (response_offset == 0)) {
return 0xFFFF;
}
/** @see RFC 1035 - 4.1.4. Message compression */
@@ -714,6 +671,9 @@ dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
return 0xFFFF;
}
++response_offset;
+ if (response_offset == 0) {
+ return 0xFFFF;
+ }
++query;
--n;
}
@@ -725,7 +685,10 @@ dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
}
} while (n != 0);
- return response_offset + 1;
+ if (response_offset == 0xFFFF) {
+ return 0xFFFF;
+ }
+ return (u16_t)(response_offset + 1);
}
/**
@@ -743,7 +706,7 @@ dns_skip_name(struct pbuf* p, u16_t query_idx)
do {
n = pbuf_try_get_at(p, offset++);
- if (n < 0) {
+ if ((n < 0) || (offset == 0)) {
return 0xFFFF;
}
/** @see RFC 1035 - 4.1.4. Message compression */
@@ -763,7 +726,10 @@ dns_skip_name(struct pbuf* p, u16_t query_idx)
}
} while (n != 0);
- return offset + 1;
+ if (offset == 0xFFFF) {
+ return 0xFFFF;
+ }
+ return (u16_t)(offset + 1);
}
/**
@@ -825,9 +791,13 @@ dns_send(u8_t idx)
++n;
}
copy_len = (u16_t)(hostname - hostname_part);
+ if (query_idx + n + 1 > 0xFFFF) {
+ /* u16_t overflow */
+ goto overflow_return;
+ }
pbuf_put_at(p, query_idx, n);
- pbuf_take_at(p, hostname_part, copy_len, query_idx + 1);
- query_idx += n + 1;
+ pbuf_take_at(p, hostname_part, copy_len, (u16_t)(query_idx + 1));
+ query_idx = (u16_t)(query_idx + n + 1);
} while (*hostname != 0);
pbuf_put_at(p, query_idx, 0);
query_idx++;
@@ -881,6 +851,9 @@ dns_send(u8_t idx)
}
return err;
+overflow_return:
+ pbuf_free(p);
+ return ERR_VAL;
}
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
@@ -888,28 +861,28 @@ static struct udp_pcb*
dns_alloc_random_port(void)
{
err_t err;
- struct udp_pcb* ret;
+ struct udp_pcb* pcb;
- ret = udp_new_ip_type(IPADDR_TYPE_ANY);
- if (ret == NULL) {
+ pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (pcb == NULL) {
/* out of memory, have to reuse an existing pcb */
return NULL;
}
do {
u16_t port = (u16_t)DNS_RAND_TXID();
- if (!DNS_PORT_ALLOWED(port)) {
+ if (DNS_PORT_ALLOWED(port)) {
+ err = udp_bind(pcb, IP_ANY_TYPE, port);
+ } else {
/* this port is not allowed, try again */
err = ERR_USE;
- continue;
}
- err = udp_bind(ret, IP_ANY_TYPE, port);
} while (err == ERR_USE);
if (err != ERR_OK) {
- udp_remove(ret);
+ udp_remove(pcb);
return NULL;
}
- udp_recv(ret, dns_recv, NULL);
- return ret;
+ udp_recv(pcb, dns_recv, NULL);
+ return pcb;
}
/**
@@ -938,8 +911,8 @@ dns_alloc_pcb(void)
}
}
/* if we come here, creating a new UDP pcb failed, so we have to use
- an already existing one */
- for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
+ an already existing one (so overflow is no issue) */
+ for (i = 0, idx = (u8_t)(dns_last_pcb_idx + 1); i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
if (idx >= DNS_MAX_SOURCE_PORTS) {
idx = 0;
}
@@ -1075,12 +1048,6 @@ dns_check_entry(u8_t i)
case DNS_STATE_ASKING:
if (--entry->tmr == 0) {
if (++entry->retries == DNS_MAX_RETRIES) {
-#if ESP_DNS
- /* skip DNS servers with zero address */
- while ((entry->server_idx + 1 < DNS_MAX_SERVERS) && ip_addr_isany_val(dns_servers[entry->server_idx + 1])) {
- entry->server_idx++;
- }
-#endif
if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])
#if LWIP_DNS_SUPPORT_MDNS_QUERIES
&& !entry->is_mdns
@@ -1152,7 +1119,7 @@ dns_correct_response(u8_t idx, u32_t ttl)
entry->state = DNS_STATE_DONE;
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
- ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr)));
+ ip_addr_debug_print_val(DNS_DEBUG, entry->ipaddr);
LWIP_DEBUGF(DNS_DEBUG, ("\n"));
/* read the answer resource record's TTL, and maximize it if needed */
@@ -1252,7 +1219,10 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
goto memerr; /* ignore this packet */
}
/* skip the rest of the "question" part */
- res_idx += SIZEOF_DNS_QUERY;
+ if (res_idx + SIZEOF_DNS_QUERY > 0xFFFF) {
+ goto memerr;
+ }
+ res_idx = (u16_t)(res_idx + SIZEOF_DNS_QUERY);
/* Check for error. If so, call callback to inform. */
if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
@@ -1269,7 +1239,10 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
goto memerr; /* ignore this packet */
}
- res_idx += SIZEOF_DNS_ANSWER;
+ if (res_idx + SIZEOF_DNS_ANSWER > 0xFFFF) {
+ goto memerr;
+ }
+ res_idx = (u16_t)(res_idx + SIZEOF_DNS_ANSWER);
if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
#if LWIP_IPV4
@@ -1292,17 +1265,18 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
}
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
- if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) {
+ if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_p_t)))) {
#if LWIP_IPV4 && LWIP_IPV6
if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
#endif /* LWIP_IPV4 && LWIP_IPV6 */
{
- ip6_addr_t ip6addr;
+ ip6_addr_p_t ip6addr;
/* read the IP address after answer resource record's header */
- if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx) != sizeof(ip6_addr_t)) {
+ if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_p_t), res_idx) != sizeof(ip6_addr_p_t)) {
goto memerr; /* ignore this packet */
}
- ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr);
+ /* @todo: scope ip6addr? Might be required for link-local addresses at least? */
+ ip_addr_copy_from_ip6_packed(dns_table[i].ipaddr, ip6addr);
pbuf_free(p);
/* handle correct response */
dns_correct_response(i, lwip_ntohl(ans.ttl));
@@ -1315,7 +1289,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
goto memerr; /* ignore this packet */
}
- res_idx += lwip_htons(ans.len);
+ res_idx = (u16_t)(res_idx + lwip_htons(ans.len));
--nanswers;
}
#if LWIP_IPV4 && LWIP_IPV6
@@ -1411,7 +1385,7 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
}
/* check if this is the oldest completed entry */
if (entry->state == DNS_STATE_DONE) {
- u8_t age = dns_seqno - entry->seqno;
+ u8_t age = (u8_t)(dns_seqno - entry->seqno);
if (age > lseq) {
lseq = age;
lseqi = i;
@@ -1518,19 +1492,6 @@ dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback foun
return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
}
-#if ESP_LWIP
-static bool dns_server_is_set (void)
-{
- int i = 0;
- for (i = 0;i < DNS_MAX_SERVERS; i++) {
- if (!ip_addr_isany_val(dns_servers[i])) {
- return true;
- }
- }
- return false;
-}
-#endif
-
/**
* @ingroup dns
* Like dns_gethostbyname, but returned address type can be controlled:
@@ -1620,11 +1581,7 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
{
/* prevent calling found callback if no server is set, return error instead */
-#if ESP_DNS
- if (dns_server_is_set() == false) {
-#else
if (ip_addr_isany_val(dns_servers[0])) {
-#endif
return ERR_VAL;
}
}
diff --git a/src/core/inet_chksum.c b/../lwip_nat/src/core/inet_chksum.c
index fc04714a..e027169e 100644
--- a/src/core/inet_chksum.c
+++ b/../lwip_nat/src/core/inet_chksum.c
@@ -256,15 +256,11 @@ lwip_standard_chksum(const void *dataptr, int len)
#endif
/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
-#if ESP_LWIP
-static u16_t ESP_IRAM_ATTR
-#else
static u16_t
-#endif
inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
{
struct pbuf *q;
- u8_t swapped = 0;
+ int swapped = 0;
/* iterate through all pbuf in chain */
for (q = p; q != NULL; q = q->next) {
@@ -276,7 +272,7 @@ inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
to check whether we really need to execute it, and does no harm */
acc = FOLD_U32T(acc);
if (q->len % 2 != 0) {
- swapped = 1 - swapped;
+ swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
@@ -310,11 +306,7 @@ inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
* @param proto_len length of the ip data part (used for checksum of pseudo header)
* @return checksum (as u16_t) to be saved directly in the protocol header
*/
-#if ESP_LWIP
-u16_t ESP_IRAM_ATTR
-#else
u16_t
-#endif
inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
const ip4_addr_t *src, const ip4_addr_t *dest)
{
@@ -323,10 +315,10 @@ inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
addr = ip4_addr_get_u32(src);
acc = (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = ip4_addr_get_u32(dest);
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
acc = FOLD_U32T(acc);
@@ -357,11 +349,11 @@ ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
for (addr_part = 0; addr_part < 4; addr_part++) {
addr = src->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = dest->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
}
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
@@ -383,11 +375,7 @@ ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
* @param proto_len length of the ip data part (used for checksum of pseudo header)
* @return checksum (as u16_t) to be saved directly in the protocol header
*/
-#if ESP_LWIP
-u16_t ESP_IRAM_ATTR
-#else
u16_t
-#endif
ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
const ip_addr_t *src, const ip_addr_t *dest)
{
@@ -412,7 +400,7 @@ inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
u16_t chksum_len, u32_t acc)
{
struct pbuf *q;
- u8_t swapped = 0;
+ int swapped = 0;
u16_t chklen;
/* iterate through all pbuf in chain */
@@ -424,13 +412,13 @@ inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
chklen = chksum_len;
}
acc += LWIP_CHKSUM(q->payload, chklen);
- chksum_len -= chklen;
+ chksum_len = (u16_t)(chksum_len - chklen);
LWIP_ASSERT("delete me", chksum_len < 0x7fff);
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
/* fold the upper bit down */
acc = FOLD_U32T(acc);
if (q->len % 2 != 0) {
- swapped = 1 - swapped;
+ swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
@@ -473,10 +461,10 @@ inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
addr = ip4_addr_get_u32(src);
acc = (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = ip4_addr_get_u32(dest);
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
acc = FOLD_U32T(acc);
@@ -509,11 +497,11 @@ ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
for (addr_part = 0; addr_part < 4; addr_part++) {
addr = src->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = dest->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
}
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
@@ -581,15 +569,14 @@ inet_chksum_pbuf(struct pbuf *p)
{
u32_t acc;
struct pbuf *q;
- u8_t swapped;
+ int swapped = 0;
acc = 0;
- swapped = 0;
for (q = p; q != NULL; q = q->next) {
acc += LWIP_CHKSUM(q->payload, q->len);
acc = FOLD_U32T(acc);
if (q->len % 2 != 0) {
- swapped = 1 - swapped;
+ swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
}
diff --git a/src/core/init.c b/../lwip_nat/src/core/init.c
index ea3b5ebe..9a73034b 100644
--- a/src/core/init.c
+++ b/../lwip_nat/src/core/init.c
@@ -95,8 +95,8 @@ PACK_STRUCT_END
#if (!LWIP_UDP && LWIP_DHCP)
#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
#endif
-#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS)
- #error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS)
+ #error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=1 in your lwipopts.h"
#endif
#if (!LWIP_UDP && LWIP_DNS)
#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
@@ -123,14 +123,11 @@ PACK_STRUCT_END
#if (LWIP_IGMP && !LWIP_IPV4)
#error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
#endif
-#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4)
- #error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h"
-#endif
#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
#error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
#endif
/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
-#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)))
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < LWIP_NUM_SYS_TIMEOUT_INTERNAL)
#error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
#endif
#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
@@ -167,6 +164,12 @@ PACK_STRUCT_END
#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
#error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && !TCP_QUEUE_OOSEQ)
+#error "To use LWIP_TCP_SACK_OUT, TCP_QUEUE_OOSEQ needs to be enabled"
+#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && (LWIP_TCP_MAX_SACK_NUM < 1))
+#error "LWIP_TCP_MAX_SACK_NUM must be greater than 0"
+#endif
#if (LWIP_NETIF_API && (NO_SYS==1))
#error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
#endif
diff --git a/src/core/ip.c b/../lwip_nat/src/core/ip.c
index 2e024085..181ebf51 100644
--- a/src/core/ip.c
+++ b/../lwip_nat/src/core/ip.c
@@ -67,6 +67,49 @@ struct ip_globals ip_data;
const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
+/**
+ * @ingroup ipaddr
+ * Convert numeric IP address (both versions) into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * representation of addr
+ */
+char *ipaddr_ntoa(const ip_addr_t *addr)
+{
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (IP_IS_V6(addr)) {
+ return ip6addr_ntoa(ip_2_ip6(addr));
+ } else {
+ return ip4addr_ntoa(ip_2_ip4(addr));
+ }
+}
+
+/**
+ * @ingroup ipaddr
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (IP_IS_V6(addr)) {
+ return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen);
+ } else {
+ return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen);
+ }
+}
+
/**
* @ingroup ipaddr
* Convert IP address string (both versions) to numeric.
diff --git a/src/core/ipv4/autoip.c b/../lwip_nat/src/core/ipv4/autoip.c
index a5a63df8..882d0061 100644
--- a/src/core/ipv4/autoip.c
+++ b/../lwip_nat/src/core/ipv4/autoip.c
@@ -67,7 +67,6 @@
#include "lwip/autoip.h"
#include "lwip/etharp.h"
#include "lwip/prot/autoip.h"
-#include "lwip/dhcp.h"
#include <string.h>
@@ -242,12 +241,6 @@ autoip_bind(struct netif *netif)
netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
/* interface is used by routing now that an address is set */
-#if ESP_LWIP
- struct dhcp *dhcp = netif_dhcp_data(netif);
- if (dhcp->cb != NULL) {
- dhcp->cb(netif);
- }
-#endif
return ERR_OK;
}
@@ -277,13 +270,12 @@ autoip_start(struct netif *netif)
/* no AutoIP client attached yet? */
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_start(): starting new AUTOIP client\n"));
- autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+ autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip));
if (autoip == NULL) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_start(): could not allocate autoip\n"));
return ERR_MEM;
}
- memset(autoip, 0, sizeof(struct autoip));
/* store this AutoIP client in the netif */
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
@@ -371,9 +363,9 @@ autoip_stop(struct netif *netif)
void
autoip_tmr(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
/* loop through netif's */
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
struct autoip* autoip = netif_autoip_data(netif);
/* only act on AutoIP configured interfaces */
if (autoip != NULL) {
@@ -445,8 +437,6 @@ autoip_tmr(void)
break;
}
}
- /* proceed to next network interface */
- netif = netif->next;
}
}
@@ -470,13 +460,13 @@ autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
*/
ip4_addr_t sipaddr, dipaddr;
struct eth_addr netifaddr;
- ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
+ SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
- /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
* structure packing (not using structure copy which breaks strict-aliasing rules).
*/
- IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
- IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
if (autoip->state == AUTOIP_STATE_PROBING) {
/* RFC 3927 Section 2.2.1:
diff --git a/src/core/ipv4/dhcp.c b/../lwip_nat/src/core/ipv4/dhcp.c
index 9e25d784..384883de 100644
--- a/src/core/ipv4/dhcp.c
+++ b/../lwip_nat/src/core/ipv4/dhcp.c
@@ -21,6 +21,9 @@
* Use dhcp_release() to end the lease and use dhcp_stop()
* to remove the DHCP client.
*
+ * @see LWIP_HOOK_DHCP_APPEND_OPTIONS
+ * @see LWIP_HOOK_DHCP_PARSE_OPTION
+ *
* @see netifapi_dhcp4
*/
@@ -78,6 +81,16 @@
#include <string.h>
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type)
+#endif
+#ifndef LWIP_HOOK_DHCP_PARSE_OPTION
+#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset)
+#endif
+
/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using
* LWIP_RAND() (this overrides DHCP_GLOBAL_XID)
*/
@@ -153,19 +166,6 @@ static u8_t dhcp_discover_request_options[] = {
#if LWIP_DHCP_PROVIDE_DNS_SERVERS
, DHCP_OPTION_DNS_SERVER
#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
-
-#if ESP_DHCP
-/**add options for support more router by liuHan**/
- , DHCP_OPTION_DOMAIN_NAME,
- DHCP_OPTION_NB_TINS,
- DHCP_OPTION_NB_TINT,
- DHCP_OPTION_NB_TIS,
- DHCP_OPTION_PRD,
- DHCP_OPTION_STATIC_ROUTER,
- DHCP_OPTION_CLASSLESS_STATIC_ROUTER,
- DHCP_OPTION_VSN
-#endif
-
#if LWIP_DHCP_GET_NTP_SRV
, DHCP_OPTION_NTP
#endif /* LWIP_DHCP_GET_NTP_SRV */
@@ -286,14 +286,6 @@ dhcp_handle_nak(struct netif *netif)
dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
/* remove IP address from interface (must no longer be used, as per RFC2131) */
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
-
- if (dhcp->cb != NULL) {
-#ifdef ESP_DHCP
- dhcp->cb(netif);
-#else
- dhcp->cb();
-#endif
- }
/* We can immediately restart discovery */
dhcp_discover(netif);
}
@@ -327,7 +319,7 @@ dhcp_check(struct netif *netif)
dhcp->tries++;
}
msecs = 500;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
}
#endif /* DHCP_DOES_ARP_CHECK */
@@ -402,9 +394,8 @@ dhcp_select(struct netif *netif)
dhcp_option_hostname(dhcp, netif);
#endif /* LWIP_NETIF_HOSTNAME */
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REQUESTING, dhcp->msg_out, DHCP_REQUEST);
dhcp_option_trailer(dhcp);
- /* shrink the pbuf to the actual content length */
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
/* send broadcast to any DHCP server */
udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
@@ -416,8 +407,8 @@ dhcp_select(struct netif *netif)
if (dhcp->tries < 255) {
dhcp->tries++;
}
- msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
return result;
}
@@ -429,10 +420,10 @@ dhcp_select(struct netif *netif)
void
dhcp_coarse_tmr(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
/* iterate through all network interfaces */
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* only act on DHCP configured interfaces */
struct dhcp *dhcp = netif_dhcp_data(netif);
if ((dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF)) {
@@ -440,8 +431,8 @@ dhcp_coarse_tmr(void)
if (dhcp->t0_timeout && (++dhcp->lease_used == dhcp->t0_timeout)) {
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n"));
/* this clients' lease time has expired */
- dhcp_release(netif);
- dhcp_discover(netif);
+ dhcp_release_and_stop(netif);
+ dhcp_start(netif);
/* timer is active (non zero), and triggers (zeroes) now? */
} else if (dhcp->t2_rebind_time && (dhcp->t2_rebind_time-- == 1)) {
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
@@ -454,8 +445,6 @@ dhcp_coarse_tmr(void)
dhcp_t1_timeout(netif);
}
}
- /* proceed to next netif */
- netif = netif->next;
}
}
@@ -469,9 +458,9 @@ dhcp_coarse_tmr(void)
void
dhcp_fine_tmr(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
/* loop through netif's */
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
struct dhcp *dhcp = netif_dhcp_data(netif);
/* only act on DHCP configured interfaces */
if (dhcp != NULL) {
@@ -487,8 +476,6 @@ dhcp_fine_tmr(void)
dhcp_timeout(netif);
}
}
- /* proceed to next network interface */
- netif = netif->next;
}
}
@@ -517,8 +504,8 @@ dhcp_timeout(struct netif *netif)
dhcp_select(netif);
} else {
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
- dhcp_release(netif);
- dhcp_discover(netif);
+ dhcp_release_and_stop(netif);
+ dhcp_start(netif);
}
#if DHCP_DOES_ARP_CHECK
/* received no ARP reply for the offered address (which is good) */
@@ -563,13 +550,9 @@ dhcp_t1_timeout(struct netif *netif)
DHCP_STATE_RENEWING, not DHCP_STATE_BOUND */
dhcp_renew(netif);
/* Calculate next timeout */
-#if ESP_DHCP_TIMER
- if (((dhcp->t2_timeout - dhcp->lease_used) / 2)*DHCP_COARSE_TIMER_SECS >= 3)
-#else
if (((dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
-#endif
{
- dhcp->t1_renew_time = ((dhcp->t2_timeout - dhcp->lease_used) / 2);
+ dhcp->t1_renew_time = (u16_t)((dhcp->t2_timeout - dhcp->lease_used) / 2);
}
}
}
@@ -594,13 +577,9 @@ dhcp_t2_timeout(struct netif *netif)
DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */
dhcp_rebind(netif);
/* Calculate next timeout */
-#if ESP_DHCP_TIMER
- if (((dhcp->t0_timeout - dhcp->lease_used) / 2)*DHCP_COARSE_TIMER_SECS >= 3)
-#else
if (((dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
-#endif
{
- dhcp->t2_rebind_time = ((dhcp->t0_timeout - dhcp->lease_used) / 2);
+ dhcp->t2_rebind_time = (u16_t)((dhcp->t0_timeout - dhcp->lease_used) / 2);
}
}
}
@@ -687,12 +666,7 @@ dhcp_handle_ack(struct netif *netif)
/* DNS servers */
for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
ip_addr_t dns_addr;
-#if ESP_DNS
- if (n == DNS_FALLBACK_SERVER_INDEX) {
- continue;
- }
-#endif
- ip_addr_set_ip4_u32(&dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ ip_addr_set_ip4_u32_val(dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
dns_setserver(n, &dns_addr);
}
#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
@@ -738,31 +712,6 @@ void dhcp_cleanup(struct netif *netif)
}
}
-/* Espressif add start. */
-
-/** Set callback for dhcp, reserved parameter for future use.
- *
- * @param netif the netif from which to remove the struct dhcp
- * @param cb callback for dhcp
- */
-#ifdef ESP_DHCP
-void dhcp_set_cb(struct netif *netif, void (*cb)(struct netif*))
-#else
-void dhcp_set_cb(struct netif *netif, void (*cb)(void))
-#endif
-{
- struct dhcp *dhcp;
- dhcp = netif_dhcp_data(netif);
-
- LWIP_ASSERT("netif != NULL", netif != NULL);
-
- if (dhcp != NULL) {
- dhcp->cb = cb;
- }
-}
-
-/* Espressif add end. */
-
/**
* @ingroup dhcp4
* Start DHCP negotiation for a network interface.
@@ -787,14 +736,6 @@ dhcp_start(struct netif *netif)
dhcp = netif_dhcp_data(netif);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-#if ESP_LWIP
- /* check hwtype of the netif */
- if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
- return ERR_ARG;
- }
-#endif
-
/* check MTU of the netif */
if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
@@ -844,21 +785,12 @@ dhcp_start(struct netif *netif)
}
#endif /* LWIP_DHCP_CHECK_LINK_UP */
-#ifdef ESP_DHCP
- // Try to restore last valid ip address obtained from DHCP server.
- // If no valid ip is available, run dhcp_discover instead.
- if(LWIP_DHCP_IP_ADDR_RESTORE()) {
- dhcp_set_state(dhcp, DHCP_STATE_BOUND);
- dhcp_network_changed(netif);
- return ERR_OK;
- }
-#endif
/* (re)start the DHCP negotiation */
result = dhcp_discover(netif);
if (result != ERR_OK) {
/* free resources allocated above */
- dhcp_stop(netif);
+ dhcp_release_and_stop(netif);
return ERR_MEM;
}
return result;
@@ -895,10 +827,9 @@ dhcp_inform(struct netif *netif)
dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, &dhcp, DHCP_STATE_INFORMING, dhcp.msg_out, DHCP_INFORM);
dhcp_option_trailer(&dhcp);
- pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
-
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
udp_sendto_if(dhcp_pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
@@ -921,8 +852,9 @@ dhcp_network_changed(struct netif *netif)
{
struct dhcp *dhcp = netif_dhcp_data(netif);
- if (!dhcp)
+ if (!dhcp) {
return;
+ }
switch (dhcp->state) {
case DHCP_STATE_REBINDING:
case DHCP_STATE_RENEWING:
@@ -935,6 +867,7 @@ dhcp_network_changed(struct netif *netif)
/* stay off */
break;
default:
+ LWIP_ASSERT("invalid dhcp->state", dhcp->state <= DHCP_STATE_BACKING_OFF);
/* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
state changes, SELECTING: continue with current 'rid' as we stay in the
same state */
@@ -1005,9 +938,8 @@ dhcp_decline(struct netif *netif)
dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_BACKING_OFF, dhcp->msg_out, DHCP_DECLINE);
dhcp_option_trailer(dhcp);
- /* resize pbuf to reflect true size of options */
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
/* per section 4.4.4, broadcast DECLINE messages */
udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
@@ -1021,7 +953,7 @@ dhcp_decline(struct netif *netif)
dhcp->tries++;
}
msecs = 10*1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
return result;
}
@@ -1050,20 +982,14 @@ dhcp_discover(struct netif *netif)
dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-#if LWIP_NETIF_HOSTNAME
- dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP NETIF HOSTNAME */
dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
}
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, dhcp->msg_out, DHCP_DISCOVER);
dhcp_option_trailer(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
@@ -1081,16 +1007,8 @@ dhcp_discover(struct netif *netif)
autoip_start(netif);
}
#endif /* LWIP_DHCP_AUTOIP_COOP */
-
-#if ESP_DHCP
-/* Since for embedded devices it's not that hard to miss a discover packet, so lower
- * the discover retry backoff time from (2,4,8,16,32,60,60)s to (500m,1,2,4,8,15,15)s.
- */
- msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 250;
-#else
- msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
-#endif
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
return result;
}
@@ -1115,47 +1033,6 @@ dhcp_bind(struct netif *netif)
/* reset time used of lease */
dhcp->lease_used = 0;
-#if ESP_DHCP_TIMER
- if (dhcp->offered_t0_lease != 0xffffffffUL) {
- /* set renewal period timer */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
- timeout = dhcp->offered_t0_lease;
- dhcp->t0_timeout = timeout;
- if (dhcp->t0_timeout == 0) {
- dhcp->t0_timeout = 120;
- }
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease*1000));
- }
-
- /* temporary DHCP lease? */
- if (dhcp->offered_t1_renew != 0xffffffffUL) {
- /* set renewal period timer */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
- timeout = dhcp->offered_t1_renew;
- dhcp->t1_timeout = timeout;
- if (dhcp->t1_timeout == 0) {
- dhcp->t1_timeout = dhcp->t0_timeout>>1;
- }
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
- dhcp->t1_renew_time = dhcp->t1_timeout;
- }
- /* set renewal period timer */
- if (dhcp->offered_t2_rebind != 0xffffffffUL) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
- timeout = dhcp->offered_t2_rebind;
- dhcp->t2_timeout = timeout;
- if (dhcp->t2_timeout == 0) {
- dhcp->t2_timeout = (dhcp->t0_timeout>>3)*7;
- }
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
- dhcp->t2_rebind_time = dhcp->t2_timeout;
- }
-
- /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
- if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
- dhcp->t1_timeout = 0;
- }
-#else
if (dhcp->offered_t0_lease != 0xffffffffUL) {
/* set renewal period timer */
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
@@ -1204,7 +1081,6 @@ dhcp_bind(struct netif *netif)
if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
dhcp->t1_timeout = 0;
}
-#endif
if (dhcp->subnet_mask_given) {
/* copy offered network mask */
@@ -1245,20 +1121,6 @@ dhcp_bind(struct netif *netif)
netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
/* interface is used by routing now that an address is set */
-
- /* Espressif add start. */
-#ifdef ESP_DHCP
- LWIP_DHCP_IP_ADDR_STORE();
-#endif
-
- if (dhcp->cb != NULL) {
-#ifdef ESP_DHCP
- dhcp->cb(netif);
-#else
- dhcp->cb();
-#endif
- }
- /* Espressif add end. */
}
/**
@@ -1292,11 +1154,10 @@ dhcp_renew(struct netif *netif)
dhcp_option_hostname(dhcp, netif);
#endif /* LWIP_NETIF_HOSTNAME */
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_RENEWING, dhcp->msg_out, DHCP_REQUEST);
/* append DHCP message trailer */
dhcp_option_trailer(dhcp);
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
udp_sendto_if(dhcp_pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
dhcp_delete_msg(dhcp);
@@ -1308,8 +1169,8 @@ dhcp_renew(struct netif *netif)
dhcp->tries++;
}
/* back-off on retries, but to a maximum of 20 seconds */
- msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
return result;
}
@@ -1344,10 +1205,9 @@ dhcp_rebind(struct netif *netif)
dhcp_option_hostname(dhcp, netif);
#endif /* LWIP_NETIF_HOSTNAME */
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBINDING, dhcp->msg_out, DHCP_DISCOVER);
dhcp_option_trailer(dhcp);
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
/* broadcast to server */
udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
dhcp_delete_msg(dhcp);
@@ -1358,8 +1218,8 @@ dhcp_rebind(struct netif *netif)
if (dhcp->tries < 255) {
dhcp->tries++;
}
- msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
return result;
}
@@ -1392,11 +1252,9 @@ dhcp_reboot(struct netif *netif)
for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
}
-
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBOOTING, dhcp->msg_out, DHCP_REQUEST);
dhcp_option_trailer(dhcp);
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
/* broadcast to server */
udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
dhcp_delete_msg(dhcp);
@@ -1407,37 +1265,36 @@ dhcp_reboot(struct netif *netif)
if (dhcp->tries < 255) {
dhcp->tries++;
}
- msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
return result;
}
-
/**
* @ingroup dhcp4
- * Release a DHCP lease (usually called before @ref dhcp_stop).
+ * Release a DHCP lease and stop DHCP statemachine (and AUTOIP if LWIP_DHCP_AUTOIP_COOP).
*
- * @param netif network interface which must release its lease
+ * @param netif network interface
*/
-err_t
-dhcp_release(struct netif *netif)
+void
+dhcp_release_and_stop(struct netif *netif)
{
struct dhcp *dhcp = netif_dhcp_data(netif);
- err_t result;
ip_addr_t server_ip_addr;
- u8_t is_dhcp_supplied_address;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release_and_stop()\n"));
if (dhcp == NULL) {
- return ERR_ARG;
+ return;
}
- ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
- is_dhcp_supplied_address = dhcp_supplied_address(netif);
+ /* already off? -> nothing to do */
+ if (dhcp->state == DHCP_STATE_OFF) {
+ return;
+ }
+
+ ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
- /* idle DHCP client */
- dhcp_set_state(dhcp, DHCP_STATE_OFF);
/* clean old DHCP offer */
ip_addr_set_zero_ip4(&dhcp->server_ip_addr);
ip4_addr_set_zero(&dhcp->offered_ip_addr);
@@ -1449,74 +1306,66 @@ dhcp_release(struct netif *netif)
dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
dhcp->t1_renew_time = dhcp->t2_rebind_time = dhcp->lease_used = dhcp->t0_timeout = 0;
- if (!is_dhcp_supplied_address) {
- /* don't issue release message when address is not dhcp-assigned */
- return ERR_OK;
- }
+ /* send release message when current IP was assigned via DHCP */
+ if (dhcp_supplied_address(netif)) {
+ /* create and initialize the DHCP message header */
+ err_t result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
- if (result == ERR_OK) {
- dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
- dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, dhcp->state, dhcp->msg_out, DHCP_RELEASE);
+ dhcp_option_trailer(dhcp);
- dhcp_option_trailer(dhcp);
-
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- udp_sendto_if(dhcp_pcb, dhcp->p_out, &server_ip_addr, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n"));
- } else {
- /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ udp_sendto_if(dhcp_pcb, dhcp->p_out, &server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n"));
+ } else {
+ /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
}
+
/* remove IP address from interface (prevents routing from selecting this interface) */
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
- if (dhcp->cb != NULL) {
-#ifdef ESP_DHCP
- dhcp->cb(netif);
-#else
- dhcp->cb();
-#endif
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
}
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+ dhcp_set_state(dhcp, DHCP_STATE_OFF);
- return result;
+ if (dhcp->pcb_allocated != 0) {
+ dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+ dhcp->pcb_allocated = 0;
+ }
}
/**
* @ingroup dhcp4
- * Remove the DHCP client from the interface.
- *
- * @param netif The network interface to stop DHCP on
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ dhcp_release_and_stop(netif);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
*/
void
dhcp_stop(struct netif *netif)
{
- struct dhcp *dhcp;
- LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
- dhcp = netif_dhcp_data(netif);
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
- /* netif is DHCP configured? */
- if (dhcp != NULL) {
-#if LWIP_DHCP_AUTOIP_COOP
- if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
- autoip_stop(netif);
- dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
- }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-
- LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
- dhcp_set_state(dhcp, DHCP_STATE_OFF);
-
- if (dhcp->pcb_allocated != 0) {
- dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
- dhcp->pcb_allocated = 0;
- }
- }
+ dhcp_release_and_stop(netif);
}
/*
@@ -1642,8 +1491,8 @@ dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
again:
q = p;
while ((q != NULL) && (options_idx >= q->len)) {
- options_idx -= q->len;
- options_idx_max -= q->len;
+ options_idx = (u16_t)(options_idx - q->len);
+ options_idx_max = (u16_t)(options_idx_max - q->len);
q = q->next;
}
if (q == NULL) {
@@ -1658,7 +1507,11 @@ again:
u8_t len;
u8_t decode_len = 0;
int decode_idx = -1;
- u16_t val_offset = offset + 2;
+ u16_t val_offset = (u16_t)(offset + 2);
+ if (val_offset < offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
/* len byte might be in the next pbuf */
if ((offset + 1) < q->len) {
len = options[offset + 1];
@@ -1733,9 +1586,16 @@ again:
default:
decode_len = 0;
LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op));
+ LWIP_HOOK_DHCP_PARSE_OPTION(ip_current_netif(), dhcp, dhcp->state, dhcp->msg_in,
+ dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) ? (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) : 0,
+ op, len, q, val_offset);
break;
}
- offset += len + 2;
+ if (offset + len + 2 > 0xFFFF) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ offset = (u16_t)(offset + len + 2);
if (decode_len > 0) {
u32_t value = 0;
u16_t copy_len;
@@ -1748,11 +1608,17 @@ decode_next:
}
if (decode_len > 4) {
/* decode more than one u32_t */
+ u16_t next_val_offset;
LWIP_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
dhcp_got_option(dhcp, decode_idx);
dhcp_set_option_value(dhcp, decode_idx, lwip_htonl(value));
- decode_len -= 4;
- val_offset += 4;
+ decode_len = (u8_t)(decode_len - 4);
+ next_val_offset = (u16_t)(val_offset + 4);
+ if (next_val_offset < val_offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ val_offset = next_val_offset;
decode_idx++;
goto decode_next;
} else if (decode_len == 4) {
@@ -1766,8 +1632,8 @@ decode_next:
}
}
if (offset >= q->len) {
- offset -= q->len;
- offset_max -= q->len;
+ offset = (u16_t)(offset - q->len);
+ offset_max = (u16_t)(offset_max - q->len);
if ((offset < offset_max) && offset_max) {
q = q->next;
LWIP_ERROR("next pbuf was null", q != NULL, return ERR_VAL;);
@@ -2015,7 +1881,7 @@ dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
/* set ciaddr to netif->ip_addr based on message_type and state */
if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) ||
((message_type == DHCP_REQUEST) && /* DHCP_STATE_BOUND not used for sending! */
- ((dhcp->state== DHCP_STATE_RENEWING) || dhcp->state== DHCP_STATE_REBINDING))) {
+ ((dhcp->state == DHCP_STATE_RENEWING) || dhcp->state == DHCP_STATE_REBINDING))) {
ip4_addr_copy(dhcp->msg_out->ciaddr, *netif_ip4_addr(netif));
}
ip4_addr_set_zero(&dhcp->msg_out->yiaddr);
@@ -2082,6 +1948,8 @@ dhcp_option_trailer(struct dhcp *dhcp)
/* add a fill/padding byte */
dhcp->msg_out->options[dhcp->options_out_len++] = 0;
}
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(dhcp->p_out, (u16_t)(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len));
}
/** check if DHCP supplied netif->ip_addr
diff --git a/src/core/ipv4/etharp.c b/../lwip_nat/src/core/ipv4/etharp.c
index 5f276ed2..d4c9928e 100644
--- a/src/core/ipv4/etharp.c
+++ b/../lwip_nat/src/core/ipv4/etharp.c
@@ -53,7 +53,6 @@
#include "lwip/dhcp.h"
#include "lwip/autoip.h"
#include "netif/ethernet.h"
-#include "lwip/netif.h"
#include <string.h>
@@ -119,10 +118,10 @@ static u8_t etharp_cached_entry;
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
#if LWIP_NETIF_HWADDRHINT
-#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
- *((netif)->addr_hint) = (hint);
+#define ETHARP_SET_ADDRHINT(netif, addrhint) do { if (((netif) != NULL) && ((netif)->hints != NULL)) { \
+ (netif)->hints->addr_hint = (addrhint); }} while(0)
#else /* LWIP_NETIF_HWADDRHINT */
-#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#define ETHARP_SET_ADDRHINT(netif, addrhint) (etharp_cached_entry = (addrhint))
#endif /* LWIP_NETIF_HWADDRHINT */
@@ -139,21 +138,6 @@ static err_t etharp_raw(struct netif *netif,
const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
const u16_t opcode);
-#if ESP_GRATUITOUS_ARP
-void garp_tmr(void)
-{
- struct netif* garp_netif = NULL;
-
- for (garp_netif = netif_list; garp_netif != NULL; garp_netif = garp_netif->next) {
- if (netif_is_up(garp_netif) && netif_is_link_up(garp_netif) && !ip4_addr_isany_val(*netif_ip4_addr(garp_netif))) {
- if ((garp_netif->flags & NETIF_FLAG_ETHARP) && (garp_netif->flags & NETIF_FLAG_GARP)) {
- etharp_gratuitous(garp_netif);
- }
- }
- }
-}
-#endif
-
#if ARP_QUEUEING
/**
* Free a complete queue of etharp entries
@@ -304,7 +288,7 @@ etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
/* remember first empty entry */
- empty = i;
+ empty = (s8_t)i;
} else if (state != ETHARP_STATE_EMPTY) {
LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
@@ -316,21 +300,21 @@ etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
/* found exact IP address match, simply bail out */
- return i;
+ return (s8_t)i;
}
/* pending entry? */
if (state == ETHARP_STATE_PENDING) {
/* pending with queued packets? */
if (arp_table[i].q != NULL) {
if (arp_table[i].ctime >= age_queue) {
- old_queue = i;
+ old_queue = (s8_t)i;
age_queue = arp_table[i].ctime;
}
} else
/* pending without queued packets? */
{
if (arp_table[i].ctime >= age_pending) {
- old_pending = i;
+ old_pending = (s8_t)i;
age_pending = arp_table[i].ctime;
}
}
@@ -343,7 +327,7 @@ etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
{
/* remember entry with oldest stable entry in oldest, its age in maxtime */
if (arp_table[i].ctime >= age_stable) {
- old_stable = i;
+ old_stable = (s8_t)i;
age_stable = arp_table[i].ctime;
}
}
@@ -371,25 +355,25 @@ etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
/* 1) empty entry available? */
if (empty < ARP_TABLE_SIZE) {
- i = empty;
+ i = (u8_t)empty;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
} else {
/* 2) found recyclable stable entry? */
if (old_stable < ARP_TABLE_SIZE) {
/* recycle oldest stable*/
- i = old_stable;
+ i = (u8_t)old_stable;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
/* no queued packets should exist on stable entries */
LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
/* 3) found recyclable pending entry without queued packets? */
} else if (old_pending < ARP_TABLE_SIZE) {
/* recycle oldest pending */
- i = old_pending;
+ i = (u8_t)old_pending;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
/* 4) found recyclable pending entry with queued packets? */
} else if (old_queue < ARP_TABLE_SIZE) {
/* recycle oldest pending (queued packets are free in etharp_free_entry) */
- i = old_queue;
+ i = (u8_t)old_queue;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
/* no empty or recyclable entries found */
} else {
@@ -480,7 +464,7 @@ etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct et
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
/* update address */
- ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN);
/* reset time stamp */
arp_table[i].ctime = 0;
/* this is where we will send out queued packets! */
@@ -687,10 +671,10 @@ etharp_input(struct pbuf *p, struct netif *netif)
autoip_arp_reply(netif, hdr);
#endif /* LWIP_AUTOIP */
- /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
* structure packing (not using structure copy which breaks strict-aliasing rules). */
- IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
- IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
/* this interface is not configured? */
if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
@@ -758,11 +742,7 @@ etharp_input(struct pbuf *p, struct netif *netif)
/** Just a small helper function that sends a pbuf to an ethernet address
* in the arp_table specified by the index 'arp_idx'.
*/
-#if ESP_LWIP
-static err_t ESP_IRAM_ATTR
-#else
static err_t
-#endif
etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
{
LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
@@ -805,11 +785,7 @@ etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
* - ERR_RTE No route to destination (no gateway to external networks),
* or the return type of either etharp_query() or ethernet_output().
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
{
const struct eth_addr *dest;
@@ -840,7 +816,7 @@ etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
dest = &mcastaddr;
/* unicast destination IP address? */
} else {
- s8_t i;
+ u8_t i;
/* outside local network? if so, this can neither be a global broadcast nor
a subnet broadcast. */
if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
@@ -874,9 +850,9 @@ etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
}
}
#if LWIP_NETIF_HWADDRHINT
- if (netif->addr_hint != NULL) {
+ if (netif->hints != NULL) {
/* per-pcb cached entry was given */
- u8_t etharp_cached_entry = *(netif->addr_hint);
+ u8_t etharp_cached_entry = netif->hints->addr_hint;
if (etharp_cached_entry < ARP_TABLE_SIZE) {
#endif /* LWIP_NETIF_HWADDRHINT */
if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
@@ -902,7 +878,7 @@ etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
#endif
(ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
/* found an existing, stable entry */
- ETHARP_SET_HINT(netif, i);
+ ETHARP_SET_ADDRHINT(netif, i);
return etharp_output_to_arp_index(netif, q, i);
}
}
@@ -956,7 +932,8 @@ etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
err_t result = ERR_MEM;
int is_new_entry = 0;
- s8_t i; /* ARP entry index */
+ s8_t i_err;
+ u8_t i;
/* non-unicast address? */
if (ip4_addr_isbroadcast(ipaddr, netif) ||
@@ -967,17 +944,18 @@ etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
}
/* find entry in ARP cache, ask to create entry if queueing packet */
- i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+ i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
/* could not find or create entry? */
- if (i < 0) {
+ if (i_err < 0) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
if (q) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
ETHARP_STATS_INC(etharp.memerr);
}
- return (err_t)i;
+ return (err_t)i_err;
}
+ i = (u8_t)i_err;
/* mark a fresh entry as pending (we just sent a request) */
if (arp_table[i].state == ETHARP_STATE_EMPTY) {
@@ -1012,7 +990,7 @@ etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
/* stable entry? */
if (arp_table[i].state >= ETHARP_STATE_STABLE) {
/* we have a valid IP->Ethernet address mapping */
- ETHARP_SET_HINT(netif, i);
+ ETHARP_SET_ADDRHINT(netif, i);
/* send the packet */
result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);
/* pending entry? (either just created or already pending */
@@ -1020,13 +998,12 @@ etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
/* entry is still pending, queue the given packet 'q' */
struct pbuf *p;
int copy_needed = 0;
- /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
- * to copy the whole queue into a new PBUF_RAM (see bug #11400)
- * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ /* IF q includes a pbuf that must be copied, copy the whole chain into a
+ * new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
p = q;
while (p) {
LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
- if (p->type != PBUF_ROM) {
+ if (PBUF_NEEDS_COPY(p)) {
copy_needed = 1;
break;
}
@@ -1034,7 +1011,7 @@ etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
}
if (copy_needed) {
/* copy the whole packet into new pbufs */
- p = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
if (p != NULL) {
if (pbuf_copy(p, q) != ERR_OK) {
pbuf_free(p);
@@ -1155,12 +1132,12 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
(netif->hwaddr_len == ETH_HWADDR_LEN));
/* Write the ARP MAC-Addresses */
- ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
- ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
- /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+ SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN);
+ SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN);
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
* structure packing. */
- IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
- IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+ IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr);
+ IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr);
hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
hdr->proto = PP_HTONS(ETHTYPE_IP);
diff --git a/src/core/ipv4/icmp.c b/../lwip_nat/src/core/ipv4/icmp.c
index 5ee24eed..dbf7349c 100644
--- a/src/core/ipv4/icmp.c
+++ b/../lwip_nat/src/core/ipv4/icmp.c
@@ -92,7 +92,7 @@ icmp_input(struct pbuf *p, struct netif *inp)
MIB2_STATS_INC(mib2.icmpinmsgs);
iphdr_in = ip4_current_header();
- hlen = IPH_HL(iphdr_in) * 4;
+ hlen = IPH_HL_BYTES(iphdr_in);
if (hlen < IP_HLEN) {
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
goto lenerr;
@@ -157,8 +157,13 @@ icmp_input(struct pbuf *p, struct netif *inp)
* allocate a new one and copy p into it
*/
struct pbuf *r;
+ u16_t alloc_len = (u16_t)(p->tot_len + hlen);
+ if (alloc_len < p->tot_len) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed (tot_len overflow)\n"));
+ goto icmperr;
+ }
/* allocate new packet buffer with space for link headers */
- r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM);
+ r = pbuf_alloc(PBUF_LINK, alloc_len, PBUF_RAM);
if (r == NULL) {
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
goto icmperr;
@@ -188,7 +193,7 @@ icmp_input(struct pbuf *p, struct netif *inp)
p = r;
} else {
/* restore p->payload to point to icmp header (cannot fail) */
- if (pbuf_header(p, -(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+ if (pbuf_header(p, (s16_t)-(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
goto icmperr;
}
@@ -210,9 +215,9 @@ icmp_input(struct pbuf *p, struct netif *inp)
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
/* adjust the checksum */
if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
- iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
+ iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);
} else {
- iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
+ iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS(ICMP_ECHO << 8));
}
}
#if LWIP_CHECKSUM_CTRL_PER_NETIF
@@ -375,7 +380,7 @@ icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
{
ip4_addr_t iphdr_dst;
ip4_addr_copy(iphdr_dst, iphdr->dest);
- netif = ip4_route_src(&iphdr_src, &iphdr_dst);
+ netif = ip4_route_src(&iphdr_dst, &iphdr_src);
}
#else
netif = ip4_route(&iphdr_src);
diff --git a/src/core/ipv4/igmp.c b/../lwip_nat/src/core/ipv4/igmp.c
index 74a6c377..2f920351 100644
--- a/src/core/ipv4/igmp.c
+++ b/../lwip_nat/src/core/ipv4/igmp.c
@@ -199,7 +199,7 @@ igmp_report_groups(struct netif *netif)
if(group != NULL) {
group = group->next;
}
-
+
while (group != NULL) {
igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
group = group->next;
@@ -207,7 +207,7 @@ igmp_report_groups(struct netif *netif)
}
/**
- * Search for a group in the global igmp_group_list
+ * Search for a group in the netif's igmp group list
*
* @param ifp the network interface for which to look
* @param addr the group ip address to search for
@@ -252,7 +252,7 @@ igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
/* Group already exists. */
return group;
}
-
+
/* Group doesn't exist yet, create a new one */
group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
if (group != NULL) {
@@ -262,7 +262,7 @@ igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
group->last_reporter_flag = 0;
group->use = 0;
- /* Ensure allsystems group is always first in list */
+ /* Ensure allsystems group is always first in list */
if (list_head == NULL) {
/* this is the first entry in linked list */
LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
@@ -286,9 +286,9 @@ igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
}
/**
- * Remove a group in the global igmp_group_list, but don't free it yet
+ * Remove a group from netif's igmp group list, but don't free it yet
*
- * @param group the group to remove from the global igmp_group_list
+ * @param group the group to remove from the netif's igmp group list
* @return ERR_OK if group was removed from the list, an err_t otherwise
*/
static err_t
@@ -304,7 +304,7 @@ igmp_remove_group(struct netif* netif, struct igmp_group *group)
break;
}
}
- /* Group not found in the global igmp_group_list */
+ /* Group not found in netif's igmp group list */
if (tmp_group == NULL) {
err = ERR_ARG;
}
@@ -456,8 +456,7 @@ igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
/* loop through netif's */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* Should we join this interface ? */
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
err = igmp_joingroup_netif(netif, groupaddr);
@@ -467,8 +466,6 @@ igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
return err;
}
}
- /* proceed to next network interface */
- netif = netif->next;
}
return err;
@@ -552,8 +549,7 @@ igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
/* loop through netif's */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* Should we leave this interface ? */
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
err_t res = igmp_leavegroup_netif(netif, groupaddr);
@@ -562,8 +558,6 @@ igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
err = res;
}
}
- /* proceed to next network interface */
- netif = netif->next;
}
return err;
@@ -638,9 +632,9 @@ igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
void
igmp_tmr(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
struct igmp_group *group = netif_igmp_data(netif);
while (group != NULL) {
@@ -652,7 +646,6 @@ igmp_tmr(void)
}
group = group->next;
}
- netif = netif->next;
}
}
@@ -691,7 +684,7 @@ static void
igmp_start_timer(struct igmp_group *group, u8_t max_time)
{
#ifdef LWIP_RAND
- group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
+ group->timer = (u16_t)(max_time > 2 ? (LWIP_RAND() % max_time) : 1);
#else /* LWIP_RAND */
/* ATTENTION: use this only if absolutely necessary! */
group->timer = max_time / 2;
diff --git a/src/core/ipv4/ip4.c b/../lwip_nat/src/core/ipv4/ip4.c
index 9ba6a9cc..ccaa0e2d 100644
--- a/src/core/ipv4/ip4.c
+++ b/../lwip_nat/src/core/ipv4/ip4.c
@@ -57,6 +57,10 @@
#include "lwip/stats.h"
#include "lwip/prot/dhcp.h"
+#if IP_NAT
+#include "lwip/ip4_nat.h"
+#endif
+
#include <string.h>
#ifdef LWIP_HOOK_FILENAME
@@ -121,68 +125,14 @@ ip4_set_default_multicast_netif(struct netif* default_multicast_netif)
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
-#if ESP_LWIP
-bool ip4_netif_exist(const ip4_addr_t *src, const ip4_addr_t *dest)
-{
- struct netif *netif = NULL;
-
- for (netif = netif_list; netif != NULL; netif = netif->next) {
- /* is the netif up, does it have a link and a valid address? */
- if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
- /* source netif and dest netif match? */
- if (ip4_addr_netcmp(src, netif_ip4_addr(netif), netif_ip4_netmask(netif)) || ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
- /* return false when both netif don't match */
- return true;
- }
- }
- }
-
- return false;
-}
-
-/**
- * Source based IPv4 routing hook function.
- */
-struct netif * ESP_IRAM_ATTR
-ip4_route_src_hook(const ip4_addr_t *dest, const ip4_addr_t *src)
-{
- struct netif *netif = NULL;
-
- /* destination IP is broadcast IP? */
- if ((src != NULL) && !ip4_addr_isany(src)) {
- /* iterate through netifs */
- for (netif = netif_list; netif != NULL; netif = netif->next) {
- /* is the netif up, does it have a link and a valid address? */
- if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
- /* source IP matches? */
- if (ip4_addr_cmp(src, netif_ip4_addr(netif))) {
- /* return netif on which to forward IP packet */
- return netif;
- }
- }
- }
- }
- return netif;
-}
-#endif
-
/**
* Source based IPv4 routing must be fully implemented in
* LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides the parameters.
*/
-#if ESP_LWIP
-struct netif * ESP_IRAM_ATTR
-#else
struct netif *
-#endif
ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
{
if (src != NULL) {
-#if ESP_LWIP
- if (!ip4_addr_isany(src) && (ip4_netif_exist(src,dest) == false)) {
- return NULL;
- }
-#endif
/* when src==NULL, the hook is called from ip4_route(dest) */
struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, src);
if (netif != NULL) {
@@ -202,13 +152,10 @@ ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
* @param dest the destination IP address for which to find the route
* @return the netif on which to send to reach dest
*/
-#if ESP_LWIP
-struct netif * ESP_IRAM_ATTR
-#else
struct netif *
-#endif
ip4_route(const ip4_addr_t *dest)
{
+#if !LWIP_SINGLE_NETIF
struct netif *netif;
#if LWIP_MULTICAST_TX_OPTIONS
@@ -263,6 +210,7 @@ ip4_route(const ip4_addr_t *dest)
return netif;
}
#endif
+#endif /* !LWIP_SINGLE_NETIF */
if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||
ip4_addr_isany_val(*netif_ip4_addr(netif_default))) {
@@ -376,9 +324,9 @@ ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
/* Incrementally update the IP checksum. */
if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
- IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1));
} else {
- IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100)));
}
LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
@@ -414,6 +362,45 @@ return_noroute:
}
#endif /* IP_FORWARD */
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip4_input_accept(struct netif *netif)
+{
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+ ip4_addr_get_u32(ip4_current_dest_addr()), ip4_addr_get_u32(netif_ip4_addr(netif)),
+ ip4_addr_get_u32(ip4_current_dest_addr()) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+ ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+ ip4_addr_get_u32(ip4_current_dest_addr()) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
+ /* unicast to this interface address? */
+ if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
+ /* or broadcast on this interface network address? */
+ ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+ || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+ ) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* accept on this netif */
+ return 1;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* accept on this netif */
+ return 1;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ return 0;
+}
+
/**
* This function is called by the network interface device driver when
* an IP packet is received. The function does the basic checks of the
@@ -428,14 +415,10 @@ return_noroute:
* @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
* processed, but currently always returns ERR_OK)
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
ip4_input(struct pbuf *p, struct netif *inp)
{
- struct ip_hdr *iphdr;
+ const struct ip_hdr *iphdr;
struct netif *netif;
u16_t iphdr_hlen;
u16_t iphdr_len;
@@ -465,10 +448,8 @@ ip4_input(struct pbuf *p, struct netif *inp)
}
#endif
- /* obtain IP header length in number of 32-bit words */
- iphdr_hlen = IPH_HL(iphdr);
- /* calculate IP header length in bytes */
- iphdr_hlen *= 4;
+ /* obtain IP header length in bytes */
+ iphdr_hlen = IPH_HL_BYTES(iphdr);
/* obtain ip length in bytes */
iphdr_len = lwip_ntohs(IPH_LEN(iphdr));
@@ -546,62 +527,31 @@ ip4_input(struct pbuf *p, struct netif *inp)
#endif /* LWIP_IGMP */
} else {
/* start trying with inp. if that's not acceptable, start walking the
- list of configured netifs.
- 'first' is used as a boolean to mark whether we started walking the list */
- int first = 1;
- netif = inp;
- do {
- LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
- ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(netif_ip4_addr(netif)),
- ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
- ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
- ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
-
- /* interface is up and configured? */
- if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
- /* unicast to this interface address? */
- if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
- /* or broadcast on this interface network address? */
- ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
-#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
- || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
-#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
- ) {
- LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
- netif->name[0], netif->name[1]));
- /* break out of for loop */
- break;
- }
-#if LWIP_AUTOIP
- /* connections to link-local addresses must persist after changing
- the netif's address (RFC3927 ch. 1.9) */
- if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
- LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
- netif->name[0], netif->name[1]));
- /* break out of for loop */
- break;
- }
-#endif /* LWIP_AUTOIP */
- }
- if (first) {
+ list of configured netifs. */
+ if (ip4_input_accept(inp)) {
+ netif = inp;
+ } else {
+ netif = NULL;
#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
- /* Packets sent to the loopback address must not be accepted on an
- * interface that does not have the loopback address assigned to it,
- * unless a non-loopback interface is used for loopback traffic. */
- if (ip4_addr_isloopback(ip4_current_dest_addr())) {
- netif = NULL;
- break;
- }
+ /* Packets sent to the loopback address must not be accepted on an
+ * interface that does not have the loopback address assigned to it,
+ * unless a non-loopback interface is used for loopback traffic. */
+ if (!ip4_addr_isloopback(ip4_current_dest_addr()))
#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
- first = 0;
- netif = netif_list;
- } else {
- netif = netif->next;
- }
- if (netif == inp) {
- netif = netif->next;
+ {
+#if !LWIP_SINGLE_NETIF
+ NETIF_FOREACH(netif) {
+ if (netif == inp) {
+ /* we checked that before already */
+ continue;
+ }
+ if (ip4_input_accept(netif)) {
+ break;
+ }
+ }
+#endif /* !LWIP_SINGLE_NETIF */
}
- } while (netif != NULL);
+ }
}
#if IP_ACCEPT_LINK_LAYER_ADDRESSING
@@ -617,7 +567,7 @@ ip4_input(struct pbuf *p, struct netif *inp)
if (netif == NULL) {
/* remote port is DHCP server? */
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
- struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+ const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n",
lwip_ntohs(udphdr->dest)));
if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
@@ -654,15 +604,29 @@ ip4_input(struct pbuf *p, struct netif *inp)
/* packet not for us? */
if (netif == NULL) {
+#if IP_FORWARD || IP_NAT
+ u8_t taken = 0;
+#endif /* IP_FORWARD || IP_NAT */
+
/* packet not for us, route or discard */
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
-#if IP_FORWARD
+#if IP_FORWARD || IP_NAT
/* non-broadcast packet? */
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
- /* try to forward IP packet on (other) interfaces */
- ip4_forward(p, iphdr, inp);
- } else
+#if IP_NAT
+ taken = ip4_nat_out(p);
+ if (!taken)
+#endif
+ {
+#if IP_FORWARD
+ /* try to forward IP packet on (other) interfaces */
+ ip4_forward(p, (struct ip_hdr *)p->payload, inp);
+ taken = 1;
#endif /* IP_FORWARD */
+ }
+ }
+ if (!taken)
+#endif /* IP_FORWARD || IP_NAT */
{
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinaddrerrors);
@@ -682,7 +646,7 @@ ip4_input(struct pbuf *p, struct netif *inp)
if (p == NULL) {
return ERR_OK;
}
- iphdr = (struct ip_hdr *)p->payload;
+ iphdr = (const struct ip_hdr *)p->payload;
#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
pbuf_free(p);
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
@@ -721,14 +685,21 @@ ip4_input(struct pbuf *p, struct netif *inp)
ip_data.current_netif = netif;
ip_data.current_input_netif = inp;
ip_data.current_ip4_header = iphdr;
- ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;
+ ip_data.current_ip_header_tot_len = IPH_HL_BYTES(iphdr);
+
+#if IP_NAT
+ if (!ip4_addr_isbroadcast(&(iphdr->dest), inp) &&
+ (ip4_nat_input(p) != 0)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet consumed by nat layer\n"));
+ } else
+#endif /* IP_NAT */
#if LWIP_RAW
/* raw input did not eat the packet? */
if (raw_input(p, inp) == 0)
#endif /* LWIP_RAW */
{
- pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
+ pbuf_header(p, (s16_t)-(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
@@ -762,8 +733,7 @@ ip4_input(struct pbuf *p, struct netif *inp)
/* send ICMP destination protocol unreachable unless is was a broadcast */
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
!ip4_addr_ismulticast(ip4_current_dest_addr())) {
- pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */
- p->payload = iphdr;
+ pbuf_header_force(p, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */
icmp_dest_unreach(p, ICMP_DUR_PROTO);
}
#endif /* LWIP_ICMP */
@@ -853,11 +823,7 @@ ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
* Same as ip_output_if() but 'src' address is not replaced by netif address
* when it is 'any'.
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
@@ -870,11 +836,7 @@ ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
* Same as ip_output_if_opt() but 'src' address is not replaced by netif address
* when it is 'any'.
*/
-#if ESP_LWIP
-err_t ESP_IRAM_ATTR
-#else
err_t
-#endif
ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
u16_t optlen)
@@ -899,11 +861,18 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d
#if CHECKSUM_GEN_IP_INLINE
int i;
#endif /* CHECKSUM_GEN_IP_INLINE */
+ if (optlen > (IP_HLEN_MAX - IP_HLEN)) {
+ /* optlen too long */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_VAL;
+ }
/* round up to a multiple of 4 */
- optlen_aligned = ((optlen + 3) & ~3);
- ip_hlen += optlen_aligned;
+ optlen_aligned = (u16_t)((optlen + 3) & ~3);
+ ip_hlen = (u16_t)(ip_hlen + optlen_aligned);
/* First write in the IP options */
- if (pbuf_header(p, optlen_aligned)) {
+ if (pbuf_header(p, (s16_t)optlen_aligned)) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
IP_STATS_INC(ip.err);
MIB2_STATS_INC(mib2.ipoutdiscards);
@@ -912,7 +881,7 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d
MEMCPY(p->payload, ip_options, optlen);
if (optlen < optlen_aligned) {
/* zero the remaining bytes */
- memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+ memset(((char*)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen));
}
#if CHECKSUM_GEN_IP_INLINE
for (i = 0; i < optlen_aligned/2; i++) {
@@ -994,6 +963,12 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d
#endif /* CHECKSUM_GEN_IP_INLINE */
} else {
/* IP header already included in p */
+ if (p->len < IP_HLEN) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
iphdr = (struct ip_hdr *)p->payload;
ip4_addr_copy(dest_addr, iphdr->dest);
dest = &dest_addr;
@@ -1066,7 +1041,7 @@ ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
}
-#if LWIP_NETIF_HWADDRHINT
+#if LWIP_NETIF_USE_HINTS
/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
* before calling ip_output_if.
*
@@ -1079,7 +1054,7 @@ ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
* @param ttl the TTL value to be set in the IP header
* @param tos the TOS value to be set in the IP header
* @param proto the PROTOCOL to be set in the IP header
- * @param addr_hint address hint pointer set to netif->addr_hint before
+ * @param netif_hint netif output hint pointer set to netif->hint before
* calling ip_output_if()
*
* @return ERR_RTE if no route is found
@@ -1087,7 +1062,7 @@ ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
*/
err_t
ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+ u8_t ttl, u8_t tos, u8_t proto, struct netif_hint *netif_hint)
{
struct netif *netif;
err_t err;
@@ -1101,13 +1076,13 @@ ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
return ERR_RTE;
}
- NETIF_SET_HWADDRHINT(netif, addr_hint);
+ NETIF_SET_HINTS(netif, netif_hint);
err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ NETIF_RESET_HINTS(netif);
return err;
}
-#endif /* LWIP_NETIF_HWADDRHINT*/
+#endif /* LWIP_NETIF_USE_HINTS*/
#if IP_DEBUG
/* Print an IP header by using LWIP_DEBUGF
diff --git a/src/core/ipv4/ip4_addr.c b/../lwip_nat/src/core/ipv4/ip4_addr.c
index 4cb2b065..f0ee4fba 100644
--- a/src/core/ipv4/ip4_addr.c
+++ b/../lwip_nat/src/core/ipv4/ip4_addr.c
@@ -54,11 +54,7 @@ const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
* @param netif the network interface against which the address is checked
* @return returns non-zero if the address is a broadcast address
*/
-#if ESP_LWIP
-u8_t ESP_IRAM_ATTR
-#else
u8_t
-#endif
ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
{
ip4_addr_t ipaddr;
@@ -164,12 +160,6 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr)
u32_t parts[4];
u32_t *pp = parts;
-#if ESP_IP4_ATON
- char ch;
- unsigned long cutoff;
- int cutlim;
-#endif
-
c = *cp;
for (;;) {
/*
@@ -191,27 +181,6 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr)
base = 8;
}
}
-
-#if ESP_IP4_ATON
- cutoff =(unsigned long)0xffffffff / (unsigned long)base;
- cutlim =(unsigned long)0xffffffff % (unsigned long)base;
- for (;;) {
- if (isdigit(c)) {
- ch = (int)(c - '0');
- if (val > cutoff || (val == cutoff && ch > cutlim))
- return (0);
- val = (val * base) + (int)(c - '0');
- c = *++cp;
- } else if (base == 16 && isxdigit(c)) {
- ch = (int)(c + 10 - (islower(c) ? 'a' : 'A'));
- if (val > cutoff || (val == cutoff && ch > cutlim))
- return (0);
- val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
- c = *++cp;
- } else
- break;
- }
-#else
for (;;) {
if (isdigit(c)) {
val = (val * base) + (u32_t)(c - '0');
@@ -223,8 +192,6 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr)
break;
}
}
-#endif
-
if (c == '.') {
/*
* Internet format:
@@ -314,7 +281,7 @@ ip4addr_ntoa(const ip4_addr_t *addr)
}
/**
- * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ * Same as ip4addr_ntoa, but reentrant since a user-supplied buffer is used.
*
* @param addr ip address in network order to convert
* @param buf target buffer where the string is stored
diff --git a/src/core/ipv4/ip4_frag.c b/../lwip_nat/src/core/ipv4/ip4_frag.c
index fdb20886..90af970e 100644
--- a/src/core/ipv4/ip4_frag.c
+++ b/../lwip_nat/src/core/ipv4/ip4_frag.c
@@ -186,7 +186,7 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
icmp_time_exceeded(p, ICMP_TE_FRAG);
clen = pbuf_clen(p);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(p);
}
#endif /* LWIP_ICMP */
@@ -202,13 +202,13 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
p = iprh->next_pbuf;
clen = pbuf_clen(pcur);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(pcur);
}
/* Then, unchain the struct ip_reassdata from the list and free it. */
ip_reass_dequeue_datagram(ipr, prev);
- LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
- ip_reass_pbufcount -= pbufs_freed;
+ LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - pbufs_freed);
return pbufs_freed;
}
@@ -345,14 +345,21 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
{
struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
struct pbuf *q;
- u16_t offset, len;
+ u16_t offset, len, clen;
+ u8_t hlen;
struct ip_hdr *fraghdr;
int valid = 1;
/* Extract length and fragment offset from current fragment */
fraghdr = (struct ip_hdr*)new_p->payload;
- len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
- offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = lwip_ntohs(IPH_LEN(fraghdr));
+ hlen = IPH_HL_BYTES(fraghdr);
+ if (hlen > len) {
+ /* invalid datagram */
+ goto freepbuf;
+ }
+ len = (u16_t)(len - hlen);
+ offset = IPH_OFFSET_BYTES(fraghdr);
/* overwrite the fragment's ip header from the pbuf with our helper struct,
* and setup the embedded helper structure. */
@@ -362,7 +369,11 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
iprh = (struct ip_reass_helper*)new_p->payload;
iprh->next_pbuf = NULL;
iprh->start = offset;
- iprh->end = offset + len;
+ iprh->end = (u16_t)(offset + len);
+ if (iprh->end < offset) {
+ /* u16_t overflow, cannot handle this */
+ goto freepbuf;
+ }
/* Iterate through until we either get to the end of the list (append),
* or we find one with a larger offset (insert). */
@@ -482,7 +493,9 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
return IP_REASS_VALIDATE_PBUF_QUEUED; /* not yet valid! */
#if IP_REASS_CHECK_OVERLAP
freepbuf:
- ip_reass_pbufcount -= pbuf_clen(new_p);
+ clen = pbuf_clen(new_p);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen);
pbuf_free(new_p);
return IP_REASS_VALIDATE_PBUF_DROPPED;
#endif /* IP_REASS_CHECK_OVERLAP */
@@ -502,6 +515,7 @@ ip4_reass(struct pbuf *p)
struct ip_reassdata *ipr;
struct ip_reass_helper *iprh;
u16_t offset, len, clen;
+ u8_t hlen;
int valid;
int is_last;
@@ -516,8 +530,14 @@ ip4_reass(struct pbuf *p)
goto nullreturn;
}
- offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
- len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = IPH_OFFSET_BYTES(fraghdr);
+ len = lwip_ntohs(IPH_LEN(fraghdr));
+ hlen = IPH_HL_BYTES(fraghdr);
+ if (hlen > len) {
+ /* invalid datagram */
+ goto nullreturn;
+ }
+ len = (u16_t)(len - hlen);
/* Check if we are allowed to enqueue more datagrams. */
clen = pbuf_clen(p);
@@ -606,7 +626,7 @@ ip4_reass(struct pbuf *p)
struct ip_reassdata *ipr_prev;
/* the totally last fragment (flag more fragments = 0) was received at least
* once AND all fragments are received */
- ipr->datagram_len += IP_HLEN;
+ u16_t datagram_len = (u16_t)(ipr->datagram_len + IP_HLEN);
/* save the second pbuf before copying the header over the pointer */
r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
@@ -614,7 +634,7 @@ ip4_reass(struct pbuf *p)
/* copy the original ip header back to the first pbuf */
fraghdr = (struct ip_hdr*)(ipr->p->payload);
SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
- IPH_LEN_SET(fraghdr, lwip_htons(ipr->datagram_len));
+ IPH_LEN_SET(fraghdr, lwip_htons(datagram_len));
IPH_OFFSET_SET(fraghdr, 0);
IPH_CHKSUM_SET(fraghdr, 0);
/* @todo: do we need to set/calculate the correct checksum? */
@@ -651,7 +671,9 @@ ip4_reass(struct pbuf *p)
ip_reass_dequeue_datagram(ipr, ipr_prev);
/* and adjust the number of pbufs currently queued for reassembly. */
- ip_reass_pbufcount -= pbuf_clen(p);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen);
MIB2_STATS_INC(mib2.ipreasmoks);
@@ -725,7 +747,7 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
#endif
struct ip_hdr *original_iphdr;
struct ip_hdr *iphdr;
- const u16_t nfb = (netif->mtu - IP_HLEN) / 8;
+ const u16_t nfb = (u16_t)((netif->mtu - IP_HLEN) / 8);
u16_t left, fragsize;
u16_t ofo;
int last;
@@ -734,18 +756,19 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
original_iphdr = (struct ip_hdr *)p->payload;
iphdr = original_iphdr;
- LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL(iphdr) * 4 == IP_HLEN, return ERR_VAL);
+ LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL_BYTES(iphdr) == IP_HLEN, return ERR_VAL);
+ LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL);
/* Save original offset */
tmp = lwip_ntohs(IPH_OFFSET(iphdr));
ofo = tmp & IP_OFFMASK;
LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL);
- left = p->tot_len - IP_HLEN;
+ left = (u16_t)(p->tot_len - IP_HLEN);
while (left) {
/* Fill this fragment */
- fragsize = LWIP_MIN(left, nfb * 8);
+ fragsize = LWIP_MIN(left, (u16_t)(nfb * 8));
#if LWIP_NETIF_TX_SINGLE_PBUF
rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
@@ -781,7 +804,8 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
left_to_copy = fragsize;
while (left_to_copy) {
struct pbuf_custom_ref *pcr;
- u16_t plen = p->len - poff;
+ u16_t plen = (u16_t)(p->len - poff);
+ LWIP_ASSERT("p->len >= poff", p->len >= poff);
newpbuflen = LWIP_MIN(left_to_copy, plen);
/* Is this pbuf already empty? */
if (!newpbuflen) {
@@ -810,13 +834,13 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
* so that it is removed when pbuf_dechain is later called on rambuf.
*/
pbuf_cat(rambuf, newpbuf);
- left_to_copy -= newpbuflen;
+ left_to_copy = (u16_t)(left_to_copy - newpbuflen);
if (left_to_copy) {
poff = 0;
p = p->next;
}
}
- poff += newpbuflen;
+ poff = (u16_t)(poff + newpbuflen);
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
/* Correct header */
@@ -828,7 +852,7 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
tmp = tmp | IP_MF;
}
IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
- IPH_LEN_SET(iphdr, lwip_htons(fragsize + IP_HLEN));
+ IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize + IP_HLEN)));
IPH_CHKSUM_SET(iphdr, 0);
#if CHECKSUM_GEN_IP
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
@@ -850,8 +874,8 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
*/
pbuf_free(rambuf);
- left -= fragsize;
- ofo += nfb;
+ left = (u16_t)(left - fragsize);
+ ofo = (u16_t)(ofo + nfb);
}
MIB2_STATS_INC(mib2.ipfragoks);
return ERR_OK;
diff --git a/../lwip_nat/src/core/ipv4/ip4_nat.c b/../lwip_nat/src/core/ipv4/ip4_nat.c
new file mode 100644
index 00000000..c7ac9c8b
--- /dev/null
+++ b/../lwip_nat/src/core/ipv4/ip4_nat.c
@@ -0,0 +1,1151 @@
+/**
+ * NAT - NAT implementation for lwIP supporting TCP/UDP and ICMP.
+ * Copyright (c) 2009 Christian Walter, ?Embedded Solutions, Vienna 2009.
+ *
+ * Copyright (c) 2010 lwIP project ;-)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is not a part of the lwIP TCP/IP stack.
+ */
+
+/*
+ * File : ipv4_nat.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2015, RT-Thread Development Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2015-01-26 Hichard porting to RT-Thread
+ * 2015-01-27 Bernard code cleanup for lwIP in RT-Thread
+ * 2016-11-19 Ajay Bhargav Modified for lwIP 2.x/lwIP Head
+ * 2017-04-01 Ajay Bhargav Updated license information
+ */
+
+/*
+ * TODOS:
+ * - we should decide if we want to use static tables for NAT or a linked
+ * list.
+ * - we should allocate icmp ping id if multiple clients are sending
+ * ping requests.
+ * - maybe we could hash the identifiers for TCP, ICMP and UDP and use
+ * a single table structure. This would reduce the code amount although
+ * it will cost performance.
+ * - NAT code must check for broadcast addresses and NOT forward
+ * them.
+ *
+ * - netif_remove must notify NAT code when a NAT'ed interface is removed
+ * - allocate NAT entries from a new memp pool instead of the heap
+ * - let ttl be ticks, not seconds
+ *
+ * HOWTO USE:
+ *
+ * Shows how to create NAT between a PPP interface and an internal NIC.
+ * In this case the network 213.129.231.168/29 is nat'ed when packets
+ * are sent to the destination network 10.0.0.0/24 (untypical example -
+ * most users will have the other way around).
+ *
+ * Step 1) Execute when network interfaces are ready.
+ *
+ * new_nat_entry.out_if = (struct netif *)&PPP_IF;
+ * new_nat_entry.in_if = (struct netif *)&EMAC_if;
+ * IP4_ADDR(&new_nat_entry.source_net, 213, 129, 231, 168);
+ * IP4_ADDR(&new_nat_entry.source_netmask, 255, 255, 255, 248);
+ * IP4_ADDR(&new_nat_entry.dest_net, 10, 0, 0, 0);
+ * IP4_ADDR(&new_nat_entry.dest_netmask, 255, 0, 0, 0);
+ * ip4_nat_add(&new_nat_entry);
+ */
+
+#include "lwip/ip4_nat.h"
+#include "lwip/opt.h"
+
+#if IP_NAT
+
+#include "lwip/ip4.h"
+#include "lwip/inet.h"
+#include "lwip/netif.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/icmp.h"
+#include "lwip/prot/tcp.h"
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/sys.h"
+#include "lwip/timeouts.h"
+#include "netif/etharp.h"
+
+#include <limits.h>
+#include <string.h>
+
+/** Define this to enable debug output of this module */
+#ifndef NAT_DEBUG
+#define NAT_DEBUG LWIP_DBG_OFF
+#endif
+
+#define LWIP_NAT_TTL_INFINITE (INT_MAX)
+#define LWIP_NAT_DEFAULT_TTL_SECONDS (128)
+#define LWIP_NAT_FORWARD_HEADER_SIZE_MIN (sizeof(struct eth_hdr))
+
+#define LWIP_NAT_DEFAULT_STATE_TABLES_ICMP (4)
+#define LWIP_NAT_DEFAULT_STATE_TABLES_TCP (32)
+#define LWIP_NAT_DEFAULT_STATE_TABLES_UDP (32)
+
+#define LWIP_NAT_DEFAULT_TCP_SOURCE_PORT (40000)
+#define LWIP_NAT_DEFAULT_UDP_SOURCE_PORT (40000)
+
+#define IPNAT_ENTRY_RESET(x) do { \
+ (x)->ttl = 0; \
+} while(0)
+
+typedef struct ip4_nat_conf
+{
+ struct ip4_nat_conf *next;
+ ip4_nat_entry_t entry;
+} ip4_nat_conf_t;
+
+typedef struct ip4_nat_entry_common
+{
+ s32_t ttl; /* @todo: do we really need this to be signed?? */
+ ip4_addr_t source;
+ ip4_addr_t dest;
+ ip4_nat_conf_t *cfg;
+} ip4_nat_entry_common_t;
+
+typedef struct ip4_nat_entries_icmp
+{
+ ip4_nat_entry_common_t common;
+ u16_t id;
+ u16_t seqno;
+} ip4_nat_entries_icmp_t;
+
+typedef struct ip4_nat_entries_tcp
+{
+ ip4_nat_entry_common_t common;
+ u16_t nport;
+ u16_t sport;
+ u16_t dport;
+} ip4_nat_entries_tcp_t;
+
+typedef struct ip4_nat_entries_udp
+{
+ ip4_nat_entry_common_t common;
+ u16_t nport;
+ u16_t sport;
+ u16_t dport;
+} ip4_nat_entries_udp_t;
+
+typedef union u_nat_entry
+{
+ ip4_nat_entry_common_t *cmn;
+ ip4_nat_entries_tcp_t *tcp;
+ ip4_nat_entries_icmp_t *icmp;
+ ip4_nat_entries_udp_t *udp;
+} nat_entry_t;
+
+static ip4_nat_conf_t *ip4_nat_cfg = NULL;
+static ip4_nat_entries_icmp_t ip4_nat_icmp_table[LWIP_NAT_DEFAULT_STATE_TABLES_ICMP];
+static ip4_nat_entries_tcp_t ip4_nat_tcp_table[LWIP_NAT_DEFAULT_STATE_TABLES_TCP];
+static ip4_nat_entries_udp_t ip4_nat_udp_table[LWIP_NAT_DEFAULT_STATE_TABLES_UDP];
+
+/* ----------------------- Static functions (COMMON) --------------------*/
+static void ip4_nat_chksum_adjust(u8_t *chksum, const u8_t *optr, s16_t olen, const u8_t *nptr, s16_t nlen);
+static void ip4_nat_cmn_init(ip4_nat_conf_t *nat_config, const struct ip_hdr *iphdr,
+ ip4_nat_entry_common_t *nat_entry);
+static ip4_nat_conf_t *ip4_nat_shallnat(const struct ip_hdr *iphdr);
+static void ip4_nat_reset_state(ip4_nat_conf_t *cfg);
+
+/* ----------------------- Static functions (DEBUG) ---------------------*/
+#if defined(LWIP_DEBUG) && (NAT_DEBUG & LWIP_DBG_ON)
+static void ip4_nat_dbg_dump(const char *msg, const struct ip_hdr *iphdr);
+static void ip4_nat_dbg_dump_ip(const ip4_addr_t *addr);
+static void ip4_nat_dbg_dump_icmp_nat_entry(const char *msg, const ip4_nat_entries_icmp_t *nat_entry);
+static void ip4_nat_dbg_dump_tcp_nat_entry(const char *msg, const ip4_nat_entries_tcp_t *nat_entry);
+static void ip4_nat_dbg_dump_udp_nat_entry(const char *msg, const ip4_nat_entries_udp_t *nat_entry);
+static void ip4_nat_dbg_dump_init(ip4_nat_conf_t *ip4_nat_cfg_new);
+static void ip4_nat_dbg_dump_remove(ip4_nat_conf_t *cur);
+#else /* defined(LWIP_DEBUG) && (NAT_DEBUG & LWIP_DBG_ON) */
+#define ip4_nat_dbg_dump(msg, iphdr)
+#define ip4_nat_dbg_dump_ip(addr)
+#define ip4_nat_dbg_dump_icmp_nat_entry(msg, nat_entry)
+#define ip4_nat_dbg_dump_tcp_nat_entry(msg, nat_entry)
+#define ip4_nat_dbg_dump_udp_nat_entry(msg, nat_entry)
+#define ip4_nat_dbg_dump_init(ip4_nat_cfg_new)
+#define ip4_nat_dbg_dump_remove(cur)
+#endif /* defined(LWIP_DEBUG) && (NAT_DEBUG & LWIP_DBG_ON) */
+
+/* ----------------------- Static functions (TCP) -----------------------*/
+static ip4_nat_entries_tcp_t *ip4_nat_tcp_lookup_incoming(const struct ip_hdr *iphdr, const struct tcp_hdr *tcphdr);
+static ip4_nat_entries_tcp_t *ip4_nat_tcp_lookup_outgoing(ip4_nat_conf_t *nat_config,
+ const struct ip_hdr *iphdr, const struct tcp_hdr *tcphdr,
+ u8_t allocate);
+
+/* ----------------------- Static functions (UDP) -----------------------*/
+static ip4_nat_entries_udp_t *ip4_nat_udp_lookup_incoming(const struct ip_hdr *iphdr, const struct udp_hdr *udphdr);
+static ip4_nat_entries_udp_t *ip4_nat_udp_lookup_outgoing(ip4_nat_conf_t *nat_config,
+ const struct ip_hdr *iphdr, const struct udp_hdr *udphdr,
+ u8_t allocate);
+
+/**
+ * Timer callback function that calls ip4_nat_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+nat_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nat_timer()\n"));
+
+ ip4_nat_tmr();
+ sys_timeout(LWIP_NAT_TMR_INTERVAL_SEC * 1000, nat_timer, NULL);
+}
+
+/** Initialize this module */
+void
+ip4_nat_init(void)
+{
+ int i;
+ extern void lwip_ip_input_set_hook(int (*hook)(struct pbuf *p, struct netif *inp));
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* @todo: this can be omitted since we trust static variables
+ to be initialized to zero */
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) {
+ IPNAT_ENTRY_RESET(&ip4_nat_icmp_table[i].common);
+ }
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) {
+ IPNAT_ENTRY_RESET(&ip4_nat_tcp_table[i].common);
+ }
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) {
+ IPNAT_ENTRY_RESET(&ip4_nat_udp_table[i].common);
+ }
+
+ /* we must lock scheduler to protect following code */
+ SYS_ARCH_PROTECT(lev);
+
+ /* add a lwip timer for NAT */
+ sys_timeout(LWIP_NAT_TMR_INTERVAL_SEC * 1000, nat_timer, NULL);
+
+ /* un-protect */
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/** Allocate a new ip4_nat_conf_t item */
+static ip4_nat_conf_t*
+ip4_nat_alloc(void)
+{
+ ip4_nat_conf_t *ret = (ip4_nat_conf_t*)mem_malloc(sizeof(ip4_nat_conf_t));
+ return ret;
+}
+
+/** Free a removed ip4_nat_conf_t item */
+static void
+ip4_nat_free(ip4_nat_conf_t *item)
+{
+ LWIP_ASSERT("item != NULL", item != NULL);
+ mem_free(item);
+}
+
+/** Add a new NAT entry
+ *
+ * @param new_entry pointer to a structure used to initialize the entry
+ * @return ERR_OK if succeeded
+ */
+err_t
+ip4_nat_add(const ip4_nat_entry_t *new_entry)
+{
+ err_t err = ERR_VAL;
+ ip4_nat_conf_t *cur = ip4_nat_cfg;
+ ip4_nat_conf_t *ip4_nat_cfg_new = ip4_nat_alloc();
+ LWIP_ASSERT("new_entry != NULL", new_entry != NULL);
+
+ if (ip4_nat_cfg_new != NULL) {
+ SMEMCPY(&ip4_nat_cfg_new->entry, new_entry, sizeof(ip4_nat_entry_t));
+ ip4_nat_cfg_new->next = NULL;
+
+ ip4_nat_dbg_dump_init(ip4_nat_cfg_new);
+
+ if (ip4_nat_cfg == NULL) {
+ ip4_nat_cfg = ip4_nat_cfg_new;
+ } else {
+ /* @todo: do we really need to enqueue the new entry at the end?? */
+ while (cur->next != NULL) {
+ cur = cur->next;
+ }
+ cur->next = ip4_nat_cfg_new;
+ }
+ err = ERR_OK;
+ } else {
+ err = ERR_MEM;
+ }
+ return err;
+}
+
+/** Remove a NAT entry previously added by 'ip4_nat_add()'.
+ *
+ * @param remove_entry describes the entry to remove
+ */
+void
+ip4_nat_remove(const ip4_nat_entry_t *remove_entry)
+{
+ ip4_nat_conf_t *cur = ip4_nat_cfg;
+ ip4_nat_conf_t *next;
+ ip4_nat_conf_t *previous = NULL;
+
+ while (cur != NULL) {
+ /* Remove the NAT interfaces */
+ if ((cur->entry.source_net.addr == remove_entry->source_net.addr) &&
+ (cur->entry.source_netmask.addr == remove_entry->source_netmask.addr) &&
+ (cur->entry.dest_net.addr == remove_entry->dest_net.addr) &&
+ (cur->entry.dest_netmask.addr == remove_entry->dest_netmask.addr) &&
+ (cur->entry.out_if == remove_entry->out_if) &&
+ (cur->entry.in_if == remove_entry->in_if))
+ {
+ ip4_nat_dbg_dump_remove(cur);
+
+ ip4_nat_reset_state(cur);
+ next = cur->next;
+ if (cur == ip4_nat_cfg) {
+ ip4_nat_cfg = next;
+ } else {
+ LWIP_ASSERT("NULL != previous", NULL != previous);
+ previous->next = next;
+ }
+ /* free 'cur' or there will be a memory leak */
+ ip4_nat_free(cur);
+ return;
+ } else {
+ previous = cur;
+ cur = cur->next;
+ }
+ }
+}
+
+/** Reset a NAT configured entry to be reused.
+ * Effectively calls IPNAT_ENTRY_RESET() on 'cfg'.
+ *
+ * @param cfg NAT entry to reset
+ */
+static void
+ip4_nat_reset_state(ip4_nat_conf_t *cfg)
+{
+ int i;
+
+ /* @todo: optimize this!!!
+ why do we search for it anyway, if we have the pointer??? */
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) {
+ if(ip4_nat_icmp_table[i].common.cfg == cfg) {
+ IPNAT_ENTRY_RESET(&ip4_nat_icmp_table[i].common);
+ }
+ }
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) {
+ if(ip4_nat_tcp_table[i].common.cfg == cfg) {
+ IPNAT_ENTRY_RESET(&ip4_nat_tcp_table[i].common);
+ }
+ }
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) {
+ if(ip4_nat_udp_table[i].common.cfg == cfg) {
+ IPNAT_ENTRY_RESET(&ip4_nat_udp_table[i].common);
+ }
+ }
+}
+
+/** Check if this packet should be routed or should be translated
+ *
+ * @param iphdr the IP header to check
+ * @return - a NAT entry if the packet shall be translated,
+ * - NULL if the packet shall be routed normally
+ */
+static ip4_nat_conf_t *
+ip4_nat_shallnat(const struct ip_hdr *iphdr)
+{
+ ip4_nat_conf_t *nat_config = ip4_nat_cfg;
+
+ for (nat_config = ip4_nat_cfg; nat_config != NULL; nat_config = nat_config->next) {
+ if (ip4_addr_netcmp(&(iphdr->dest), &(nat_config->entry.dest_net),
+ &(nat_config->entry.dest_netmask)) ||
+ ip4_addr_netcmp(&(iphdr->src), &(nat_config->entry.source_net),
+ &(nat_config->entry.source_netmask))) {
+ break;
+ }
+ }
+
+ return nat_config;
+}
+
+/** Check if the IP header can be hidden and if the remaining packet
+ * is long enough. p->payload is reset to the IP header on return.
+ *
+ * @param p received packet, p->payload pointing to IP header
+ * @param min_size minimum p->tot_len after hiding IP header
+ * @return a pointer to the next header (after IP header),
+ * NULL if hiding IP header fails or the packet is too short
+ */
+static void*
+ip4_nat_check_header(struct pbuf *p, u16_t min_size)
+{
+ void *ret = NULL;
+ struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
+ s16_t iphdr_len = IPH_HL(iphdr) * 4;
+
+ if(!pbuf_header(p, -iphdr_len)) {
+ if(p->tot_len >= min_size) {
+ ret = p->payload;
+ }
+ /* Restore pbuf payload pointer from previous header check. */
+ pbuf_header(p, iphdr_len);
+ }
+ return ret;
+}
+
+/** Input processing: check if a received packet belongs to a NAT entry
+ * and if so, translated it and send it on.
+ *
+ * @param p received packet
+ * @return 1 if the packet has been consumed (it was a NAT packet),
+ * 0 if the packet has not been consumed (no NAT packet)
+ */
+u8_t
+ip4_nat_input(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
+ struct tcp_hdr *tcphdr;
+ struct udp_hdr *udphdr;
+ struct icmp_echo_hdr *icmphdr;
+ nat_entry_t nat_entry;
+ err_t err;
+ u8_t consumed = 0;
+ int i;
+ struct pbuf *q = NULL;
+
+ nat_entry.cmn = NULL;
+ ip4_nat_dbg_dump("ip4_nat_in: checking nat for", iphdr);
+
+ switch (IPH_PROTO(iphdr)) {
+ case IP_PROTO_TCP:
+ tcphdr = (struct tcp_hdr*)ip4_nat_check_header(p, sizeof(struct tcp_hdr));
+ if (tcphdr == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_input: short tcp packet (%" U16_F " bytes) discarded\n", p->tot_len));
+ } else {
+ nat_entry.tcp = ip4_nat_tcp_lookup_incoming(iphdr, tcphdr);
+ if (nat_entry.tcp != NULL) {
+ /* Refresh TCP entry */
+ nat_entry.tcp->common.ttl = LWIP_NAT_DEFAULT_TTL_SECONDS;
+ tcphdr->dest = nat_entry.tcp->sport;
+ /* Adjust TCP checksum for changed destination port */
+ ip4_nat_chksum_adjust((u8_t *)&(tcphdr->chksum),
+ (u8_t *)&(nat_entry.tcp->nport), 2, (u8_t *)&(tcphdr->dest), 2);
+ /* Adjust TCP checksum for changing dest IP address */
+ ip4_nat_chksum_adjust((u8_t *)&(tcphdr->chksum),
+ (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4,
+ (u8_t *)&(nat_entry.cmn->source.addr), 4);
+
+ consumed = 1;
+ }
+ }
+ break;
+
+ case IP_PROTO_UDP:
+ udphdr = (struct udp_hdr *)ip4_nat_check_header(p, sizeof(struct udp_hdr));
+ if (udphdr == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_input: short udp packet (%" U16_F " bytes) discarded\n",
+ p->tot_len));
+ } else {
+ nat_entry.udp = ip4_nat_udp_lookup_incoming(iphdr, udphdr);
+ if (nat_entry.udp != NULL) {
+ /* Refresh UDP entry */
+ nat_entry.udp->common.ttl = LWIP_NAT_DEFAULT_TTL_SECONDS;
+ udphdr->dest = nat_entry.udp->sport;
+ /* Adjust UDP checksum for changed destination port */
+ ip4_nat_chksum_adjust((u8_t *)&(udphdr->chksum),
+ (u8_t *)&(nat_entry.udp->nport), 2, (u8_t *)&(udphdr->dest), 2);
+ /* Adjust UDP checksum for changing dest IP address */
+ ip4_nat_chksum_adjust((u8_t *)&(udphdr->chksum),
+ (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4,
+ (u8_t *)&(nat_entry.cmn->source.addr), 4);
+
+ consumed = 1;
+ }
+ }
+ break;
+
+ case IP_PROTO_ICMP:
+ icmphdr = (struct icmp_echo_hdr *)ip4_nat_check_header(p, sizeof(struct icmp_echo_hdr));
+ if (icmphdr == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_out: short icmp echo reply packet (%" U16_F " bytes) discarded\n",
+ p->tot_len));
+ } else {
+ if (ICMP_ER == ICMPH_TYPE(icmphdr)) {
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) {
+ nat_entry.icmp = &ip4_nat_icmp_table[i];
+ if ((nat_entry.icmp->common.ttl) &&
+ (iphdr->src.addr == nat_entry.icmp->common.dest.addr) &&
+ (nat_entry.icmp->id == icmphdr->id) &&
+ (nat_entry.icmp->seqno == icmphdr->seqno)) {
+ ip4_nat_dbg_dump_icmp_nat_entry("found existing nat entry: ", nat_entry.icmp);
+ consumed = 1;
+ IPNAT_ENTRY_RESET(nat_entry.cmn);
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(consumed) {
+ /* packet consumed, send it out on in_if */
+ struct netif *in_if;
+
+ /* check if the pbuf has room for link headers */
+ if (pbuf_header(p, PBUF_LINK_HLEN)) {
+ /* pbuf has no room for link headers, allocate an extra pbuf */
+ q = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_input: no pbuf for outgoing header\n"));
+ /* @todo: stats? */
+ pbuf_free(p);
+ p = NULL;
+ return 1;
+ } else {
+ pbuf_cat(q, p);
+ }
+ } else {
+ /* restore p->payload to IP header */
+ if (pbuf_header(p, -PBUF_LINK_HLEN)) {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_input: restoring header failed\n"));
+ /* @todo: stats? */
+ pbuf_free(p);
+ p = NULL;
+ return 1;
+ }
+ else q = p;
+ }
+ /* if we come here, q is the pbuf to send (either points to p or to a chain) */
+ in_if = nat_entry.cmn->cfg->entry.in_if;
+ iphdr->dest.addr = nat_entry.cmn->source.addr;
+ ip4_nat_chksum_adjust((u8_t *) & IPH_CHKSUM(iphdr),
+ (u8_t *) & (nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4,
+ (u8_t *) & (iphdr->dest.addr), 4);
+
+ ip4_nat_dbg_dump("ip4_nat_input: packet back to source after nat: ", iphdr);
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_input: sending packet on interface ("));
+ ip4_nat_dbg_dump_ip(&(in_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (")\n"));
+
+ err = in_if->output(in_if, q, (ip4_addr_t *)&(iphdr->dest));
+ if(err != ERR_OK) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_input: failed to send rewritten packet. link layer returned %d\n",
+ err));
+ }
+ /* now that q (and/or p) is sent (or not), give up the reference to it
+ this frees the input pbuf (p) as we have consumed it. */
+ pbuf_free(q);
+ }
+ return consumed;
+}
+
+/** Check if one NAT entry timed out */
+static void
+ip4_nat_check_timeout(ip4_nat_entry_common_t *nat_entry)
+{
+ if(nat_entry->ttl > 0) {
+ if(nat_entry->ttl != LWIP_NAT_TTL_INFINITE) {
+ /* this is not a 'no-timeout' entry */
+ if(nat_entry->ttl > LWIP_NAT_TMR_INTERVAL_SEC) {
+ nat_entry->ttl -= LWIP_NAT_TMR_INTERVAL_SEC;
+ } else {
+ nat_entry->ttl = 0;
+ }
+ }
+ }
+}
+
+/** The NAT timer function, to be called at an interval of
+ * LWIP_NAT_TMR_INTERVAL_SEC seconds.
+ */
+void
+ip4_nat_tmr(void)
+{
+ int i;
+
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_tmr: removing old entries\n"));
+
+ for(i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) {
+ ip4_nat_check_timeout((ip4_nat_entry_common_t *) & ip4_nat_icmp_table[i]);
+ }
+ for(i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) {
+ ip4_nat_check_timeout((ip4_nat_entry_common_t *) & ip4_nat_tcp_table[i]);
+ }
+ for(i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) {
+ ip4_nat_check_timeout((ip4_nat_entry_common_t *) & ip4_nat_udp_table[i]);
+ }
+}
+
+/** Check if we want to perform NAT with this packet. If so, send it out on
+ * the correct interface.
+ *
+ * @param p the packet to test/send
+ * @return 1: the packet has been sent using NAT,
+ * 0: the packet did not belong to a NAT entry
+ */
+u8_t
+ip4_nat_out(struct pbuf *p)
+{
+ u8_t sent = 0;
+ err_t err;
+ struct ip_hdr *iphdr = p->payload;
+ struct icmp_echo_hdr *icmphdr;
+ struct tcp_hdr *tcphdr;
+ struct udp_hdr *udphdr;
+ ip4_nat_conf_t *nat_config;
+ nat_entry_t nat_entry;
+ int i;
+
+ nat_entry.cmn = NULL;
+
+ ip4_nat_dbg_dump("ip4_nat_out: checking nat for", iphdr);
+
+ /* Check if this packet should be routed or should be translated */
+ nat_config = ip4_nat_shallnat(iphdr);
+ if (nat_config != NULL ) {
+ if (nat_config->entry.out_if == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_out: no external interface for nat table entry\n"));
+ } else {
+ switch (IPH_PROTO(iphdr))
+ {
+ case IP_PROTO_TCP:
+ tcphdr = (struct tcp_hdr *)ip4_nat_check_header(p, sizeof(struct tcp_hdr));
+ if (tcphdr == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_out: short tcp packet (%" U16_F " bytes) discarded\n", p->tot_len));
+ } else {
+ nat_entry.tcp = ip4_nat_tcp_lookup_outgoing(nat_config, iphdr, tcphdr, 1);
+ if (nat_entry.tcp != NULL) {
+ /* Adjust TCP checksum for changing source port */
+ tcphdr->src = nat_entry.tcp->nport;
+ ip4_nat_chksum_adjust((u8_t *)&(tcphdr->chksum),
+ (u8_t *)&(nat_entry.tcp->sport), 2, (u8_t *)&(tcphdr->src), 2);
+ /* Adjust TCP checksum for changing source IP address */
+ ip4_nat_chksum_adjust((u8_t *)&(tcphdr->chksum),
+ (u8_t *)&(nat_entry.cmn->source.addr), 4,
+ (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4);
+ }
+ }
+ break;
+
+ case IP_PROTO_UDP:
+ udphdr = (struct udp_hdr *)ip4_nat_check_header(p, sizeof(struct udp_hdr));
+ if (udphdr == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_out: short udp packet (%" U16_F " bytes) discarded\n", p->tot_len));
+ } else {
+ nat_entry.udp = ip4_nat_udp_lookup_outgoing(nat_config, iphdr, udphdr, 1);
+ if (nat_entry.udp != NULL) {
+ /* Adjust UDP checksum for changing source port */
+ udphdr->src = nat_entry.udp->nport;
+ ip4_nat_chksum_adjust((u8_t *)&(udphdr->chksum),
+ (u8_t *)&(nat_entry.udp->sport), 2, (u8_t *) & (udphdr->src), 2);
+ /* Adjust UDP checksum for changing source IP address */
+ ip4_nat_chksum_adjust((u8_t *)&(udphdr->chksum),
+ (u8_t *)&(nat_entry.cmn->source.addr), 4,
+ (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4);
+ }
+ }
+ break;
+
+ case IP_PROTO_ICMP:
+ icmphdr = (struct icmp_echo_hdr *)ip4_nat_check_header(p, sizeof(struct icmp_echo_hdr));
+ if(icmphdr == NULL) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_out: short icmp echo packet (%" U16_F " bytes) discarded\n", p->tot_len));
+ } else {
+ if (ICMPH_TYPE(icmphdr) == ICMP_ECHO) {
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) {
+ if (!ip4_nat_icmp_table[i].common.ttl) {
+ nat_entry.icmp = &ip4_nat_icmp_table[i];
+ ip4_nat_cmn_init(nat_config, iphdr, nat_entry.cmn);
+ nat_entry.icmp->id = icmphdr->id;
+ nat_entry.icmp->seqno = icmphdr->seqno;
+ ip4_nat_dbg_dump_icmp_nat_entry(" ip4_nat_out: created new NAT entry ", nat_entry.icmp);
+ break;
+ }
+ }
+ if (NULL == nat_entry.icmp)
+ {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_out: no more NAT entries for ICMP available\n"));
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (nat_entry.cmn != NULL) {
+ struct netif *out_if = nat_entry.cmn->cfg->entry.out_if;
+ /* Exchange the IP source address with the address of the interface
+ * where the packet will be sent.
+ */
+ /* @todo: check nat_config->entry.out_if agains nat_entry.cmn->cfg->entry.out_if */
+ iphdr->src.addr = nat_config->entry.out_if->ip_addr.addr;
+ ip4_nat_chksum_adjust((u8_t *) & IPH_CHKSUM(iphdr),
+ (u8_t *) & (nat_entry.cmn->source.addr), 4, (u8_t *) & iphdr->src.addr, 4);
+
+ ip4_nat_dbg_dump("ip4_nat_out: rewritten packet", iphdr);
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_out: sending packet on interface ("));
+ ip4_nat_dbg_dump_ip(&(out_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (")\n"));
+
+ err = out_if->output(out_if, p, (ip4_addr_t *)&(iphdr->dest));
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(NAT_DEBUG,
+ ("ip4_nat_out: failed to send rewritten packet. link layer returned %d\n", err));
+ } else {
+ sent = 1;
+ }
+ }
+ }
+ }
+
+ return sent;
+}
+
+/** Initialize common parts of a NAT entry
+ *
+ * @param nat_config NAT config entry
+ * @param iphdr IP header from which to initialize the entry
+ * @param nat_entry entry to initialize
+ */
+static void
+ip4_nat_cmn_init(ip4_nat_conf_t *nat_config, const struct ip_hdr *iphdr, ip4_nat_entry_common_t *nat_entry)
+{
+ LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry);
+ LWIP_ASSERT("NULL != nat_config", NULL != nat_config);
+ LWIP_ASSERT("NULL != iphdr", NULL != iphdr);
+ nat_entry->cfg = nat_config;
+ ip4_addr_set(&nat_entry->dest, &iphdr->dest);
+ ip4_addr_set(&nat_entry->source, &iphdr->src);
+ nat_entry->ttl = LWIP_NAT_DEFAULT_TTL_SECONDS;
+}
+
+/**
+ * This function checks for incoming packets if we already have a NAT entry.
+ * If yes a pointer to the NAT entry is returned. Otherwise NULL.
+ *
+ * @param nat_config NAT configuration.
+ * @param iphdr The IP header.
+ * @param udphdr The UDP header.
+ * @return A pointer to an existing NAT entry or
+ * NULL if none is found.
+ */
+static ip4_nat_entries_udp_t *
+ip4_nat_udp_lookup_incoming(const struct ip_hdr *iphdr, const struct udp_hdr *udphdr)
+{
+ int i;
+ ip4_nat_entries_udp_t *nat_entry = NULL;
+
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) {
+ if (ip4_nat_udp_table[i].common.ttl) {
+ if ((iphdr->src.addr == ip4_nat_udp_table[i].common.dest.addr) &&
+ (udphdr->src == ip4_nat_udp_table[i].dport) &&
+ (udphdr->dest == ip4_nat_udp_table[i].nport)) {
+ nat_entry = &ip4_nat_udp_table[i];
+ ip4_nat_dbg_dump_udp_nat_entry("ip4_nat_udp_lookup_incoming: found existing nat entry: ",
+ nat_entry);
+ break;
+ }
+ }
+ }
+ return nat_entry;
+}
+
+/**
+ * This function checks if we already have a NAT entry for this UDP connection.
+ * If yes the a pointer to this NAT entry is returned.
+ *
+ * @param iphdr The IP header.
+ * @param udphdr The UDP header.
+ * @param allocate If no existing NAT entry is found and this flag is true
+ * a NAT entry is allocated.
+ */
+static ip4_nat_entries_udp_t *
+ip4_nat_udp_lookup_outgoing(ip4_nat_conf_t *nat_config, const struct ip_hdr *iphdr,
+ const struct udp_hdr *udphdr, u8_t allocate)
+{
+ int i;
+ nat_entry_t nat_entry;
+ int last_free = -1;
+
+ nat_entry.cmn = NULL;
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) {
+ if (ip4_nat_udp_table[i].common.ttl) {
+ if ((iphdr->src.addr == ip4_nat_udp_table[i].common.source.addr) &&
+ (iphdr->dest.addr == ip4_nat_udp_table[i].common.dest.addr) &&
+ (udphdr->src == ip4_nat_udp_table[i].sport) &&
+ (udphdr->dest == ip4_nat_udp_table[i].dport)) {
+ nat_entry.udp = &ip4_nat_udp_table[i];
+
+ ip4_nat_dbg_dump_udp_nat_entry("ip4_nat_udp_lookup_outgoing: found existing nat entry: ",
+ nat_entry.udp);
+ break;
+ }
+ } else {
+ last_free = i;
+ }
+ }
+ if (nat_entry.cmn == NULL) {
+ if (allocate) {
+ if (last_free != -1) {
+ nat_entry.udp = &ip4_nat_udp_table[last_free];
+ nat_entry.udp->nport = htons((u16_t) (LWIP_NAT_DEFAULT_UDP_SOURCE_PORT + i));
+ nat_entry.udp->sport = udphdr->src;
+ nat_entry.udp->dport = udphdr->dest;
+ ip4_nat_cmn_init(nat_config, iphdr, nat_entry.cmn);
+
+ ip4_nat_dbg_dump_udp_nat_entry("ip4_nat_udp_lookup_outgoing: created new nat entry: ",
+ nat_entry.udp);
+ } else {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_udp_lookup_outgoing: no more NAT entries available\n"));
+ }
+ }
+ }
+ return nat_entry.udp;
+}
+
+/**
+ * This function checks for incoming packets if we already have a NAT entry.
+ * If yes a pointer to the NAT entry is returned. Otherwise NULL.
+ *
+ * @param nat_config NAT configuration.
+ * @param iphdr The IP header.
+ * @param tcphdr The TCP header.
+ * @return A pointer to an existing NAT entry or NULL if none is found.
+ */
+static ip4_nat_entries_tcp_t *
+ip4_nat_tcp_lookup_incoming(const struct ip_hdr *iphdr, const struct tcp_hdr *tcphdr)
+{
+ int i;
+ ip4_nat_entries_tcp_t *nat_entry = NULL;
+
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) {
+ if (ip4_nat_tcp_table[i].common.ttl) {
+ if ((iphdr->src.addr == ip4_nat_tcp_table[i].common.dest.addr) &&
+ (tcphdr->src == ip4_nat_tcp_table[i].dport) &&
+ (tcphdr->dest == ip4_nat_tcp_table[i].nport)) {
+ nat_entry = &ip4_nat_tcp_table[i];
+
+ ip4_nat_dbg_dump_tcp_nat_entry("ip4_nat_tcp_lookup_incoming: found existing nat entry: ",
+ nat_entry);
+ break;
+ }
+ }
+ }
+ return nat_entry;
+}
+
+/**
+ * This function checks if we already have a NAT entry for this TCP connection.
+ * If yes the a pointer to this NAT entry is returned.
+ *
+ * @param iphdr The IP header.
+ * @param tcphdr The TCP header.
+ * @param allocate If no existing NAT entry is found and this flag is true
+ * a NAT entry is allocated.
+ */
+static ip4_nat_entries_tcp_t *
+ip4_nat_tcp_lookup_outgoing(ip4_nat_conf_t *nat_config, const struct ip_hdr *iphdr,
+ const struct tcp_hdr *tcphdr, u8_t allocate)
+{
+ int i;
+ nat_entry_t nat_entry;
+ int last_free = -1;
+
+ nat_entry.cmn = NULL;
+ for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) {
+ if (ip4_nat_tcp_table[i].common.ttl) {
+ if ((iphdr->src.addr == ip4_nat_tcp_table[i].common.source.addr) &&
+ (iphdr->dest.addr == ip4_nat_tcp_table[i].common.dest.addr) &&
+ (tcphdr->src == ip4_nat_tcp_table[i].sport) &&
+ (tcphdr->dest == ip4_nat_tcp_table[i].dport)) {
+ nat_entry.tcp = &ip4_nat_tcp_table[i];
+
+ ip4_nat_dbg_dump_tcp_nat_entry("ip4_nat_tcp_lookup_outgoing: found existing nat entry: ",
+ nat_entry.tcp);
+ break;
+ }
+ } else {
+ last_free = i;
+ }
+ }
+ if (nat_entry.cmn == NULL) {
+ if (allocate) {
+ if (last_free != -1) {
+ nat_entry.tcp = &ip4_nat_tcp_table[last_free];
+ nat_entry.tcp->nport = htons((u16_t) (LWIP_NAT_DEFAULT_TCP_SOURCE_PORT + i));
+ nat_entry.tcp->sport = tcphdr->src;
+ nat_entry.tcp->dport = tcphdr->dest;
+ ip4_nat_cmn_init(nat_config, iphdr, nat_entry.cmn);
+
+ ip4_nat_dbg_dump_tcp_nat_entry("ip4_nat_tcp_lookup_outgoing: created new nat entry: ",
+ nat_entry.tcp);
+ } else {
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_udp_lookup_outgoing: no more NAT entries available\n"));
+ }
+ }
+ }
+ return nat_entry.tcp;
+}
+
+/** Adjusts the checksum of a NAT'ed packet without having to completely recalculate it
+ * @todo: verify this works for little- and big-endian
+ *
+ * @param chksum points to the chksum in the packet
+ * @param optr points to the old data in the packet
+ * @param olen length of old data
+ * @param nptr points to the new data in the packet
+ * @param nlen length of new data
+ */
+static void
+ip4_nat_chksum_adjust(u8_t *chksum, const u8_t *optr, s16_t olen, const u8_t *nptr, s16_t nlen)
+{
+ s32_t x, oldval, newval;
+
+ LWIP_ASSERT("NULL != chksum", NULL != chksum);
+ LWIP_ASSERT("NULL != optr", NULL != optr);
+ LWIP_ASSERT("NULL != nptr", NULL != nptr);
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_chksum_adjust: chksum=%p, optr=%p, olen=%" U16_F ", nptr=%p, nlen=%" U16_F "\n",
+ chksum, optr, olen, nptr, nlen));
+ x = chksum[0] * 256 + chksum[1];
+ x = ~x & 0xFFFF;
+ while (olen) {
+ oldval = optr[0] * 256 + optr[1];
+ optr += 2;
+ x -= oldval & 0xffff;
+ if (x <= 0) {
+ x--;
+ x &= 0xffff;
+ }
+ olen -= 2;
+ }
+ while (nlen) {
+ newval = nptr[0] * 256 + nptr[1];
+ nptr += 2;
+ x += newval & 0xffff;
+ if (x & 0x10000) {
+ x++;
+ x &= 0xffff;
+ }
+ nlen -= 2;
+ }
+ x = ~x & 0xFFFF;
+ chksum[0] = x / 256;
+ chksum[1] = x & 0xff;
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_chksum_adjust: chksum = 0x%x\n", *((u16_t *) chksum)));
+}
+
+#if defined(LWIP_DEBUG) && (NAT_DEBUG & LWIP_DBG_ON)
+/**
+ * This function dumps an IP address
+ *
+ * @param addr IP address
+ */
+static void
+ip4_nat_dbg_dump_ip(const ip4_addr_t *addr)
+{
+ LWIP_ASSERT("NULL != addr", NULL != addr);
+ LWIP_DEBUGF(NAT_DEBUG, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F,
+ ip4_addr1(addr), ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr)));
+}
+
+/**
+ * This function dumps an IP header
+ *
+ * @param msg a message to print
+ * @param iphdr IP header
+ */
+static void
+ip4_nat_dbg_dump(const char *msg, const struct ip_hdr *iphdr)
+{
+ LWIP_ASSERT("NULL != msg", NULL != msg);
+ LWIP_ASSERT("NULL != iphdr", NULL != iphdr);
+ LWIP_DEBUGF(NAT_DEBUG, ("%s: IP: (", msg));
+ ip4_nat_dbg_dump_ip((ip4_addr_t *)&(iphdr->src));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip((ip4_addr_t *)&(iphdr->dest));
+ LWIP_DEBUGF(NAT_DEBUG, (" id=%" U16_F ", chksum=%" U16_F ")\n",
+ ntohs(IPH_ID(iphdr)), ntohs(IPH_CHKSUM(iphdr))));
+}
+
+/**
+ * This function dumps an ICMP echo reply/recho request nat entry.
+ *
+ * @param msg a message to print
+ * @param nat_entry the ICMP NAT entry to print
+ */
+static void
+ip4_nat_dbg_dump_icmp_nat_entry(const char *msg, const ip4_nat_entries_icmp_t *nat_entry)
+{
+ LWIP_ASSERT("NULL != msg", NULL != msg);
+ LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry);
+ LWIP_ASSERT("NULL != nat_entry->common.cfg", NULL != nat_entry->common.cfg);
+ LWIP_ASSERT("NULL != nat_entry->common.cfg->entry.out_if",
+ NULL != nat_entry->common.cfg->entry.out_if);
+ LWIP_DEBUGF(NAT_DEBUG, ("%s", msg));
+ LWIP_DEBUGF(NAT_DEBUG, ("ICMP : ("));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.source));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.dest));
+ LWIP_DEBUGF(NAT_DEBUG, (" id=%" U16_F, ntohs(nat_entry->id)));
+ LWIP_DEBUGF(NAT_DEBUG, (", seq=%" U16_F, ntohs(nat_entry->seqno)));
+ LWIP_DEBUGF(NAT_DEBUG, (") mapped at ("));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.cfg->entry.out_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.dest));
+ LWIP_DEBUGF(NAT_DEBUG, (" id=%" U16_F, ntohs(nat_entry->id)));
+ LWIP_DEBUGF(NAT_DEBUG, (", seq=%" U16_F, ntohs(nat_entry->seqno)));
+ LWIP_DEBUGF(NAT_DEBUG, (")\n"));
+}
+
+/**
+ * This function dumps an TCP nat entry.
+ *
+ * @param msg a message to print
+ * @param nat_entry the TCP NAT entry to print
+ */
+static void
+ip4_nat_dbg_dump_tcp_nat_entry(const char *msg, const ip4_nat_entries_tcp_t *nat_entry)
+{
+ LWIP_ASSERT("NULL != msg", NULL != msg);
+ LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry);
+ LWIP_ASSERT("NULL != nat_entry->common.cfg", NULL != nat_entry->common.cfg);
+ LWIP_ASSERT("NULL != nat_entry->common.cfg->entry.out_if",
+ NULL != nat_entry->common.cfg->entry.out_if);
+ LWIP_DEBUGF(NAT_DEBUG, ("%s", msg));
+ LWIP_DEBUGF(NAT_DEBUG, ("TCP : ("));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.source));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->sport)));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.dest));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport)));
+ LWIP_DEBUGF(NAT_DEBUG, (") mapped at ("));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.cfg->entry.out_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->nport)));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.dest));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport)));
+ LWIP_DEBUGF(NAT_DEBUG, (")\n"));
+}
+
+/**
+ * This function dumps a UDP NAT entry.
+ *
+ * @param msg a message to print
+ * @param nat_entry the UDP NAT entry to print
+ */
+static void
+ip4_nat_dbg_dump_udp_nat_entry(const char *msg, const ip4_nat_entries_udp_t *nat_entry)
+{
+ LWIP_ASSERT("NULL != msg", NULL != msg);
+ LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry);
+ LWIP_ASSERT("NULL != nat_entry->common.cfg", NULL != nat_entry->common.cfg);
+ LWIP_ASSERT("NULL != nat_entry->common.cfg->entry.out_if",
+ NULL != nat_entry->common.cfg->entry.out_if);
+ LWIP_DEBUGF(NAT_DEBUG, ("%s", msg));
+ LWIP_DEBUGF(NAT_DEBUG, ("UDP : ("));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.source));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->sport)));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.dest));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport)));
+ LWIP_DEBUGF(NAT_DEBUG, (") mapped at ("));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.cfg->entry.out_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->nport)));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(nat_entry->common.dest));
+ LWIP_DEBUGF(NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport)));
+ LWIP_DEBUGF(NAT_DEBUG, (")\n"));
+}
+
+/** Prints some info when creating a new NAT entry */
+static void
+ip4_nat_dbg_dump_init(ip4_nat_conf_t *ip4_nat_cfg_new)
+{
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_init: added new NAT interface\n"));
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_init: "));
+ ip4_nat_dbg_dump_ip(&(ip4_nat_cfg_new->entry.source_net));
+ LWIP_DEBUGF(NAT_DEBUG, ("/"));
+ ip4_nat_dbg_dump_ip(&(ip4_nat_cfg_new->entry.source_netmask));
+ LWIP_DEBUGF(NAT_DEBUG, ("@"));
+ ip4_nat_dbg_dump_ip(&(ip4_nat_cfg_new->entry.in_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(ip4_nat_cfg_new->entry.dest_net));
+ LWIP_DEBUGF(NAT_DEBUG, ("/"));
+ ip4_nat_dbg_dump_ip(&(ip4_nat_cfg_new->entry.dest_netmask));
+ LWIP_DEBUGF(NAT_DEBUG, ("@"));
+ ip4_nat_dbg_dump_ip(&(ip4_nat_cfg_new->entry.out_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, ("\n"));
+}
+
+/** Prints some info when removing a NAT entry */
+static void
+ip4_nat_dbg_dump_remove(ip4_nat_conf_t *cur)
+{
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_remove: removing existing NAT interface\n"));
+ LWIP_DEBUGF(NAT_DEBUG, ("ip4_nat_remove: "));
+ ip4_nat_dbg_dump_ip(&(cur->entry.source_net));
+ LWIP_DEBUGF(NAT_DEBUG, ("/"));
+ ip4_nat_dbg_dump_ip(&(cur->entry.source_netmask));
+ LWIP_DEBUGF(NAT_DEBUG, ("@"));
+ ip4_nat_dbg_dump_ip(&(cur->entry.in_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, (" --> "));
+ ip4_nat_dbg_dump_ip(&(cur->entry.dest_net));
+ LWIP_DEBUGF(NAT_DEBUG, ("/"));
+ ip4_nat_dbg_dump_ip(&(cur->entry.dest_netmask));
+ LWIP_DEBUGF(NAT_DEBUG, ("@"));
+ ip4_nat_dbg_dump_ip(&(cur->entry.out_if->ip_addr));
+ LWIP_DEBUGF(NAT_DEBUG, ("\n"));
+}
+#endif /* defined(LWIP_DEBUG) && (NAT_DEBUG & LWIP_DBG_ON) */
+
+#endif /* IP_NAT */
diff --git a/src/core/ipv6/ethip6.c b/../lwip_nat/src/core/ipv6/ethip6.c
index 8f9a91b5..904005c4 100644
--- a/src/core/ipv6/ethip6.c
+++ b/../lwip_nat/src/core/ipv6/ethip6.c
@@ -82,6 +82,9 @@ ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
const u8_t *hwaddr;
err_t result;
+ /* The destination IP address must be properly zoned from here on down. */
+ IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
+
/* multicast destination IP address? */
if (ip6_addr_ismulticast(ip6addr)) {
/* Hash IP multicast address to MAC address.*/
diff --git a/src/core/ipv6/icmp6.c b/../lwip_nat/src/core/ipv6/icmp6.c
index 323b69a2..2302c8e6 100644
--- a/src/core/ipv6/icmp6.c
+++ b/../lwip_nat/src/core/ipv6/icmp6.c
@@ -66,6 +66,10 @@
/* Forward declarations */
static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
+static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data,
+ u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
+static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data,
+ u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif);
/**
@@ -118,7 +122,6 @@ icmp6_input(struct pbuf *p, struct netif *inp)
case ICMP6_TYPE_PTB: /* Packet too big */
nd6_input(p, inp);
return;
- break;
case ICMP6_TYPE_RS:
#if LWIP_IPV6_FORWARD
/* @todo implement router functionality */
@@ -130,7 +133,6 @@ icmp6_input(struct pbuf *p, struct netif *inp)
case ICMP6_TYPE_MLD:
mld6_input(p, inp);
return;
- break;
#endif
case ICMP6_TYPE_EREQ:
#if !LWIP_MULTICAST_PING
@@ -209,6 +211,9 @@ icmp6_input(struct pbuf *p, struct netif *inp)
/**
* Send an icmpv6 'destination unreachable' packet.
*
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
* @param p the input packet for which the 'unreachable' should be sent,
* p->payload pointing to the IPv6 header
* @param c ICMPv6 code for the unreachable type
@@ -222,6 +227,9 @@ icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
/**
* Send an icmpv6 'packet too big' packet.
*
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
* @param p the input packet for which the 'packet too big' should be sent,
* p->payload pointing to the IPv6 header
* @param mtu the maximum mtu that we can accept
@@ -235,7 +243,10 @@ icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
/**
* Send an icmpv6 'time exceeded' packet.
*
- * @param p the input packet for which the 'unreachable' should be sent,
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
* p->payload pointing to the IPv6 header
* @param c ICMPv6 code for the time exceeded type
*/
@@ -245,9 +256,34 @@ icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
}
+/**
+ * Send an icmpv6 'time exceeded' packet, with explicit source and destination
+ * addresses.
+ *
+ * This function may be used to send a response sometime after receiving the
+ * packet for which this response is meant. The provided source and destination
+ * addresses are used primarily to retain their zone information.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ * @param src_addr source address of the original packet, with zone information
+ * @param dest_addr destination address of the original packet, with zone
+ * information
+ */
+void
+icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
+ const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
+{
+ icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr);
+}
+
/**
* Send an icmpv6 'parameter problem' packet.
*
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
* @param p the input packet for which the 'param problem' should be sent,
* p->payload pointing to the IP header
* @param c ICMPv6 code for the param problem type
@@ -261,6 +297,7 @@ icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
/**
* Send an ICMPv6 packet in response to an incoming packet.
+ * The packet is sent *to* ip_current_src_addr() on ip_current_netif().
*
* @param p the input packet for which the response should be sent,
* p->payload pointing to the IPv6 header
@@ -270,14 +307,86 @@ icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
*/
static void
icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
+{
+ const struct ip6_addr *reply_src, *reply_dest;
+ struct netif *netif = ip_current_netif();
+
+ LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL);
+ reply_dest = ip6_current_src_addr();
+
+ /* Select an address to use as source. */
+ reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
+ if (reply_src == NULL) {
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ *
+ * Call this function if the packet is NOT sent as a direct response to an
+ * incoming packet, but rather sometime later (e.g. for a fragment reassembly
+ * timeout). The caller must provide the zoned source and destination addresses
+ * from the original packet with the src_addr and dest_addr parameters. The
+ * reason for this approach is that while the addresses themselves are part of
+ * the original packet, their zone information is not, thus possibly resulting
+ * in a link-local response being sent over the wrong link.
+ *
+ * @param p the input packet for which the response should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ * @param src_addr original source address
+ * @param dest_addr original destination address
+ */
+static void
+icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type,
+ const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
+{
+ const struct ip6_addr *reply_src, *reply_dest;
+ struct netif *netif;
+
+ /* Get the destination address and netif for this ICMP message. */
+ LWIP_ASSERT("must provide both source and destination", src_addr != NULL);
+ LWIP_ASSERT("must provide both source and destination", dest_addr != NULL);
+
+ /* Special case, as ip6_current_xxx is either NULL, or points
+ to a different packet than the one that expired. */
+ IP6_ADDR_ZONECHECK(src_addr);
+ IP6_ADDR_ZONECHECK(dest_addr);
+ /* Swap source and destination for the reply. */
+ reply_dest = src_addr;
+ reply_src = dest_addr;
+ netif = ip6_route(reply_src, reply_dest);
+ if (netif == NULL) {
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
+ reply_dest, netif);
+}
+
+/**
+ * Send an ICMPv6 packet (with srd/dst address and netif given).
+ *
+ * @param p the input packet for which the response should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ * @param reply_src source address of the packet to send
+ * @param reply_dest destination address of the packet to send
+ * @param netif netif to send the packet
+ */
+static void
+icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
+ const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
{
struct pbuf *q;
struct icmp6_hdr *icmp6hdr;
- const ip6_addr_t *reply_src;
- ip6_addr_t *reply_dest;
- ip6_addr_t reply_src_local, reply_dest_local;
- struct ip6_hdr *ip6hdr;
- struct netif *netif;
/* ICMPv6 header + IPv6 header + data */
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
@@ -299,40 +408,6 @@ icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
IP6_HLEN + LWIP_ICMP6_DATASIZE);
- /* Get the destination address and netif for this ICMP message. */
- if ((ip_current_netif() == NULL) ||
- ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
- /* Special case, as ip6_current_xxx is either NULL, or points
- * to a different packet than the one that expired.
- * We must use the addresses that are stored in the expired packet. */
- ip6hdr = (struct ip6_hdr *)p->payload;
- /* copy from packed address to aligned address */
- ip6_addr_copy(reply_dest_local, ip6hdr->src);
- ip6_addr_copy(reply_src_local, ip6hdr->dest);
- reply_dest = &reply_dest_local;
- reply_src = &reply_src_local;
- netif = ip6_route(reply_src, reply_dest);
- if (netif == NULL) {
- /* drop */
- pbuf_free(q);
- ICMP6_STATS_INC(icmp6.rterr);
- return;
- }
- }
- else {
- netif = ip_current_netif();
- reply_dest = ip6_current_src_addr();
-
- /* Select an address to use as source. */
- reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
- if (reply_src == NULL) {
- /* drop */
- pbuf_free(q);
- ICMP6_STATS_INC(icmp6.rterr);
- return;
- }
- }
-
/* calculate checksum */
icmp6hdr->chksum = 0;
#if CHECKSUM_GEN_ICMP6
diff --git a/src/core/ipv6/ip6.c b/../lwip_nat/src/core/ipv6/ip6.c
index e975771e..6ee63da1 100644
--- a/src/core/ipv6/ip6.c
+++ b/../lwip_nat/src/core/ipv6/ip6.c
@@ -68,13 +68,15 @@
* Finds the appropriate network interface for a given IPv6 address. It tries to select
* a netif following a sequence of heuristics:
* 1) if there is only 1 netif, return it
- * 2) if the destination is a link-local address, try to match the src address to a netif.
- * this is a tricky case because with multiple netifs, link-local addresses only have
- * meaning within a particular subnet/link.
- * 3) tries to match the destination subnet to a configured address
- * 4) tries to find a router
- * 5) tries to match the source address to the netif
- * 6) returns the default netif, if configured
+ * 2) if the destination is a zoned address, match its zone to a netif
+ * 3) if the either the source or destination address is a scoped address,
+ * match the source address's zone (if set) or address (if not) to a netif
+ * 4) tries to match the destination subnet to a configured address
+ * 5) tries to find a router-announced route
+ * 6) tries to match the (unscoped) source address to the netif
+ * 7) returns the default netif, if configured
+ *
+ * Note that each of the two given addresses may or may not be properly zoned.
*
* @param src the source IPv6 address, if known
* @param dest the destination IPv6 address for which to find the route
@@ -83,50 +85,99 @@
struct netif *
ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
{
+#if LWIP_SINGLE_NETIF
+ LWIP_UNUSED_ARG(src);
+ LWIP_UNUSED_ARG(dest);
+#else /* LWIP_SINGLE_NETIF */
struct netif *netif;
s8_t i;
/* If single netif configuration, fast return. */
if ((netif_list != NULL) && (netif_list->next == NULL)) {
- if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list)) {
+ if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list) ||
+ (ip6_addr_has_zone(dest) && !ip6_addr_test_zone(dest, netif_list))) {
return NULL;
}
return netif_list;
}
- /* Special processing for link-local addresses. */
- if (ip6_addr_islinklocal(dest)) {
- if (ip6_addr_isany(src)) {
- /* Use default netif, if Up. */
- if (netif_default == NULL || !netif_is_up(netif_default) ||
- !netif_is_link_up(netif_default)) {
- return NULL;
- }
- return netif_default;
- }
-
- /* Try to find the netif for the source address, checking that link is up. */
+#if LWIP_IPV6_SCOPES
+ /* Special processing for zoned destination addresses. This includes link-
+ * local unicast addresses and interface/link-local multicast addresses. Use
+ * the zone to find a matching netif. If the address is not zoned, then there
+ * is technically no "wrong" netif to choose, and we leave routing to other
+ * rules; in most cases this should be the scoped-source rule below. */
+ if (ip6_addr_has_zone(dest)) {
+ IP6_ADDR_ZONECHECK(dest);
+ /* Find a netif based on the zone. For custom mappings, one zone may map
+ * to multiple netifs, so find one that can actually send a packet. */
for (netif = netif_list; netif != NULL; netif = netif->next) {
- if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
- continue;
+ if (ip6_addr_test_zone(dest, netif) &&
+ netif_is_up(netif) && netif_is_link_up(netif)) {
+ return netif;
}
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+ }
+ /* No matching netif found. Do no try to route to a different netif,
+ * as that would be a zone violation, resulting in any packets sent to
+ * that netif being dropped on output. */
+ return NULL;
+ }
+#endif /* LWIP_IPV6_SCOPES */
+
+ /* Special processing for scoped source and destination addresses. If we get
+ * here, the destination address does not have a zone, so either way we need
+ * to look at the source address, which may or may not have a zone. If it
+ * does, the zone is restrictive: there is (typically) only one matching
+ * netif for it, and we should avoid routing to any other netif as that would
+ * result in guaranteed zone violations. For scoped source addresses that do
+ * not have a zone, use (only) a netif that has that source address locally
+ * assigned. This case also applies to the loopback source address, which has
+ * an implied link-local scope. If only the destination address is scoped
+ * (but, again, not zoned), we still want to use only the source address to
+ * determine its zone because that's most likely what the user/application
+ * wants, regardless of whether the source address is scoped. Finally, some
+ * of this story also applies if scoping is disabled altogether. */
+#if LWIP_IPV6_SCOPES
+ if (ip6_addr_has_scope(dest, IP6_UNKNOWN) ||
+ ip6_addr_has_scope(src, IP6_UNICAST) ||
+#else /* LWIP_IPV6_SCOPES */
+ if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_iflocal(dest) ||
+ ip6_addr_ismulticast_linklocal(dest) || ip6_addr_islinklocal(src) ||
+#endif /* LWIP_IPV6_SCOPES */
+ ip6_addr_isloopback(src)) {
+#if LWIP_IPV6_SCOPES
+ if (ip6_addr_has_zone(src)) {
+ /* Find a netif matching the source zone (relatively cheap). */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_is_up(netif) && netif_is_link_up(netif) &&
+ ip6_addr_test_zone(src, netif)) {
return netif;
}
}
+ } else
+#endif /* LWIP_IPV6_SCOPES */
+ {
+ /* Find a netif matching the source address (relatively expensive). */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+ continue;
+ }
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp_zoneless(src, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
}
-
- /* netif not found, use default netif, if up */
- if (netif_default == NULL || !netif_is_up(netif_default) ||
- !netif_is_link_up(netif_default)) {
- return NULL;
- }
- return netif_default;
+ /* Again, do not use any other netif in this case, as that could result in
+ * zone boundary violations. */
+ return NULL;
}
- /* we come here for non-link-local addresses */
+ /* We come here only if neither source nor destination is scoped. */
+ IP6_ADDR_ZONECHECK(src);
+
#ifdef LWIP_HOOK_IP6_ROUTE
netif = LWIP_HOOK_IP6_ROUTE(src, dest);
if (netif != NULL) {
@@ -134,26 +185,34 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
}
#endif
- /* See if the destination subnet matches a configured address. */
+ /* See if the destination subnet matches a configured address. In accordance
+ * with RFC 5942, dynamically configured addresses do not have an implied
+ * local subnet, and thus should be considered /128 assignments. However, as
+ * such, the destination address may still match a local address, and so we
+ * still need to check for exact matches here. By (lwIP) policy, statically
+ * configured addresses do always have an implied local /64 subnet. */
for (netif = netif_list; netif != NULL; netif = netif->next) {
if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
continue;
}
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i)) &&
+ (netif_ip6_addr_isstatic(netif, i) ||
+ ip6_addr_nethostcmp(dest, netif_ip6_addr(netif, i)))) {
return netif;
}
}
}
- /* Get the netif for a suitable router. */
+ /* Get the netif for a suitable router-announced route. */
netif = nd6_find_route(dest);
- if ((netif != NULL) && netif_is_up(netif) && netif_is_link_up(netif)) {
+ if (netif != NULL) {
return netif;
}
- /* try with the netif that matches the source address. */
+ /* Try with the netif that matches the source address. Given the earlier rule
+ * for scoped source addresses, this applies to unscoped addresses only. */
if (!ip6_addr_isany(src)) {
for (netif = netif_list; netif != NULL; netif = netif->next) {
if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
@@ -184,6 +243,7 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
return NULL;
}
#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+#endif /* !LWIP_SINGLE_NETIF */
/* no matching netif found, use default netif, if up */
if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
@@ -194,85 +254,100 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
/**
* @ingroup ip6
- * Select the best IPv6 source address for a given destination
- * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior
- * is assumed.
+ * Select the best IPv6 source address for a given destination IPv6 address.
+ *
+ * This implementation follows RFC 6724 Sec. 5 to the following extent:
+ * - Rules 1, 2, 3: fully implemented
+ * - Rules 4, 5, 5.5: not applicable
+ * - Rule 6: not implemented
+ * - Rule 7: not applicable
+ * - Rule 8: limited to "prefer /64 subnet match over non-match"
+ *
+ * For Rule 2, we deliberately deviate from RFC 6724 Sec. 3.1 by considering
+ * ULAs to be of smaller scope than global addresses, to avoid that a preferred
+ * ULA is picked over a deprecated global address when given a global address
+ * as destination, as that would likely result in broken two-way communication.
+ *
+ * As long as temporary addresses are not supported (as used in Rule 7), a
+ * proper implementation of Rule 8 would obviate the need to implement Rule 6.
*
* @param netif the netif on which to send a packet
- * @param dest the destination we are trying to reach
+ * @param dest the destination we are trying to reach (possibly not properly
+ * zoned)
* @return the most suitable source address to use, or NULL if no suitable
* source address is found
*/
const ip_addr_t *
ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest)
{
- const ip_addr_t *src = NULL;
- u8_t i;
-
- /* If dest is link-local, choose a link-local source. */
- if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_islinklocal(netif_ip6_addr(netif, i))) {
- return netif_ip_addr6(netif, i);
- }
- }
+ const ip_addr_t *best_addr;
+ const ip6_addr_t *cand_addr;
+ s8_t dest_scope, cand_scope;
+ s8_t best_scope = IP6_MULTICAST_SCOPE_RESERVED;
+ u8_t i, cand_pref, cand_bits;
+ u8_t best_pref = 0;
+ u8_t best_bits = 0;
+
+ /* Start by determining the scope of the given destination address. These
+ * tests are hopefully (roughly) in order of likeliness to match. */
+ if (ip6_addr_isglobal(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+ } else if (ip6_addr_islinklocal(dest) || ip6_addr_isloopback(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_LINK_LOCAL;
+ } else if (ip6_addr_isuniquelocal(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;
+ } else if (ip6_addr_ismulticast(dest)) {
+ dest_scope = ip6_addr_multicast_scope(dest);
+ } else if (ip6_addr_issitelocal(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_SITE_LOCAL;
+ } else {
+ /* no match, consider scope global */
+ dest_scope = IP6_MULTICAST_SCOPE_GLOBAL;
}
- /* Choose a site-local with matching prefix. */
- if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_issitelocal(netif_ip6_addr(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
- return netif_ip_addr6(netif, i);
- }
- }
- }
+ best_addr = NULL;
- /* Choose a unique-local with matching prefix. */
- if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
- return netif_ip_addr6(netif, i);
- }
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ /* Consider only valid (= preferred and deprecated) addresses. */
+ if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ continue;
}
- }
-
- /* Choose a global with best matching prefix. */
- if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_isglobal(netif_ip6_addr(netif, i))) {
- if (src == NULL) {
- src = netif_ip_addr6(netif, i);
- }
- else {
- /* Replace src only if we find a prefix match. */
- /* @todo find longest matching prefix. */
- if ((!(ip6_addr_netcmp(ip_2_ip6(src), dest))) &&
- ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) {
- src = netif_ip_addr6(netif, i);
- }
- }
- }
+ /* Determine the scope of this candidate address. Same ordering idea. */
+ cand_addr = netif_ip6_addr(netif, i);
+ if (ip6_addr_isglobal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+ } else if (ip6_addr_islinklocal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_LINK_LOCAL;
+ } else if (ip6_addr_isuniquelocal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;
+ } else if (ip6_addr_issitelocal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_SITE_LOCAL;
+ } else {
+ /* no match, treat as low-priority global scope */
+ cand_scope = IP6_MULTICAST_SCOPE_RESERVEDF;
}
- if (src != NULL) {
- return src;
+ cand_pref = ip6_addr_ispreferred(netif_ip6_addr_state(netif, i));
+ /* @todo compute the actual common bits, for longest matching prefix. */
+ /* We cannot count on the destination address having a proper zone
+ * assignment, so do not compare zones in this case. */
+ cand_bits = ip6_addr_netcmp_zoneless(cand_addr, dest); /* just 1 or 0 for now */
+ if (cand_bits && ip6_addr_nethostcmp(cand_addr, dest)) {
+ return netif_ip_addr6(netif, i); /* Rule 1 */
}
- }
-
- /* Last resort: see if arbitrary prefix matches. */
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
- return netif_ip_addr6(netif, i);
+ if ((best_addr == NULL) || /* no alternative yet */
+ ((cand_scope < best_scope) && (cand_scope >= dest_scope)) ||
+ ((cand_scope > best_scope) && (best_scope < dest_scope)) || /* Rule 2 */
+ ((cand_scope == best_scope) && ((cand_pref > best_pref) || /* Rule 3 */
+ ((cand_pref == best_pref) && (cand_bits > best_bits))))) { /* Rule 8 */
+ /* We found a new "winning" candidate. */
+ best_addr = netif_ip_addr6(netif, i);
+ best_scope = cand_scope;
+ best_pref = cand_pref;
+ best_bits = cand_bits;
}
}
- return NULL;
+ return best_addr; /* may be NULL */
}
#if LWIP_IPV6_FORWARD
@@ -321,6 +396,20 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
IP6_STATS_INC(ip6.drop);
return;
}
+#if LWIP_IPV6_SCOPES
+ /* Do not forward packets with a zoned (e.g., link-local) source address
+ * outside of their zone. We determined the zone a bit earlier, so we know
+ * that the address is properly zoned here, so we can safely use has_zone.
+ * Also skip packets with a loopback source address (link-local implied). */
+ if ((ip6_addr_has_zone(ip6_current_src_addr()) &&
+ !ip6_addr_test_zone(ip6_current_src_addr(), netif)) ||
+ ip6_addr_isloopback(ip6_current_src_addr())) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding packet beyond its source address zone.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+#endif /* LWIP_IPV6_SCOPES */
/* Do not forward packets onto the same network interface on which
* they arrived. */
if (netif == inp) {
@@ -373,6 +462,33 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
}
#endif /* LWIP_IPV6_FORWARD */
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip6_input_accept(struct netif *netif)
+{
+ /* interface is up? */
+ if (netif_is_up(netif)) {
+ u8_t i;
+ /* unicast to this interface address? address configured? */
+ /* If custom scopes are used, the destination zone will be tested as
+ * part of the local-address comparison, but we need to test the source
+ * scope as well (e.g., is this interface on the same link?). */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))
+#if IPV6_CUSTOM_SCOPES
+ && (!ip6_addr_has_zone(ip6_current_src_addr()) ||
+ ip6_addr_test_zone(ip6_current_src_addr(), netif))
+#endif /* IPV6_CUSTOM_SCOPES */
+ ) {
+ /* accept on this netif */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
/**
* This function is called by the network interface device driver when
* an IPv6 packet is received. The function does the basic checks of the
@@ -393,8 +509,7 @@ ip6_input(struct pbuf *p, struct netif *inp)
struct ip6_hdr *ip6hdr;
struct netif *netif;
u8_t nexth;
- u16_t hlen; /* the current header length */
- u8_t i;
+ u16_t hlen, hlen_tot; /* the current header length */
#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
@todo
int check_ip_src=1;
@@ -421,7 +536,7 @@ ip6_input(struct pbuf *p, struct netif *inp)
#endif
/* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
- if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) {
+ if ((IP6_HLEN > p->len) || (IP6H_PLEN(ip6hdr) > (p->tot_len - IP6_HLEN))) {
if (IP6_HLEN > p->len) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
@@ -441,11 +556,11 @@ ip6_input(struct pbuf *p, struct netif *inp)
/* Trim pbuf. This should have been done at the netif layer,
* but we'll do it anyway just to be sure that its done. */
- pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr));
+ pbuf_realloc(p, (u16_t)(IP6_HLEN + IP6H_PLEN(ip6hdr)));
/* copy IP addresses to aligned ip6_addr_t */
- ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest);
- ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src);
+ ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_dest, ip6hdr->dest);
+ ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_src, ip6hdr->src);
/* Don't accept virtual IPv4 mapped IPv6 addresses.
* Don't accept multicast source addresses. */
@@ -457,6 +572,10 @@ ip6_input(struct pbuf *p, struct netif *inp)
return ERR_OK;
}
+ /* Set the appropriate zone identifier on the addresses. */
+ ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_dest), IP6_UNKNOWN, inp);
+ ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_src), IP6_UNICAST, inp);
+
/* current header pointer. */
ip_data.current_ip6_header = ip6hdr;
@@ -477,6 +596,7 @@ ip6_input(struct pbuf *p, struct netif *inp)
}
#else /* LWIP_IPV6_MLD */
else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) {
+ u8_t i;
/* Filter solicited node packets when MLD is not enabled
* (for Neighbor discovery). */
netif = NULL;
@@ -496,46 +616,44 @@ ip6_input(struct pbuf *p, struct netif *inp)
}
} else {
/* start trying with inp. if that's not acceptable, start walking the
- list of configured netifs.
- 'first' is used as a boolean to mark whether we started walking the list */
- int first = 1;
- netif = inp;
- do {
- /* interface is up? */
- if (netif_is_up(netif)) {
- /* unicast to this interface address? address configured? */
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) {
- /* exit outer loop */
- goto netif_found;
- }
- }
+ list of configured netifs. */
+ if (ip6_input_accept(inp)) {
+ netif = inp;
+ } else {
+ netif = NULL;
+#if !IPV6_CUSTOM_SCOPES
+ /* Shortcut: stop looking for other interfaces if either the source or
+ * the destination has a scope constrained to this interface. Custom
+ * scopes may break the 1:1 link/interface mapping, however. */
+ if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+ ip6_addr_islinklocal(ip6_current_src_addr())) {
+ goto netif_found;
}
- if (first) {
- if (ip6_addr_islinklocal(ip6_current_dest_addr())
+#endif /* !IPV6_CUSTOM_SCOPES */
#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
- || ip6_addr_isloopback(ip6_current_dest_addr())
+ /* The loopback address is to be considered link-local. Packets to it
+ * should be dropped on other interfaces, as per RFC 4291 Sec. 2.5.3.
+ * Its implied scope means packets *from* the loopback address should
+ * not be accepted on other interfaces, either. These requirements
+ * cannot be implemented in the case that loopback traffic is sent
+ * across a non-loopback interface, however. */
+ if (ip6_addr_isloopback(ip6_current_dest_addr()) ||
+ ip6_addr_isloopback(ip6_current_src_addr())) {
+ goto netif_found;
+ }
#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
- ) {
- /* Do not match link-local addresses to other netifs. The loopback
- * address is to be considered link-local and packets to it should be
- * dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. This
- * requirement cannot be implemented in the case that loopback
- * traffic is sent across a non-loopback interface, however.
- */
- netif = NULL;
+#if !LWIP_SINGLE_NETIF
+ NETIF_FOREACH(netif) {
+ if (netif == inp) {
+ /* we checked that before already */
+ continue;
+ }
+ if (ip6_input_accept(netif)) {
break;
}
- first = 0;
- netif = netif_list;
- } else {
- netif = netif->next;
- }
- if (netif == inp) {
- netif = netif->next;
}
- } while (netif != NULL);
+#endif /* !LWIP_SINGLE_NETIF */
+ }
netif_found:
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n",
netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X'));
@@ -574,7 +692,7 @@ netif_found:
nexth = IP6H_NEXTH(ip6hdr);
/* Init header length. */
- hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+ hlen = hlen_tot = IP6_HLEN;
/* Move to payload. */
pbuf_header(p, -IP6_HLEN);
@@ -585,15 +703,9 @@ netif_found:
switch (nexth) {
case IP6_NEXTH_HOPBYHOP:
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
- /* Get next header type. */
- nexth = *((u8_t *)p->payload);
-
- /* Get the header length. */
- hlen = 8 * (1 + *((u8_t *)p->payload + 1));
- ip_data.current_ip_header_tot_len += hlen;
-
- /* Skip over this header. */
- if (hlen > p->len) {
+ /* Get and check the header length, while staying in packet bounds. */
+ hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+ if ((p->len < 8) || (hlen > p->len)) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
hlen, p->len));
@@ -604,19 +716,20 @@ netif_found:
goto ip6_input_cleanup;
}
- pbuf_header(p, -(s16_t)hlen);
- break;
- case IP6_NEXTH_DESTOPTS:
- LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
/* Get next header type. */
nexth = *((u8_t *)p->payload);
- /* Get the header length. */
- hlen = 8 * (1 + *((u8_t *)p->payload + 1));
- ip_data.current_ip_header_tot_len += hlen;
-
/* Skip over this header. */
- if (hlen > p->len) {
+ pbuf_header(p, (s16_t)-(s16_t)hlen);
+ break;
+ case IP6_NEXTH_DESTOPTS:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+
+ /* Get and check the header length, while staying in packet bounds. */
+ hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+ if ((p->len < 8) || (hlen > p->len)) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
hlen, p->len));
@@ -627,19 +740,20 @@ netif_found:
goto ip6_input_cleanup;
}
- pbuf_header(p, -(s16_t)hlen);
- break;
- case IP6_NEXTH_ROUTING:
- LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
/* Get next header type. */
nexth = *((u8_t *)p->payload);
- /* Get the header length. */
- hlen = 8 * (1 + *((u8_t *)p->payload + 1));
- ip_data.current_ip_header_tot_len += hlen;
-
/* Skip over this header. */
- if (hlen > p->len) {
+ pbuf_header(p, (s16_t)-(s16_t)hlen);
+ break;
+ case IP6_NEXTH_ROUTING:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+
+ /* Get and check the header length, while staying in packet bounds. */
+ hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+ if ((p->len < 8) || (hlen > p->len)) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
hlen, p->len));
@@ -650,7 +764,13 @@ netif_found:
goto ip6_input_cleanup;
}
- pbuf_header(p, -(s16_t)hlen);
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Skip over this header. */
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
+ pbuf_header(p, (s16_t)-(s16_t)hlen);
break;
case IP6_NEXTH_FRAGMENT:
@@ -658,14 +778,8 @@ netif_found:
struct ip6_frag_hdr *frag_hdr;
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
- frag_hdr = (struct ip6_frag_hdr *)p->payload;
-
- /* Get next header type. */
- nexth = frag_hdr->_nexth;
-
/* Fragment Header length. */
hlen = 8;
- ip_data.current_ip_header_tot_len += hlen;
/* Make sure this header fits in current pbuf. */
if (hlen > p->len) {
@@ -679,16 +793,23 @@ netif_found:
goto ip6_input_cleanup;
}
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
+ frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+ /* Get next header type. */
+ nexth = frag_hdr->_nexth;
+
/* Offset == 0 and more_fragments == 0? */
if ((frag_hdr->_fragment_offset &
PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) {
- /* This is a 1-fragment packet, usually a packet that we have
- * already reassembled. Skip this header anc continue. */
- pbuf_header(p, -(s16_t)hlen);
+ /* This is a 1-fragment packet. Skip this header and continue. */
+ pbuf_header(p, (s16_t)-(s16_t)hlen);
} else {
#if LWIP_IPV6_REASS
/* reassemble the packet */
+ ip_data.current_ip_header_tot_len = hlen_tot;
p = ip6_reass(p);
/* packet not fully reassembled yet? */
if (p == NULL) {
@@ -699,7 +820,7 @@ netif_found:
* Update all our variables and pointers and continue. */
ip6hdr = (struct ip6_hdr *)p->payload;
nexth = IP6H_NEXTH(ip6hdr);
- hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+ hlen = hlen_tot = IP6_HLEN;
pbuf_header(p, -IP6_HLEN);
#else /* LWIP_IPV6_REASS */
@@ -715,24 +836,37 @@ netif_found:
}
default:
goto options_done;
- break;
}
}
options_done:
- /* p points to IPv6 header again. */
- pbuf_header_force(p, (s16_t)ip_data.current_ip_header_tot_len);
+ if (hlen_tot >= 0x8000) {
+ /* s16_t overflow */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: header length overflow: %"U16_F"\n", hlen_tot));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.proterr);
+ IP6_STATS_INC(ip6.drop);
+ goto options_done;
+ }
/* send to upper layers */
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
ip6_debug_print(p);
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+ ip_data.current_ip_header_tot_len = hlen_tot;
+
#if LWIP_RAW
+ /* p points to IPv6 header again for raw_input. */
+ pbuf_header_force(p, (s16_t)hlen_tot);
/* raw input did not eat the packet? */
if (raw_input(p, inp) == 0)
-#endif /* LWIP_RAW */
{
+ /* Point to payload. */
+ pbuf_header(p, (s16_t)-(s16_t)hlen_tot);
+#else /* LWIP_RAW */
+ {
+#endif /* LWIP_RAW */
switch (nexth) {
case IP6_NEXTH_NONE:
pbuf_free(p);
@@ -742,31 +876,27 @@ options_done:
#if LWIP_UDPLITE
case IP6_NEXTH_UDPLITE:
#endif /* LWIP_UDPLITE */
- /* Point to payload. */
- pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
udp_input(p, inp);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
case IP6_NEXTH_TCP:
- /* Point to payload. */
- pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
tcp_input(p, inp);
break;
#endif /* LWIP_TCP */
#if LWIP_ICMP6
case IP6_NEXTH_ICMP6:
- /* Point to payload. */
- pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
icmp6_input(p, inp);
break;
#endif /* LWIP_ICMP */
default:
#if LWIP_ICMP6
+ /* p points to IPv6 header again for raw_input. */
+ pbuf_header_force(p, (s16_t)hlen_tot);
/* send ICMP parameter problem unless it was a multicast or ICMPv6 */
if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
(IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
- icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen);
+ icmp6_param_problem(p, ICMP6_PP_HEADER, (u32_t)(hlen_tot - hlen));
}
#endif /* LWIP_ICMP */
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
@@ -803,8 +933,10 @@ ip6_input_cleanup:
IPv6 header and p->payload points to that IPv6 header)
* @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
* IP address of the netif is selected and used as source address.
- * if src == NULL, IP6_ADDR_ANY is used as source)
- * @param dest the destination IPv6 address to send the packet to
+ * if src == NULL, IP6_ADDR_ANY is used as source) (src is possibly not
+ * properly zoned)
+ * @param dest the destination IPv6 address to send the packet to (possibly not
+ * properly zoned)
* @param hl the Hop Limit value to be set in the IPv6 header
* @param tc the Traffic Class value to be set in the IPv6 header
* @param nexth the Next Header to be set in the IPv6 header
@@ -849,6 +981,20 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
/* Should the IPv6 header be generated or is it already included in p? */
if (dest != LWIP_IP_HDRINCL) {
+#if LWIP_IPV6_SCOPES
+ /* If the destination address is scoped but lacks a zone, add a zone now,
+ * based on the outgoing interface. The lower layers (e.g., nd6) absolutely
+ * require addresses to be properly zoned for correctness. In some cases,
+ * earlier attempts will have been made to add a zone to the destination,
+ * but this function is the only one that is called in all (other) cases,
+ * so we must do this here. */
+ if (ip6_addr_lacks_zone(dest, IP6_UNKNOWN)) {
+ ip6_addr_copy(dest_addr, *dest);
+ ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif);
+ dest = &dest_addr;
+ }
+#endif /* LWIP_IPV6_SCOPES */
+
/* generate IPv6 header */
if (pbuf_header(p, IP6_HLEN)) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n"));
@@ -864,21 +1010,22 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
IP6H_NEXTH_SET(ip6hdr, nexth);
/* dest cannot be NULL here */
- ip6_addr_copy(ip6hdr->dest, *dest);
+ ip6_addr_copy_to_packed(ip6hdr->dest, *dest);
IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
- IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN);
+ IP6H_PLEN_SET(ip6hdr, (u16_t)(p->tot_len - IP6_HLEN));
if (src == NULL) {
src = IP6_ADDR_ANY6;
}
/* src cannot be NULL here */
- ip6_addr_copy(ip6hdr->src, *src);
+ ip6_addr_copy_to_packed(ip6hdr->src, *src);
} else {
/* IP header already included in p */
ip6hdr = (struct ip6_hdr *)p->payload;
- ip6_addr_copy(dest_addr, ip6hdr->dest);
+ ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
+ ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif);
dest = &dest_addr;
}
@@ -904,13 +1051,11 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
}
}
}
-#if ESP_LWIP
-#if LWIP_IPV6_MLD
+#if LWIP_MULTICAST_TX_OPTIONS
if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
netif_loop_output(netif, p);
}
-#endif /* LWIP_IPV6_MLD */
-#endif
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
#endif /* ENABLE_LOOPBACK */
#if LWIP_IPV6_FRAG
/* don't fragment if interface has mtu set to 0 [loopif] */
@@ -956,8 +1101,8 @@ ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
} else {
/* IP header included in p, read addresses. */
ip6hdr = (struct ip6_hdr *)p->payload;
- ip6_addr_copy(src_addr, ip6hdr->src);
- ip6_addr_copy(dest_addr, ip6hdr->dest);
+ ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
+ ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
netif = ip6_route(&src_addr, &dest_addr);
}
@@ -979,7 +1124,7 @@ ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
}
-#if LWIP_NETIF_HWADDRHINT
+#if LWIP_NETIF_USE_HINTS
/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
* before calling ip6_output_if.
*
@@ -993,7 +1138,7 @@ ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
* @param hl the Hop Limit value to be set in the IPv6 header
* @param tc the Traffic Class value to be set in the IPv6 header
* @param nexth the Next Header to be set in the IPv6 header
- * @param addr_hint address hint pointer set to netif->addr_hint before
+ * @param netif_hint netif output hint pointer set to netif->hint before
* calling ip_output_if()
*
* @return ERR_RTE if no route is found
@@ -1001,7 +1146,7 @@ ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
*/
err_t
ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
- u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint)
+ u8_t hl, u8_t tc, u8_t nexth, struct netif_hint *netif_hint)
{
struct netif *netif;
struct ip6_hdr *ip6hdr;
@@ -1015,8 +1160,8 @@ ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
} else {
/* IP header included in p, read addresses. */
ip6hdr = (struct ip6_hdr *)p->payload;
- ip6_addr_copy(src_addr, ip6hdr->src);
- ip6_addr_copy(dest_addr, ip6hdr->dest);
+ ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
+ ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
netif = ip6_route(&src_addr, &dest_addr);
}
@@ -1034,13 +1179,13 @@ ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
return ERR_RTE;
}
- NETIF_SET_HWADDRHINT(netif, addr_hint);
+ NETIF_SET_HINTS(netif, netif_hint);
err = ip6_output_if(p, src, dest, hl, tc, nexth, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ NETIF_RESET_HINTS(netif);
return err;
}
-#endif /* LWIP_NETIF_HWADDRHINT*/
+#endif /* LWIP_NETIF_USE_HINTS*/
#if LWIP_IPV6_MLD
/**
diff --git a/src/core/ipv6/ip6_addr.c b/../lwip_nat/src/core/ipv6/ip6_addr.c
index aa06659a..56a21ceb 100644
--- a/src/core/ipv6/ip6_addr.c
+++ b/../lwip_nat/src/core/ipv6/ip6_addr.c
@@ -57,7 +57,7 @@ const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
#define islower(c) in_range(c, 'a', 'z')
#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
-#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
+#define xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
#endif
/**
@@ -160,6 +160,8 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
return 0;
}
+ ip6_addr_clear_zone(addr);
+
return 1;
}
diff --git a/src/core/ipv6/ip6_frag.c b/../lwip_nat/src/core/ipv6/ip6_frag.c
index ff07f71c..5c36d546 100644
--- a/src/core/ipv6/ip6_frag.c
+++ b/../lwip_nat/src/core/ipv6/ip6_frag.c
@@ -71,6 +71,8 @@
#endif /* IP_REASS_FREE_OLDEST */
#if IPV6_FRAG_COPYHEADER
+/* The number of bytes we need to "borrow" from (i.e., overwrite in) the header
+ * that precedes the fragment header for reassembly pruposes. */
#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
#endif
@@ -158,17 +160,28 @@ ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
/* 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*)IPV6_FRAG_HDRREF(ipr->iphdr)))) {
+ 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 {
- icmp6_time_exceeded(p, ICMP6_TE_FRAG);
+ /* 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 += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(p);
}
#endif /* LWIP_ICMP6 */
@@ -184,7 +197,7 @@ ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
p = iprh->next_pbuf;
clen = pbuf_clen(pcur);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(pcur);
}
@@ -207,7 +220,7 @@ ip6_reass_free_complete_datagram(struct 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;
+ ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - pbufs_freed);
}
#if IP_REASS_FREE_OLDEST
@@ -261,19 +274,17 @@ 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 offset, len, start, end;
+ ptrdiff_t hdrdiff;
u16_t clen;
u8_t valid = 1;
- struct pbuf *q;
+ struct pbuf *q, *next_pbuf;
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;
- }
+ /* 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;
@@ -285,8 +296,22 @@ ip6_reass(struct pbuf *p)
* 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;
+ 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. */
@@ -295,8 +320,8 @@ ip6_reass(struct pbuf *p)
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_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;
}
@@ -322,7 +347,6 @@ ip6_reass(struct pbuf *p)
#endif /* IP_REASS_FREE_OLDEST */
{
IP6_FRAG_STATS_INC(ip6_frag.memerr);
- IP6_FRAG_STATS_INC(ip6_frag.drop);
goto nullreturn;
}
}
@@ -337,13 +361,22 @@ ip6_reass(struct pbuf *p)
/* 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;
+#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;
@@ -368,7 +401,6 @@ ip6_reass(struct pbuf *p)
/* @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;
}
}
@@ -386,35 +418,37 @@ ip6_reass(struct pbuf *p)
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;
- iprh->next_pbuf = NULL;
- iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
- iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
+ 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 (iprh->start < iprh_tmp->start) {
+ if (start < iprh_tmp->start) {
#if IP_REASS_CHECK_OVERLAP
- if (iprh->end > iprh_tmp->start) {
+ if (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) {
+ if (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;
+ next_pbuf = q;
if (iprh_prev != NULL) {
/* not the fragment with the lowest offset */
iprh_prev->next_pbuf = p;
@@ -423,15 +457,13 @@ ip6_reass(struct pbuf *p)
ipr->p = p;
}
break;
- } else if (iprh->start == iprh_tmp->start) {
+ } else if (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) {
+ } else if (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 {
@@ -454,10 +486,10 @@ ip6_reass(struct pbuf *p)
/* 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);
+ 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 != iprh->start) {
+ if (iprh_prev->end != start) {
valid = 0;
}
} else {
@@ -472,19 +504,23 @@ ip6_reass(struct pbuf *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;
+ ip6_reass_pbufcount = (u16_t)(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 */
+ if (start == 0) {
/* need to use the none-const pointer here: */
ipr->iphdr = ip_data.current_ip6_header;
-#endif /* IPV6_FRAG_COPYHEADER */
+ /* 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) {
@@ -520,7 +556,7 @@ ip6_reass(struct pbuf *p)
/* 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;
+ 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;
@@ -544,39 +580,61 @@ ip6_reass(struct pbuf *p)
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) */
- u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
+ hdrerr = pbuf_header(p, -(s16_t)(IPV6_FRAG_REQROOM));
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
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
+ /* 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)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
- + IP6_FRAG_HLEN
+ 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);
- /* 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;
+ /* 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 sources allocate for the fragment queue entry */
+ /* release the resources allocated for the fragment queue entry */
if (reassdatagrams == ipr) {
/* it was the first in the list */
reassdatagrams = ipr->next;
@@ -588,10 +646,11 @@ ip6_reass(struct pbuf *p)
memp_free(MEMP_IP6_REASSDATA, ipr);
/* adjust the number of pbufs currently queued for reassembly. */
- ip6_reass_pbufcount -= pbuf_clen(p);
+ 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 cannot fail since we already checked when receiving this fragment. */
+ /* 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);
@@ -605,6 +664,7 @@ ip6_reass(struct pbuf *p)
return NULL;
nullreturn:
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
pbuf_free(p);
return NULL;
}
@@ -669,9 +729,9 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
u16_t left_to_copy;
#endif
static u32_t identification;
- u16_t nfb;
u16_t left, cop;
- u16_t mtu;
+ const u16_t mtu = nd6_get_destination_mtu(dest, netif);
+ const u16_t nfb = (u16_t)((mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK);
u16_t fragment_offset = 0;
u16_t last;
u16_t poff = IP6_HLEN;
@@ -680,12 +740,9 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
original_ip6hdr = (struct ip6_hdr *)p->payload;
- mtu = nd6_get_destination_mtu(dest, netif);
-
/* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
- left = p->tot_len - IP6_HLEN;
-
- nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
+ LWIP_ASSERT("p->tot_len >= IP6_HLEN", p->tot_len >= IP6_HLEN);
+ left = (u16_t)(p->tot_len - IP6_HLEN);
while (left) {
last = (left <= nfb);
@@ -731,8 +788,8 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
/* Can just adjust p directly for needed offset. */
p->payload = (u8_t *)p->payload + poff;
- p->len -= poff;
- p->tot_len -= poff;
+ p->len = (u16_t)(p->len - poff);
+ p->tot_len = (u16_t)(p->tot_len - poff);
left_to_copy = cop;
while (left_to_copy) {
@@ -765,7 +822,7 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
* so that it is removed when pbuf_dechain is later called on rambuf.
*/
pbuf_cat(rambuf, newpbuf);
- left_to_copy -= newpbuflen;
+ left_to_copy = (u16_t)(left_to_copy - newpbuflen);
if (left_to_copy) {
p = p->next;
}
@@ -776,11 +833,11 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
/* Set headers */
frag_hdr->_nexth = original_ip6hdr->_nexth;
frag_hdr->reserved = 0;
- frag_hdr->_fragment_offset = lwip_htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
+ frag_hdr->_fragment_offset = lwip_htons((u16_t)((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)));
frag_hdr->_identification = lwip_htonl(identification);
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
- IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
+ IP6H_PLEN_SET(ip6hdr, (u16_t)(cop + IP6_FRAG_HLEN));
/* No need for separate header pbuf - we allowed room for it in rambuf
* when allocated.
@@ -796,8 +853,8 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
*/
pbuf_free(rambuf);
- left -= cop;
- fragment_offset += cop;
+ left = (u16_t)(left - cop);
+ fragment_offset = (u16_t)(fragment_offset + cop);
}
return ERR_OK;
}
diff --git a/src/core/ipv6/mld6.c b/../lwip_nat/src/core/ipv6/mld6.c
index 9acb82fe..69aa7a8b 100644
--- a/src/core/ipv6/mld6.c
+++ b/../lwip_nat/src/core/ipv6/mld6.c
@@ -293,11 +293,17 @@ mld6_input(struct pbuf *p, struct netif *inp)
/**
* @ingroup mld6
- * Join a group on a network interface.
+ * Join a group on one or all network interfaces.
+ *
+ * If the group is to be joined on all interfaces, the given group address must
+ * not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE).
+ * If the group is to be joined on one particular interface, the given group
+ * address may or may not have a zone set.
*
- * @param srcaddr ipv6 address of the network interface which should
- * join a new group. If IP6_ADDR_ANY, join on all netifs
- * @param groupaddr the ipv6 address of the group to join
+ * @param srcaddr ipv6 address (zoned) of the network interface which should
+ * join a new group. If IP6_ADDR_ANY6, join on all netifs
+ * @param groupaddr the ipv6 address of the group to join (possibly but not
+ * necessarily zoned)
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
*/
err_t
@@ -307,8 +313,7 @@ mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
struct netif *netif;
/* loop through netif's */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* Should we join this interface ? */
if (ip6_addr_isany(srcaddr) ||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
@@ -317,9 +322,6 @@ mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
return err;
}
}
-
- /* proceed to next network interface */
- netif = netif->next;
}
return err;
@@ -330,13 +332,26 @@ mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
* Join a group on a network interface.
*
* @param netif the network interface which should join a new group.
- * @param groupaddr the ipv6 address of the group to join
+ * @param groupaddr the ipv6 address of the group to join (possibly but not
+ * necessarily zoned)
* @return ERR_OK if group was joined on the netif, an err_t otherwise
*/
err_t
mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
{
struct mld_group *group;
+#if LWIP_IPV6_SCOPES
+ ip6_addr_t ip6addr;
+
+ /* If the address has a particular scope but no zone set, use the netif to
+ * set one now. Within the mld6 module, all addresses are properly zoned. */
+ if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+ ip6_addr_set(&ip6addr, groupaddr);
+ ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+ groupaddr = &ip6addr;
+ }
+ IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
/* find group or create a new one if not found */
group = mld6_lookfor_group(netif, groupaddr);
@@ -368,9 +383,12 @@ mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
* @ingroup mld6
* Leave a group on a network interface.
*
- * @param srcaddr ipv6 address of the network interface which should
- * leave the group. If IP6_ISANY, leave on all netifs
- * @param groupaddr the ipv6 address of the group to leave
+ * Zoning of address follows the same rules as @ref mld6_joingroup.
+ *
+ * @param srcaddr ipv6 address (zoned) of the network interface which should
+ * leave the group. If IP6_ADDR_ANY6, leave on all netifs
+ * @param groupaddr the ipv6 address of the group to leave (possibly, but not
+ * necessarily zoned)
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
*/
err_t
@@ -380,8 +398,7 @@ mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
struct netif *netif;
/* loop through netif's */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* Should we leave this interface ? */
if (ip6_addr_isany(srcaddr) ||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
@@ -391,8 +408,6 @@ mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
err = res;
}
}
- /* proceed to next network interface */
- netif = netif->next;
}
return err;
@@ -403,13 +418,24 @@ mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
* Leave a group on a network interface.
*
* @param netif the network interface which should leave the group.
- * @param groupaddr the ipv6 address of the group to leave
+ * @param groupaddr the ipv6 address of the group to leave (possibly, but not
+ * necessarily zoned)
* @return ERR_OK if group was left on the netif, an err_t otherwise
*/
err_t
mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
{
struct mld_group *group;
+#if LWIP_IPV6_SCOPES
+ ip6_addr_t ip6addr;
+
+ if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+ ip6_addr_set(&ip6addr, groupaddr);
+ ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+ groupaddr = &ip6addr;
+ }
+ IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
/* find group */
group = mld6_lookfor_group(netif, groupaddr);
@@ -456,9 +482,9 @@ mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
void
mld6_tmr(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
struct mld_group *group = netif_mld6_data(netif);
while (group != NULL) {
@@ -475,7 +501,6 @@ mld6_tmr(void)
}
group = group->next;
}
- netif = netif->next;
}
}
@@ -484,20 +509,20 @@ mld6_tmr(void)
*
* @param group the mld_group for which "delaying" membership report
* should be sent
- * @param maxresp the max resp delay provided in the query
+ * @param maxresp_in the max resp delay provided in the query
*/
static void
-mld6_delayed_report(struct mld_group *group, u16_t maxresp)
+mld6_delayed_report(struct mld_group *group, u16_t maxresp_in)
{
/* Convert maxresp from milliseconds to tmr ticks */
- maxresp = maxresp / MLD6_TMR_INTERVAL;
+ u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL;
if (maxresp == 0) {
maxresp = 1;
}
#ifdef LWIP_RAND
/* Randomize maxresp. (if LWIP_RAND is supported) */
- maxresp = LWIP_RAND() % maxresp;
+ maxresp = (u16_t)(LWIP_RAND() % maxresp);
if (maxresp == 0) {
maxresp = 1;
}
@@ -561,7 +586,7 @@ mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
mld_hdr->chksum = 0;
mld_hdr->max_resp_delay = 0;
mld_hdr->reserved = 0;
- ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
+ ip6_addr_copy_to_packed(mld_hdr->multicast_address, group->group_address);
#if CHECKSUM_GEN_ICMP6
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
diff --git a/src/core/ipv6/nd6.c b/../lwip_nat/src/core/ipv6/nd6.c
index 0b03ee84..31a1f694 100644
--- a/src/core/ipv6/nd6.c
+++ b/../lwip_nat/src/core/ipv6/nd6.c
@@ -89,8 +89,16 @@ static u8_t nd6_cached_destination_index;
/* Multicast address holder. */
static ip6_addr_t multicast_address;
-/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */
-static u8_t nd6_ra_buffer[sizeof(struct prefix_option)];
+/* Static buffer to parse RA packet options */
+union ra_options {
+ struct lladdr_option lladdr;
+ struct mtu_option mtu;
+ struct prefix_option prefix;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+ struct rdnss_option rdnss;
+#endif
+};
+static union ra_options nd6_ra_buffer;
/* Forward declarations. */
static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr);
@@ -102,13 +110,14 @@ static s8_t nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *neti
static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif);
static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif);
-static s8_t nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
-static s8_t nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif);
static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q);
#define ND6_SEND_FLAG_MULTICAST_DEST 0x01
#define ND6_SEND_FLAG_ALLNODES_DEST 0x02
+#define ND6_SEND_FLAG_ANY_SRC 0x04
static void nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
static void nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
static void nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags);
@@ -124,6 +133,147 @@ static void nd6_free_q(struct nd6_q_entry *q);
static void nd6_send_q(s8_t i);
+/**
+ * A local address has been determined to be a duplicate. Take the appropriate
+ * action(s) on the address and the interface as a whole.
+ *
+ * @param netif the netif that owns the address
+ * @param addr_idx the index of the address detected to be a duplicate
+ */
+static void
+nd6_duplicate_addr_detected(struct netif *netif, s8_t addr_idx)
+{
+
+ /* Mark the address as duplicate, but leave its lifetimes alone. If this was
+ * a manually assigned address, it will remain in existence as duplicate, and
+ * as such be unusable for any practical purposes until manual intervention.
+ * If this was an autogenerated address, the address will follow normal
+ * expiration rules, and thus disappear once its valid lifetime expires. */
+ netif_ip6_addr_set_state(netif, addr_idx, IP6_ADDR_DUPLICATED);
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* If the affected address was the link-local address that we use to generate
+ * all other addresses, then we should not continue to use those derived
+ * addresses either, so mark them as duplicate as well. For autoconfig-only
+ * setups, this will make the interface effectively unusable, approaching the
+ * intention of RFC 4862 Sec. 5.4.5. @todo implement the full requirements */
+ if (addr_idx == 0) {
+ s8_t i;
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+ !netif_ip6_addr_isstatic(netif, i)) {
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_DUPLICATED);
+ }
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+#if LWIP_IPV6_AUTOCONFIG
+/**
+ * We received a router advertisement that contains a prefix with the
+ * autoconfiguration flag set. Add or update an associated autogenerated
+ * address.
+ *
+ * @param netif the netif on which the router advertisement arrived
+ * @param prefix_opt a pointer to the prefix option data
+ * @param prefix_addr an aligned copy of the prefix address
+ */
+static void
+nd6_process_autoconfig_prefix(struct netif *netif,
+ struct prefix_option *prefix_opt, const ip6_addr_t *prefix_addr)
+{
+ ip6_addr_t ip6addr;
+ u32_t valid_life, pref_life;
+ u8_t addr_state;
+ s8_t i, free_idx;
+
+ /* The caller already checks RFC 4862 Sec. 5.5.3 points (a) and (b). We do
+ * the rest, starting with checks for (c) and (d) here. */
+ valid_life = lwip_htonl(prefix_opt->valid_lifetime);
+ pref_life = lwip_htonl(prefix_opt->preferred_lifetime);
+ if (pref_life > valid_life || prefix_opt->prefix_length != 64) {
+ return; /* silently ignore this prefix for autoconfiguration purposes */
+ }
+
+ /* If an autogenerated address already exists for this prefix, update its
+ * lifetimes. An address is considered autogenerated if 1) it is not static
+ * (i.e., manually assigned), and 2) there is an advertised autoconfiguration
+ * prefix for it (the one we are processing here). This does not necessarily
+ * exclude the possibility that the address was actually assigned by, say,
+ * DHCPv6. If that distinction becomes important in the future, more state
+ * must be kept. As explained elsewhere we also update lifetimes of tentative
+ * and duplicate addresses. Skip address slot 0 (the link-local address). */
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ addr_state = netif_ip6_addr_state(netif, i);
+ if (!ip6_addr_isinvalid(addr_state) && !netif_ip6_addr_isstatic(netif, i) &&
+ ip6_addr_netcmp(prefix_addr, netif_ip6_addr(netif, i))) {
+ /* Update the valid lifetime, as per RFC 4862 Sec. 5.5.3 point (e).
+ * The valid lifetime will never drop to zero as a result of this. */
+ u32_t remaining_life = netif_ip6_addr_valid_life(netif, i);
+ if (valid_life > ND6_2HRS || valid_life > remaining_life) {
+ netif_ip6_addr_set_valid_life(netif, i, valid_life);
+ } else if (remaining_life > ND6_2HRS) {
+ netif_ip6_addr_set_valid_life(netif, i, ND6_2HRS);
+ }
+ LWIP_ASSERT("bad valid lifetime", !netif_ip6_addr_isstatic(netif, i));
+ /* Update the preferred lifetime. No bounds checks are needed here. In
+ * rare cases the advertisement may un-deprecate the address, though.
+ * Deprecation is left to the timer code where it is handled anyway. */
+ if (pref_life > 0 && addr_state == IP6_ADDR_DEPRECATED) {
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
+ }
+ netif_ip6_addr_set_pref_life(netif, i, pref_life);
+ return; /* there should be at most one matching address */
+ }
+ }
+
+ /* No autogenerated address exists for this prefix yet. See if we can add a
+ * new one. However, if IPv6 autoconfiguration is administratively disabled,
+ * do not generate new addresses, but do keep updating lifetimes for existing
+ * addresses. Also, when adding new addresses, we must protect explicitly
+ * against a valid lifetime of zero, because again, we use that as a special
+ * value. The generated address would otherwise expire immediately anyway.
+ * Finally, the original link-local address must be usable at all. We start
+ * creating addresses even if the link-local address is still in tentative
+ * state though, and deal with the fallout of that upon DAD collision. */
+ addr_state = netif_ip6_addr_state(netif, 0);
+ if (!netif->ip6_autoconfig_enabled || valid_life == IP6_ADDR_LIFE_STATIC ||
+ ip6_addr_isinvalid(addr_state) || ip6_addr_isduplicated(addr_state)) {
+ return;
+ }
+
+ /* Construct the new address that we intend to use, and then see if that
+ * address really does not exist. It might have been added manually, after
+ * all. As a side effect, find a free slot. Note that we cannot use
+ * netif_add_ip6_address() here, as it would return ERR_OK if the address
+ * already did exist, resulting in that address being given lifetimes. */
+ IP6_ADDR(&ip6addr, prefix_addr->addr[0], prefix_addr->addr[1],
+ netif_ip6_addr(netif, 0)->addr[2], netif_ip6_addr(netif, 0)->addr[3]);
+ ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif);
+
+ free_idx = 0;
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
+ if (ip6_addr_cmp(&ip6addr, netif_ip6_addr(netif, i))) {
+ return; /* formed address already exists */
+ }
+ } else if (free_idx == 0) {
+ free_idx = i;
+ }
+ }
+ if (free_idx == 0) {
+ return; /* no address slots available, try again on next advertisement */
+ }
+
+ /* Assign the new address to the interface. */
+ ip_addr_copy_from_ip6(netif->ip6_addr[free_idx], ip6addr);
+ netif_ip6_addr_set_valid_life(netif, free_idx, valid_life);
+ netif_ip6_addr_set_pref_life(netif, free_idx, pref_life);
+ netif_ip6_addr_set_state(netif, free_idx, IP6_ADDR_TENTATIVE);
+}
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
/**
* Process an incoming neighbor discovery message
*
@@ -144,6 +294,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
{
struct na_header *na_hdr;
struct lladdr_option *lladdr_opt;
+ ip6_addr_t target_address;
/* Check that na header fits in packet. */
if (p->len < (sizeof(struct na_header))) {
@@ -156,36 +307,36 @@ nd6_input(struct pbuf *p, struct netif *inp)
na_hdr = (struct na_header *)p->payload;
+ /* Create an aligned, zoned copy of the target address. */
+ ip6_addr_copy_from_packed(target_address, na_hdr->target_address);
+ ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+ /* Check a subset of the other RFC 4861 Sec. 7.1.2 requirements. */
+ if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || na_hdr->code != 0 ||
+ ip6_addr_ismulticast(&target_address)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: if IP destination is multicast, Solicited flag is zero */
+ /* @todo RFC MUST: all included options have a length greater than zero */
+
/* Unsolicited NA?*/
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
- ip6_addr_t target_address;
-
/* This is an unsolicited NA.
* link-layer changed?
* part of DAD mechanism? */
- /* Create an aligned copy. */
- ip6_addr_set(&target_address, &(na_hdr->target_address));
-
#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
/* If the target address matches this netif, it is a DAD response. */
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ !ip6_addr_isduplicated(netif_ip6_addr_state(inp, i)) &&
ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) {
/* We are using a duplicate address. */
- netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
-
-#if LWIP_IPV6_AUTOCONFIG
- /* Check to see if this address was autoconfigured. */
- if (!ip6_addr_islinklocal(&target_address)) {
- i = nd6_get_onlink_prefix(&target_address, inp);
- if (i >= 0) {
- /* Mark this prefix as duplicate, so that we don't use it
- * to generate this address again. */
- prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE;
- }
- }
-#endif /* LWIP_IPV6_AUTOCONFIG */
+ nd6_duplicate_addr_detected(inp, i);
pbuf_free(p);
return;
@@ -220,15 +371,10 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
}
} else {
- ip6_addr_t target_address;
-
/* This is a solicited NA.
* neighbor address resolution response?
* neighbor unreachability detection response? */
- /* Create an aligned copy. */
- ip6_addr_set(&target_address, &(na_hdr->target_address));
-
/* Find the cache entry corresponding to this na. */
i = nd6_find_neighbor_cache_entry(&target_address);
if (i < 0) {
@@ -278,6 +424,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
{
struct ns_header *ns_hdr;
struct lladdr_option *lladdr_opt;
+ ip6_addr_t target_address;
u8_t accepted;
/* Check that ns header fits in packet. */
@@ -291,6 +438,23 @@ nd6_input(struct pbuf *p, struct netif *inp)
ns_hdr = (struct ns_header *)p->payload;
+ /* Create an aligned, zoned copy of the target address. */
+ ip6_addr_copy_from_packed(target_address, ns_hdr->target_address);
+ ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+ /* Check a subset of the other RFC 4861 Sec. 7.1.1 requirements. */
+ if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ns_hdr->code != 0 ||
+ ip6_addr_ismulticast(&target_address)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: all included options have a length greater than zero */
+ /* @todo RFC MUST: if IP source is 'any', destination is solicited-node multicast address */
+ /* @todo RFC MUST: if IP source is 'any', there is no source LL address option */
+
/* Check if there is a link-layer address provided. Only point to it if in this buffer. */
if (p->len >= (sizeof(struct ns_header) + 2)) {
lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
@@ -307,7 +471,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) ||
(ip6_addr_istentative(netif_ip6_addr_state(inp, i)) &&
ip6_addr_isany(ip6_current_src_addr()))) &&
- ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) {
accepted = 1;
break;
}
@@ -324,18 +488,16 @@ nd6_input(struct pbuf *p, struct netif *inp)
/* Sender is validating this address. */
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
- ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) {
/* Send a NA back so that the sender does not use this address. */
nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST);
if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) {
/* We shouldn't use this address either. */
- netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+ nd6_duplicate_addr_detected(inp, i);
}
}
}
} else {
- ip6_addr_t target_address;
-
/* Sender is trying to resolve our address. */
/* Verify that they included their own link-layer address. */
if (lladdr_opt == NULL) {
@@ -379,9 +541,6 @@ nd6_input(struct pbuf *p, struct netif *inp)
neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
}
- /* Create an aligned copy. */
- ip6_addr_set(&target_address, &(ns_hdr->target_address));
-
/* Send back a NA for us. Allocate the reply pbuf. */
nd6_send_na(inp, &target_address, ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE);
}
@@ -394,7 +553,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
u8_t *buffer; /* Used to copy options. */
u16_t offset;
#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
- /* There can by multiple RDNSS options per RA */
+ /* There can be multiple RDNSS options per RA */
u8_t rdnss_server_idx = 0;
#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
@@ -409,6 +568,17 @@ nd6_input(struct pbuf *p, struct netif *inp)
ra_hdr = (struct ra_header *)p->payload;
+ /* Check a subset of the other RFC 4861 Sec. 6.1.2 requirements. */
+ if (!ip6_addr_islinklocal(ip6_current_src_addr()) ||
+ IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ra_hdr->code != 0) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: all included options have a length greater than zero */
+
/* If we are sending RS messages, stop. */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
/* ensure at least one solicitation is sent */
@@ -455,30 +625,44 @@ nd6_input(struct pbuf *p, struct netif *inp)
offset = sizeof(struct ra_header);
/* Process each option. */
- while ((p->tot_len - offset) > 0) {
+ while ((p->tot_len - offset) >= 2) {
+ u8_t option_type;
+ u16_t option_len;
+ int option_len8 = pbuf_try_get_at(p, offset + 1);
+ if (option_len8 <= 0) {
+ /* read beyond end or zero length */
+ goto lenerr_drop_free_return;
+ }
+ option_len = ((u8_t)option_len8) << 3;
+ if (option_len > p->tot_len - offset) {
+ /* short packet (option does not fit in) */
+ goto lenerr_drop_free_return;
+ }
if (p->len == p->tot_len) {
/* no need to copy from contiguous pbuf */
buffer = &((u8_t*)p->payload)[offset];
} else {
- buffer = nd6_ra_buffer;
- if (pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset) != sizeof(struct prefix_option)) {
- pbuf_free(p);
- ND6_STATS_INC(nd6.lenerr);
- ND6_STATS_INC(nd6.drop);
- return;
+ /* check if this option fits into our buffer */
+ if (option_len > sizeof(nd6_ra_buffer)) {
+ option_type = pbuf_get_at(p, offset);
+ /* invalid option length */
+ if (option_type != ND6_OPTION_TYPE_RDNSS) {
+ goto lenerr_drop_free_return;
+ }
+ /* we allow RDNSS option to be longer - we'll just drop some servers */
+ option_len = sizeof(nd6_ra_buffer);
}
+ buffer = (u8_t*)&nd6_ra_buffer;
+ option_len = pbuf_copy_partial(p, &nd6_ra_buffer, option_len, offset);
}
- if (buffer[1] == 0) {
- /* zero-length extension. drop packet */
- pbuf_free(p);
- ND6_STATS_INC(nd6.lenerr);
- ND6_STATS_INC(nd6.drop);
- return;
- }
- switch (buffer[0]) {
+ option_type = buffer[0];
+ switch (option_type) {
case ND6_OPTION_TYPE_SOURCE_LLADDR:
{
struct lladdr_option *lladdr_opt;
+ if (option_len < sizeof(struct lladdr_option)) {
+ goto lenerr_drop_free_return;
+ }
lladdr_opt = (struct lladdr_option *)buffer;
if ((default_router_list[i].neighbor_entry != NULL) &&
(default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) {
@@ -491,6 +675,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
case ND6_OPTION_TYPE_MTU:
{
struct mtu_option *mtu_opt;
+ if (option_len < sizeof(struct mtu_option)) {
+ goto lenerr_drop_free_return;
+ }
mtu_opt = (struct mtu_option *)buffer;
if (lwip_htonl(mtu_opt->mtu) >= 1280) {
#if LWIP_ND6_ALLOW_RA_UPDATES
@@ -502,35 +689,42 @@ nd6_input(struct pbuf *p, struct netif *inp)
case ND6_OPTION_TYPE_PREFIX_INFO:
{
struct prefix_option *prefix_opt;
+ ip6_addr_t prefix_addr;
+ if (option_len < sizeof(struct prefix_option)) {
+ goto lenerr_drop_free_return;
+ }
+
prefix_opt = (struct prefix_option *)buffer;
- if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) &&
- (prefix_opt->prefix_length == 64) &&
- !ip6_addr_islinklocal(&(prefix_opt->prefix))) {
- /* Add to on-link prefix list. */
- s8_t prefix;
- ip6_addr_t prefix_addr;
-
- /* Get a memory-aligned copy of the prefix. */
- ip6_addr_set(&prefix_addr, &(prefix_opt->prefix));
-
- /* find cache entry for this prefix. */
- prefix = nd6_get_onlink_prefix(&prefix_addr, inp);
- if (prefix < 0) {
- /* Create a new cache entry. */
- prefix = nd6_new_onlink_prefix(&prefix_addr, inp);
- }
- if (prefix >= 0) {
- prefix_list[prefix].invalidation_timer = lwip_htonl(prefix_opt->valid_lifetime);
+ /* Get a memory-aligned copy of the prefix. */
+ ip6_addr_copy_from_packed(prefix_addr, prefix_opt->prefix);
+ ip6_addr_assign_zone(&prefix_addr, IP6_UNICAST, inp);
-#if LWIP_IPV6_AUTOCONFIG
- if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
- /* Mark prefix as autonomous, so that address autoconfiguration can take place.
- * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/
- prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
+ if (!ip6_addr_islinklocal(&prefix_addr)) {
+ if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) &&
+ (prefix_opt->prefix_length == 64)) {
+ /* Add to on-link prefix list. */
+ u32_t valid_life;
+ s8_t prefix;
+
+ valid_life = lwip_htonl(prefix_opt->valid_lifetime);
+
+ /* find cache entry for this prefix. */
+ prefix = nd6_get_onlink_prefix(&prefix_addr, inp);
+ if (prefix < 0 && valid_life > 0) {
+ /* Create a new cache entry. */
+ prefix = nd6_new_onlink_prefix(&prefix_addr, inp);
+ }
+ if (prefix >= 0) {
+ prefix_list[prefix].invalidation_timer = valid_life;
}
-#endif /* LWIP_IPV6_AUTOCONFIG */
}
+#if LWIP_IPV6_AUTOCONFIG
+ if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
+ /* Perform processing for autoconfiguration. */
+ nd6_process_autoconfig_prefix(inp, prefix_opt, &prefix_addr);
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
}
break;
@@ -545,26 +739,33 @@ nd6_input(struct pbuf *p, struct netif *inp)
case ND6_OPTION_TYPE_RDNSS:
{
u8_t num, n;
+ u16_t copy_offset = offset + SIZEOF_RDNSS_OPTION_BASE;
struct rdnss_option * rdnss_opt;
+ if (option_len < SIZEOF_RDNSS_OPTION_BASE) {
+ goto lenerr_drop_free_return;
+ }
rdnss_opt = (struct rdnss_option *)buffer;
num = (rdnss_opt->length - 1) / 2;
for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) {
ip_addr_t rdnss_address;
- /* Get a memory-aligned copy of the prefix. */
- ip_addr_copy_from_ip6(rdnss_address, rdnss_opt->rdnss_address[n]);
-
- if (htonl(rdnss_opt->lifetime) > 0) {
- /* TODO implement Lifetime > 0 */
- dns_setserver(rdnss_server_idx++, &rdnss_address);
- } else {
- /* TODO implement DNS removal in dns.c */
- u8_t s;
- for (s = 0; s < DNS_MAX_SERVERS; s++) {
- const ip_addr_t *addr = dns_getserver(s);
- if(ip_addr_cmp(addr, &rdnss_address)) {
- dns_setserver(s, NULL);
+ /* Copy directly from pbuf to get an aligned, zoned copy of the prefix. */
+ if (pbuf_copy_partial(p, &rdnss_address, sizeof(ip6_addr_p_t), copy_offset) == sizeof(ip6_addr_p_t)) {
+ IP_SET_TYPE_VAL(rdnss_address, IPADDR_TYPE_V6);
+ ip6_addr_assign_zone(ip_2_ip6(&rdnss_address), IP6_UNKNOWN, inp);
+
+ if (htonl(rdnss_opt->lifetime) > 0) {
+ /* TODO implement Lifetime > 0 */
+ dns_setserver(rdnss_server_idx++, &rdnss_address);
+ } else {
+ /* TODO implement DNS removal in dns.c */
+ u8_t s;
+ for (s = 0; s < DNS_MAX_SERVERS; s++) {
+ const ip_addr_t *addr = dns_getserver(s);
+ if(ip_addr_cmp(addr, &rdnss_address)) {
+ dns_setserver(s, NULL);
+ }
}
}
}
@@ -578,7 +779,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
break;
}
/* option length is checked earlier to be non-zero to make sure loop ends */
- offset += 8 * ((u16_t)buffer[1]);
+ offset += 8 * (u8_t)option_len8;
}
break; /* ICMP6_TYPE_RA */
@@ -587,7 +788,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
{
struct redirect_header *redir_hdr;
struct lladdr_option *lladdr_opt;
- ip6_addr_t tmp;
+ ip6_addr_t destination_address, target_address;
/* Check that Redir header fits in packet. */
if (p->len < sizeof(struct redirect_header)) {
@@ -600,6 +801,24 @@ nd6_input(struct pbuf *p, struct netif *inp)
redir_hdr = (struct redirect_header *)p->payload;
+ /* Create an aligned, zoned copy of the destination address. */
+ ip6_addr_copy_from_packed(destination_address, redir_hdr->destination_address);
+ ip6_addr_assign_zone(&destination_address, IP6_UNICAST, inp);
+
+ /* Check a subset of the other RFC 4861 Sec. 8.1 requirements. */
+ if (!ip6_addr_islinklocal(ip6_current_src_addr()) ||
+ IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM ||
+ redir_hdr->code != 0 || ip6_addr_ismulticast(&destination_address)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: IP source address equals first-hop router for destination_address */
+ /* @todo RFC MUST: ICMP target address is either link-local address or same as destination_address */
+ /* @todo RFC MUST: all included options have a length greater than zero */
+
if (p->len >= (sizeof(struct redirect_header) + 2)) {
lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) {
@@ -609,33 +828,31 @@ nd6_input(struct pbuf *p, struct netif *inp)
lladdr_opt = NULL;
}
- /* Copy original destination address to current source address, to have an aligned copy. */
- ip6_addr_set(&tmp, &(redir_hdr->destination_address));
-
/* Find dest address in cache */
- i = nd6_find_destination_cache_entry(&tmp);
+ i = nd6_find_destination_cache_entry(&destination_address);
if (i < 0) {
/* Destination not in cache, drop packet. */
pbuf_free(p);
return;
}
+ /* Create an aligned, zoned copy of the target address. */
+ ip6_addr_copy_from_packed(target_address, redir_hdr->target_address);
+ ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
/* Set the new target address. */
- ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address));
+ ip6_addr_copy(destination_cache[i].next_hop_addr, target_address);
/* If Link-layer address of other router is given, try to add to neighbor cache. */
if (lladdr_opt != NULL) {
if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
- /* Copy target address to current source address, to have an aligned copy. */
- ip6_addr_set(&tmp, &(redir_hdr->target_address));
-
- i = nd6_find_neighbor_cache_entry(&tmp);
+ i = nd6_find_neighbor_cache_entry(&target_address);
if (i < 0) {
i = nd6_new_neighbor_cache_entry();
if (i >= 0) {
neighbor_cache[i].netif = inp;
MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
- ip6_addr_set(&(neighbor_cache[i].next_hop_address), &tmp);
+ ip6_addr_copy(neighbor_cache[i].next_hop_address, target_address);
/* Receiving a message does not prove reachability: only in one direction.
* Delay probe in case we get confirmation of reachability from upper layer (TCP). */
@@ -661,7 +878,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
struct icmp6_hdr *icmp6hdr; /* Packet too big message */
struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */
u32_t pmtu;
- ip6_addr_t tmp;
+ ip6_addr_t destination_address;
/* Check that ICMPv6 header + IPv6 header fit in payload */
if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
@@ -675,11 +892,12 @@ nd6_input(struct pbuf *p, struct netif *inp)
icmp6hdr = (struct icmp6_hdr *)p->payload;
ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
- /* Copy original destination address to current source address, to have an aligned copy. */
- ip6_addr_set(&tmp, &(ip6hdr->dest));
+ /* Create an aligned, zoned copy of the destination address. */
+ ip6_addr_copy_from_packed(destination_address, ip6hdr->dest);
+ ip6_addr_assign_zone(&destination_address, IP6_UNKNOWN, inp);
/* Look for entry in destination cache. */
- i = nd6_find_destination_cache_entry(&tmp);
+ i = nd6_find_destination_cache_entry(&destination_address);
if (i < 0) {
/* Destination not in cache, drop packet. */
pbuf_free(p);
@@ -700,24 +918,13 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
pbuf_free(p);
+ return;
+lenerr_drop_free_return:
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ pbuf_free(p);
}
-#ifdef ESP_LWIP
-
-/** Set callback for ipv6 addr status changed .
- *
- * @param netif the netif from which to remove the struct dhcp
- * @param cb callback for dhcp
- */
-void nd6_set_cb(struct netif *netif, void (*cb)(struct netif *netif, u8_t ip_index))
-{
- LWIP_ASSERT("netif != NULL", netif != NULL);
-
- if (netif != NULL && netif_is_up(netif)) {
- netif->ipv6_addr_cb = cb;
- }
-}
-#endif
/**
* Periodic timer for Neighbor discovery functions:
@@ -725,6 +932,7 @@ void nd6_set_cb(struct netif *netif, void (*cb)(struct netif *netif, u8_t ip_ind
* - Update neighbor reachability states
* - Update destination cache entries age
* - Update invalidation timers of default routers and on-link prefixes
+ * - Update lifetimes of our addresses
* - Perform duplicate address detection (DAD) for our addresses
* - Send router solicitations
*/
@@ -800,15 +1008,22 @@ nd6_tmr(void)
for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
if (default_router_list[i].neighbor_entry != NULL) {
/* Active entry. */
- if (default_router_list[i].invalidation_timer > 0) {
- default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
- }
- if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
- /* Less than 1 second remaining. Clear this entry. */
+ if (default_router_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) {
+ /* No more than 1 second remaining. Clear this entry. Also clear any of
+ * its destination cache entries, as per RFC 4861 Sec. 5.3 and 6.3.5. */
+ s8_t j;
+ for (j = 0; j < LWIP_ND6_NUM_DESTINATIONS; j++) {
+ if (ip6_addr_cmp(&destination_cache[j].next_hop_addr,
+ &default_router_list[i].neighbor_entry->next_hop_address)) {
+ ip6_addr_set_any(&destination_cache[j].destination_addr);
+ }
+ }
default_router_list[i].neighbor_entry->isrouter = 0;
default_router_list[i].neighbor_entry = NULL;
default_router_list[i].invalidation_timer = 0;
default_router_list[i].flags = 0;
+ } else {
+ default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
}
}
}
@@ -816,95 +1031,89 @@ nd6_tmr(void)
/* Process prefix entries. */
for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
if (prefix_list[i].netif != NULL) {
- if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+ if (prefix_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) {
/* Entry timed out, remove it */
prefix_list[i].invalidation_timer = 0;
-
-#if LWIP_IPV6_AUTOCONFIG
- /* If any addresses were configured with this prefix, remove them */
- if (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED) {
- s8_t j;
-
- for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
- if ((netif_ip6_addr_state(prefix_list[i].netif, j) != IP6_ADDR_INVALID) &&
- ip6_addr_netcmp(&prefix_list[i].prefix, netif_ip6_addr(prefix_list[i].netif, j))) {
- netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_INVALID);
- prefix_list[i].flags = 0;
-
- /* Exit loop. */
- break;
- }
- }
- }
-#endif /* LWIP_IPV6_AUTOCONFIG */
-
prefix_list[i].netif = NULL;
- prefix_list[i].flags = 0;
} else {
prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
-
-#if LWIP_IPV6_AUTOCONFIG
- /* Initiate address autoconfiguration for this prefix, if conditions are met. */
- if (prefix_list[i].netif->ip6_autoconfig_enabled &&
- (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
- !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
- s8_t j;
- /* Try to get an address on this netif that is invalid.
- * Skip 0 index (link-local address) */
- for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
- if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDR_INVALID) {
- /* Generate an address using this prefix and interface ID from link-local address. */
- netif_ip6_addr_set_parts(prefix_list[i].netif, j,
- prefix_list[i].prefix.addr[0], prefix_list[i].prefix.addr[1],
- netif_ip6_addr(prefix_list[i].netif, 0)->addr[2], netif_ip6_addr(prefix_list[i].netif, 0)->addr[3]);
-
- /* Mark it as tentative (DAD will be performed if configured). */
- netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
-
- /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
- prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
-
- /* Exit loop. */
- break;
- }
- }
- }
-#endif /* LWIP_IPV6_AUTOCONFIG */
}
}
}
-
- /* Process our own addresses, if DAD configured. */
- for (netif = netif_list; netif != NULL; netif = netif->next) {
+ /* Process our own addresses, updating address lifetimes and/or DAD state. */
+ NETIF_FOREACH(netif) {
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
- u8_t addr_state = netif_ip6_addr_state(netif, i);
+ u8_t addr_state;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ /* Step 1: update address lifetimes (valid and preferred). */
+ addr_state = netif_ip6_addr_state(netif, i);
+ /* RFC 4862 is not entirely clear as to whether address lifetimes affect
+ * tentative addresses, and is even less clear as to what should happen
+ * with duplicate addresses. We choose to track and update lifetimes for
+ * both those types, although for different reasons:
+ * - for tentative addresses, the line of thought of Sec. 5.7 combined
+ * with the potentially long period that an address may be in tentative
+ * state (due to the interface being down) suggests that lifetimes
+ * should be independent of external factors which would include DAD;
+ * - for duplicate addresses, retiring them early could result in a new
+ * but unwanted attempt at marking them as valid, while retiring them
+ * late/never could clog up address slots on the netif.
+ * As a result, we may end up expiring addresses of either type here.
+ */
+ if (!ip6_addr_isinvalid(addr_state) &&
+ !netif_ip6_addr_isstatic(netif, i)) {
+ u32_t life = netif_ip6_addr_valid_life(netif, i);
+ if (life <= ND6_TMR_INTERVAL / 1000) {
+ /* The address has expired. */
+ netif_ip6_addr_set_valid_life(netif, i, 0);
+ netif_ip6_addr_set_pref_life(netif, i, 0);
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
+ } else {
+ if (!ip6_addr_life_isinfinite(life)) {
+ life -= ND6_TMR_INTERVAL / 1000;
+ LWIP_ASSERT("bad valid lifetime", life != IP6_ADDR_LIFE_STATIC);
+ netif_ip6_addr_set_valid_life(netif, i, life);
+ }
+ /* The address is still here. Update the preferred lifetime too. */
+ life = netif_ip6_addr_pref_life(netif, i);
+ if (life <= ND6_TMR_INTERVAL / 1000) {
+ /* This case must also trigger if 'life' was already zero, so as to
+ * deal correctly with advertised preferred-lifetime reductions. */
+ netif_ip6_addr_set_pref_life(netif, i, 0);
+ if (addr_state == IP6_ADDR_PREFERRED)
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_DEPRECATED);
+ } else if (!ip6_addr_life_isinfinite(life)) {
+ life -= ND6_TMR_INTERVAL / 1000;
+ netif_ip6_addr_set_pref_life(netif, i, life);
+ }
+ }
+ }
+ /* The address state may now have changed, so reobtain it next. */
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+ /* Step 2: update DAD state. */
+ addr_state = netif_ip6_addr_state(netif, i);
if (ip6_addr_istentative(addr_state)) {
if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) {
- /* No NA received in response. Mark address as valid. */
- netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
-#ifdef ESP_LWIP
- if (netif->ipv6_addr_cb != NULL) {
- netif->ipv6_addr_cb(netif, i);
- }
-#endif
- /* @todo implement preferred and valid lifetimes. */
- } else if (netif->flags & NETIF_FLAG_UP) {
-#if ESP_LWIP
-#if LWIP_IPV6_MLD
- if ((netif_ip6_addr_state(netif, i) & IP6_ADDR_TENTATIVE_COUNT_MASK) == 0) {
- /* Join solicited node multicast group. */
- ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]);
- mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address);
+ /* No NA received in response. Mark address as valid. For dynamic
+ * addresses with an expired preferred lifetime, the state is set to
+ * deprecated right away. That should almost never happen, though. */
+ addr_state = IP6_ADDR_PREFERRED;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ if (!netif_ip6_addr_isstatic(netif, i) &&
+ netif_ip6_addr_pref_life(netif, i) == 0) {
+ addr_state = IP6_ADDR_DEPRECATED;
}
-#endif /* LWIP_IPV6_MLD */
-#endif
- /* Send a NS for this address. */
- nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+ netif_ip6_addr_set_state(netif, i, addr_state);
+ } else if (netif_is_up(netif) && netif_is_link_up(netif)) {
/* tentative: set next state by increasing by one */
netif_ip6_addr_set_state(netif, i, addr_state + 1);
- /* @todo send max 1 NS per tmr call? enable return*/
- /*return;*/
+ /* Send a NS for this address. Use the unspecified address as source
+ * address in all cases (RFC 4862 Sec. 5.4.2), not in the least
+ * because as it is, we only consider multicast replies for DAD. */
+ nd6_send_ns(netif, netif_ip6_addr(netif, i),
+ ND6_SEND_FLAG_MULTICAST_DEST | ND6_SEND_FLAG_ANY_SRC);
}
}
}
@@ -912,9 +1121,11 @@ nd6_tmr(void)
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
/* Send router solicitation messages, if necessary. */
- for (netif = netif_list; netif != NULL; netif = netif->next) {
- if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP) &&
- (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)))) {
+ NETIF_FOREACH(netif) {
+ if ((netif->rs_count > 0) && netif_is_up(netif) &&
+ netif_is_link_up(netif) &&
+ !ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)) &&
+ !ip6_addr_isduplicated(netif_ip6_addr_state(netif, 0))) {
if (nd6_send_rs(netif) == ERR_OK) {
netif->rs_count--;
}
@@ -950,7 +1161,10 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
const ip6_addr_t *src_addr;
u16_t lladdr_opt_len;
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
+ LWIP_ASSERT("target address is required", target_addr != NULL);
+
+ if (!(flags & ND6_SEND_FLAG_ANY_SRC) &&
+ ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
/* Use link-local address as source address. */
src_addr = netif_ip6_addr(netif, 0);
/* calculate option length (in 8-byte-blocks) */
@@ -975,7 +1189,7 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
ns_hdr->code = 0;
ns_hdr->chksum = 0;
ns_hdr->reserved = 0;
- ip6_addr_set(&(ns_hdr->target_address), target_addr);
+ ip6_addr_copy_to_packed(ns_hdr->target_address, *target_addr);
if (lladdr_opt_len != 0) {
struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
@@ -987,6 +1201,7 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
/* Generate the solicited node address for the target address. */
if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
target_addr = &multicast_address;
}
@@ -1000,7 +1215,7 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
/* Send the packet out. */
ND6_STATS_INC(nd6.xmit);
ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr,
- LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(p);
}
@@ -1021,6 +1236,8 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
const ip6_addr_t *dest_addr;
u16_t lladdr_opt_len;
+ LWIP_ASSERT("target address is required", target_addr != NULL);
+
/* Use link-local address as source address. */
/* src_addr = netif_ip6_addr(netif, 0); */
/* Use target address as source address. */
@@ -1045,7 +1262,7 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
na_hdr->reserved[0] = 0;
na_hdr->reserved[1] = 0;
na_hdr->reserved[2] = 0;
- ip6_addr_set(&(na_hdr->target_address), target_addr);
+ ip6_addr_copy_to_packed(na_hdr->target_address, *target_addr);
lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
lladdr_opt->length = (u8_t)lladdr_opt_len;
@@ -1054,9 +1271,11 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
/* Generate the solicited node address for the target address. */
if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
dest_addr = &multicast_address;
} else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
ip6_addr_set_allnodes_linklocal(&multicast_address);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
dest_addr = &multicast_address;
} else {
dest_addr = ip6_current_src_addr();
@@ -1072,7 +1291,7 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
/* Send the packet out. */
ND6_STATS_INC(nd6.xmit);
ip6_output_if(p, src_addr, dest_addr,
- LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(p);
}
@@ -1101,6 +1320,7 @@ nd6_send_rs(struct netif *netif)
/* Generate the all routers target address. */
ip6_addr_set_allrouters_linklocal(&multicast_address);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
/* Allocate a packet. */
if (src_addr != IP6_ADDR_ANY6) {
@@ -1139,7 +1359,7 @@ nd6_send_rs(struct netif *netif)
ND6_STATS_INC(nd6.xmit);
err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address,
- LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(p);
return err;
@@ -1316,6 +1536,9 @@ static s8_t
nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr)
{
s8_t i;
+
+ IP6_ADDR_ZONECHECK(ip6addr);
+
for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) {
return i;
@@ -1373,7 +1596,8 @@ nd6_clear_destination_cache(void)
}
/**
- * Determine whether an address matches an on-link prefix.
+ * Determine whether an address matches an on-link prefix or the subnet of a
+ * statically assigned address.
*
* @param ip6addr the IPv6 address to match
* @return 1 if the address is on-link, 0 otherwise
@@ -1382,6 +1606,8 @@ static s8_t
nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
{
s8_t i;
+
+ /* Check to see if the address matches an on-link prefix. */
for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
if ((prefix_list[i].netif == netif) &&
(prefix_list[i].invalidation_timer > 0) &&
@@ -1389,9 +1615,13 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
return 1;
}
}
- /* Check to see if address prefix matches a (manually?) configured address. */
+ /* Check to see if address prefix matches a manually configured (= static)
+ * address. Static addresses have an implied /64 subnet assignment. Dynamic
+ * addresses (from autoconfiguration) have no implied subnet assignment, and
+ * are thus effectively /128 assignments. See RFC 5942 for more on this. */
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ netif_ip6_addr_isstatic(netif, i) &&
ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {
return 1;
}
@@ -1402,6 +1632,11 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
/**
* Select a default router for a destination.
*
+ * This function is used both for routing and for finding a next-hop target for
+ * a packet. In the former case, the given netif is NULL, and the returned
+ * router entry must be for a netif suitable for sending packets (up, link up).
+ * In the latter case, the given netif is not NULL and restricts router choice.
+ *
* @param ip6addr the destination address
* @param netif the netif for the outgoing packet, if known
* @return the default router entry index, or -1 if no suitable
@@ -1410,48 +1645,58 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
static s8_t
nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
{
- s8_t i;
- /* last_router is used for round-robin router selection (as recommended
- * in RFC). This is more robust in case one router is not reachable,
- * we are not stuck trying to resolve it. */
+ struct netif *router_netif;
+ s8_t i, j, valid_router;
static s8_t last_router;
- (void)ip6addr; /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
+
+ LWIP_UNUSED_ARG(ip6addr); /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
/* @todo: implement default router preference */
- /* Look for reachable routers. */
+ /* Look for valid routers. A reachable router is preferred. */
+ valid_router = -1;
for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
- if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
- last_router = 0;
- }
- if ((default_router_list[i].neighbor_entry != NULL) &&
- (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
- (default_router_list[i].invalidation_timer > 0) &&
- (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) {
- return i;
+ /* Is the router netif both set and apppropriate? */
+ if (default_router_list[i].neighbor_entry != NULL) {
+ router_netif = default_router_list[i].neighbor_entry->netif;
+ if ((router_netif != NULL) && (netif != NULL ? netif == router_netif :
+ (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) {
+ /* Is the router valid, i.e., reachable or probably reachable as per
+ * RFC 4861 Sec. 6.3.6? Note that we will never return a router that
+ * has no neighbor cache entry, due to the netif association tests. */
+ if (default_router_list[i].neighbor_entry->state != ND6_INCOMPLETE) {
+ /* Is the router known to be reachable? */
+ if (default_router_list[i].neighbor_entry->state == ND6_REACHABLE) {
+ return i; /* valid and reachable - done! */
+ } else if (valid_router < 0) {
+ valid_router = i; /* valid but not known to be reachable */
+ }
+ }
+ }
}
}
-
- /* Look for router in other reachability states, but still valid according to timer. */
- for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
- if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
- last_router = 0;
- }
- if ((default_router_list[i].neighbor_entry != NULL) &&
- (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
- (default_router_list[i].invalidation_timer > 0)) {
- return i;
- }
+ if (valid_router >= 0) {
+ return valid_router;
}
/* Look for any router for which we have any information at all. */
- for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
- if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
- last_router = 0;
+ /* last_router is used for round-robin selection of incomplete routers, as
+ * recommended in RFC 4861 Sec. 6.3.6 point (2). Advance only when picking a
+ * route, to select the same router as next-hop target in the common case. */
+ if ((netif == NULL) && (++last_router >= LWIP_ND6_NUM_ROUTERS)) {
+ last_router = 0;
+ }
+ i = last_router;
+ for (j = 0; j < LWIP_ND6_NUM_ROUTERS; j++) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ router_netif = default_router_list[i].neighbor_entry->netif;
+ if ((router_netif != NULL) && (netif != NULL ? netif == router_netif :
+ (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) {
+ return i;
+ }
}
- if (default_router_list[i].neighbor_entry != NULL &&
- (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) {
- return i;
+ if (++i >= LWIP_ND6_NUM_ROUTERS) {
+ i = 0;
}
}
@@ -1460,10 +1705,11 @@ nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
}
/**
- * Find a router-announced route to the given destination.
+ * Find a router-announced route to the given destination. This route may be
+ * based on an on-link prefix or a default router.
*
- * The caller is responsible for checking whether the returned netif, if any,
- * is in a suitable state (up, link up) to be used for packet transmission.
+ * If a suitable route is found, the returned netif is guaranteed to be in a
+ * suitable state (up, link up) to be used for packet transmission.
*
* @param ip6addr the destination IPv6 address
* @return the netif to use for the destination, or NULL if none found
@@ -1471,13 +1717,27 @@ nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
struct netif *
nd6_find_route(const ip6_addr_t *ip6addr)
{
+ struct netif *netif;
s8_t i;
+ /* @todo decide if it makes sense to check the destination cache first */
+
+ /* Check if there is a matching on-link prefix. There may be multiple
+ * matches. Pick the first one that is associated with a suitable netif. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+ netif = prefix_list[i].netif;
+ if ((netif != NULL) && ip6_addr_netcmp(&prefix_list[i].prefix, ip6addr) &&
+ netif_is_up(netif) && netif_is_link_up(netif)) {
+ return netif;
+ }
+ }
+
+ /* No on-link prefix match. Find a router that can forward the packet. */
i = nd6_select_router(ip6addr, NULL);
if (i >= 0) {
- if (default_router_list[i].neighbor_entry != NULL) {
- return default_router_list[i].neighbor_entry->netif; /* may be NULL */
- }
+ LWIP_ASSERT("selected router must have a neighbor entry",
+ default_router_list[i].neighbor_entry != NULL);
+ return default_router_list[i].neighbor_entry->netif;
}
return NULL;
@@ -1495,6 +1755,8 @@ nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif)
{
s8_t i;
+ IP6_ADDR_ZONECHECK_NETIF(router_addr, netif);
+
/* Look for router. */
for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
if ((default_router_list[i].neighbor_entry != NULL) &&
@@ -1522,6 +1784,8 @@ nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
s8_t free_router_index;
s8_t neighbor_index;
+ IP6_ADDR_ZONECHECK_NETIF(router_addr, netif);
+
/* Do we have a neighbor entry for this router? */
neighbor_index = nd6_find_neighbor_cache_entry(router_addr);
if (neighbor_index < 0) {
@@ -1577,7 +1841,7 @@ nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
* @return the index on the prefix table, or -1 if not found
*/
static s8_t
-nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
+nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
{
s8_t i;
@@ -1601,7 +1865,7 @@ nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
* @return the index on the prefix table, or -1 if not created
*/
static s8_t
-nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
+nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
{
s8_t i;
@@ -1612,9 +1876,6 @@ nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
/* Found empty prefix entry. */
prefix_list[i].netif = netif;
ip6_addr_set(&(prefix_list[i].prefix), prefix);
-#if LWIP_IPV6_AUTOCONFIG
- prefix_list[i].flags = 0;
-#endif /* LWIP_IPV6_AUTOCONFIG */
return i;
}
}
@@ -1643,10 +1904,12 @@ nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
#endif /* LWIP_HOOK_ND6_GET_GW */
s8_t i;
+ IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
+
#if LWIP_NETIF_HWADDRHINT
- if (netif->addr_hint != NULL) {
+ if (netif->hints != NULL) {
/* per-pcb cached entry was given */
- u8_t addr_hint = *(netif->addr_hint);
+ u8_t addr_hint = netif->hints->addr_hint;
if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) {
nd6_cached_destination_index = addr_hint;
}
@@ -1705,9 +1968,9 @@ nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
}
#if LWIP_NETIF_HWADDRHINT
- if (netif->addr_hint != NULL) {
+ if (netif->hints != NULL) {
/* per-pcb cached entry was given */
- *(netif->addr_hint) = nd6_cached_destination_index;
+ netif->hints->addr_hint = nd6_cached_destination_index;
}
#endif /* LWIP_NETIF_HWADDRHINT */
@@ -1771,12 +2034,11 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
return ERR_ARG;
}
- /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
- * to copy the whole queue into a new PBUF_RAM (see bug #11400)
- * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ /* IF q includes a pbuf that must be copied, we have to copy the whole chain
+ * into a new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
p = q;
while (p) {
- if (p->type != PBUF_ROM) {
+ if (PBUF_NEEDS_COPY(p)) {
copy_needed = 1;
break;
}
@@ -1911,7 +2173,9 @@ nd6_send_q(s8_t i)
/* Get ipv6 header. */
ip6hdr = (struct ip6_hdr *)(q->p->payload);
/* Create an aligned copy. */
- ip6_addr_set(&dest, &(ip6hdr->dest));
+ ip6_addr_copy_from_packed(dest, ip6hdr->dest);
+ /* Restore the zone, if applicable. */
+ ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif);
/* send the queued IPv6 packet */
(neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest);
/* free the queued IP packet */
@@ -1924,7 +2188,9 @@ nd6_send_q(s8_t i)
/* Get ipv6 header. */
ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
/* Create an aligned copy. */
- ip6_addr_set(&dest, &(ip6hdr->dest));
+ ip6_addr_copy_from_packed(dest, ip6hdr->dest);
+ /* Restore the zone, if applicable. */
+ ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif);
/* send the queued IPv6 packet */
(neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest);
/* free the queued IP packet */
@@ -2078,7 +2344,6 @@ nd6_cleanup_netif(struct netif *netif)
for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
if (prefix_list[i].netif == netif) {
prefix_list[i].netif = NULL;
- prefix_list[i].flags = 0;
}
}
for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
@@ -2093,6 +2358,10 @@ nd6_cleanup_netif(struct netif *netif)
nd6_free_neighbor_cache_entry(i);
}
}
+ /* Clear the destination cache, since many entries may now have become
+ * invalid for one of several reasons. As destination cache entries have no
+ * netif association, use a sledgehammer approach (this can be improved). */
+ nd6_clear_destination_cache();
}
#if LWIP_IPV6_MLD
@@ -2114,11 +2383,12 @@ nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
/* Determine whether we were, and should be, a member of the solicited-node
* multicast group for this address. For tentative addresses, the group is
* not joined until the address enters the TENTATIVE_1 (or VALID) state. */
- old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_TENTATIVE);
- new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_TENTATIVE);
+ old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_DUPLICATED && old_state != IP6_ADDR_TENTATIVE);
+ new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_DUPLICATED && new_state != IP6_ADDR_TENTATIVE);
if (old_member != new_member) {
ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
if (new_member) {
mld6_joingroup_netif(netif, &multicast_address);
diff --git a/src/core/mem.c b/../lwip_nat/src/core/mem.c
index db3b7cc5..8fb7f334 100644
--- a/src/core/mem.c
+++ b/../lwip_nat/src/core/mem.c
@@ -66,6 +66,10 @@
#include <stdlib.h> /* for malloc()/free() */
#endif
+#define MEM_STATS_INC_LOCKED(x) SYS_ARCH_LOCKED(MEM_STATS_INC(x))
+#define MEM_STATS_INC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_INC_USED(x, y))
+#define MEM_STATS_DEC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_DEC_USED(x, y))
+
#if MEM_LIBC_MALLOC || MEM_USE_POOLS
/** mem_init is not used when using pools instead of a heap or using
@@ -123,13 +127,13 @@ mem_malloc(mem_size_t size)
{
void* ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE);
if (ret == NULL) {
- MEM_STATS_INC(err);
+ MEM_STATS_INC_LOCKED(err);
} else {
LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret);
#if LWIP_STATS && MEM_STATS
*(mem_size_t*)ret = size;
ret = (u8_t*)ret + MEM_LIBC_STATSHELPER_SIZE;
- MEM_STATS_INC_USED(used, size);
+ MEM_STATS_INC_USED_LOCKED(used, size);
#endif
}
return ret;
@@ -146,7 +150,7 @@ mem_free(void *rmem)
LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
#if LWIP_STATS && MEM_STATS
rmem = (u8_t*)rmem - MEM_LIBC_STATSHELPER_SIZE;
- MEM_STATS_DEC_USED(used, *(mem_size_t*)rmem);
+ MEM_STATS_DEC_USED_LOCKED(used, *(mem_size_t*)rmem);
#endif
mem_clib_free(rmem);
}
@@ -183,7 +187,7 @@ mem_malloc(mem_size_t size)
continue;
}
#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
- MEM_STATS_INC(err);
+ MEM_STATS_INC_LOCKED(err);
return NULL;
}
break;
@@ -191,7 +195,7 @@ mem_malloc(mem_size_t size)
}
if (poolnr > MEMP_POOL_LAST) {
LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
- MEM_STATS_INC(err);
+ MEM_STATS_INC_LOCKED(err);
return NULL;
}
@@ -203,7 +207,7 @@ mem_malloc(mem_size_t size)
#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
/* truncating to u16_t is safe because struct memp_desc::size is u16_t */
element->size = (u16_t)size;
- MEM_STATS_INC_USED(used, element->size);
+ MEM_STATS_INC_USED_LOCKED(used, element->size);
#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
#if MEMP_OVERFLOW_CHECK
/* initialize unused memory (diff between requested size and selected pool's size) */
@@ -235,7 +239,7 @@ mem_free(void *rmem)
LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
- MEM_STATS_DEC_USED(used, hmem->size);
+ MEM_STATS_DEC_USED_LOCKED(used, hmem->size);
#if MEMP_OVERFLOW_CHECK
{
u16_t i;
@@ -317,11 +321,11 @@ static volatile u8_t mem_free_count;
#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-/* Protect the heap only by using a semaphore */
+/* Protect the heap only by using a mutex */
#define LWIP_MEM_FREE_DECL_PROTECT()
#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
-/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+/* mem_malloc is protected using mutex AND LWIP_MEM_ALLOC_PROTECT */
#define LWIP_MEM_ALLOC_DECL_PROTECT()
#define LWIP_MEM_ALLOC_PROTECT()
#define LWIP_MEM_ALLOC_UNPROTECT()
@@ -431,12 +435,9 @@ mem_free(void *rmem)
(u8_t *)rmem < (u8_t *)ram_end);
if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
- SYS_ARCH_DECL_PROTECT(lev);
LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
/* protect mem stats from concurrent access */
- SYS_ARCH_PROTECT(lev);
- MEM_STATS_INC(illegal);
- SYS_ARCH_UNPROTECT(lev);
+ MEM_STATS_INC_LOCKED(illegal);
return;
}
/* protect the heap from concurrent access */
@@ -468,16 +469,16 @@ mem_free(void *rmem)
* Shrink memory returned by mem_malloc().
*
* @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
- * @param newsize required size after shrinking (needs to be smaller than or
+ * @param new_size required size after shrinking (needs to be smaller than or
* equal to the previous size)
* @return for compatibility reasons: is always == rmem, at the moment
* or NULL if newsize is > old size, in which case rmem is NOT touched
* or freed!
*/
void *
-mem_trim(void *rmem, mem_size_t newsize)
+mem_trim(void *rmem, mem_size_t new_size)
{
- mem_size_t size;
+ mem_size_t size, newsize;
mem_size_t ptr, ptr2;
struct mem *mem, *mem2;
/* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
@@ -485,27 +486,23 @@ mem_trim(void *rmem, mem_size_t newsize)
/* Expand the size of the allocated memory region so that we can
adjust for alignment. */
- newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+ newsize = (mem_size_t)LWIP_MEM_ALIGN_SIZE(new_size);
+ if ((newsize > MEM_SIZE_ALIGNED) || (newsize < new_size)) {
+ return NULL;
+ }
if (newsize < MIN_SIZE_ALIGNED) {
/* every data block must be at least MIN_SIZE_ALIGNED long */
newsize = MIN_SIZE_ALIGNED;
}
- if (newsize > MEM_SIZE_ALIGNED) {
- return NULL;
- }
-
LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
(u8_t *)rmem < (u8_t *)ram_end);
if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
- SYS_ARCH_DECL_PROTECT(lev);
LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
/* protect mem stats from concurrent access */
- SYS_ARCH_PROTECT(lev);
- MEM_STATS_INC(illegal);
- SYS_ARCH_UNPROTECT(lev);
+ MEM_STATS_INC_LOCKED(illegal);
return rmem;
}
/* Get the corresponding struct mem ... */
@@ -514,7 +511,7 @@ mem_trim(void *rmem, mem_size_t newsize)
/* ... and its offset pointer */
ptr = (mem_size_t)((u8_t *)mem - ram);
- size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+ size = (mem_size_t)((mem_size_t)(mem->next - ptr) - SIZEOF_STRUCT_MEM);
LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
if (newsize > size) {
/* not supported */
@@ -535,7 +532,7 @@ mem_trim(void *rmem, mem_size_t newsize)
/* remember the old next pointer */
next = mem2->next;
/* create new struct mem which is moved directly after the shrinked mem */
- ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + newsize);
if (lfree == mem2) {
lfree = (struct mem *)(void *)&ram[ptr2];
}
@@ -563,7 +560,7 @@ mem_trim(void *rmem, mem_size_t newsize)
* @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
* region that couldn't hold data, but when mem->next gets freed,
* the 2 regions would be combined, resulting in more free memory */
- ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + newsize);
mem2 = (struct mem *)(void *)&ram[ptr2];
if (mem2 < lfree) {
lfree = mem2;
@@ -594,38 +591,38 @@ mem_trim(void *rmem, mem_size_t newsize)
/**
* Allocate a block of memory with a minimum of 'size' bytes.
*
- * @param size is the minimum size of the requested block in bytes.
+ * @param size_in is the minimum size of the requested block in bytes.
* @return pointer to allocated memory or NULL if no free memory was found.
*
* Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
*/
void *
-mem_malloc(mem_size_t size)
+mem_malloc(mem_size_t size_in)
{
- mem_size_t ptr, ptr2;
+ mem_size_t ptr, ptr2, size;
struct mem *mem, *mem2;
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
u8_t local_mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
LWIP_MEM_ALLOC_DECL_PROTECT();
- if (size == 0) {
+ if (size_in == 0) {
return NULL;
}
/* Expand the size of the allocated memory region so that we can
adjust for alignment. */
- size = LWIP_MEM_ALIGN_SIZE(size);
+ size = (mem_size_t)LWIP_MEM_ALIGN_SIZE(size_in);
+ if ((size > MEM_SIZE_ALIGNED) ||
+ (size < size_in)) {
+ return NULL;
+ }
if (size < MIN_SIZE_ALIGNED) {
/* every data block must be at least MIN_SIZE_ALIGNED long */
size = MIN_SIZE_ALIGNED;
}
- if (size > MEM_SIZE_ALIGNED) {
- return NULL;
- }
-
/* protect the heap from concurrent access */
sys_mutex_lock(&mem_mutex);
LWIP_MEM_ALLOC_PROTECT();
@@ -670,7 +667,7 @@ mem_malloc(mem_size_t size)
* region that couldn't hold data, but when mem->next gets freed,
* the 2 regions would be combined, resulting in more free memory
*/
- ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+ ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + size);
/* create mem2 struct */
mem2 = (struct mem *)(void *)&ram[ptr2];
mem2->used = 0;
@@ -734,10 +731,10 @@ mem_malloc_adjust_lfree:
/* if we got interrupted by a mem_free, try again */
} while (local_mem_free_count != 0);
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
- LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
MEM_STATS_INC(err);
LWIP_MEM_ALLOC_UNPROTECT();
sys_mutex_unlock(&mem_mutex);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
return NULL;
}
@@ -765,12 +762,18 @@ void *
mem_calloc(mem_size_t count, mem_size_t size)
{
void *p;
+ size_t alloc_size = (size_t)count * (size_t)size;
+
+ if ((size_t)(mem_size_t)alloc_size != alloc_size) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_calloc: could not allocate %"SZT_F" bytes\n", alloc_size));
+ return NULL;
+ }
/* allocate 'count' objects of size 'size' */
- p = mem_malloc(count * size);
+ p = mem_malloc((mem_size_t)alloc_size);
if (p) {
/* zero the memory */
- memset(p, 0, (size_t)count * (size_t)size);
+ memset(p, 0, alloc_size);
}
return p;
}
diff --git a/src/core/memp.c b/../lwip_nat/src/core/memp.c
index 58fab1a2..09c84f5c 100644
--- a/src/core/memp.c
+++ b/../lwip_nat/src/core/memp.c
@@ -57,12 +57,14 @@
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/priv/tcp_priv.h"
+#include "lwip/altcp.h"
#include "lwip/ip4_frag.h"
#include "lwip/netbuf.h"
#include "lwip/api.h"
#include "lwip/priv/tcpip_priv.h"
#include "lwip/priv/api_msg.h"
#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.h"
#include "lwip/netifapi.h"
#include "lwip/etharp.h"
#include "lwip/igmp.h"
@@ -237,6 +239,14 @@ memp_init_pool(const struct memp_desc *desc)
*desc->tab = NULL;
memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);
+#if MEMP_MEM_INIT
+ /* force memset on pool memory */
+ memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+ ));
+#endif
/* create a linked list of memp elements */
for (i = 0; i < desc->num; ++i) {
memp->next = *desc->tab;
@@ -337,13 +347,13 @@ do_memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int
/* cast through u8_t* to get rid of alignment warnings */
return ((u8_t*)memp + MEMP_SIZE);
} else {
- LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
#if MEMP_STATS
desc->stats->err++;
#endif
+ SYS_ARCH_UNPROTECT(old_level);
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
}
- SYS_ARCH_UNPROTECT(old_level);
return NULL;
}
diff --git a/src/core/netif.c b/../lwip_nat/src/core/netif.c
index fed070ae..19348ec6 100644
--- a/src/core/netif.c
+++ b/../lwip_nat/src/core/netif.c
@@ -50,7 +50,8 @@
#include "lwip/opt.h"
-#include <string.h>
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
#include "lwip/def.h"
#include "lwip/ip_addr.h"
@@ -101,9 +102,16 @@
#define NETIF_LINK_CALLBACK(n)
#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+static netif_ext_callback_t* ext_callback;
+#endif
+
+#if !LWIP_SINGLE_NETIF
struct netif *netif_list;
+#endif /* !LWIP_SINGLE_NETIF */
struct netif *netif_default;
+#define netif_index_to_num(index) ((index) - 1)
static u8_t netif_num;
#if LWIP_NUM_NETIF_CLIENT_DATA > 0
@@ -153,7 +161,7 @@ netif_loopif_init(struct netif *netif)
netif->output_ip6 = netif_loop_output_ipv6;
#endif
#if LWIP_LOOPIF_MULTICAST
- netif->flags |= NETIF_FLAG_IGMP;
+ netif_set_flags(netif, NETIF_FLAG_IGMP);
#endif
return ERR_OK;
}
@@ -210,6 +218,22 @@ netif_input(struct pbuf *p, struct netif *inp)
return ip_input(p, inp);
}
+/**
+ * @ingroup netif
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * Same as @ref netif_add but without IPv4 addresses
+ */
+struct netif *
+netif_add_noaddr(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input)
+{
+ return netif_add(netif,
+#if LWIP_IPV4
+ NULL, NULL, NULL,
+#endif /* LWIP_IPV4*/
+ state, init, input);
+}
+
/**
* @ingroup netif
* Add a network interface to the list of lwIP netifs.
@@ -248,10 +272,27 @@ netif_add(struct netif *netif,
s8_t i;
#endif
+#if LWIP_SINGLE_NETIF
+ if (netif_default != NULL) {
+ LWIP_ASSERT("single netif already set", 0);
+ return NULL;
+ }
+#endif
+
LWIP_ASSERT("No init function given", init != NULL);
- /* reset new interface configuration state */
#if LWIP_IPV4
+ if (ipaddr == NULL) {
+ ipaddr = ip_2_ip4(IP4_ADDR_ANY);
+ }
+ if (netmask == NULL) {
+ netmask = ip_2_ip4(IP4_ADDR_ANY);
+ }
+ if (gw == NULL) {
+ gw = ip_2_ip4(IP4_ADDR_ANY);
+ }
+
+ /* reset new interface configuration state */
ip_addr_set_zero_ip4(&netif->ip_addr);
ip_addr_set_zero_ip4(&netif->netmask);
ip_addr_set_zero_ip4(&netif->gw);
@@ -260,6 +301,10 @@ netif_add(struct netif *netif,
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
ip_addr_set_zero_ip6(&netif->ip6_addr[i]);
netif->ip6_addr_state[i] = IP6_ADDR_INVALID;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ netif->ip6_addr_valid_life[i] = IP6_ADDR_LIFE_STATIC;
+ netif->ip6_addr_pref_life[i] = IP6_ADDR_LIFE_STATIC;
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
}
netif->output_ip6 = netif_null_output_ip6;
#endif /* LWIP_IPV6 */
@@ -269,12 +314,8 @@ netif_add(struct netif *netif,
memset(netif->client_data, 0, sizeof(netif->client_data));
#endif /* LWIP_NUM_NETIF_CLIENT_DATA */
#if LWIP_IPV6_AUTOCONFIG
-#if ESP_IPV6_AUTOCONFIG
- netif->ip6_autoconfig_enabled = 1;
-#else
/* IPv6 address autoconfiguration not enabled by default */
netif->ip6_autoconfig_enabled = 0;
-#endif
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
@@ -298,10 +339,10 @@ netif_add(struct netif *netif,
/* remember netif specific state information data */
netif->state = state;
- netif->num = netif_num++;
+ netif->num = netif_num;
netif->input = input;
- NETIF_SET_HWADDRHINT(netif, NULL);
+ NETIF_RESET_HINTS(netif);
#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
netif->loop_cnt_current = 0;
#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
@@ -315,9 +356,40 @@ netif_add(struct netif *netif,
return NULL;
}
+#if !LWIP_SINGLE_NETIF
+ /* Assign a unique netif number in the range [0..254], so that (num+1) can
+ serve as an interface index that fits in a u8_t.
+ We assume that the new netif has not yet been added to the list here.
+ This algorithm is O(n^2), but that should be OK for lwIP.
+ */
+ {
+ struct netif *netif2;
+ int num_netifs;
+ do {
+ if (netif->num == 255) {
+ netif->num = 0;
+ }
+ num_netifs = 0;
+ for (netif2 = netif_list; netif2 != NULL; netif2 = netif2->next) {
+ num_netifs++;
+ LWIP_ASSERT("too many netifs, max. supported number is 255", num_netifs <= 255);
+ if (netif2->num == netif->num) {
+ netif->num++;
+ break;
+ }
+ }
+ } while (netif2 != NULL);
+ }
+ if (netif->num == 254) {
+ netif_num = 0;
+ } else {
+ netif_num = (u8_t)(netif->num + 1);
+ }
+
/* add this netif to the list */
netif->next = netif_list;
netif_list = netif;
+#endif /* "LWIP_SINGLE_NETIF */
mib2_netif_added(netif);
#if LWIP_IGMP
@@ -338,6 +410,9 @@ netif_add(struct netif *netif,
ip4_addr_debug_print(NETIF_DEBUG, gw);
#endif /* LWIP_IPV4 */
LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+
+ netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);
+
return netif;
}
@@ -356,6 +431,16 @@ void
netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
const ip4_addr_t *gw)
{
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ u8_t something_changed = 0;
+
+ if ((ip4_addr_cmp(ipaddr, netif_ip4_addr(netif)) == 0) ||
+ (ip4_addr_cmp(gw, netif_ip4_gw(netif)) == 0) ||
+ (ip4_addr_cmp(netmask, netif_ip4_netmask(netif)) == 0)) {
+ something_changed = 1;
+ }
+#endif
+
if (ip4_addr_isany(ipaddr)) {
/* when removing an address, we have to remove it *before* changing netmask/gw
to ensure that tcp RST segment can be sent correctly */
@@ -368,18 +453,14 @@ netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *
/* set ipaddr last to ensure netmask/gw have been set when status callback is called */
netif_set_ipaddr(netif, ipaddr);
}
-}
-#endif /* LWIP_IPV4*/
-/**
- * Set the netif flags for GARP
- */
-#if ESP_GRATUITOUS_ARP
-void netif_set_garp_flag(struct netif *netif)
-{
- netif->flags |= NETIF_FLAG_GARP;
-}
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ if (something_changed != 0) {
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_SETTINGS_CHANGED, NULL);
+ }
#endif
+}
+#endif /* LWIP_IPV4*/
/**
* @ingroup netif
@@ -398,6 +479,8 @@ netif_remove(struct netif *netif)
return;
}
+ netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_REMOVED, NULL);
+
#if LWIP_IPV4
if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
#if LWIP_TCP
@@ -443,13 +526,6 @@ netif_remove(struct netif *netif)
netif_set_down(netif);
}
-#if ESP_DHCP
-#if LWIP_DHCP
- /* netif not under DHCP control by default */
- dhcp_cleanup(netif);
-#endif /* LWIP_DHCP */
-#endif
-
mib2_remove_ip4(netif);
/* this netif is default? */
@@ -457,6 +533,7 @@ netif_remove(struct netif *netif)
/* reset default netif */
netif_set_default(NULL);
}
+#if !LWIP_SINGLE_NETIF
/* is it the first netif? */
if (netif_list == netif) {
netif_list = netif->next;
@@ -473,6 +550,7 @@ netif_remove(struct netif *netif)
return; /* netif is not on the list */
}
}
+#endif /* !LWIP_SINGLE_NETIF */
mib2_netif_removed(netif);
#if LWIP_NETIF_REMOVE_CALLBACK
if (netif->remove_callback) {
@@ -482,37 +560,6 @@ netif_remove(struct netif *netif)
LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
}
-/**
- * @ingroup netif
- * Find a network interface by searching for its name
- *
- * @param name the name of the netif (like netif->name) plus concatenated number
- * in ascii representation (e.g. 'en0')
- */
-struct netif *
-netif_find(const char *name)
-{
- struct netif *netif;
- u8_t num;
-
- if (name == NULL) {
- return NULL;
- }
-
- num = (u8_t)(name[2] - '0');
-
- for (netif = netif_list; netif != NULL; netif = netif->next) {
- if (num == netif->num &&
- name[0] == netif->name[0] &&
- name[1] == netif->name[1]) {
- LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
- return netif;
- }
- }
- LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
- return NULL;
-}
-
#if LWIP_IPV4
/**
* @ingroup netif_ip4
@@ -531,33 +578,20 @@ netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
*ip_2_ip4(&new_addr) = (ipaddr ? *ipaddr : *IP4_ADDR_ANY4);
IP_SET_TYPE_VAL(new_addr, IPADDR_TYPE_V4);
-#if ESP_LWIP
-#if ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES
- ip_addr_t *last_addr = &(netif->last_ip_addr);
-#else
- ip_addr_t *last_addr = &(netif->ip_addr);
-#endif
-#endif
-
/* address is actually being changed? */
if (ip4_addr_cmp(ip_2_ip4(&new_addr), netif_ip4_addr(netif)) == 0) {
+ ip_addr_t old_addr;
+ ip_addr_copy(old_addr, *netif_ip_addr4(netif));
+
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
#if LWIP_TCP
-#if ESP_LWIP
- tcp_netif_ip_addr_changed(last_addr, &new_addr);
-#else
- tcp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
-#endif
+ tcp_netif_ip_addr_changed(&old_addr, &new_addr);
#endif /* LWIP_TCP */
#if LWIP_UDP
-#if ESP_LWIP
- udp_netif_ip_addr_changed(last_addr, &new_addr);
-#else
- udp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
-#endif
+ udp_netif_ip_addr_changed(&old_addr, &new_addr);
#endif /* LWIP_UDP */
#if LWIP_RAW
- raw_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+ raw_netif_ip_addr_changed(&old_addr, &new_addr);
#endif /* LWIP_RAW */
mib2_remove_ip4(netif);
@@ -571,6 +605,13 @@ netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4);
NETIF_STATUS_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.ipv4_changed.old_address = &old_addr;
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_ADDRESS_CHANGED, &args);
+ }
+#endif
}
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
@@ -579,12 +620,6 @@ netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
ip4_addr2_16(netif_ip4_addr(netif)),
ip4_addr3_16(netif_ip4_addr(netif)),
ip4_addr4_16(netif_ip4_addr(netif))));
-
-#if ESP_LWIP
- if (ipaddr && !ip4_addr_isany(ipaddr)) {
- ip4_addr_set(ip_2_ip4(&netif->last_ip_addr), ipaddr);
- }
-#endif
}
/**
@@ -599,14 +634,27 @@ netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
void
netif_set_gw(struct netif *netif, const ip4_addr_t *gw)
{
- ip4_addr_set(ip_2_ip4(&netif->gw), gw);
- IP_SET_TYPE_VAL(netif->gw, IPADDR_TYPE_V4);
- LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- netif->name[0], netif->name[1],
- ip4_addr1_16(netif_ip4_gw(netif)),
- ip4_addr2_16(netif_ip4_gw(netif)),
- ip4_addr3_16(netif_ip4_gw(netif)),
- ip4_addr4_16(netif_ip4_gw(netif))));
+ const ip4_addr_t *safe_gw = gw ? gw : IP4_ADDR_ANY4;
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ netif_ext_callback_args_t args;
+ ip_addr_t old_addr;
+ ip_addr_copy(old_addr, *netif_ip_gw4(netif));
+ args.ipv4_gw_changed.old_address = &old_addr;
+#endif
+
+ /* address is actually being changed? */
+ if (ip4_addr_cmp(safe_gw, netif_ip4_gw(netif)) == 0) {
+ ip4_addr_set(ip_2_ip4(&netif->gw), gw);
+ IP_SET_TYPE_VAL(netif->gw, IPADDR_TYPE_V4);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(netif_ip4_gw(netif)),
+ ip4_addr2_16(netif_ip4_gw(netif)),
+ ip4_addr3_16(netif_ip4_gw(netif)),
+ ip4_addr4_16(netif_ip4_gw(netif))));
+
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_GATEWAY_CHANGED, &args);
+ }
}
/**
@@ -622,17 +670,30 @@ netif_set_gw(struct netif *netif, const ip4_addr_t *gw)
void
netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask)
{
- mib2_remove_route_ip4(0, netif);
- /* set new netmask to netif */
- ip4_addr_set(ip_2_ip4(&netif->netmask), netmask);
- IP_SET_TYPE_VAL(netif->netmask, IPADDR_TYPE_V4);
- mib2_add_route_ip4(0, netif);
- LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- netif->name[0], netif->name[1],
- ip4_addr1_16(netif_ip4_netmask(netif)),
- ip4_addr2_16(netif_ip4_netmask(netif)),
- ip4_addr3_16(netif_ip4_netmask(netif)),
- ip4_addr4_16(netif_ip4_netmask(netif))));
+ const ip4_addr_t *safe_netmask = netmask ? netmask : IP4_ADDR_ANY4;
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ netif_ext_callback_args_t args;
+ ip_addr_t old_addr;
+ ip_addr_copy(old_addr, *netif_ip_netmask4(netif));
+ args.ipv4_nm_changed.old_address = &old_addr;
+#endif
+
+ /* address is actually being changed? */
+ if (ip4_addr_cmp(safe_netmask, netif_ip4_netmask(netif)) == 0) {
+ mib2_remove_route_ip4(0, netif);
+ /* set new netmask to netif */
+ ip4_addr_set(ip_2_ip4(&netif->netmask), netmask);
+ IP_SET_TYPE_VAL(netif->netmask, IPADDR_TYPE_V4);
+ mib2_add_route_ip4(0, netif);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(netif_ip4_netmask(netif)),
+ ip4_addr2_16(netif_ip4_netmask(netif)),
+ ip4_addr3_16(netif_ip4_netmask(netif)),
+ ip4_addr4_16(netif_ip4_netmask(netif))));
+
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_NETMASK_CHANGED, &args);
+ }
}
#endif /* LWIP_IPV4 */
@@ -667,12 +728,20 @@ void
netif_set_up(struct netif *netif)
{
if (!(netif->flags & NETIF_FLAG_UP)) {
- netif->flags |= NETIF_FLAG_UP;
+ netif_set_flags(netif, NETIF_FLAG_UP);
MIB2_COPY_SYSUPTIME_TO(&netif->ts);
NETIF_STATUS_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.status_changed.state = 1;
+ netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
+ }
+#endif
+
if (netif->flags & NETIF_FLAG_LINK_UP) {
netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
}
@@ -725,7 +794,15 @@ void
netif_set_down(struct netif *netif)
{
if (netif->flags & NETIF_FLAG_UP) {
- netif->flags &= ~NETIF_FLAG_UP;
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.status_changed.state = 0;
+ netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
+ }
+#endif
+
+ netif_clear_flags(netif, NETIF_FLAG_UP);
MIB2_COPY_SYSUPTIME_TO(&netif->ts);
#if LWIP_IPV4 && LWIP_ARP
@@ -778,7 +855,7 @@ void
netif_set_link_up(struct netif *netif)
{
if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
- netif->flags |= NETIF_FLAG_LINK_UP;
+ netif_set_flags(netif, NETIF_FLAG_LINK_UP);
#if LWIP_DHCP
dhcp_network_changed(netif);
@@ -792,6 +869,13 @@ netif_set_link_up(struct netif *netif)
netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
}
NETIF_LINK_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.link_changed.state = 1;
+ netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
+ }
+#endif
}
}
@@ -803,8 +887,15 @@ void
netif_set_link_down(struct netif *netif )
{
if (netif->flags & NETIF_FLAG_LINK_UP) {
- netif->flags &= ~NETIF_FLAG_LINK_UP;
+ netif_clear_flags(netif, NETIF_FLAG_LINK_UP);
NETIF_LINK_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.link_changed.state = 0;
+ netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
+ }
+#endif
}
}
@@ -869,14 +960,14 @@ netif_loop_output(struct netif *netif, struct pbuf *p)
clen = pbuf_clen(r);
/* check for overflow or too many pbuf on queue */
if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
- ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+ ((netif->loop_cnt_current + clen) > LWIP_MIN(LWIP_LOOPBACK_MAX_PBUFS, 0xFFFF))) {
pbuf_free(r);
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
return ERR_MEM;
}
- netif->loop_cnt_current += clen;
+ netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current + clen);
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
/* Copy the whole pbuf queue p into the single pbuf r */
@@ -913,7 +1004,7 @@ netif_loop_output(struct netif *netif, struct pbuf *p)
#if LWIP_NETIF_LOOPBACK_MULTITHREADING
/* For multithreading environment, schedule a call to netif_poll */
- tcpip_callback_with_block((tcpip_callback_fn)netif_poll, netif, 0);
+ tcpip_try_callback((tcpip_callback_fn)netif_poll, netif);
#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
return ERR_OK;
@@ -980,7 +1071,7 @@ netif_poll(struct netif *netif)
/* adjust the number of pbufs on queue */
LWIP_ASSERT("netif->loop_cnt_current underflow",
((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
- netif->loop_cnt_current -= clen;
+ netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current - clen);
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
/* 'in_end' now points to the last pbuf from 'in' */
@@ -996,6 +1087,8 @@ netif_poll(struct netif *netif)
in_end->next = NULL;
SYS_ARCH_UNPROTECT(lev);
+ in->if_idx = netif_get_index(netif);
+
LINK_STATS_INC(link.recv);
MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
@@ -1015,12 +1108,10 @@ netif_poll(struct netif *netif)
void
netif_poll_all(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
/* loop through netifs */
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
netif_poll(netif);
- /* proceed to next network interface */
- netif = netif->next;
}
}
#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
@@ -1039,8 +1130,11 @@ netif_alloc_client_data_id(void)
u8_t result = netif_client_id;
netif_client_id++;
+#if LWIP_NUM_NETIF_CLIENT_DATA > 256
+#error LWIP_NUM_NETIF_CLIENT_DATA must be <= 256
+#endif
LWIP_ASSERT("Increase LWIP_NUM_NETIF_CLIENT_DATA in lwipopts.h", result < LWIP_NUM_NETIF_CLIENT_DATA);
- return result + LWIP_NETIF_CLIENT_DATA_INDEX_MAX;
+ return (u8_t)(result + LWIP_NETIF_CLIENT_DATA_INDEX_MAX);
}
#endif
@@ -1076,21 +1170,23 @@ netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6)
void
netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3)
{
- const ip6_addr_t *old_addr;
+ ip_addr_t old_addr;
+ ip_addr_t new_ipaddr;
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
- old_addr = netif_ip6_addr(netif, addr_idx);
+ ip6_addr_copy(*ip_2_ip6(&old_addr), *netif_ip6_addr(netif, addr_idx));
+ IP_SET_TYPE_VAL(old_addr, IPADDR_TYPE_V6);
+
/* address is actually being changed? */
- if ((old_addr->addr[0] != i0) || (old_addr->addr[1] != i1) ||
- (old_addr->addr[2] != i2) || (old_addr->addr[3] != i3)) {
+ if ((ip_2_ip6(&old_addr)->addr[0] != i0) || (ip_2_ip6(&old_addr)->addr[1] != i1) ||
+ (ip_2_ip6(&old_addr)->addr[2] != i2) || (ip_2_ip6(&old_addr)->addr[3] != i3)) {
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n"));
+ IP_ADDR6(&new_ipaddr, i0, i1, i2, i3);
+ ip6_addr_assign_zone(ip_2_ip6(&new_ipaddr), IP6_UNICAST, netif);
+
if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
-#if LWIP_TCP || LWIP_UDP
- ip_addr_t new_ipaddr;
- IP_ADDR6(&new_ipaddr, i0, i1, i2, i3);
-#endif /* LWIP_TCP || LWIP_UDP */
#if LWIP_TCP
tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
#endif /* LWIP_TCP */
@@ -1103,13 +1199,21 @@ netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1,
}
/* @todo: remove/readd mib2 ip6 entries? */
- IP6_ADDR(ip_2_ip6(&(netif->ip6_addr[addr_idx])), i0, i1, i2, i3);
- IP_SET_TYPE_VAL(netif->ip6_addr[addr_idx], IPADDR_TYPE_V6);
+ ip_addr_copy(netif->ip6_addr[addr_idx], new_ipaddr);
if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
NETIF_STATUS_CALLBACK(netif);
}
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.ipv6_set.addr_index = addr_idx;
+ args.ipv6_set.old_address = &old_addr;
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV6_SET, &args);
+ }
+#endif
}
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
@@ -1165,24 +1269,40 @@ netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state)
if (!old_valid && new_valid) {
/* address added by setting valid */
+ /* This is a good moment to check that the address is properly zoned. */
+ IP6_ADDR_ZONECHECK_NETIF(netif_ip6_addr(netif, addr_idx), netif);
/* @todo: add mib2 ip6 entries? */
netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
}
- if ((old_state & IP6_ADDR_PREFERRED) != (state & IP6_ADDR_PREFERRED)) {
- /* address state has changed (valid flag changed or switched between
- preferred and deprecated) -> call the callback function */
+ if ((old_state & ~IP6_ADDR_TENTATIVE_COUNT_MASK) !=
+ (state & ~IP6_ADDR_TENTATIVE_COUNT_MASK)) {
+ /* address state has changed -> call the callback function */
NETIF_STATUS_CALLBACK(netif);
}
}
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.ipv6_addr_state_changed.addr_index = addr_idx;
+ args.ipv6_addr_state_changed.address = netif_ip_addr6(netif, addr_idx);
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV6_ADDR_STATE_CHANGED, &args);
+ }
+#endif
+
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
netif_ip6_addr_state(netif, addr_idx)));
}
/**
- * Checks if a specific address is assigned to the netif and returns its
- * index.
+ * Checks if a specific local address is present on the netif and returns its
+ * index. Depending on its state, it may or may not be assigned to the
+ * interface (as per RFC terminology).
+ *
+ * The given address may or may not be zoned (i.e., have a zone index other
+ * than IP6_NO_ZONE). If the address is zoned, it must have the correct zone
+ * for the given netif, or no match will be found.
*
* @param netif the netif to check
* @param ip6addr the IPv6 address to find
@@ -1193,9 +1313,16 @@ s8_t
netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr)
{
s8_t i;
+
+#if LWIP_IPV6_SCOPES
+ if (ip6_addr_has_zone(ip6addr) && !ip6_addr_test_zone(ip6addr, netif)) {
+ return -1; /* wrong zone, no match */
+ }
+#endif /* LWIP_IPV6_SCOPES */
+
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) {
+ ip6_addr_cmp_zoneless(netif_ip6_addr(netif, i), ip6addr)) {
return i;
}
}
@@ -1226,7 +1353,7 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit)
((u32_t)(netif->hwaddr[1]) << 16) |
((u32_t)(netif->hwaddr[2]) << 8) |
(0xff));
- ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((0xfeul << 24) |
+ ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((u32_t)(0xfeul << 24) |
((u32_t)(netif->hwaddr[3]) << 16) |
((u32_t)(netif->hwaddr[4]) << 8) |
(netif->hwaddr[5]));
@@ -1244,6 +1371,16 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit)
}
}
+ /* Set a link-local zone. Even though the zone is implied by the owning
+ * netif, setting the zone anyway has two important conceptual advantages:
+ * 1) it avoids the need for a ton of exceptions in internal code, allowing
+ * e.g. ip6_addr_cmp() to be used on local addresses;
+ * 2) the properly zoned address is visible externally, e.g. when any outside
+ * code enumerates available addresses or uses one to bind a socket.
+ * Any external code unaware of address scoping is likely to just ignore the
+ * zone field, so this should not create any compatibility problems. */
+ ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[0]), IP6_UNICAST, netif);
+
/* Set address state. */
#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
/* Will perform duplicate address detection (DAD). */
@@ -1278,10 +1415,11 @@ netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chos
return ERR_OK;
}
- /* Find a free slot -- musn't be the first one (reserved for link local) */
- for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ /* Find a free slot. The first one is reserved for link-local addresses. */
+ for (i = ip6_addr_islinklocal(ip6addr) ? 0 : 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr);
+ ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[i]), IP6_UNICAST, netif);
netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE);
if (chosen_idx != NULL) {
*chosen_idx = i;
@@ -1308,3 +1446,132 @@ netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipa
return ERR_IF;
}
#endif /* LWIP_IPV6 */
+
+/**
+* @ingroup netif
+* Return the interface index for the netif with name
+* or NETIF_NO_INDEX if not found/on error
+*
+* @param name the name of the netif
+*/
+u8_t
+netif_name_to_index(const char *name)
+{
+ struct netif *netif = netif_find(name);
+ if (netif != NULL) {
+ return netif_get_index(netif);
+ }
+ /* No name found, return invalid index */
+ return NETIF_NO_INDEX;
+}
+
+/**
+* @ingroup netif
+* Return the interface name for the netif matching index
+* or NULL if not found/on error
+*
+* @param idx the interface index of the netif
+* @param name char buffer of at least NETIF_NAMESIZE bytes
+*/
+char *
+netif_index_to_name(u8_t idx, char *name)
+{
+ struct netif *netif = netif_get_by_index(idx);
+
+ if (netif != NULL) {
+ name[0] = netif->name[0];
+ name[1] = netif->name[1];
+ lwip_itoa(&name[2], NETIF_NAMESIZE - 2, netif_index_to_num(idx));
+ return name;
+ }
+ return NULL;
+}
+
+/**
+* @ingroup netif
+* Return the interface for the netif index
+*
+* @param idx index of netif to find
+*/
+struct netif*
+netif_get_by_index(u8_t idx)
+{
+ struct netif* netif;
+
+ if (idx != NETIF_NO_INDEX) {
+ NETIF_FOREACH(netif) {
+ if (idx == netif_get_index(netif)) {
+ return netif; /* found! */
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @ingroup netif
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(const char *name)
+{
+ struct netif *netif;
+ u8_t num;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ num = (u8_t)atoi(&name[2]);
+
+ NETIF_FOREACH(netif) {
+ if (num == netif->num &&
+ name[0] == netif->name[0] &&
+ name[1] == netif->name[1]) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+ return netif;
+ }
+ }
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+ return NULL;
+}
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+/**
+ * @ingroup netif
+ * Add extended netif events listener
+ * @param callback pointer to listener structure
+ * @param fn callback function
+ */
+void netif_add_ext_callback(netif_ext_callback_t* callback, netif_ext_callback_fn fn)
+{
+ LWIP_ASSERT("callback must be != NULL", callback != NULL);
+ LWIP_ASSERT("fn must be != NULL", fn != NULL);
+
+ callback->callback_fn = fn;
+ callback->next = ext_callback;
+ ext_callback = callback;
+}
+
+/**
+ * Invoke extended netif status event
+ * @param netif netif that is affected by change
+ * @param reason change reason
+ * @param args depends on reason, see reason description
+ */
+void netif_invoke_ext_callback(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
+{
+ netif_ext_callback_t* callback = ext_callback;
+
+ LWIP_ASSERT("netif must be != NULL", netif != NULL);
+
+ while (callback != NULL) {
+ callback->callback_fn(netif, reason, args);
+ callback = callback->next;
+ }
+}
+#endif /* LWIP_NETIF_EXT_STATUS_CALLBACK */
diff --git a/src/core/pbuf.c b/../lwip_nat/src/core/pbuf.c
index 574fe2b4..2eba75e1 100644
--- a/src/core/pbuf.c
+++ b/../lwip_nat/src/core/pbuf.c
@@ -33,48 +33,7 @@
* Therefore, looping through a pbuf of a single packet, has an
* loop end condition (tot_len == p->len), NOT (next == NULL).
*
- * Example of custom pbuf usage for zero-copy RX:
- @code{.c}
-typedef struct my_custom_pbuf
-{
- struct pbuf_custom p;
- void* dma_descriptor;
-} my_custom_pbuf_t;
-
-LWIP_MEMPOOL_DECLARE(RX_POOL, 10, sizeof(my_custom_pbuf_t), "Zero-copy RX PBUF pool");
-
-void my_pbuf_free_custom(void* p)
-{
- my_custom_pbuf_t* my_puf = (my_custom_pbuf_t*)p;
-
- LOCK_INTERRUPTS();
- free_rx_dma_descriptor(my_pbuf->dma_descriptor);
- LWIP_MEMPOOL_FREE(RX_POOL, my_pbuf);
- UNLOCK_INTERRUPTS();
-}
-
-void eth_rx_irq()
-{
- dma_descriptor* dma_desc = get_RX_DMA_descriptor_from_ethernet();
- my_custom_pbuf_t* my_pbuf = (my_custom_pbuf_t*)LWIP_MEMPOOL_ALLOC(RX_POOL);
-
- my_pbuf->p.custom_free_function = my_pbuf_free_custom;
- my_pbuf->dma_descriptor = dma_desc;
-
- invalidate_cpu_cache(dma_desc->rx_data, dma_desc->rx_length);
-
- struct pbuf* p = pbuf_alloced_custom(PBUF_RAW,
- dma_desc->rx_length,
- PBUF_REF,
- &my_pbuf->p,
- dma_desc->rx_data,
- dma_desc->max_buffer_size);
-
- if(netif->input(p, netif) != ERR_OK) {
- pbuf_free(p);
- }
-}
- @endcode
+ * Example of custom pbuf usage: @ref zerocopyrx
*/
/*
@@ -111,12 +70,13 @@ void eth_rx_irq()
#include "lwip/opt.h"
+#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
-#include "lwip/pbuf.h"
#include "lwip/sys.h"
+#include "lwip/netif.h"
#if LWIP_TCP && TCP_QUEUE_OOSEQ
#include "lwip/priv/tcp_priv.h"
#endif
@@ -139,7 +99,7 @@ void eth_rx_irq()
#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
#include "lwip/tcpip.h"
#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \
- if (tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \
+ if (tcpip_try_callback(pbuf_free_ooseq_callback, NULL) != ERR_OK) { \
SYS_ARCH_PROTECT(old_level); \
pbuf_free_ooseq_pending = 0; \
SYS_ARCH_UNPROTECT(old_level); \
@@ -168,11 +128,10 @@ pbuf_free_ooseq(void)
SYS_ARCH_SET(pbuf_free_ooseq_pending, 0);
for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
- if (NULL != pcb->ooseq) {
+ if (pcb->ooseq != NULL) {
/** Free the ooseq pbufs of one PCB only */
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
- tcp_segs_free(pcb->ooseq);
- pcb->ooseq = NULL;
+ tcp_free_ooseq(pcb);
return;
}
}
@@ -212,6 +171,20 @@ pbuf_pool_is_empty(void)
}
#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+/* Initialize members of struct pbuf after allocation */
+static void
+pbuf_init_alloced_pbuf(struct pbuf *p, void* payload, u16_t tot_len, u16_t len, pbuf_type type, u8_t flags)
+{
+ p->next = NULL;
+ p->payload = payload;
+ p->tot_len = tot_len;
+ p->len = len;
+ p->type_internal = (u8_t)type;
+ p->flags = flags;
+ p->ref = 1;
+ p->if_idx = NETIF_NO_INDEX;
+}
+
/**
* @ingroup pbuf
* Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
@@ -220,7 +193,7 @@ pbuf_pool_is_empty(void)
* layer at which the pbuf is allocated and the requested size
* (from the size parameter).
*
- * @param layer flag to define header size
+ * @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:
@@ -244,182 +217,136 @@ pbuf_pool_is_empty(void)
* @return the allocated pbuf. If multiple pbufs where allocated, this
* is the first pbuf of a pbuf chain.
*/
-#if ESP_LWIP
-struct pbuf * ESP_IRAM_ATTR
-#else
struct pbuf *
-#endif
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 */
+ struct pbuf *p;
+ u16_t offset = (u16_t)layer;
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_REF: /* fall through */
+ case PBUF_ROM:
+ p = pbuf_alloc_reference(NULL, length, type);
+ break;
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;
+ {
+ 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;
}
- /* end of chain */
- /*r->next = NULL;*/
-
- break;
case PBUF_RAM:
{
- mem_size_t alloc_len = LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length);
-
+ 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 (alloc_len < LWIP_MEM_ALIGN_SIZE(length)) {
+ 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;
}
-
- 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;
-
-#if ESP_LWIP
- p->l2_owner = NULL;
- p->l2_buf = NULL;
-#endif
-
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
return p;
}
+/**
+ * @ingroup pbuf
+ * Allocates a pbuf for referenced data.
+ * Referenced data can be volatile (PBUF_REF) or long-lived (PBUF_ROM).
+ *
+ * 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 payload referenced payload
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_ROM: 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: 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.
+ *
+ * @return the allocated pbuf.
+ */
+struct pbuf *
+pbuf_alloc_reference(void *payload, u16_t length, pbuf_type type)
+{
+ struct pbuf *p;
+ LWIP_ASSERT("invalid pbuf_type", (type == PBUF_REF) || (type == PBUF_ROM));
+ /* 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_reference: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ pbuf_init_alloced_pbuf(p, payload, length, length, type, 0);
+ return p;
+}
+
+
#if LWIP_SUPPORT_CUSTOM_PBUF
/**
* @ingroup pbuf
* Initialize a custom pbuf (already allocated).
+ * Example of custom pbuf usage: @ref zerocopyrx
*
- * @param l flag to define header size
+ * @param l header size
* @param length size of the pbuf's payload
* @param type type of the pbuf (only used to treat the pbuf accordingly, as
* this function allocates no memory)
@@ -435,50 +362,21 @@ struct pbuf*
pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
void *payload_mem, u16_t payload_mem_len)
{
- u16_t offset;
+ u16_t offset = (u16_t)l;
+ void *payload;
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
- /* determine header offset */
- switch (l) {
- 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:
- offset = 0;
- break;
- default:
- LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
- return NULL;
- }
-
if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
return NULL;
}
- p->pbuf.next = NULL;
if (payload_mem != NULL) {
- p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
+ payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
} else {
- p->pbuf.payload = NULL;
+ payload = NULL;
}
- p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
- p->pbuf.len = p->pbuf.tot_len = length;
- p->pbuf.type = type;
- p->pbuf.ref = 1;
+ pbuf_init_alloced_pbuf(&p->pbuf, payload, length, length, type, PBUF_FLAG_IS_CUSTOM);
return &p->pbuf;
}
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
@@ -504,13 +402,9 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
{
struct pbuf *q;
u16_t rem_len; /* remaining length */
- s32_t grow;
+ u16_t shrink;
LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
- LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
- p->type == PBUF_ROM ||
- p->type == PBUF_RAM ||
- p->type == PBUF_REF);
/* desired length larger than current length? */
if (new_len >= p->tot_len) {
@@ -520,7 +414,7 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
/* the pbuf chain grows by (new_len - p->tot_len) bytes
* (which may be negative in case of shrinking) */
- grow = new_len - p->tot_len;
+ shrink = (u16_t)(p->tot_len - new_len);
/* first, step over any pbufs that should remain in the chain */
rem_len = new_len;
@@ -528,10 +422,9 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
/* should this pbuf be kept? */
while (rem_len > q->len) {
/* decrease remaining length by pbuf length */
- rem_len -= q->len;
+ rem_len = (u16_t)(rem_len - q->len);
/* decrease total length indicator */
- LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
- q->tot_len += (u16_t)grow;
+ q->tot_len = (u16_t)(q->tot_len - shrink);
/* proceed to next pbuf in chain */
q = q->next;
LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
@@ -541,13 +434,13 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
/* shrink allocated memory for PBUF_RAM */
/* (other types merely adjust their length fields */
- if ((q->type == PBUF_RAM) && (rem_len != q->len)
+ if (pbuf_match_allocsrc(q, PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) && (rem_len != q->len)
#if LWIP_SUPPORT_CUSTOM_PBUF
&& ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0)
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
) {
/* reallocate and adjust the length of the pbuf that will be split */
- q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+ q = (struct pbuf *)mem_trim(q, (mem_size_t)(((u8_t *)q->payload - (u8_t *)q) + rem_len));
LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
}
/* adjust length fields for new last pbuf */
@@ -565,8 +458,8 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
}
/**
- * Adjusts the payload pointer to hide or reveal headers in the payload.
- * @see pbuf_header.
+ * Adjusts the payload pointer to reveal headers in the payload.
+ * @see pbuf_add_header.
*
* @param p pbuf to change the header size.
* @param header_size_increment Number of bytes to increment header size.
@@ -575,46 +468,30 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
* @return non-zero on failure, zero on success.
*
*/
-#if ESP_LWIP
-static u8_t ESP_IRAM_ATTR
-#else
static u8_t
-#endif
-pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
+pbuf_add_header_impl(struct pbuf *p, size_t header_size_increment, u8_t force)
{
- u16_t type;
+ u16_t type_internal;
void *payload;
u16_t increment_magnitude;
LWIP_ASSERT("p != NULL", p != NULL);
- if ((header_size_increment == 0) || (p == NULL)) {
+ if ((header_size_increment == 0) || (p == NULL) || (header_size_increment > 0xFFFF)) {
return 0;
}
- if (header_size_increment < 0) {
- increment_magnitude = (u16_t)-header_size_increment;
- /* Check that we aren't going to move off the end of the pbuf */
- LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
- } else {
- increment_magnitude = (u16_t)header_size_increment;
-#if 0
- /* Can't assert these as some callers speculatively call
- pbuf_header() to see if it's OK. Will return 1 below instead. */
- /* Check that we've got the correct type of pbuf to work with */
- LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
- p->type == PBUF_RAM || p->type == PBUF_POOL);
- /* Check that we aren't going to move off the beginning of the pbuf */
- LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
- (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
-#endif
+ increment_magnitude = (u16_t)header_size_increment;
+ /* Do not allow tot_len to wrap as a result. */
+ if ((u16_t)(increment_magnitude + p->tot_len) < increment_magnitude) {
+ return 1;
}
- type = p->type;
+ type_internal = p->type_internal;
/* remember current payload pointer */
payload = p->payload;
/* pbuf types containing payloads? */
- if (type == PBUF_RAM || type == PBUF_POOL) {
+ if (type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) {
/* set new payload pointer */
p->payload = (u8_t *)p->payload - header_size_increment;
/* boundary check fails? */
@@ -628,33 +505,123 @@ pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
return 1;
}
/* pbuf types referring to external payloads? */
- } else if (type == PBUF_REF || type == PBUF_ROM) {
+ } else {
/* hide a header in the payload? */
- if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
- /* increase payload pointer */
- p->payload = (u8_t *)p->payload - header_size_increment;
- } else if ((header_size_increment > 0) && force) {
+ if (force) {
p->payload = (u8_t *)p->payload - header_size_increment;
} else {
/* cannot expand payload to front (yet!)
* bail out unsuccessfully */
return 1;
}
+ }
+ /* modify pbuf length fields */
+ p->len = (u16_t)(p->len + increment_magnitude);
+ p->tot_len = (u16_t)(p->tot_len + increment_magnitude);
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"U16_F")\n",
+ (void *)payload, (void *)p->payload, increment_magnitude));
+
+ return 0;
+}
+
+/**
+ * Adjusts the payload pointer to reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * If hdr_size_inc is 0, this function does nothing and returns successful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ *
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_add_header(struct pbuf *p, size_t header_size_increment)
+{
+ return pbuf_add_header_impl(p, header_size_increment, 0);
+}
+
+/**
+ * Same as @ref pbuf_add_header but does not check if 'header_size > 0' is allowed.
+ * This is used internally only, to allow PBUF_REF for RX.
+ */
+u8_t
+pbuf_add_header_force(struct pbuf *p, size_t header_size_increment)
+{
+ return pbuf_add_header_impl(p, header_size_increment, 1);
+}
+
+/**
+ * Adjusts the payload pointer to hide headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * disappears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_decrement Number of bytes to decrement header size which
+ * decreases the size of the pbuf.
+ * If hdr_size_inc is 0, this function does nothing and returns successful.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_remove_header(struct pbuf *p, size_t header_size_decrement)
+{
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_decrement == 0) || (p == NULL) || (header_size_decrement > 0xFFFF)) {
+ return 0;
+ }
+
+ increment_magnitude = (u16_t)header_size_decrement;
+ /* Check that we aren't going to move off the end of the pbuf */
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+
+ /* remember current payload pointer */
+ payload = p->payload;
+
+ if (increment_magnitude <= p->len) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload + header_size_decrement;
} else {
- /* Unknown type */
- LWIP_ASSERT("bad pbuf type", 0);
+ /* cannot expand payload to front (yet!)
+ * bail out unsuccessfully */
return 1;
}
/* modify pbuf length fields */
- p->len += header_size_increment;
- p->tot_len += header_size_increment;
+ p->len = (u16_t)(p->len - increment_magnitude);
+ p->tot_len = (u16_t)(p->tot_len - increment_magnitude);
- LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
- (void *)payload, (void *)p->payload, header_size_increment));
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"U16_F")\n",
+ (void *)payload, (void *)p->payload, increment_magnitude));
return 0;
}
+static u8_t
+pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
+{
+ if (header_size_increment < 0) {
+ return pbuf_remove_header(p, (size_t)-header_size_increment);
+ } else {
+ return pbuf_add_header_impl(p, (size_t)header_size_increment, force);
+ }
+}
+
/**
* Adjusts the payload pointer to hide or reveal headers in the payload.
*
@@ -675,11 +642,7 @@ pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
* @return non-zero on failure, zero on success.
*
*/
-#if ESP_LWIP
-u8_t ESP_IRAM_ATTR
-#else
u8_t
-#endif
pbuf_header(struct pbuf *p, s16_t header_size_increment)
{
return pbuf_header_impl(p, header_size_increment, 0);
@@ -695,6 +658,36 @@ pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
return pbuf_header_impl(p, header_size_increment, 1);
}
+/** Similar to pbuf_header(-size) but de-refs header pbufs for (size >= p->len)
+ *
+ * @param q pbufs to operate on
+ * @param size The number of bytes to remove from the beginning of the pbuf list.
+ * While size >= p->len, pbufs are freed.
+ * ATTENTION: this is the opposite direction as @ref pbuf_header, but
+ * takes an u16_t not s16_t!
+ * @return the new head pbuf
+ */
+struct pbuf*
+pbuf_free_header(struct pbuf *q, u16_t size)
+{
+ struct pbuf *p = q;
+ u16_t free_left = size;
+ while (free_left && p) {
+ s16_t free_len = (free_left > INT16_MAX ? INT16_MAX : (s16_t)free_left);
+ if (free_len >= p->len) {
+ struct pbuf *f = p;
+ free_left = (u16_t)(free_left - p->len);
+ p = p->next;
+ f->next = 0;
+ pbuf_free(f);
+ } else {
+ pbuf_header(p, (s16_t)-free_len);
+ free_left = (u16_t)(free_left - free_len);
+ }
+ }
+ return p;
+}
+
/**
* @ingroup pbuf
* Dereference a pbuf chain or queue and deallocate any no-longer-used
@@ -732,7 +725,7 @@ pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
u8_t
pbuf_free(struct pbuf *p)
{
- u16_t type;
+ u8_t alloc_src;
struct pbuf *q;
u8_t count;
@@ -747,15 +740,11 @@ pbuf_free(struct pbuf *p)
PERF_START;
- LWIP_ASSERT("pbuf_free: sane type",
- p->type == PBUF_RAM || p->type == PBUF_ROM ||
- p->type == PBUF_REF || p->type == PBUF_POOL);
-
count = 0;
/* de-allocate all consecutive pbufs from the head of the chain that
* obtain a zero reference count after decrementing*/
while (p != NULL) {
- u16_t ref;
+ LWIP_PBUF_REF_T ref;
SYS_ARCH_DECL_PROTECT(old_level);
/* Since decrementing ref cannot be guaranteed to be a single machine operation
* we must protect it. We put the new ref into a local variable to prevent
@@ -771,7 +760,7 @@ pbuf_free(struct pbuf *p)
/* remember next pbuf in chain for next iteration */
q = p->next;
LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
- type = p->type;
+ alloc_src = pbuf_get_allocsrc(p);
#if LWIP_SUPPORT_CUSTOM_PBUF
/* is this a custom pbuf? */
if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
@@ -782,21 +771,17 @@ pbuf_free(struct pbuf *p)
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
{
/* is this a pbuf from the pool? */
- if (type == PBUF_POOL) {
+ if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) {
memp_free(MEMP_PBUF_POOL, p);
/* is this a ROM or RAM referencing pbuf? */
- } else if (type == PBUF_ROM || type == PBUF_REF) {
-#if ESP_LWIP
- if (p->l2_owner != NULL
- && p->l2_buf != NULL
- && p->l2_owner->l2_buffer_free_notify != NULL) {
- p->l2_owner->l2_buffer_free_notify(p->l2_buf);
- }
-#endif
+ } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) {
memp_free(MEMP_PBUF, p);
/* type == PBUF_RAM */
- } else {
+ } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) {
mem_free(p);
+ } else {
+ /* @todo: support freeing other types */
+ LWIP_ASSERT("invalid pbuf type", 0);
}
}
count++;
@@ -846,7 +831,7 @@ pbuf_ref(struct pbuf *p)
{
/* pbuf given? */
if (p != NULL) {
- SYS_ARCH_INC(p->ref, 1);
+ SYS_ARCH_SET(p->ref, (u8_t)(p->ref + 1));
LWIP_ASSERT("pbuf ref overflow", p->ref > 0);
}
}
@@ -859,6 +844,10 @@ pbuf_ref(struct pbuf *p)
* @note The caller MAY NOT reference the tail pbuf afterwards.
* Use pbuf_chain() for that purpose.
*
+ * This function explicitly does not check for tot_len overflow to prevent
+ * failing to queue too long pbufs. This can produce invalid pbufs, so
+ * handle with care!
+ *
* @see pbuf_chain()
*/
void
@@ -872,13 +861,13 @@ pbuf_cat(struct pbuf *h, struct pbuf *t)
/* proceed to last pbuf of chain */
for (p = h; p->next != NULL; p = p->next) {
/* add total length of second chain to all totals of first chain */
- p->tot_len += t->tot_len;
+ p->tot_len = (u16_t)(p->tot_len + t->tot_len);
}
/* { p is last pbuf of first h chain, p->next == NULL } */
LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
LWIP_ASSERT("p->next == NULL", p->next == NULL);
/* add total length of second chain to last pbuf total of first chain */
- p->tot_len += t->tot_len;
+ p->tot_len = (u16_t)(p->tot_len + t->tot_len);
/* chain last pbuf of head (p) with first of tail (t) */
p->next = t;
/* p->next now references t, but the caller will drop its reference to t,
@@ -932,7 +921,7 @@ pbuf_dechain(struct pbuf *p)
/* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
/* enforce invariant if assertion is disabled */
- q->tot_len = p->tot_len - p->len;
+ q->tot_len = (u16_t)(p->tot_len - p->len);
/* decouple pbuf from remainder */
p->next = NULL;
/* total length of pbuf p is its own length only */
@@ -972,7 +961,7 @@ pbuf_dechain(struct pbuf *p)
err_t
pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
{
- u16_t offset_to=0, offset_from=0, len;
+ size_t offset_to = 0, offset_from = 0, len;
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
(const void*)p_to, (const void*)p_from));
@@ -1040,41 +1029,79 @@ u16_t
pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
{
const struct pbuf *p;
- u16_t left;
+ u16_t left = 0;
u16_t buf_copy_len;
u16_t copied_total = 0;
LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
- left = 0;
-
- if ((buf == NULL) || (dataptr == NULL)) {
- return 0;
- }
-
/* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
for (p = buf; len != 0 && p != NULL; p = p->next) {
if ((offset != 0) && (offset >= p->len)) {
/* don't copy from this buffer -> on to the next */
- offset -= p->len;
+ offset = (u16_t)(offset - p->len);
} else {
/* copy from this buffer. maybe only partially. */
- buf_copy_len = p->len - offset;
+ buf_copy_len = (u16_t)(p->len - offset);
if (buf_copy_len > len) {
buf_copy_len = len;
}
/* copy the necessary parts of the buffer */
MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
- copied_total += buf_copy_len;
- left += buf_copy_len;
- len -= buf_copy_len;
+ copied_total = (u16_t)(copied_total + buf_copy_len);
+ left = (u16_t)(left + buf_copy_len);
+ len = (u16_t)(len - buf_copy_len);
offset = 0;
}
}
return copied_total;
}
+/**
+ * @ingroup pbuf
+ * Get part of a pbuf's payload as contiguous memory. The returned memory is
+ * either a pointer into the pbuf's payload or, if split over multiple pbufs,
+ * a copy into the user-supplied buffer.
+ *
+ * @param p the pbuf from which to copy data
+ * @param buffer the application supplied buffer
+ * @param bufsize size of the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+void *
+pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset)
+{
+ const struct pbuf *q;
+
+ LWIP_ERROR("pbuf_get_contiguous: invalid buf", (p != NULL), return NULL;);
+ LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (buffer != NULL), return NULL;);
+ LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (bufsize >= len), return NULL;);
+
+ for (q = p; q != NULL; q = q->next) {
+ if ((offset != 0) && (offset >= q->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset = (u16_t)(offset - q->len);
+ } else {
+ if (q->len >= (offset + len)) {
+ /* all data in this pbuf, return zero-copy */
+ return (u8_t*)q->payload + offset;
+ }
+ /* need to copy */
+ if (pbuf_copy_partial(q, buffer, len, offset) != len) {
+ /* copying failed: pbuf is too short */
+ return NULL;
+ }
+ return buffer;
+ }
+ }
+ /* pbuf is too short (offset does not fit in) */
+ return NULL;
+}
+
#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
/**
* This method modifies a 'pbuf chain', so that its total length is
@@ -1098,7 +1125,7 @@ void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
/* continue until the total length (summed up as u16_t) overflows */
while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) {
- tot_len_front += r->len;
+ tot_len_front = (u16_t)(tot_len_front + r->len);
i = r;
r = r->next;
}
@@ -1109,7 +1136,7 @@ void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
if (r != NULL) {
/* Update the tot_len field in the first part */
for (i = p; i != NULL; i = i->next) {
- i->tot_len -= r->tot_len;
+ i->tot_len = (u16_t)(i->tot_len - r->tot_len);
LWIP_ASSERT("tot_len/len mismatch in last pbuf",
(i->next != NULL) || (i->tot_len == i->len));
}
@@ -1134,7 +1161,7 @@ pbuf_skip_const(const struct pbuf* in, u16_t in_offset, u16_t* out_offset)
/* get the correct pbuf */
while ((q != NULL) && (q->len <= offset_left)) {
- offset_left -= q->len;
+ offset_left = (u16_t)(offset_left - q->len);
q = q->next;
}
if (out_offset != NULL) {
@@ -1174,9 +1201,9 @@ err_t
pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
{
struct pbuf *p;
- u16_t buf_copy_len;
- u16_t total_copy_len = len;
- u16_t copied_total = 0;
+ size_t buf_copy_len;
+ size_t total_copy_len = len;
+ size_t copied_total = 0;
LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;);
LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
@@ -1225,9 +1252,11 @@ pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)
u16_t remaining_len = len;
const u8_t* src_ptr = (const u8_t*)dataptr;
/* copy the part that goes into the first pbuf */
- u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len);
+ u16_t first_copy_len;
+ LWIP_ASSERT("chekc pbuf_skip result", target_offset < q->len);
+ first_copy_len = (u16_t)LWIP_MIN(q->len - target_offset, len);
MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len);
- remaining_len -= first_copy_len;
+ remaining_len = (u16_t)(remaining_len - first_copy_len);
src_ptr += first_copy_len;
if (remaining_len > 0) {
return pbuf_take(q->next, src_ptr, remaining_len);
@@ -1254,19 +1283,42 @@ struct pbuf*
pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
{
struct pbuf *q;
- err_t err;
if (p->next == NULL) {
return p;
}
- q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+ q = pbuf_clone(layer, PBUF_RAM, p);
if (q == NULL) {
/* @todo: what do we do now? */
return p;
}
+ pbuf_free(p);
+ return q;
+}
+
+/**
+ * @ingroup pbuf
+ * Allocates a new pbuf of same length (via pbuf_alloc()) and copies the source
+ * pbuf into this new pbuf (using pbuf_copy()).
+ *
+ * @param layer pbuf_layer of the new pbuf
+ * @param type this parameter decides how and where the pbuf should be allocated
+ * (@see pbuf_alloc())
+ * @param p the source pbuf
+ *
+ * @return a new pbuf or NULL if allocation fails
+ */
+struct pbuf *
+pbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p)
+{
+ struct pbuf *q;
+ err_t err;
+ q = pbuf_alloc(layer, p->tot_len, type);
+ if (q == NULL) {
+ return NULL;
+ }
err = pbuf_copy(q, p);
LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
- pbuf_free(p);
return q;
}
@@ -1397,17 +1449,17 @@ pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n)
/* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */
while ((q != NULL) && (q->len <= start)) {
- start -= q->len;
+ start = (u16_t)(start - q->len);
q = q->next;
}
/* return requested data if pbuf is OK */
for (i = 0; i < n; i++) {
/* We know pbuf_get_at() succeeds because of p->tot_len check above. */
- u8_t a = pbuf_get_at(q, start + i);
+ u8_t a = pbuf_get_at(q, (u16_t)(start + i));
u8_t b = ((const u8_t*)s2)[i];
if (a != b) {
- return i+1;
+ return (u16_t)LWIP_MIN(i+1, 0xFFFF);
}
}
return 0;
@@ -1429,9 +1481,9 @@ u16_t
pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
{
u16_t i;
- u16_t max = p->tot_len - mem_len;
+ u16_t max_cmp_start = (u16_t)(p->tot_len - mem_len);
if (p->tot_len >= mem_len + start_offset) {
- for (i = start_offset; i <= max; i++) {
+ for (i = start_offset; i <= max_cmp_start; i++) {
u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
if (plus == 0) {
return i;
diff --git a/src/core/raw.c b/../lwip_nat/src/core/raw.c
index 80cf9ec6..96c75790 100644
--- a/src/core/raw.c
+++ b/../lwip_nat/src/core/raw.c
@@ -65,9 +65,15 @@
static struct raw_pcb *raw_pcbs;
static u8_t
-raw_input_match(struct raw_pcb *pcb, u8_t broadcast)
+raw_input_local_match(struct raw_pcb *pcb, u8_t broadcast)
{
LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+ /* check if PCB is bound to specific netif */
+ if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+ (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+ return 0;
+ }
#if LWIP_IPV4 && LWIP_IPV6
/* Dual-stack: PCBs listening to any IP type also listen to any IP address */
@@ -157,7 +163,9 @@ raw_input(struct pbuf *p, struct netif *inp)
/* loop through all raw pcbs until the packet is eaten by one */
/* this allows multiple pcbs to match against the packet by design */
while ((eaten == 0) && (pcb != NULL)) {
- if ((pcb->protocol == proto) && raw_input_match(pcb, broadcast)) {
+ if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) &&
+ (((pcb->flags & RAW_FLAGS_CONNECTED) == 0) ||
+ ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
/* receive callback function available? */
if (pcb->recv != NULL) {
#ifndef LWIP_NOASSERT
@@ -213,9 +221,40 @@ raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
return ERR_VAL;
}
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * This is legacy support: scope-aware callers should always provide properly
+ * zoned source addresses. */
+ if (IP_IS_V6(&pcb->local_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNKNOWN)) {
+ ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip));
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
return ERR_OK;
}
+/**
+ * @ingroup raw_raw
+ * Bind an RAW PCB to a specific netif.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb RAW PCB to be bound with netif.
+ * @param netif netif to bind to. Can be NULL.
+ *
+ * @see raw_disconnect()
+ */
+void
+raw_bind_netif(struct raw_pcb *pcb, const struct netif *netif)
+{
+ if (netif != NULL) {
+ pcb->netif_idx = netif_get_index(netif);
+ } else {
+ pcb->netif_idx = NETIF_NO_INDEX;
+ }
+}
+
/**
* @ingroup raw_raw
* Connect an RAW PCB. This function is required by upper layers
@@ -237,9 +276,42 @@ raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
return ERR_VAL;
}
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now,
+ * using the bound address to make a more informed decision when possible. */
+ if (IP_IS_V6(&pcb->remote_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
+ ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+ raw_set_flags(pcb, RAW_FLAGS_CONNECTED);
return ERR_OK;
}
+/**
+ * @ingroup raw_raw
+ * Disconnect a RAW PCB.
+ *
+ * @param pcb the raw pcb to disconnect.
+ */
+void
+raw_disconnect(struct raw_pcb *pcb)
+{
+ /* reset remote address association */
+#if LWIP_IPV4 && LWIP_IPV6
+ if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+ ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE);
+ } else {
+#endif
+ ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
+#if LWIP_IPV4 && LWIP_IPV6
+ }
+#endif
+ pcb->netif_idx = NETIF_NO_INDEX;
+ /* mark PCB as unconnected */
+ raw_clear_flags(pcb, RAW_FLAGS_CONNECTED);
+}
+
/**
* @ingroup raw_raw
* Set the callback function for received packets that match the
@@ -261,11 +333,9 @@ raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
/**
* @ingroup raw_raw
- * Send the raw IP packet to the given address. Note that actually you cannot
- * modify the IP headers (this is inconsistent with the receive callback where
- * you actually get the IP headers), you can only specify the IP payload here.
- * It requires some more changes in lwIP. (there will be a raw_send() function
- * then.)
+ * Send the raw IP packet to the given address. An IP header will be prepended
+ * to the packet, unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that
+ * case, the packet must include an IP header, which will then be sent as is.
*
* @param pcb the raw pcb which to send
* @param p the IP payload to send
@@ -275,11 +345,8 @@ raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
err_t
raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
{
- err_t err;
struct netif *netif;
const ip_addr_t *src_ip;
- struct pbuf *q; /* q will be sent down the stack */
- s16_t header_size;
if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
@@ -287,15 +354,102 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+ if (pcb->netif_idx != NETIF_NO_INDEX) {
+ netif = netif_get_by_index(pcb->netif_idx);
+ } else {
+#if LWIP_MULTICAST_TX_OPTIONS
+ netif = NULL;
+ if (ip_addr_ismulticast(ipaddr)) {
+ /* For multicast-destined packets, use the user-provided interface index to
+ * determine the outgoing interface, if an interface index is set and a
+ * matching netif can be found. Otherwise, fall back to regular routing. */
+ netif = netif_get_by_index(pcb->mcast_ifindex);
+ }
+
+ if (netif == NULL)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+ {
+ netif = ip_route(&pcb->local_ip, ipaddr);
+ }
+ }
+
+ if (netif == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
+ ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
+ return ERR_RTE;
+ }
+
+ if (ip_addr_isany(&pcb->local_ip) || ip_addr_ismulticast(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = ip_netif_get_local_ip(netif, ipaddr);
+#if LWIP_IPV6
+ if (src_ip == NULL) {
+ return ERR_RTE;
+ }
+#endif /* LWIP_IPV6 */
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &pcb->local_ip;
+ }
+
+ return raw_sendto_if_src(pcb, p, ipaddr, netif, src_ip);
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address, using a particular outgoing
+ * netif and source IP address. An IP header will be prepended to the packet,
+ * unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that case, the
+ * packet must include an IP header, which will then be sent as is.
+ *
+ * @param pcb RAW PCB used to send the data
+ * @param p chain of pbufs to be sent
+ * @param dst_ip destination IP address
+ * @param netif the netif used for sending
+ * @param src_ip source IP address
+ */
+err_t
+raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+ struct netif *netif, const ip_addr_t *src_ip)
+{
+ err_t err;
+ struct pbuf *q; /* q will be sent down the stack */
+ s16_t header_size;
+ u8_t ttl;
+
+ if ((pcb == NULL) || (dst_ip == NULL) || (netif == NULL) || (src_ip == NULL) ||
+ !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+ return ERR_VAL;
+ }
+
header_size = (
#if LWIP_IPV4 && LWIP_IPV6
- IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN);
+ IP_IS_V6(dst_ip) ? IP6_HLEN : IP_HLEN);
#elif LWIP_IPV4
IP_HLEN);
#else
IP6_HLEN);
#endif
+ /* Handle the HDRINCL option as an exception: none of the code below applies
+ * to this case, and sending the packet needs to be done differently too. */
+ if (pcb->flags & RAW_FLAGS_HDRINCL) {
+ /* A full header *must* be present in the first pbuf of the chain, as the
+ * output routines may access its fields directly. */
+ if (p->len < header_size) {
+ return ERR_VAL;
+ }
+ /* @todo multicast loop support, if at all desired for this scenario.. */
+ NETIF_SET_HINTS(netif, &pcb->netif_hints);
+ err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif);
+ NETIF_RESET_HINTS(netif);
+ return err;
+ }
+
+ /* packet too large to add an IP header without causing an overflow? */
+ if ((u16_t)(p->tot_len + header_size) < p->tot_len) {
+ return ERR_MEM;
+ }
/* not enough space to add an IP header to first pbuf in given p chain? */
if (pbuf_header(p, header_size)) {
/* allocate header in new pbuf */
@@ -314,34 +468,17 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
} else {
/* first pbuf q equals given pbuf */
q = p;
- if (pbuf_header(q, -header_size)) {
+ if (pbuf_header(q, (s16_t)-header_size)) {
LWIP_ASSERT("Can't restore header we just removed!", 0);
return ERR_MEM;
}
}
- if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
- /* Don't call ip_route() with IP_ANY_TYPE */
- netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(ipaddr)), ipaddr);
- } else {
- netif = ip_route(&pcb->local_ip, ipaddr);
- }
-
- if (netif == NULL) {
- LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
- ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
- /* free any temporary header pbuf allocated by pbuf_header() */
- if (q != p) {
- pbuf_free(q);
- }
- return ERR_RTE;
- }
-
#if IP_SOF_BROADCAST
- if (IP_IS_V4(ipaddr))
+ if (IP_IS_V4(dst_ip))
{
/* broadcast filter? */
- if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
+ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(dst_ip, netif)) {
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
/* free any temporary header pbuf allocated by pbuf_header() */
if (q != p) {
@@ -352,35 +489,33 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
}
#endif /* IP_SOF_BROADCAST */
- if (ip_addr_isany(&pcb->local_ip)) {
- /* use outgoing network interface IP address as source address */
- src_ip = ip_netif_get_local_ip(netif, ipaddr);
-#if LWIP_IPV6
- if (src_ip == NULL) {
- if (q != p) {
- pbuf_free(q);
- }
- return ERR_RTE;
- }
-#endif /* LWIP_IPV6 */
- } else {
- /* use RAW PCB local IP address as source address */
- src_ip = &pcb->local_ip;
+ /* Multicast Loop? */
+#if LWIP_MULTICAST_TX_OPTIONS
+ if (((pcb->flags & RAW_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
+ q->flags |= PBUF_FLAG_MCASTLOOP;
}
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
#if LWIP_IPV6
/* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
compute the checksum and update the checksum in the payload. */
- if (IP_IS_V6(ipaddr) && pcb->chksum_reqd) {
- u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(ipaddr));
+ if (IP_IS_V6(dst_ip) && pcb->chksum_reqd) {
+ u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip));
LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
}
#endif
- NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
- err = ip_output_if(q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ /* Determine TTL to use */
+#if LWIP_MULTICAST_TX_OPTIONS
+ ttl = (ip_addr_ismulticast(dst_ip) ? raw_get_multicast_ttl(pcb) : pcb->ttl);
+#else /* LWIP_MULTICAST_TX_OPTIONS */
+ ttl = pcb->ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+ NETIF_SET_HINTS(netif, &pcb->netif_hints);
+ err = ip_output_if(q, src_ip, dst_ip, ttl, pcb->tos, pcb->protocol, netif);
+ NETIF_RESET_HINTS(netif);
/* did we chain a header earlier? */
if (q != p) {
@@ -460,6 +595,9 @@ raw_new(u8_t proto)
memset(pcb, 0, sizeof(struct raw_pcb));
pcb->protocol = proto;
pcb->ttl = RAW_TTL;
+#if LWIP_MULTICAST_TX_OPTIONS
+ raw_set_multicast_ttl(pcb, RAW_TTL);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
pcb->next = raw_pcbs;
raw_pcbs = pcb;
}
diff --git a/src/core/stats.c b/../lwip_nat/src/core/stats.c
index ec1e25e9..34e9b270 100644
--- a/src/core/stats.c
+++ b/../lwip_nat/src/core/stats.c
@@ -105,17 +105,17 @@ void
stats_display_mem(struct stats_mem *mem, const char *name)
{
LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
- LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail));
- LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used));
- LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max));
- LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
+ LWIP_PLATFORM_DIAG(("avail: %"MEM_SIZE_F"\n\t", mem->avail));
+ LWIP_PLATFORM_DIAG(("used: %"MEM_SIZE_F"\n\t", mem->used));
+ LWIP_PLATFORM_DIAG(("max: %"MEM_SIZE_F"\n\t", mem->max));
+ LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n", mem->err));
}
#if MEMP_STATS
void
-stats_display_memp(struct stats_mem *mem, int index)
+stats_display_memp(struct stats_mem *mem, int idx)
{
- if (index < MEMP_MAX) {
+ if (idx < MEMP_MAX) {
stats_display_mem(mem, mem->name);
}
}
@@ -127,15 +127,15 @@ void
stats_display_sys(struct stats_sys *sys)
{
LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
- LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
- LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
- LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
- LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
- LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
- LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
- LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
- LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
- LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n", (u32_t)sys->mbox.err));
+ LWIP_PLATFORM_DIAG(("sem.used: %"STAT_COUNTER_F"\n\t", sys->sem.used));
+ LWIP_PLATFORM_DIAG(("sem.max: %"STAT_COUNTER_F"\n\t", sys->sem.max));
+ LWIP_PLATFORM_DIAG(("sem.err: %"STAT_COUNTER_F"\n\t", sys->sem.err));
+ LWIP_PLATFORM_DIAG(("mutex.used: %"STAT_COUNTER_F"\n\t", sys->mutex.used));
+ LWIP_PLATFORM_DIAG(("mutex.max: %"STAT_COUNTER_F"\n\t", sys->mutex.max));
+ LWIP_PLATFORM_DIAG(("mutex.err: %"STAT_COUNTER_F"\n\t", sys->mutex.err));
+ LWIP_PLATFORM_DIAG(("mbox.used: %"STAT_COUNTER_F"\n\t", sys->mbox.used));
+ LWIP_PLATFORM_DIAG(("mbox.max: %"STAT_COUNTER_F"\n\t", sys->mbox.max));
+ LWIP_PLATFORM_DIAG(("mbox.err: %"STAT_COUNTER_F"\n", sys->mbox.err));
}
#endif /* SYS_STATS */
@@ -165,23 +165,5 @@ stats_display(void)
}
#endif /* LWIP_STATS_DISPLAY */
-#if ESP_STATS_DROP
-void stats_display_esp(struct stats_esp *esp)
-{
- LWIP_PLATFORM_DIAG(("\nESP\n\t"));
- LWIP_PLATFORM_DIAG(("esp.rx_rawmbox_post_fail: %"U32_F"\n\t", (u32_t)esp->rx_rawmbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.rx_udpmbox_post_fail: %"U32_F"\n\t", (u32_t)esp->rx_udpmbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.rx_tcpmbox_post_fail: %"U32_F"\n\t", (u32_t)esp->rx_tcpmbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.err_tcp_rxmbox_post_fail: %"U32_F"\n\t", (u32_t)esp->err_tcp_rxmbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.err_tcp_acceptmbox_post_fail: %"U32_F"\n\t", (u32_t)esp->err_tcp_acceptmbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.acceptmbox_post_fail: %"U32_F"\n\t", (u32_t)esp->acceptmbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.free_mbox_post_fail: %"U32_F"\n\t", (u32_t)esp->free_mbox_post_fail));
- LWIP_PLATFORM_DIAG(("esp.tcpip_inpkt_post_fail: %"U32_F"\n\t", (u32_t)esp->tcpip_inpkt_post_fail));
- LWIP_PLATFORM_DIAG(("esp.tcpip_cb_post_fail: %"U32_F"\n\t", (u32_t)esp->tcpip_cb_post_fail));
- LWIP_PLATFORM_DIAG(("esp.wlanif_input_pbuf_fail: %"U32_F"\n\t", (u32_t)esp->wlanif_input_pbuf_fail));
- LWIP_PLATFORM_DIAG(("esp.wlanif_outut_pbuf_fail: %"U32_F"\n\t", (u32_t)esp->wlanif_outut_pbuf_fail));
-}
-#endif
-
#endif /* LWIP_STATS */
diff --git a/src/core/tcp.c b/../lwip_nat/src/core/tcp.c
index 5c3b944e..c5c6d297 100644
--- a/src/core/tcp.c
+++ b/../lwip_nat/src/core/tcp.c
@@ -71,7 +71,7 @@
"The Dynamic and/or Private Ports are those from 49152 through 65535" */
#define TCP_LOCAL_PORT_RANGE_START 0xc000
#define TCP_LOCAL_PORT_RANGE_END 0xffff
-#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START))
+#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & (u16_t)~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START))
#endif
#if LWIP_TCP_KEEPALIVE
@@ -144,9 +144,9 @@ static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb);
void
tcp_init(void)
{
-#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
+#ifdef LWIP_RAND
tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
-#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+#endif /* LWIP_RAND */
}
/**
@@ -239,7 +239,7 @@ tcp_backlog_accepted(struct tcp_pcb* pcb)
if (pcb->listener != NULL) {
LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
pcb->listener->accepts_pending--;
- pcb->flags &= ~TF_BACKLOGPEND;
+ tcp_clear_flags(pcb, TF_BACKLOGPEND);
}
}
}
@@ -272,7 +272,7 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
/* 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,
+ tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
pcb->local_port, pcb->remote_port);
tcp_pcb_purge(pcb);
@@ -370,12 +370,6 @@ tcp_close_shutdown_fin(struct tcp_pcb *pcb)
} else if (err == ERR_MEM) {
/* Mark this pcb for closing. Closing is retried from tcp_tmr. */
pcb->flags |= TF_CLOSEPEND;
- /* We have to return ERR_OK from here to indicate to the callers that this
- pcb should not be used any more as it will be freed soon via tcp_tmr.
- This is OK here since sending FIN does not guarantee a time frime for
- actually freeing the pcb, either (it is left in closure states for
- remote ACK or timeout) */
- return ERR_OK;
}
return err;
}
@@ -413,8 +407,8 @@ tcp_close(struct tcp_pcb *pcb)
* @ingroup tcp_raw
* Causes all or part of a full-duplex connection of this PCB to be shut down.
* This doesn't deallocate the PCB unless shutting down both sides!
- * Shutting down both sides is the same as calling tcp_close, so if it succeds
- * (i.e. returns ER_OK), the PCB must not be referenced any more!
+ * Shutting down both sides is the same as calling tcp_close, so if it succeds,
+ * the PCB should not be referenced any more.
*
* @param pcb PCB to shutdown
* @param shut_rx shut down receive side if this is != 0
@@ -513,15 +507,12 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
tcp_segs_free(pcb->ooseq);
-#if ESP_LWIP
- pcb->ooseq = NULL;
-#endif
}
#endif /* TCP_QUEUE_OOSEQ */
tcp_backlog_accepted(pcb);
if (send_rst) {
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
- tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port);
+ tcp_rst(pcb, seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port);
}
last_state = pcb->state;
memp_free(MEMP_TCP_PCB, pcb);
@@ -554,7 +545,7 @@ tcp_abort(struct tcp_pcb *pcb)
*
* @param pcb the tcp_pcb to bind (no check is done whether this pcb is
* already bound!)
- * @param ipaddr the local ip address to bind to (use IP4_ADDR_ANY to bind
+ * @param ipaddr the local ip address to bind to (use IPx_ADDR_ANY to bind
* to any local address
* @param port the local port to bind to
* @return ERR_USE if the port is already in use
@@ -567,6 +558,9 @@ tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
int i;
int max_pcb_list = NUM_TCP_PCB_LISTS;
struct tcp_pcb *cpcb;
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ ip_addr_t zoned_ipaddr;
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
#if LWIP_IPV4
/* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
@@ -593,6 +587,18 @@ tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
}
#endif /* SO_REUSE */
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * This is legacy support: scope-aware callers should always provide properly
+ * zoned source addresses. Do the zone selection before the address-in-use
+ * check below; as such we have to make a temporary copy of the address. */
+ if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) {
+ ip_addr_copy(zoned_ipaddr, *ipaddr);
+ ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
+ ipaddr = &zoned_ipaddr;
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
if (port == 0) {
port = tcp_new_port();
if (port == 0) {
@@ -632,6 +638,27 @@ tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
return ERR_OK;
}
+
+/**
+ * @ingroup tcp_raw
+ * Binds the connection to a netif and IP address.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb the tcp_pcb to bind.
+ * @param netif the netif to bind to. Can be NULL.
+ */
+void
+tcp_bind_netif(struct tcp_pcb *pcb, const struct netif *netif)
+{
+ if (netif != NULL) {
+ pcb->netif_idx = netif_get_index(netif);
+ } else {
+ pcb->netif_idx = NETIF_NO_INDEX;
+ }
+}
+
#if LWIP_CALLBACK_API
/**
* Default accept callback if no accept callback is specified by the user.
@@ -726,6 +753,7 @@ tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
lpcb->state = LISTEN;
lpcb->prio = pcb->prio;
lpcb->so_options = pcb->so_options;
+ lpcb->netif_idx = NETIF_NO_INDEX;
lpcb->ttl = pcb->ttl;
lpcb->tos = pcb->tos;
#if LWIP_IPV4 && LWIP_IPV6
@@ -796,13 +824,13 @@ tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
void
tcp_recved(struct tcp_pcb *pcb, u16_t len)
{
- int wnd_inflation;
+ u32_t wnd_inflation;
/* pcb->state LISTEN not allowed here */
LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
pcb->state != LISTEN);
- pcb->rcv_wnd += len;
+ pcb->rcv_wnd = (tcpwnd_size_t)(pcb->rcv_wnd + len);
if (pcb->rcv_wnd > TCP_WND_MAX(pcb)) {
pcb->rcv_wnd = TCP_WND_MAX(pcb);
} else if (pcb->rcv_wnd == 0) {
@@ -845,16 +873,9 @@ tcp_new_port(void)
struct tcp_pcb *pcb;
again:
-
-#if ESP_RANDOM_TCP_PORT
- tcp_port = abs(LWIP_RAND()) % (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START);
- tcp_port += TCP_LOCAL_PORT_RANGE_START;
-#else
if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
tcp_port = TCP_LOCAL_PORT_RANGE_START;
}
-#endif
-
/* Check all PCB lists. */
for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
for (pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
@@ -887,6 +908,7 @@ err_t
tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
tcp_connected_fn connected)
{
+ struct netif *netif = NULL;
err_t ret;
u32_t iss;
u16_t old_local_port;
@@ -901,21 +923,35 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
ip_addr_set(&pcb->remote_ip, ipaddr);
pcb->remote_port = port;
- /* check if we have a route to the remote host */
+ if (pcb->netif_idx != NETIF_NO_INDEX) {
+ netif = netif_get_by_index(pcb->netif_idx);
+ } else {
+ /* check if we have a route to the remote host */
+ netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+ }
+ if (netif == NULL) {
+ /* Don't even try to send a SYN packet if we have no route since that will fail. */
+ return ERR_RTE;
+ }
+
+ /* check if local IP has been assigned to pcb, if not, get one */
if (ip_addr_isany(&pcb->local_ip)) {
- /* no local IP address set, yet. */
- struct netif *netif;
- const ip_addr_t *local_ip;
- ip_route_get_local_ip(&pcb->local_ip, &pcb->remote_ip, netif, local_ip);
- if ((netif == NULL) || (local_ip == NULL)) {
- /* Don't even try to send a SYN packet if we have no route
- since that will fail. */
+ const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, ipaddr);
+ if (local_ip == NULL) {
return ERR_RTE;
}
- /* Use the address as local address of the pcb. */
ip_addr_copy(pcb->local_ip, *local_ip);
}
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * Given that we already have the target netif, this is easy and cheap. */
+ if (IP_IS_V6(&pcb->remote_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST)) {
+ ip6_addr_assign_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST, netif);
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
old_local_port = pcb->local_port;
if (pcb->local_port == 0) {
pcb->local_port = tcp_new_port();
@@ -960,7 +996,7 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
The send MSS is updated when an MSS option is received. */
pcb->mss = INITIAL_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
- pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
+ pcb->mss = tcp_eff_send_mss_netif(pcb->mss, netif, &pcb->remote_ip);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
pcb->cwnd = 1;
#if LWIP_CALLBACK_API
@@ -1037,17 +1073,21 @@ tcp_slowtmr_start:
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
} else {
if (pcb->persist_backoff > 0) {
- /* If snd_wnd is zero, use persist timer to send 1 byte probes
- * instead of using the standard retransmission mechanism. */
- u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
- if (pcb->persist_cnt < backoff_cnt) {
- pcb->persist_cnt++;
- }
- if (pcb->persist_cnt >= backoff_cnt) {
- if (tcp_zero_window_probe(pcb) == ERR_OK) {
- pcb->persist_cnt = 0;
- if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
- pcb->persist_backoff++;
+ if (pcb->persist_probe >= TCP_MAXRTX) {
+ ++pcb_remove; /* max probes reached */
+ } else {
+ /* If snd_wnd is zero, use persist timer to send 1 byte probes
+ * instead of using the standard retransmission mechanism. */
+ u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
+ if (pcb->persist_cnt < backoff_cnt) {
+ pcb->persist_cnt++;
+ }
+ if (pcb->persist_cnt >= backoff_cnt) {
+ if (tcp_zero_window_probe(pcb) == ERR_OK) {
+ pcb->persist_cnt = 0;
+ if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+ pcb->persist_backoff++;
+ }
}
}
}
@@ -1063,13 +1103,12 @@ tcp_slowtmr_start:
" pcb->rto %"S16_F"\n",
pcb->rtime, pcb->rto));
- ESP_STATS_TCP_PCB(pcb);
-
/* Double retransmission time-out unless we are trying to
* connect to somebody (i.e., we are in SYN_SENT). */
if (pcb->state != SYN_SENT) {
u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff)-1);
- pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
+ int calc_rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
+ pcb->rto = (s16_t)LWIP_MIN(calc_rto, 0x7FFF);
}
/* Reset the retransmission timer. */
@@ -1079,12 +1118,13 @@ tcp_slowtmr_start:
eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
pcb->ssthresh = eff_wnd >> 1;
if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) {
- pcb->ssthresh = (pcb->mss << 1);
+ pcb->ssthresh = (tcpwnd_size_t)(pcb->mss << 1);
}
pcb->cwnd = pcb->mss;
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F
" ssthresh %"TCPWNDSIZE_F"\n",
pcb->cwnd, pcb->ssthresh));
+ pcb->bytes_acked = 0;
/* The following needs to be called AFTER cwnd is set to one
mss - STJ */
@@ -1093,12 +1133,7 @@ tcp_slowtmr_start:
}
}
/* Check if this PCB has stayed too long in FIN-WAIT-2 */
-#if ESP_LWIP
- /* If the TCP connection is in FIN_WAIT_1 for too long time, remove it */
- if ((pcb->state == FIN_WAIT_2) || (pcb->state == FIN_WAIT_1)) {
-#else
if (pcb->state == FIN_WAIT_2) {
-#endif
/* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */
if (pcb->flags & TF_RXCLOSED) {
/* PCB was fully closed (either through close() or SHUT_RDWR):
@@ -1119,7 +1154,7 @@ tcp_slowtmr_start:
(pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)
{
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
- ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+ ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
++pcb_remove;
@@ -1140,10 +1175,9 @@ tcp_slowtmr_start:
be retransmitted). */
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL &&
- (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
- tcp_segs_free(pcb->ooseq);
- pcb->ooseq = NULL;
+ (tcp_ticks - pcb->tmr >= (u32_t)pcb->rto * TCP_OOSEQ_TIMEOUT)) {
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+ tcp_free_ooseq(pcb);
}
#endif /* TCP_QUEUE_OOSEQ */
@@ -1184,8 +1218,8 @@ tcp_slowtmr_start:
}
if (pcb_reset) {
- tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
- pcb->local_port, pcb->remote_port);
+ tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
}
err_arg = pcb->callback_arg;
@@ -1260,7 +1294,7 @@ tcp_slowtmr_start:
/**
* Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
- * "refused" by upper layer (application) and sends delayed ACKs.
+ * "refused" by upper layer (application) and sends delayed ACKs or pending FINs.
*
* Automatically called from tcp_tmr().
*/
@@ -1283,12 +1317,12 @@ tcp_fasttmr_start:
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
tcp_ack_now(pcb);
tcp_output(pcb);
- pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
}
/* send pending FIN */
if (pcb->flags & TF_CLOSEPEND) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
- pcb->flags &= ~(TF_CLOSEPEND);
+ tcp_clear_flags(pcb, TF_CLOSEPEND);
tcp_close_shutdown_fin(pcb);
}
@@ -1348,7 +1382,7 @@ tcp_process_refused_data(struct tcp_pcb *pcb)
TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
if (err == ERR_OK) {
/* did refused_data include a FIN? */
- if (refused_flags & PBUF_FLAG_TCP_FIN
+ if ((refused_flags & PBUF_FLAG_TCP_FIN)
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
&& (rest == NULL)
#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
@@ -1418,6 +1452,7 @@ tcp_seg_free(struct tcp_seg *seg)
}
/**
+ * @ingroup tcp
* Sets the priority of a connection.
*
* @param pcb the tcp_pcb to manipulate
@@ -1514,9 +1549,7 @@ tcp_kill_state(enum tcp_state state)
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
-#if !ESP_LWIP
LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK));
-#endif
inactivity = 0;
inactive = NULL;
@@ -1564,58 +1597,6 @@ tcp_kill_timewait(void)
}
}
-#if ESP_LWIP
-typedef struct {
- u8_t time_wait;
- u8_t closing;
- u8_t fin_wait2;
- u8_t last_ack;
- u8_t fin_wait1;
- u8_t listen;
- u8_t bound;
- u8_t total;
-}tcp_pcb_num_t;
-
-static void tcp_pcb_num_cal(tcp_pcb_num_t *tcp_pcb_num)
-{
- struct tcp_pcb_listen *listen;
- struct tcp_pcb *pcb;
-
- if (!tcp_pcb_num){
- return;
- }
-
- memset(tcp_pcb_num, 0, sizeof(*tcp_pcb_num));
- for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
- tcp_pcb_num->total ++;
- tcp_pcb_num->time_wait ++;
- }
-
- for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next){
- tcp_pcb_num->total ++;
- if (pcb->state == FIN_WAIT_2){
- tcp_pcb_num->fin_wait2 ++;
- } else if (pcb->state == LAST_ACK) {
- tcp_pcb_num->last_ack ++;
- } else if (pcb->state == CLOSING) {
- tcp_pcb_num->closing ++;
- } else if (pcb->state == FIN_WAIT_1){
- tcp_pcb_num->fin_wait1 ++;
- }
- }
-
- for (listen = tcp_listen_pcbs.listen_pcbs; listen != NULL; listen = listen->next){
- tcp_pcb_num->total ++;
- tcp_pcb_num->listen ++;
- }
-
- for (pcb = tcp_bound_pcbs; pcb != NULL; pcb = pcb->next){
- tcp_pcb_num->total ++;
- tcp_pcb_num->bound ++;
- }
-}
-#endif
-
/**
* Allocate a new tcp_pcb structure.
*
@@ -1627,37 +1608,6 @@ tcp_alloc(u8_t prio)
{
struct tcp_pcb *pcb;
-#if ESP_LWIP
- tcp_pcb_num_t tcp_pcb_num;
-
- tcp_pcb_num_cal(&tcp_pcb_num);
-
- if (tcp_pcb_num.total >= MEMP_NUM_TCP_PCB){
- if (tcp_pcb_num.time_wait > 0){
- tcp_kill_timewait();
- } else if (tcp_pcb_num.last_ack > 0){
- tcp_kill_state(LAST_ACK);
- } else if (tcp_pcb_num.closing > 0){
- tcp_kill_state(CLOSING);
- } else if (tcp_pcb_num.fin_wait2 > 0){
- tcp_kill_state(FIN_WAIT_2);
- } else if (tcp_pcb_num.fin_wait1 > 0){
- tcp_kill_state(FIN_WAIT_1);
- } else {
- tcp_kill_prio(prio);
- }
- }
-
- tcp_pcb_num_cal(&tcp_pcb_num);
- if (tcp_pcb_num.total >= MEMP_NUM_TCP_PCB){
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: no available tcp pcb %d %d %d %d %d %d %d %d\n",
- tcp_pcb_num.total, tcp_pcb_num.time_wait, tcp_pcb_num.last_ack, tcp_pcb_num.closing,
- tcp_pcb_num.fin_wait2, tcp_pcb_num.fin_wait1, tcp_pcb_num.listen, tcp_pcb_num.bound));
- return NULL;
- }
-
-#endif
-
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing oldest connection in TIME-WAIT. */
@@ -1934,9 +1884,8 @@ tcp_pcb_purge(struct tcp_pcb *pcb)
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
LWIP_DEBUGF(TCP_DEBUG, (
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment