Merge "Merge commit 'e94c9ffa' from origin/upstream/master"
diff --git a/.gitignore b/.gitignore
index 0092ea6..cb0ed53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+*.o
+*.a
/doc/doxygen/output/html
/src/apps/snmp/LwipMibCompiler/CCodeGeneration/bin/
/src/apps/snmp/LwipMibCompiler/CCodeGeneration/obj/
@@ -11,3 +13,6 @@
/src/apps/snmp/LwipMibCompiler/SharpSnmpLib/obj/
/src/apps/snmp/LwipMibCompiler/LwipMibCompiler.userprefs
/src/apps/snmp/LwipMibCompiler/*.suo
+/test/fuzz/output
+/test/fuzz/lwip_fuzz
+/test/fuzz/.depend
diff --git a/CHANGELOG b/CHANGELOG
index 8f030fd..051f7ae 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,74 @@
(git master)
* [Enter new changes just after this line - do not remove this line]
+
+ ++ New features:
+
+ 2016-12-31: Simon Goldschmidt
+ * tcp.h/.c: added function tcp_listen_with_backlog_and_err() to get the error
+ reason when listening fails (bug #49861)
+
+ 2016-12-14: Jan Breuer:
+ * opt.h, ndc.h/.c: add support for RDNSS option (as per RFC 6106)
+
+ 2016-12-14: David van Moolenbroek
+ * opt.h, nd6.c: Added LWIP_HOOK_ND6_GET_GW()
+
+ 2016-12-09: Dirk Ziegelmeier
+ * ip6_frag.c: Implemented support for LWIP_NETIF_TX_SINGLE_PBUF
+
+ 2016-12-09: Simon Goldschmidt
+ * dns.c: added one-shot multicast DNS queries
+
+ 2016-11-24: Ambroz Bizjak, David van Moolenbroek
+ * tcp_out.c: Optimize passing contiguous nocopy buffers to tcp_write (bug #46290)
+
+ 2016-11-16: Dirk Ziegelmeier
+ * sockets.c: added support for IPv6 mapped IPv4 addresses
+
+ ++ Bugfixes:
+
+ 2016-12-16: Thomas Mueller
+ * api_lib.c: fixed race condition in return value of netconn_gethostbyname()
+ (and thus also lwip_gethostbyname/_r() and lwip_getaddrinfo())
+
+ 2016-12-15: David van Moolenbroek
+ * opt.h, tcp: added LWIP_HOOK_TCP_ISN() to implement less predictable initial
+ sequence numbers (see contrib/addons/tcp_isn for an example implementation)
+
+ 2016-12-05: Dirk Ziegelmeier
+ * fixed compiling with IPv4 disabled (IPv6 only case)
+
+ 2016-11-28: Simon Goldschmidt
+ * api_lib.c: fixed bug #49725 (send-timeout: netconn_write() can return
+ ERR_OK without all bytes being written)
+
+ 2016-11-28: Ambroz Bizjak
+ * tcpi_in.c: fixed bug #49717 (window size in received SYN and SYN-ACK
+ assumed scaled)
+
+ 2016-11-25: Simon Goldschmidt
+ * dhcp.c: fixed bug #49676 (Possible endless loop when parsing dhcp options)
+
+ 2016-11-23: Dirk Ziegelmeier
+ * udp.c: fixed bug #49662: multicast traffic is now only received on a UDP PCB
+ (and therefore on a UDP socket/netconn) when the PCB is bound to IP_ADDR_ANY
+
+ 2016-11-16: Dirk Ziegelmeier
+ * *: Fixed dual-stack behaviour, IPv6 mapped IPv4 support in socket API
+
+ 2016-11-14: Joel Cunningham
+ * tcp_out.c: fixed bug #49533 (start persist timer when unsent seg can't fit
+ in window)
+
+ 2016-11-16: Roberto Barbieri Carrera
+ * autoip.c: fixed bug #49610 (sometimes AutoIP fails to reuse the same address)
+
+ 2016-11-11: Dirk Ziegelmeier
+ * sockets.c: fixed bug #49578 (dropping multicast membership does not work
+ with LWIP_SOCKET_OFFSET)
+
+(STABLE-2.0.0)
++ New features:
diff --git a/UPGRADING b/UPGRADING
index c42c67d..d9b9d4a 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -8,7 +8,12 @@
* [Enter new changes just after this line - do not remove this line]
- * TODO
+ ++ Application changes:
+
+ * UDP does NOT receive multicast traffic from ALL netifs on an UDP PCB bound to a specific
+ netif any more. Users need to bind to IP_ADDR_ANY to receive multicast traffic and compare
+ ip_current_netif() to the desired netif for every packet.
+ See bug #49662 for an explanation.
(2.0.0)
diff --git a/doc/doxygen/generate.sh b/doc/doxygen/generate.sh
index 99afb12..89344b0 100755
--- a/doc/doxygen/generate.sh
+++ b/doc/doxygen/generate.sh
@@ -1 +1,3 @@
+#!/bin/sh
+
doxygen lwip.Doxyfile
diff --git a/doc/doxygen/lwip.Doxyfile b/doc/doxygen/lwip.Doxyfile
index 125d323..177d4c4 100644
--- a/doc/doxygen/lwip.Doxyfile
+++ b/doc/doxygen/lwip.Doxyfile
@@ -32,19 +32,19 @@
# title of most generated pages and in a few other places.
# The default value is: My Project.
-PROJECT_NAME = "lwIP 2.0.0"
+PROJECT_NAME = "lwIP"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = "lwIP 2.0.0"
+PROJECT_NUMBER = "2.0.1"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
-PROJECT_BRIEF = Lightweight IP stack
+PROJECT_BRIEF = "Lightweight IP stack"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
diff --git a/doc/doxygen/main_page.h b/doc/doxygen/main_page.h
index 31cc0df..4f43d60 100644
--- a/doc/doxygen/main_page.h
+++ b/doc/doxygen/main_page.h
@@ -103,7 +103,8 @@
* *not* *from* *interrupt* *context*. You can allocate a @ref pbuf in interrupt
* context and put them into a queue which is processed from mainloop.\n
* Call sys_check_timeouts() periodically in the mainloop.\n
- * Porting: implement all functions in @ref sys_time and @ref sys_prot.\n
+ * Porting: implement all functions in @ref sys_time, @ref sys_prot and
+ * @ref compiler_abstraction.\n
* You can only use @ref callbackstyle_api in this mode.\n
* Sample code:\n
* @include NO_SYS_SampleCode.c
diff --git a/doc/mqtt_client.txt b/doc/mqtt_client.txt
new file mode 100644
index 0000000..3e67def
--- /dev/null
+++ b/doc/mqtt_client.txt
@@ -0,0 +1,162 @@
+MQTT client for lwIP
+
+Author: Erik Andersson
+
+Details of the MQTT protocol can be found at:
+http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
+
+-----------------------------------------------------------------
+1. Initial steps, reserve memory and make connection to server:
+
+1.1: Provide storage
+
+Static allocation:
+ mqtt_client_t static_client;
+ example_do_connect(&static_client);
+
+Dynamic allocation:
+ mqtt_client_t *client = mqtt_client_new();
+ if(client != NULL) {
+ example_do_connect(&client);
+ }
+
+1.2: Establish Connection with server
+
+void example_do_connect(mqtt_client_t *client)
+{
+ struct mqtt_connect_client_info_t ci;
+ err_t err;
+
+ /* Setup an empty client info structure */
+ memset(&ci, 0, sizeof(ci));
+
+ /* Minimal amount of information required is client identifier, so set it here */
+ ci.client_id = "lwip_test";
+
+ /* Initiate client and connect to server, if this fails immediately an error code is returned
+ otherwise mqtt_connection_cb will be called with connection result after attempting
+ to establish a connection with the server.
+ For now MQTT version 3.1.1 is always used */
+
+ err = mqtt_client_connect(client, ip_addr, MQTT_PORT, mqtt_connection_cb, 0, &ci);
+
+ /* For now just print the result code if something goes wrong
+ if(err != ERR_OK) {
+ printf("mqtt_connect return %d\n", err);
+ }
+}
+
+Connection to server can also be probed by calling mqtt_client_is_connected(client)
+
+-----------------------------------------------------------------
+2. Implementing the connection status callback
+
+
+static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
+{
+ err_t err;
+ if(status == MQTT_CONNECT_ACCEPTED) {
+ printf("mqtt_connection_cb: Successfully connected\n");
+
+ /* Setup callback for incoming publish requests */
+ mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
+
+ /* Subscribe to a topic named "subtopic" with QoS level 1, call mqtt_sub_request_cb with result */
+ err = mqtt_subscribe(client, "subtopic", 1, mqtt_sub_request_cb, arg);
+
+ if(err != ERR_OK) {
+ printf("mqtt_subscribe return: %d\n", err);
+ }
+ } else {
+ printf("mqtt_connection_cb: Disconnected, reason: %d\n", status);
+
+ /* Its more nice to be connected, so try to reconnect */
+ example_do_connect(client);
+ }
+}
+
+static void mqtt_sub_request_cb(void *arg, err_t result)
+{
+ /* Just print the result code here for simplicity,
+ normal behaviour would be to take some action if subscribe fails like
+ notifying user, retry subscribe or disconnect from server */
+ printf("Subscribe result: %d\n", result);
+}
+
+-----------------------------------------------------------------
+3. Implementing callbacks for incoming publish and data
+
+/* The idea is to demultiplex topic and create some reference to be used in data callbacks
+ Example here uses a global variable, better would be to use a member in arg
+ If RAM and CPU budget allows it, the easiest implementation might be to just take a copy of
+ the topic string and use it in mqtt_incoming_data_cb
+*/
+static int inpub_id;
+static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
+{
+ printf("Incoming publish at topic %s with total length %u\n", topic, (unsigned int)tot_len);
+
+ /* Decode topic string into a user defined reference */
+ if(strcmp(topic, "print_payload") == 0) {
+ inpub_id = 0;
+ } else if(topic[0] == 'A') {
+ /* All topics starting with 'A' might be handled at the same way */
+ inpub_id = 1;
+ } else {
+ /* For all other topics */
+ inpub_id = 2;
+ }
+}
+
+static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
+{
+ printf("Incoming publish payload with length %d, flags %u\n", len, (unsigned int)flags);
+
+ if(flags & MQTT_DATA_FLAG_LAST) {
+ /* Last fragment of payload received (or whole part if payload fits receive buffer
+ See MQTT_VAR_HEADER_BUFFER_LEN) */
+
+ /* Call function or do action depending on reference, in this case inpub_id */
+ if(inpub_id == 0) {
+ /* Don't trust the publisher, check zero termination */
+ if(data[len-1] == 0) {
+ printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
+ }
+ } else if(inpub_id == 1) {
+ /* Call an 'A' function... */
+ } else {
+ printf("mqtt_incoming_data_cb: Ignoring payload...\n");
+ }
+ } else {
+ /* Handle fragmented payload, store in buffer, write to file or whatever */
+ }
+}
+
+-----------------------------------------------------------------
+4. Using outgoing publish
+
+
+void example_publish(mqtt_client_t *client, void *arg)
+{
+ const char *pub_payload= "PubSubHubLubJub";
+ err_t err;
+ u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
+ u8_t retain = 0; /* No don't retain such crappy payload... */
+ err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
+ if(err != ERR_OK) {
+ printf("Publish err: %d\n", err);
+ }
+}
+
+/* Called when publish is complete either with sucess or failure */
+static void mqtt_pub_request_cb(void *arg, err_t result)
+{
+ if(result != ERR_OK) {
+ printf("Publish result: %d\n", result);
+ }
+}
+
+-----------------------------------------------------------------
+5. Disconnecting
+
+Simply call mqtt_disconnect(client)
diff --git a/doc/sys_arch.txt b/doc/sys_arch.txt
index 333946d..4dc727b 100644
--- a/doc/sys_arch.txt
+++ b/doc/sys_arch.txt
@@ -29,7 +29,7 @@
Semaphores are represented by the type "sys_sem_t" which is typedef'd
in the sys_arch.h file. Mailboxes are equivalently represented by the
-type "sys_mbox_t". Mutexes are represented ny the type "sys_mutex_t".
+type "sys_mbox_t". Mutexes are represented by the type "sys_mutex_t".
lwIP does not place any restrictions on how these types are represented
internally.
diff --git a/src/Filelists.mk b/src/Filelists.mk
index ac79422..7d30bb8 100644
--- a/src/Filelists.mk
+++ b/src/Filelists.mk
@@ -167,6 +167,9 @@
# TFTPFILES: TFTP server files
TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
+# MQTTFILES: MQTT client files
+MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c
+
# LWIPAPPFILES: All LWIP APPs
LWIPAPPFILES=$(SNMPFILES) \
$(HTTPDFILES) \
@@ -174,4 +177,5 @@
$(SNTPFILES) \
$(MDNSFILES) \
$(NETBIOSNSFILES) \
- $(TFTPFILES)
+ $(TFTPFILES) \
+ $(MQTTFILES)
diff --git a/src/api/api_lib.c b/src/api/api_lib.c
index b4fc403..3c1d6a6 100644
--- a/src/api/api_lib.c
+++ b/src/api/api_lib.c
@@ -254,10 +254,22 @@
LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+#if LWIP_IPV4
/* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
if (addr == NULL) {
addr = IP4_ADDR_ANY;
}
+#endif /* LWIP_IPV4 */
+
+#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 0, use IP_ANY_TYPE to bind
+ */
+ if ((netconn_get_ipv6only(conn) == 0) &&
+ ip_addr_cmp(addr, IP6_ADDR_ANY)) {
+ addr = IP_ANY_TYPE;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
API_MSG_VAR_ALLOC(msg);
API_MSG_VAR_REF(msg).conn = conn;
@@ -286,10 +298,12 @@
LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+#if LWIP_IPV4
/* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
if (addr == NULL) {
addr = IP4_ADDR_ANY;
}
+#endif /* LWIP_IPV4 */
API_MSG_VAR_ALLOC(msg);
API_MSG_VAR_REF(msg).conn = conn;
@@ -376,7 +390,6 @@
#if LWIP_TCP
void *accept_ptr;
struct netconn *newconn;
- err_t err;
#if TCP_LISTEN_BACKLOG
API_MSG_VAR_DECLARE(msg);
#endif /* TCP_LISTEN_BACKLOG */
@@ -385,11 +398,10 @@
*new_conn = NULL;
LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
- err = conn->last_err;
- if (ERR_IS_FATAL(err)) {
+ if (ERR_IS_FATAL(conn->last_err)) {
/* don't recv on fatal errors: this might block the application task
waiting on acceptmbox forever! */
- return err;
+ return conn->last_err;
}
if (!sys_mbox_valid(&conn->acceptmbox)) {
return ERR_CLSD;
@@ -465,7 +477,6 @@
{
void *buf = NULL;
u16_t len;
- err_t err;
#if LWIP_TCP
API_MSG_VAR_DECLARE(msg);
#if LWIP_MPU_COMPATIBLE
@@ -489,13 +500,12 @@
#endif /* LWIP_TCP */
LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
- err = conn->last_err;
- if (ERR_IS_FATAL(err)) {
+ 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 err;
+ return conn->last_err;
}
#if LWIP_TCP
#if (LWIP_UDP || LWIP_RAW)
@@ -562,7 +572,7 @@
#if (LWIP_UDP || LWIP_RAW)
{
LWIP_ASSERT("buf != NULL", buf != NULL);
- len = netbuf_len((struct netbuf *)buf);
+ len = netbuf_len((struct netbuf*)buf);
}
#endif /* (LWIP_UDP || LWIP_RAW) */
@@ -697,6 +707,7 @@
LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+
API_MSG_VAR_ALLOC(msg);
API_MSG_VAR_REF(msg).conn = conn;
API_MSG_VAR_REF(msg).msg.b = buf;
@@ -734,6 +745,11 @@
return ERR_OK;
}
dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout != 0) {
+ dontblock = 1;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
if (dontblock && !bytes_written) {
/* This implies netconn_write() cannot be used for non-blocking send, since
it has no way to return the number of bytes written. */
@@ -761,11 +777,7 @@
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
-#if LWIP_SO_SNDTIMEO
- || (conn->send_timeout != 0)
-#endif /* LWIP_SO_SNDTIMEO */
- ) {
+ if (dontblock) {
/* nonblocking write: maybe the data has been sent partly */
*bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
} else {
@@ -869,6 +881,7 @@
API_MSG_VAR_ALLOC(msg);
+#if LWIP_IPV4
/* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
if (multiaddr == NULL) {
multiaddr = IP4_ADDR_ANY;
@@ -876,6 +889,7 @@
if (netif_addr == NULL) {
netif_addr = IP4_ADDR_ANY;
}
+#endif /* LWIP_IPV4 */
API_MSG_VAR_REF(msg).conn = conn;
API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
@@ -914,6 +928,7 @@
sys_sem_t sem;
#endif /* LWIP_MPU_COMPATIBLE */
err_t err;
+ err_t cberr;
LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
@@ -946,13 +961,13 @@
}
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
- err = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
- if (err != ERR_OK) {
+ cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
+ if (cberr != ERR_OK) {
#if !LWIP_NETCONN_SEM_PER_THREAD
sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
- return err;
+ return cberr;
}
sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
#if !LWIP_NETCONN_SEM_PER_THREAD
diff --git a/src/api/api_msg.c b/src/api/api_msg.c
index 86546c2..4927ee5 100644
--- a/src/api/api_msg.c
+++ b/src/api/api_msg.c
@@ -43,6 +43,7 @@
#include "lwip/priv/api_msg.h"
#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/raw.h"
@@ -544,13 +545,22 @@
static void
pcb_new(struct api_msg *msg)
{
- LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+ enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
+ LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+#if LWIP_IPV6 && LWIP_IPV4
+ /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+ if(NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+ iptype = IPADDR_TYPE_ANY;
+ }
+#endif
+
/* Allocate a PCB for this connection */
switch(NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
- msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
+ msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
if (msg->conn->pcb.raw != NULL) {
raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
}
@@ -558,7 +568,7 @@
#endif /* LWIP_RAW */
#if LWIP_UDP
case NETCONN_UDP:
- msg->conn->pcb.udp = udp_new();
+ msg->conn->pcb.udp = udp_new_ip_type(iptype);
if (msg->conn->pcb.udp != NULL) {
#if LWIP_UDPLITE
if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
@@ -574,7 +584,7 @@
#endif /* LWIP_UDP */
#if LWIP_TCP
case NETCONN_TCP:
- msg->conn->pcb.tcp = tcp_new();
+ msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
if (msg->conn->pcb.tcp != NULL) {
setup_tcp(msg->conn);
}
@@ -588,15 +598,6 @@
if (msg->conn->pcb.ip == NULL) {
msg->err = ERR_MEM;
}
-#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 */
}
/**
@@ -1113,37 +1114,20 @@
} else {
msg->err = ERR_VAL;
if (msg->conn->pcb.tcp != NULL) {
- const ip_addr_t *ipaddr = API_EXPR_REF(msg->msg.bc.ipaddr);
-
-#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(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 */
- ipaddr = IP_ANY_TYPE;
- }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
- msg->err = raw_bind(msg->conn->pcb.raw, ipaddr);
+ msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
break;
#endif /* LWIP_RAW */
#if LWIP_UDP
case NETCONN_UDP:
- msg->err = udp_bind(msg->conn->pcb.udp, ipaddr, msg->msg.bc.port);
+ msg->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:
- msg->err = tcp_bind(msg->conn->pcb.tcp, ipaddr, msg->msg.bc.port);
+ msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
break;
#endif /* LWIP_TCP */
default:
@@ -1323,6 +1307,13 @@
/* connection is not closed, cannot listen */
msg->err = ERR_VAL;
} else {
+ err_t err;
+ u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+ backlog = msg->msg.lb.backlog;
+#else /* TCP_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
@@ -1335,15 +1326,11 @@
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
-#if TCP_LISTEN_BACKLOG
- lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
-#else /* TCP_LISTEN_BACKLOG */
- lpcb = tcp_listen(msg->conn->pcb.tcp);
-#endif /* TCP_LISTEN_BACKLOG */
+ 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_MEM;
+ msg->err = err;
} else {
/* delete the recvmbox and allocate the acceptmbox */
if (sys_mbox_valid(&msg->conn->recvmbox)) {
@@ -1400,7 +1387,7 @@
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
- if (ip_addr_isany(&msg->msg.b->addr)) {
+ 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);
} else {
msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
@@ -1588,10 +1575,11 @@
write_finished = 1;
conn->current_msg->msg.w.len = 0;
}
- } else if ((err == ERR_MEM) && !dontblock) {
- /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
- we do NOT return to the application thread, since ERR_MEM is
- only a temporary error! */
+ } else if (err == ERR_MEM) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+ For blocking sockets, we do NOT return to the application
+ thread, since ERR_MEM is only a temporary error! Non-blocking
+ will remain non-writable until sent_tcp/poll_tcp is called */
/* tcp_write returned ERR_MEM, try tcp_output anyway */
err_t out_err = tcp_output(conn->pcb.tcp);
@@ -1602,6 +1590,11 @@
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;
+ write_finished = 1;
+ conn->current_msg->msg.w.len = 0;
}
} else {
/* On errors != ERR_MEM, we don't try writing any more but return
@@ -1710,6 +1703,7 @@
ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
msg->conn->pcb.ip->remote_ip);
}
+
msg->err = ERR_OK;
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
diff --git a/src/api/err.c b/src/api/err.c
index 1593e70..35e9c02 100644
--- a/src/api/err.c
+++ b/src/api/err.c
@@ -64,6 +64,15 @@
ENOTCONN, /* ERR_CLSD -15 Connection closed. */
EIO /* ERR_ARG -16 Illegal argument. */
};
+
+int
+err_to_errno(err_t err)
+{
+ if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+ return EIO;
+ }
+ return err_to_errno_table[-err];
+}
#endif /* !NO_SYS */
#ifdef LWIP_DEBUG
@@ -104,14 +113,3 @@
}
#endif /* LWIP_DEBUG */
-
-#if !NO_SYS
-int
-err_to_errno(err_t err)
-{
- if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
- return EIO;
- }
- return err_to_errno_table[-err];
-}
-#endif /* !NO_SYS */
diff --git a/src/api/netdb.c b/src/api/netdb.c
index ef0c79b..ac1d8c7 100644
--- a/src/api/netdb.c
+++ b/src/api/netdb.c
@@ -47,7 +47,6 @@
#include "lwip/dns.h"
#include <string.h>
-#include <stdlib.h>
/** helper struct for gethostbyname_r to access the char* buffer */
struct gethostbyname_r_helper {
@@ -382,7 +381,7 @@
#if LWIP_IPV4
struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
/* set up sockaddr */
- inet_addr_from_ipaddr(&sa4->sin_addr, ip_2_ip4(&addr));
+ inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
sa4->sin_family = AF_INET;
sa4->sin_len = sizeof(struct sockaddr_in);
sa4->sin_port = lwip_htons((u16_t)port_nr);
diff --git a/src/api/sockets.c b/src/api/sockets.c
index 960ac7e..24b3b5f 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -82,10 +82,10 @@
(sin)->sin_len = sizeof(struct sockaddr_in); \
(sin)->sin_family = AF_INET; \
(sin)->sin_port = lwip_htons((port)); \
- inet_addr_from_ipaddr(&(sin)->sin_addr, ipaddr); \
+ inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
- inet_addr_to_ipaddr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+ inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
(port) = lwip_ntohs((sin)->sin_port); }while(0)
#endif /* LWIP_IPV4 */
@@ -482,7 +482,7 @@
if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
- sock_set_errno(sock, EWOULDBLOCK);
+ set_errno(EWOULDBLOCK);
return -1;
}
@@ -584,6 +584,14 @@
ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+ if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&local_addr))) {
+ unmap_ipv6_mapped_ipv4(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+ IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
err = netconn_bind(sock->conn, &local_addr, local_port);
if (err != ERR_OK) {
@@ -668,6 +676,14 @@
ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+ if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&remote_addr))) {
+ unmap_ipv6_mapped_ipv4(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+ IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
err = netconn_connect(sock->conn, &remote_addr, remote_port);
}
@@ -755,7 +771,7 @@
return off;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
- sock_set_errno(sock, EWOULDBLOCK);
+ set_errno(EWOULDBLOCK);
return -1;
}
@@ -847,6 +863,15 @@
port = netbuf_fromport((struct netbuf *)buf);
fromaddr = netbuf_fromaddr((struct netbuf *)buf);
}
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv6 mapped IPv4 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) {
+ ip4_2_ipv6_mapped_ipv4(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);
ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
@@ -1066,6 +1091,14 @@
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+ if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&chain_buf->addr))) {
+ unmap_ipv6_mapped_ipv4(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);
}
@@ -1107,12 +1140,6 @@
#endif /* LWIP_TCP */
}
- if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) {
- /* sockaddr does not match socket type (IPv4/IPv6) */
- sock_set_errno(sock, err_to_errno(ERR_VAL));
- return -1;
- }
-
/* @todo: split into multiple sendto's? */
LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
short_size = (u16_t)size;
@@ -1162,6 +1189,14 @@
err = netbuf_ref(&buf, data, short_size);
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+ if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&buf.addr))) {
+ unmap_ipv6_mapped_ipv4(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+ IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
/* send the data */
err = netconn_send(sock->conn, &buf);
}
@@ -1179,9 +1214,7 @@
struct netconn *conn;
int i;
-#if !LWIP_IPV6
LWIP_UNUSED_ARG(domain); /* @todo: check this */
-#endif /* LWIP_IPV6 */
/* create a netconn */
switch (type) {
@@ -1244,7 +1277,7 @@
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_iov = LWIP_CONST_CAST(struct iovec *, iov);
msg.msg_iovlen = iovcnt;
msg.msg_control = NULL;
msg.msg_controllen = 0;
@@ -1710,12 +1743,21 @@
}
/* get the IP address and port */
- /* @todo: this does not work for IPv6, yet */
err = netconn_getaddr(sock->conn, &naddr, &port, local);
if (err != ERR_OK) {
sock_set_errno(sock, err_to_errno(err));
return -1;
}
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv6 mapped IPv4 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+ IP_IS_V4_VAL(naddr)) {
+ ip4_2_ipv6_mapped_ipv4(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+ IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
@@ -2001,7 +2043,7 @@
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
return ENOPROTOOPT;
}
- inet_addr_from_ipaddr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+ inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
s, *(u32_t *)optval));
break;
@@ -2029,6 +2071,9 @@
case IPPROTO_TCP:
/* 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) {
+ return EINVAL;
+ }
switch (optname) {
case TCP_NODELAY:
*(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
@@ -2073,10 +2118,6 @@
switch (optname) {
case IPV6_V6ONLY:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
- /* @todo: this does not work for datagram sockets, yet */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
- return ENOPROTOOPT;
- }
*(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
s, *(int *)optval));
@@ -2365,7 +2406,7 @@
{
ip4_addr_t if_addr;
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
- inet_addr_to_ipaddr(&if_addr, (const struct in_addr*)optval);
+ inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval);
udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
}
break;
@@ -2389,8 +2430,8 @@
ip4_addr_t if_addr;
ip4_addr_t multi_addr;
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
- inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
- inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
+ inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+ inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
if (optname == IP_ADD_MEMBERSHIP) {
if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
/* cannot track membership (out of memory) */
@@ -2422,6 +2463,9 @@
case IPPROTO_TCP:
/* 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) {
+ return EINVAL;
+ }
switch (optname) {
case TCP_NODELAY:
if (*(const int*)optval) {
@@ -2469,7 +2513,6 @@
case IPPROTO_IPV6:
switch (optname) {
case IPV6_V6ONLY:
- /* @todo: this does not work for datagram sockets, yet */
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
if (*(const int*)optval) {
netconn_set_ipv6only(sock->conn, 1);
diff --git a/src/apps/httpd/httpd.c b/src/apps/httpd/httpd.c
index 103f9ac..bdffee3 100644
--- a/src/apps/httpd/httpd.c
+++ b/src/apps/httpd/httpd.c
@@ -99,7 +99,6 @@
#include "lwip/tcp.h"
#include <string.h>
-#include <stdlib.h>
#include <stdio.h>
#if LWIP_TCP
@@ -2563,6 +2562,7 @@
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);
diff --git a/src/apps/lwiperf/lwiperf.c b/src/apps/lwiperf/lwiperf.c
index 1996cd1..54bf2bc 100644
--- a/src/apps/lwiperf/lwiperf.c
+++ b/src/apps/lwiperf/lwiperf.c
@@ -294,7 +294,7 @@
} else {
/* transmit data */
/* @todo: every x bytes, transmit the settings again */
- txptr = (void*)(size_t)&lwiperf_txbuf_const[conn->bytes_transferred % 10];
+ txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
txlen_max = TCP_MSS;
if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
txlen_max = TCP_MSS - 24;
@@ -494,10 +494,8 @@
return ERR_VAL;
}
}
- packet_idx += i;
-#else
- packet_idx += q->len;
#endif
+ packet_idx += q->len;
}
LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
conn->bytes_transferred += packet_idx;
@@ -579,7 +577,7 @@
void*
lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg)
{
- return lwiperf_start_tcp_server(IP4_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
+ return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
report_fn, report_arg);
}
diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index 3c5cf2d..14334fc 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -65,7 +65,6 @@
#include "lwip/prot/dns.h"
#include <string.h>
-#include <stdlib.h>
#if LWIP_MDNS_RESPONDER
@@ -82,13 +81,13 @@
#if LWIP_IPV4
#include "lwip/igmp.h"
/* IPv4 multicast group 224.0.0.251 */
-static const ip_addr_t v4group = IPADDR4_INIT(PP_HTONL(0xE00000FBUL));
+static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
#endif
#if LWIP_IPV6
#include "lwip/mld6.h"
/* IPv6 multicast group FF02::FB */
-static const ip_addr_t v6group = IPADDR6_INIT(PP_HTONL(0xFF020000UL), PP_HTONL(0x00000000UL), PP_HTONL(0x00000000UL), PP_HTONL(0x000000FBUL));
+static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
#endif
#define MDNS_PORT 5353
@@ -570,7 +569,7 @@
LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd")-1));
LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
- res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)(sizeof(dnssd_protos[DNSSD_PROTO_UDP])-1));
+ res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
return mdns_add_dotlocal(domain);
}
@@ -595,7 +594,7 @@
}
res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
- res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)(sizeof(dnssd_protos[DNSSD_PROTO_UDP])-1));
+ res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
return mdns_add_dotlocal(domain);
}
@@ -1821,6 +1820,7 @@
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);
@@ -2019,7 +2019,7 @@
err_t
mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
{
- LWIP_ASSERT("mdns_resp_add_service: service != NULL", service);
+ LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
/* Use a mdns_domain struct to store txt chunks since it is the same encoding */
return mdns_domain_add_label(&service->txtdata, txt, txt_len);
diff --git a/src/apps/mqtt/mqtt.c b/src/apps/mqtt/mqtt.c
new file mode 100644
index 0000000..5f4a9ef
--- /dev/null
+++ b/src/apps/mqtt/mqtt.c
@@ -0,0 +1,1335 @@
+/**
+ * @file
+ * MQTT client
+ *
+ * @defgroup mqtt MQTT client
+ * @ingroup apps
+ * @verbinclude mqtt_client.txt
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson <erian747@gmail.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: Erik Andersson <erian747@gmail.com>
+ *
+ *
+ * @todo:
+ * - Handle large outgoing payloads for PUBLISH messages
+ * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
+ * - Add support for legacy MQTT protocol version
+ *
+ * Please coordinate changes and requests with Erik Andersson
+ * Erik Andersson <erian747@gmail.com>
+ *
+ */
+#include <string.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/apps/mqtt.h"
+
+/**
+ * MQTT_DEBUG: Default is off.
+ */
+#if !defined MQTT_DEBUG || defined __DOXYGEN__
+#define MQTT_DEBUG LWIP_DBG_OFF
+#endif
+
+#define MQTT_DEBUG_TRACE (MQTT_DEBUG | LWIP_DBG_TRACE)
+#define MQTT_DEBUG_STATE (MQTT_DEBUG | LWIP_DBG_STATE)
+#define MQTT_DEBUG_WARN (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#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
+ */
+enum {
+ TCP_DISCONNECTED,
+ TCP_CONNECTING,
+ MQTT_CONNECTING,
+ MQTT_CONNECTED
+};
+
+/**
+ * MQTT control message types
+ */
+enum mqtt_message_type {
+ MQTT_MSG_TYPE_CONNECT = 1,
+ MQTT_MSG_TYPE_CONNACK = 2,
+ MQTT_MSG_TYPE_PUBLISH = 3,
+ MQTT_MSG_TYPE_PUBACK = 4,
+ MQTT_MSG_TYPE_PUBREC = 5,
+ MQTT_MSG_TYPE_PUBREL = 6,
+ MQTT_MSG_TYPE_PUBCOMP = 7,
+ MQTT_MSG_TYPE_SUBSCRIBE = 8,
+ MQTT_MSG_TYPE_SUBACK = 9,
+ MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
+ MQTT_MSG_TYPE_UNSUBACK = 11,
+ MQTT_MSG_TYPE_PINGREQ = 12,
+ MQTT_MSG_TYPE_PINGRESP = 13,
+ MQTT_MSG_TYPE_DISCONNECT = 14
+};
+
+/** Helpers to extract control packet type and qos from first byte in fixed header */
+#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
+#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
+
+/**
+ * MQTT connect flags, only used in CONNECT message
+ */
+enum mqtt_connect_flag {
+ MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
+ MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
+ MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
+ MQTT_CONNECT_FLAG_WILL = 1 << 2,
+ MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
+};
+
+
+#if defined(LWIP_DEBUG)
+static const char * const mqtt_message_type_str[15] =
+{
+ "UNDEFINED",
+ "CONNECT",
+ "CONNACK",
+ "PUBLISH",
+ "PUBACK",
+ "PUBREC",
+ "PUBREL",
+ "PUBCOMP",
+ "SUBSCRIBE",
+ "SUBACK",
+ "UNSUBSCRIBE",
+ "UNSUBACK",
+ "PINGREQ",
+ "PINGRESP",
+ "DISCONNECT"
+};
+
+/**
+ * Message type value to string
+ * @param msg_type see enum mqtt_message_type
+ *
+ * @return Control message type text string
+ */
+static const char *
+mqtt_msg_type_to_str(u8_t msg_type)
+{
+ if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
+ msg_type = 0;
+ }
+ return mqtt_message_type_str[msg_type];
+}
+
+#endif
+
+
+/**
+ * Generate MQTT packet identifier
+ * @param client MQTT client
+ * @return New packet identifier, range 1 to 65535
+ */
+static u16_t
+msg_generate_packet_id(mqtt_client_t *client)
+{
+ client->pkt_id_seq++;
+ if (client->pkt_id_seq == 0) {
+ client->pkt_id_seq++;
+ }
+ return client->pkt_id_seq;
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output ring buffer */
+
+
+#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1)
+
+/** Add single item to ring buffer */
+#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item)
+
+/** Return number of bytes in ring buffer */
+#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get))
+
+/** 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))
+
+
+/**
+ * Try send as many bytes as possible from output ring buffer
+ * @param rb Output ring buffer
+ * @param tpcb TCP connection handle
+ */
+static void
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_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);
+ LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
+
+ if (send_len == 0 || ringbuf_lin_len == 0) {
+ return;
+ }
+
+ 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)));
+
+ if (send_len > ringbuf_lin_len) {
+ /* Space in TCP output buffer is larger than available in ring buffer linear portion */
+ send_len = ringbuf_lin_len;
+ /* 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));
+ 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);
+ }
+
+ if (err == ERR_OK) {
+ mqtt_ringbuf_advance_get_idx(rb, send_len);
+ /* Flush */
+ tcp_output(tpcb);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+ }
+}
+
+
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Request queue */
+
+/**
+ * Create request item
+ * @param r_objs Pointer to request objects
+ * @param pkt_id Packet identifier of request
+ * @param cb Packet callback to call when requests lifetime ends
+ * @param arg Parameter following callback
+ * @return Request or NULL if failed to create
+ */
+static struct mqtt_request_t *
+mqtt_create_request(struct mqtt_request_t *r_objs, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
+{
+ struct mqtt_request_t *r = NULL;
+ u8_t n;
+ LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
+ for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT && r == NULL; n++) {
+ /* Item point to itself if not in use */
+ if (r_objs[n].next == &r_objs[n]) {
+ r = &r_objs[n];
+ r->next = NULL;
+ r->cb = cb;
+ r->arg = arg;
+ r->pkt_id = pkt_id;
+ }
+ }
+ return r;
+}
+
+
+/**
+ * Append request to pending request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param r Request to append
+ */
+static void
+mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
+{
+ struct mqtt_request_t *head = NULL;
+ s16_t time_before = 0;
+ struct mqtt_request_t *iter = *tail;
+
+ LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
+
+ /* Iterate trough queue to find head, and count total timeout time */
+ for (iter = *tail; iter != NULL; iter = iter->next) {
+ time_before += iter->timeout_diff;
+ head = iter;
+ }
+
+ LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
+ r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
+ if (head == NULL) {
+ *tail = r;
+ } else {
+ head->next = r;
+ }
+}
+
+
+/**
+ * Delete request item
+ * @param r Request item to delete
+ */
+static void
+mqtt_delete_request(struct mqtt_request_t *r)
+{
+ if (r != NULL) {
+ r->next = r;
+ }
+}
+
+/**
+ * Remove a request item with a specific packet identifier from request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param pkt_id Packet identifier of request to take
+ * @return Request item if found, NULL if not
+ */
+static struct mqtt_request_t *
+mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
+{
+ struct mqtt_request_t *iter = NULL, *prev = NULL;
+ LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
+ /* Search all request for pkt_id */
+ for (iter = *tail; iter != NULL; iter = iter->next) {
+ if (iter->pkt_id == pkt_id) {
+ break;
+ }
+ prev = iter;
+ }
+
+ /* If request was found */
+ if (iter != NULL) {
+ /* unchain */
+ if (prev == NULL) {
+ *tail= iter->next;
+ } else {
+ prev->next = iter->next;
+ }
+ /* If exists, add remaining timeout time for the request to next */
+ if (iter->next != NULL) {
+ iter->next->timeout_diff += iter->timeout_diff;
+ }
+ iter->next = NULL;
+ }
+ return iter;
+}
+
+/**
+ * Handle requests timeout
+ * @param tail Pointer to request queue tail pointer
+ * @param t Time since last call in seconds
+ */
+static void
+mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
+{
+ struct mqtt_request_t *r = *tail;
+ LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+ while (t > 0 && r != NULL) {
+ if (t >= r->timeout_diff) {
+ t -= (u8_t)r->timeout_diff;
+ /* Unchain */
+ *tail = r->next;
+ /* Notify upper layer about timeout */
+ if (r->cb != NULL) {
+ r->cb(r->arg, ERR_TIMEOUT);
+ }
+ mqtt_delete_request(r);
+ /* Tail might be be modified in callback, so re-read it in every iteration */
+ r = *(struct mqtt_request_t * const volatile *)tail;
+ } else {
+ r->timeout_diff -= t;
+ t = 0;
+ }
+ }
+}
+
+/**
+ * Free all request items
+ * @param tail Pointer to request queue tail pointer
+ */
+static void
+mqtt_clear_requests(struct mqtt_request_t **tail)
+{
+ struct mqtt_request_t *iter, *next;
+ LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
+ for (iter = *tail; iter != NULL; iter = next) {
+ next = iter->next;
+ mqtt_delete_request(iter);
+ }
+ *tail = NULL;
+}
+/**
+ * Initialize all request items
+ * @param r_objs Pointer to request objects
+ */
+static void
+mqtt_init_requests(struct mqtt_request_t *r_objs)
+{
+ u8_t n;
+ LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
+ for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) {
+ /* Item pointing to itself indicates unused */
+ r_objs[n].next = &r_objs[n];
+ }
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output message build helpers */
+
+
+static void
+mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
+{
+ mqtt_ringbuf_put(rb, value);
+}
+
+static
+void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
+{
+ mqtt_ringbuf_put(rb, value >> 8);
+ mqtt_ringbuf_put(rb, value & 0xff);
+}
+
+static void
+mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
+{
+ u16_t n;
+ for (n = 0; n < length; n++) {
+ mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
+ }
+}
+
+static void
+mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
+{
+ u16_t n;
+ mqtt_ringbuf_put(rb, length >> 8);
+ mqtt_ringbuf_put(rb, length & 0xff);
+ for (n = 0; n < length; n++) {
+ mqtt_ringbuf_put(rb, str[n]);
+ }
+}
+
+/**
+ * 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 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)
+{
+ /* Start with control byte */
+ mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1)));
+ /* Encode remaining length field */
+ do {
+ mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
+ r_length >>= 7;
+ } while (r_length > 0);
+}
+
+
+/**
+ * Check output buffer space
+ * @param rb Output ring buffer
+ * @param r_length Remaining length after fixed header
+ * @return 1 if message will fit, 0 if not enough buffer space
+ */
+static u8_t
+mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
+{
+ /* Start with length of type byte + remaining length */
+ u16_t total_len = 1 + r_length;
+
+ LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
+
+ /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
+ do {
+ total_len++;
+ r_length >>= 7;
+ } while (r_length > 0);
+
+ return (total_len <= mqtt_ringbuf_free(rb));
+}
+
+
+/**
+ * Close connection to server
+ * @param client MQTT client
+ * @param reason Reason for disconnection
+ */
+static void
+mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
+{
+ LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
+
+ /* 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);
+ if (res != ERR_OK) {
+ tcp_abort(client->conn);
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res)));
+ }
+ client->conn = NULL;
+ }
+
+ /* Remove all pending requests */
+ mqtt_clear_requests(&client->pend_req_queue);
+ /* Stop cyclic timer */
+ sys_untimeout(mqtt_cyclic_timer, client);
+
+ /* Notify upper layer of disconnection if changed state */
+ if (client->conn_state != TCP_DISCONNECTED) {
+
+ client->conn_state = TCP_DISCONNECTED;
+ if (client->connect_cb != NULL) {
+ client->connect_cb(client, client->connect_arg, reason);
+ }
+ }
+}
+
+
+/**
+ * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
+ * @param arg MQTT client
+ */
+static void
+mqtt_cyclic_timer(void *arg)
+{
+ u8_t restart_timer = 1;
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
+
+ if (client->conn_state == MQTT_CONNECTING) {
+ client->cyclic_tick++;
+ if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
+ /* Disconnect TCP */
+ mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+ restart_timer = 0;
+ }
+ } else if (client->conn_state == MQTT_CONNECTED) {
+ /* Handle timeout for pending requests */
+ mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
+
+ /* keep_alive > 0 means keep alive functionality shall be used */
+ if (client->keep_alive > 0) {
+
+ client->server_watchdog++;
+ /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
+ if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive/2)) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
+ mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+ restart_timer = 0;
+ }
+
+ /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
+ if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
+ if (mqtt_output_check_space(&client->output, 0) != 0) {
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
+ client->cyclic_tick = 0;
+ }
+ } else {
+ client->cyclic_tick++;
+ }
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
+ restart_timer = 0;
+ }
+ if (restart_timer) {
+ sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, arg);
+ }
+}
+
+
+/**
+ * Send PUBACK, PUBREC or PUBREL response message
+ * @param client MQTT client
+ * @param msg PUBACK, PUBREC or PUBREL
+ * @param pkt_id Packet identifier
+ * @param qos QoS value
+ * @return ERR_OK if successful, ERR_MEM if out of memory
+ */
+static err_t
+pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
+{
+ err_t err = ERR_OK;
+ if (mqtt_output_check_space(&client->output, 2)) {
+ mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
+ mqtt_output_append_u16(&client->output, pkt_id);
+ mqtt_output_send(&client->output, client->conn);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
+ mqtt_msg_type_to_str(msg), pkt_id));
+ err = ERR_MEM;
+ }
+ return err;
+}
+
+/**
+ * Subscribe response from server
+ * @param r Matching request
+ * @param result Result code from server
+ */
+static void
+mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
+{
+ if (r->cb != NULL) {
+ r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
+ }
+}
+
+
+/**
+ * Complete MQTT message received or buffer full
+ * @param client MQTT client
+ * @param fixed_hdr_idx header index
+ * @param length length received part
+ * @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_connection_status_t res = MQTT_CONNECT_ACCEPTED;
+
+ u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
+
+ /* Control packet type */
+ u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
+ u16_t pkt_id = 0;
+
+ if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
+ if (client->conn_state == MQTT_CONNECTING) {
+ /* Get result code from CONNACK */
+ res = (mqtt_connection_status_t)var_hdr_payload[1];
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: Connect response code %d\n", res));
+ if (res == MQTT_CONNECT_ACCEPTED) {
+ /* Reset cyclic_tick when changing to connected state */
+ client->cyclic_tick = 0;
+ client->conn_state = MQTT_CONNECTED;
+ /* Notify upper layer */
+ if (client->connect_cb != 0) {
+ client->connect_cb(client, client->connect_arg, res);
+ }
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Received CONNACK in connected state\n"));
+ }
+ } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,( "mqtt_message_received: Received PINGRESP from server\n"));
+
+ } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
+ u16_t payload_offset = 0;
+ u16_t payload_length = length;
+ u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
+
+ if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
+ /* Should have topic and pkt id*/
+ uint8_t *topic;
+ uint16_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]);
+
+ topic = var_hdr_payload + 2;
+ after_topic = 2 + topic_len;
+ /* Check length, add one byte even for QoS 0 so that zero termination will fit */
+ if ((after_topic + qos ? 2 : 1) > length) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
+ goto out_disconnect;
+ }
+
+ /* id for QoS 1 and 2 */
+ if (qos > 0) {
+ client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
+ after_topic += 2;
+ } else {
+ client->inpub_pkt_id = 0;
+ }
+ /* Take backup of byte after topic */
+ bkp = topic[topic_len];
+ /* Zero terminate string */
+ topic[topic_len] = 0;
+ /* Payload data remaining in receive buffer */
+ payload_length = length - after_topic;
+ payload_offset = after_topic;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %d\n",
+ qos, topic, remaining_length + payload_length));
+ if (client->pub_cb != NULL) {
+ client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
+ }
+ /* Restore byte after topic */
+ topic[topic_len] = bkp;
+ }
+ if (payload_length > 0 || remaining_length == 0) {
+ client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+ /* Reply if QoS > 0 */
+ if (remaining_length == 0 && qos > 0) {
+ /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
+ u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
+ mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
+ pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
+ }
+ }
+ } else {
+ /* Get packet identifier */
+ pkt_id = (u16_t)var_hdr_payload[0] << 8;
+ pkt_id |= (u16_t)var_hdr_payload[1];
+ if (pkt_id == 0) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
+ goto out_disconnect;
+ }
+ if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n",pkt_id));
+ pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
+
+ } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n",pkt_id));
+ pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
+
+ } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
+ pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
+ struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
+ if (r != NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+ if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
+ if (length < 3) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: To small SUBACK packet\n"));
+ goto out_disconnect;
+ } else {
+ mqtt_incomming_suback(r, var_hdr_payload[2]);
+ }
+ } else if (r->cb != NULL) {
+ r->cb(r->arg, ERR_OK);
+ }
+ mqtt_delete_request(r);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
+ goto out_disconnect;
+ }
+ }
+ return res;
+out_disconnect:
+ return MQTT_CONNECT_DISCONNECTED;
+}
+
+
+/**
+ * MQTT incoming message parser
+ * @param client MQTT client
+ * @param p PBUF chain of received data
+ * @return Connection status
+ */
+static mqtt_connection_status_t
+mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
+{
+ u16_t in_offset = 0;
+ u32_t msg_rem_len = 0;
+ u8_t fixed_hdr_idx = 0;
+ u8_t b = 0;
+
+ while (p->tot_len > in_offset) {
+ if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
+
+ if (fixed_hdr_idx < client->msg_idx) {
+ b = client->rx_buffer[fixed_hdr_idx];
+ } else {
+ b = pbuf_get_at(p, in_offset++);
+ client->rx_buffer[client->msg_idx++] = b;
+ }
+ fixed_hdr_idx++;
+
+ if (fixed_hdr_idx >= 2) {
+ msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
+ if ((b & 0x80) == 0) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: Remaining length after fixed header: %d\n", msg_rem_len));
+ if (msg_rem_len == 0) {
+ /* Complete message with no extra headers of payload received */
+ mqtt_message_received(client, fixed_hdr_idx, 0, 0);
+ client->msg_idx = 0;
+ fixed_hdr_idx = 0;
+ } else {
+ /* Bytes remaining in message */
+ msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
+ }
+ }
+ }
+ } else {
+ u16_t cpy_len, cpy_start, buffer_space;
+
+ cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
+
+ /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
+ cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
+
+ /* Limit to available space in buffer */
+ buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
+ if (cpy_len > buffer_space) {
+ cpy_len = buffer_space;
+ }
+ pbuf_copy_partial(p, client->rx_buffer+cpy_start, cpy_len, in_offset);
+
+ /* Advance get and put indexes */
+ client->msg_idx += cpy_len;
+ in_offset += cpy_len;
+ msg_rem_len -= cpy_len;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: msg_idx: %d, cpy_len: %d, remaining %d\n", client->msg_idx, cpy_len, msg_rem_len));
+ if (msg_rem_len == 0 || cpy_len == buffer_space) {
+ /* Whole message received or buffer is full */
+ mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
+ if (res != MQTT_CONNECT_ACCEPTED) {
+ return res;
+ }
+ if (msg_rem_len == 0) {
+ /* Reset parser state */
+ client->msg_idx = 0;
+ /* msg_tot_len = 0; */
+ fixed_hdr_idx = 0;
+ }
+ }
+ }
+ }
+ return MQTT_CONNECT_ACCEPTED;
+}
+
+
+/**
+ * TCP received callback function. @see tcp_recv_fn
+ * @param arg MQTT client
+ * @param p PBUF chain of received data
+ * @param err Passed as return value if not ERR_OK
+ * @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_client_t *client = (mqtt_client_t *)arg;
+ LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
+ LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
+
+ if (p == NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
+ mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+ } else {
+ mqtt_connection_status_t res;
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_recv_cb: Recv err=%d\n", err));
+ pbuf_free(p);
+ return err;
+ }
+
+ /* Tell remote that data has been received */
+ tcp_recved(pcb, p->tot_len);
+ res = mqtt_parse_incoming(client, p);
+ pbuf_free(p);
+
+ if (res != MQTT_CONNECT_ACCEPTED) {
+ mqtt_close(client, res);
+ }
+ /* If keep alive functionality is used */
+ if (client->keep_alive != 0) {
+ /* Reset server alive watchdog */
+ client->server_watchdog = 0;
+ }
+
+ }
+ return ERR_OK;
+}
+
+
+/**
+ * TCP data sent callback function. @see tcp_sent_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @param len Number of bytes sent
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+
+ LWIP_UNUSED_ARG(tpcb);
+ LWIP_UNUSED_ARG(len);
+
+ if (client->conn_state == MQTT_CONNECTED) {
+ struct mqtt_request_t *r;
+
+ /* Reset keep-alive send timer and server watchdog */
+ client->cyclic_tick = 0;
+ client->server_watchdog = 0;
+ /* QoS 0 publish has no response from server, so call its callbacks here */
+ while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
+ if (r->cb != NULL) {
+ r->cb(r->arg, ERR_OK);
+ }
+ mqtt_delete_request(r);
+ }
+ /* Try send any remaining buffers from output queue */
+ mqtt_output_send(&client->output, client->conn);
+ }
+ return ERR_OK;
+}
+
+/**
+ * TCP error callback function. @see tcp_err_fn
+ * @param arg MQTT client
+ * @param err Error encountered
+ */
+static void
+mqtt_tcp_err_cb(void *arg, err_t err)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_UNUSED_ARG(err); /* only used for debug output */
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
+ LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
+ /* Set conn to null before calling close as pcb is already deallocated*/
+ client->conn = 0;
+ mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+}
+
+/**
+ * TCP poll callback function. @see tcp_poll_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @return err ERR_OK
+ */
+static err_t
+mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ if (client->conn_state == MQTT_CONNECTED) {
+ /* Try send any remaining buffers from output queue */
+ mqtt_output_send(&client->output, tpcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * TCP connect callback function. @see tcp_connected_fn
+ * @param arg MQTT client
+ * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ mqtt_client_t* client = (mqtt_client_t *)arg;
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
+ return err;
+ }
+
+ /* Initiate receiver state */
+ 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);
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n"));
+ /* Enter MQTT connect state */
+ client->conn_state = MQTT_CONNECTING;
+
+ /* Start cyclic timer */
+ sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, client);
+ client->cyclic_tick = 0;
+
+ /* Start transmission from output queue, connect message is the first one out*/
+ mqtt_output_send(&client->output, client->conn);
+
+ return ERR_OK;
+}
+
+
+
+/*---------------------------------------------------------------------------------------------------- */
+/* Public API */
+
+
+/**
+ * @ingroup mqtt
+ * MQTT publish function.
+ * @param client MQTT client
+ * @param topic Publish topic string
+ * @param payload Data to publish (NULL is allowed)
+ * @param payload_length: Length of payload (0 is allowed)
+ * @param qos Quality of service, 0 1 or 2
+ * @param retain MQTT retain flag
+ * @param cb Callback to call when publish is complete or has timed out
+ * @param arg User supplied argument to publish callback
+ * @return ERR_OK if successful
+ * ERR_CONN if client is disconnected
+ * ERR_MEM if short on memory
+ */
+err_t
+mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+ mqtt_request_cb_t cb, void *arg)
+{
+ struct mqtt_request_t *r;
+ u16_t pkt_id;
+ size_t topic_strlen;
+ size_t total_len;
+ u16_t topic_len;
+ u16_t remaining_length;
+
+ LWIP_ASSERT("mqtt_publish: client != NULL", client);
+ LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
+ LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
+
+ topic_strlen = strlen(topic);
+ LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+ topic_len = (u16_t)topic_strlen;
+ total_len = 2 + topic_len + payload_length;
+ LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+ remaining_length = (u16_t)total_len;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
+
+ if (qos > 0) {
+ remaining_length += 2;
+ /* Generate pkt_id id for QoS1 and 2 */
+ pkt_id = msg_generate_packet_id(client);
+ } else {
+ /* Use reserved value pkt_id 0 for QoS 0 in request handle */
+ pkt_id = 0;
+ }
+
+ r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+ if (r == NULL) {
+ return ERR_MEM;
+ }
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ mqtt_delete_request(r);
+ return ERR_MEM;
+ }
+ /* Append fixed header */
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
+
+ /* Append Topic */
+ mqtt_output_append_string(&client->output, topic, topic_len);
+
+ /* Append packet if for QoS 1 and 2*/
+ if (qos > 0) {
+ mqtt_output_append_u16(&client->output, pkt_id);
+ }
+
+ /* Append optional publish payload */
+ if ((payload != NULL) && (payload_length > 0)) {
+ mqtt_output_append_buf(&client->output, payload, payload_length);
+ }
+
+ mqtt_append_request(&client->pend_req_queue, r);
+ mqtt_output_send(&client->output, client->conn);
+ return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * MQTT subscribe/unsubscribe function.
+ * @param client MQTT client
+ * @param topic topic to subscribe to
+ * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
+ * @param cb Callback to call when subscribe/unsubscribe reponse is received
+ * @param arg User supplied argument to publish callback
+ * @param sub 1 for subscribe, 0 for unsubscribe
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
+{
+ size_t topic_strlen;
+ size_t total_len;
+ u16_t topic_len;
+ u16_t remaining_length;
+ u16_t pkt_id;
+ struct mqtt_request_t *r;
+
+ LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
+ LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
+
+ topic_strlen = strlen(topic);
+ LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+ topic_len = (u16_t)topic_strlen;
+ /* Topic string, pkt_id, qos for subscribe */
+ total_len = topic_len + 2 + 2 + (sub != 0);
+ LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+ remaining_length = (u16_t)total_len;
+
+ LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
+ if (client->conn_state == TCP_DISCONNECTED) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
+ return ERR_CONN;
+ }
+
+ pkt_id = msg_generate_packet_id(client);
+ r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+ if (r == NULL) {
+ return ERR_MEM;
+ }
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ mqtt_delete_request(r);
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
+
+ mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
+ /* Packet id */
+ mqtt_output_append_u16(&client->output, pkt_id);
+ /* Topic */
+ mqtt_output_append_string(&client->output, topic, topic_len);
+ /* QoS */
+ if (sub != 0) {
+ mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
+ }
+
+ mqtt_append_request(&client->pend_req_queue, r);
+ mqtt_output_send(&client->output, client->conn);
+ return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Set callback to handle incoming publish requests from server
+ * @param client MQTT client
+ * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
+ * @param data_cb Callback for each fragment of payload that arrives
+ * @param arg User supplied argument to both callbacks
+ */
+void
+mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+ mqtt_incoming_data_cb_t data_cb, void *arg)
+{
+ LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
+ client->data_cb = data_cb;
+ client->pub_cb = pub_cb;
+ client->inpub_arg = arg;
+}
+
+/**
+ * @ingroup mqtt
+ * Create a new MQTT client instance
+ * @return Pointer to instance on success, NULL otherwise
+ */
+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;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Connect to MQTT server
+ * @param client MQTT client
+ * @param ip_addr Server IP
+ * @param port Server port
+ * @param cb Connection state change callback
+ * @param arg User supplied argument to connection callback
+ * @param client_info Client identification and connection options
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+ const struct mqtt_connect_client_info_t *client_info)
+{
+ err_t err;
+ size_t len;
+ u16_t client_id_length;
+ /* 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;
+
+ LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
+ LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
+ LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
+ LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
+
+ if (client->conn_state != TCP_DISCONNECTED) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n"));
+ return ERR_ISCONN;
+ }
+
+ /* Wipe clean */
+ memset(client, 0, sizeof(mqtt_client_t));
+ client->connect_arg = arg;
+ client->connect_cb = cb;
+ client->keep_alive = client_info->keep_alive;
+ mqtt_init_requests(client->req_list);
+
+ /* Build connect message */
+ if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
+ flags |= MQTT_CONNECT_FLAG_WILL;
+ flags |= (client_info->will_qos & 3) << 3;
+ if (client_info->will_retain) {
+ flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+ }
+ len = strlen(client_info->will_topic);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
+ will_topic_len = (u8_t)len;
+ len = strlen(client_info->will_msg);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
+ will_msg_len = (u8_t)len;
+ len = remaining_length + 2 + will_topic_len + 2 + will_msg_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;
+
+ len = strlen(client_info->client_id);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
+ client_id_length = (u16_t)len;
+ len = remaining_length + 2 + client_id_length;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ return ERR_MEM;
+ }
+
+ client->conn = tcp_new();
+ if (client->conn == NULL) {
+ return ERR_MEM;
+ }
+
+ /* Set arg pointer for callbacks */
+ tcp_arg(client->conn, client);
+ /* Any local address, pick random local port number */
+ err = tcp_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;
+ }
+ 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);
+ 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);
+ client->conn_state = TCP_CONNECTING;
+
+ /* Append fixed header */
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
+ /* Append Protocol string */
+ mqtt_output_append_string(&client->output, "MQTT", 4);
+ /* Append Protocol level */
+ mqtt_output_append_u8(&client->output, 4);
+ /* Append connect flags */
+ mqtt_output_append_u8(&client->output, flags);
+ /* Append keep-alive */
+ mqtt_output_append_u16(&client->output, client_info->keep_alive);
+ /* Append client id */
+ mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
+ /* Append will message if used */
+ if (will_topic_len > 0) {
+ 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);
+ }
+ return ERR_OK;
+
+tcp_fail:
+ tcp_abort(client->conn);
+ client->conn = NULL;
+ return err;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Disconnect from MQTT server
+ * @param client MQTT client
+ */
+void
+mqtt_disconnect(mqtt_client_t *client)
+{
+ LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
+ /* If connection in not already closed */
+ if (client->conn_state != TCP_DISCONNECTED) {
+ /* Set conn_state before calling mqtt_close to prevent callback from being called */
+ client->conn_state = TCP_DISCONNECTED;
+ mqtt_close(client, (mqtt_connection_status_t)0);
+ }
+}
+
+/**
+ * @ingroup mqtt
+ * Check connection with server
+ * @param client MQTT client
+ * @return 1 if connected to server, 0 otherwise
+ */
+u8_t
+mqtt_client_is_connected(mqtt_client_t *client)
+{
+ LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
+ return client->conn_state == MQTT_CONNECTED;
+}
diff --git a/src/apps/snmp/snmp_mib2_ip.c b/src/apps/snmp/snmp_mib2_ip.c
index 913d97f..4f05180 100644
--- a/src/apps/snmp/snmp_mib2_ip.c
+++ b/src/apps/snmp/snmp_mib2_ip.c
@@ -581,7 +581,7 @@
snmp_ip4_to_oid(ip, &test_oid[1]);
/* check generated OID: is it a candidate for the next one? */
- snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), (void*)(size_t)i);
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void*, i));
}
}
@@ -589,7 +589,7 @@
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
/* fill in object properties */
- return ip_NetToMediaTable_get_cell_value_core((u8_t)(size_t)state.reference, column, value, value_len);
+ return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(u8_t, state.reference), column, value, value_len);
}
/* not found */
diff --git a/src/apps/snmp/snmp_netconn.c b/src/apps/snmp/snmp_netconn.c
index 070b41c..24c3e26 100644
--- a/src/apps/snmp/snmp_netconn.c
+++ b/src/apps/snmp/snmp_netconn.c
@@ -36,6 +36,7 @@
#if LWIP_SNMP && SNMP_USE_NETCONN
+#include <string.h>
#include "lwip/api.h"
#include "lwip/ip.h"
#include "lwip/udp.h"
@@ -52,7 +53,7 @@
LWIP_UNUSED_ARG(arg);
/* Bind to SNMP port with default IP address */
- #if LWIP_IPV6
+#if LWIP_IPV6
conn = netconn_new(NETCONN_UDP_IPV6);
netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT);
#else /* LWIP_IPV6 */
diff --git a/src/apps/snmp/snmp_threadsync.c b/src/apps/snmp/snmp_threadsync.c
index 858c40e..204f265 100644
--- a/src/apps/snmp/snmp_threadsync.c
+++ b/src/apps/snmp/snmp_threadsync.c
@@ -211,6 +211,7 @@
err_t err = sys_mutex_new(&instance->sem_usage_mutex);
LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
err = sys_sem_new(&instance->sem, 0);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
instance->sync_fn = sync_fn;
}
diff --git a/src/apps/sntp/sntp.c b/src/apps/sntp/sntp.c
index fb952111..71b2abe 100644
--- a/src/apps/sntp/sntp.c
+++ b/src/apps/sntp/sntp.c
@@ -573,6 +573,7 @@
{
if (sntp_pcb != NULL) {
sys_untimeout(sntp_request, NULL);
+ sys_untimeout(sntp_try_next_server, NULL);
udp_remove(sntp_pcb);
sntp_pcb = NULL;
}
@@ -688,7 +689,7 @@
if (idx < SNTP_MAX_SERVERS) {
return &sntp_servers[idx].addr;
}
- return IP4_ADDR_ANY;
+ return IP_ADDR_ANY;
}
#if SNTP_SERVER_DNS
diff --git a/src/core/def.c b/src/core/def.c
index 99f3d62..8125313 100644
--- a/src/core/def.c
+++ b/src/core/def.c
@@ -20,6 +20,10 @@
* @ingroup sys_layer
* lwIP provides default implementations for non-standard functions.
* These can be mapped to OS functions to reduce code footprint if desired.
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
*/
/*
@@ -101,13 +105,13 @@
lwip_strnstr(const char* buffer, const char* token, size_t n)
{
const char* p;
- int tokenlen = (int)strlen(token);
+ size_t tokenlen = strlen(token);
if (tokenlen == 0) {
- return (char *)(size_t)buffer;
+ return LWIP_CONST_CAST(char *, buffer);
}
for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
- return (char *)(size_t)p;
+ return LWIP_CONST_CAST(char *, p);
}
}
return NULL;
diff --git a/src/core/dns.c b/src/core/dns.c
index 2aa1dd9..056672b 100644
--- a/src/core/dns.c
+++ b/src/core/dns.c
@@ -24,6 +24,11 @@
* the resolver code calls a specified callback function (which
* must be implemented by the module that uses the resolver).
*
+ * Multicast DNS queries are supported for names ending on ".local".
+ * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
+ * chapter 5.1), this is not a fully compliant implementation of continuous
+ * mDNS querying!
+ *
* All functions must be called from TCPIP thread.
*
* @see @ref netconn_common for thread-safe access.
@@ -71,6 +76,7 @@
/** @todo: define good default values (rfc compliance) */
/** @todo: improve answer parsing, more checkings... */
/** @todo: check RFC1035 - 7.3. Processing responses */
+/** @todo: one-shot mDNS: dual-stack fallback to another IP version */
/*-----------------------------------------------------------------------------
* Includes
@@ -116,6 +122,13 @@
#error DNS_MAX_TTL must be a positive 32-bit value
#endif
+#if DNS_TABLE_SIZE > 255
+#error DNS_TABLE_SIZE must fit into an u8_t
+#endif
+#if DNS_MAX_SERVERS > 255
+#error DNS_MAX_SERVERS must fit into an u8_t
+#endif
+
/* The number of parallel requests (i.e. calls to dns_gethostbyname
* that cannot be answered from the DNS table.
* This is set to the table size by default.
@@ -123,6 +136,10 @@
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
#ifndef DNS_MAX_REQUESTS
#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
+#else
+#if DNS_MAX_REQUESTS > 255
+#error DNS_MAX_REQUESTS must fit into an u8_t
+#endif
#endif
#else
/* In this configuration, both arrays have to have the same size and are used
@@ -134,6 +151,10 @@
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
#ifndef DNS_MAX_SOURCE_PORTS
#define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS
+#else
+#if DNS_MAX_SOURCE_PORTS > 255
+#error DNS_MAX_SOURCE_PORTS must fit into an u8_t
+#endif
#endif
#else
#ifdef DNS_MAX_SOURCE_PORTS
@@ -160,6 +181,12 @@
#define LWIP_DNS_SET_ADDRTYPE(x, y)
#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+#define LWIP_DNS_ISMDNS_ARG(x) , x
+#else
+#define LWIP_DNS_ISMDNS_ARG(x)
+#endif
+
/** DNS query message structure.
No packing needed: only used locally on the stack. */
struct dns_query {
@@ -209,6 +236,9 @@
#if LWIP_IPV4 && LWIP_IPV6
u8_t reqaddrtype;
#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ u8_t is_mdns;
+#endif
};
/** DNS request table entry: used when dns_gehostbyname cannot answer the
@@ -272,6 +302,13 @@
static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+#if LWIP_IPV4
+const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif /* LWIP_IPV6 */
+
/**
* Initialize the resolver: set up the UDP pcb and configure the default server
* (if DNS_SERVER_ADDRESS is set).
@@ -329,7 +366,7 @@
if (dnsserver != NULL) {
dns_servers[numdns] = (*dnsserver);
} else {
- dns_servers[numdns] = *IP4_ADDR_ANY;
+ dns_servers[numdns] = *IP_ADDR_ANY;
}
}
}
@@ -348,7 +385,7 @@
if (numdns < DNS_MAX_SERVERS) {
return &dns_servers[numdns];
} else {
- return IP4_ADDR_ANY;
+ return IP_ADDR_ANY;
}
}
@@ -665,7 +702,11 @@
LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
(u16_t)(entry->server_idx), entry->name));
LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
- if (ip_addr_isany_val(dns_servers[entry->server_idx])) {
+ if (ip_addr_isany_val(dns_servers[entry->server_idx])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ && !entry->is_mdns
+#endif
+ ) {
/* DNS server not valid anymore, e.g. PPP netif has been shut down */
/* call specified callback function if provided */
dns_call_found(idx, NULL);
@@ -678,6 +719,8 @@
p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
SIZEOF_DNS_QUERY), PBUF_RAM);
if (p != NULL) {
+ const ip_addr_t* dst;
+ u16_t dst_port;
/* fill dns header */
memset(&hdr, 0, SIZEOF_DNS_HDR);
hdr.id = lwip_htons(entry->txid);
@@ -720,7 +763,30 @@
/* send dns packet */
LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
entry->txid, entry->name, entry->server_idx));
- err = udp_sendto(dns_pcbs[pcb_idx], p, &dns_servers[entry->server_idx], DNS_SERVER_PORT);
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (entry->is_mdns) {
+ dst_port = DNS_MQUERY_PORT;
+#if LWIP_IPV6
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+ {
+ dst = &dns_mquery_v6group;
+ }
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif
+#if LWIP_IPV4
+ {
+ dst = &dns_mquery_v4group;
+ }
+#endif
+ } else
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ dst_port = DNS_SERVER_PORT;
+ dst = &dns_servers[entry->server_idx];
+ }
+ err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
/* free pbuf */
pbuf_free(p);
@@ -923,7 +989,11 @@
case DNS_STATE_ASKING:
if (--entry->tmr == 0) {
if (++entry->retries == DNS_MAX_RETRIES) {
- if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])) {
+ 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
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ ) {
/* change of server */
entry->server_idx++;
entry->tmr = 1;
@@ -1060,10 +1130,15 @@
goto memerr; /* ignore this packet */
}
- /* Check whether response comes from the same network address to which the
- question was sent. (RFC 5452) */
- if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
- goto memerr; /* ignore this packet */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (!entry->is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ /* Check whether response comes from the same network address to which the
+ question was sent. (RFC 5452) */
+ if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
+ goto memerr; /* ignore this packet */
+ }
}
/* Check if the name in the "question" part match with the name in the entry and
@@ -1195,7 +1270,7 @@
*/
static err_t
dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
- void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+ void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
{
u8_t i;
u8_t lseq, lseqi;
@@ -1244,8 +1319,9 @@
}
/* check if this is the oldest completed entry */
if (entry->state == DNS_STATE_DONE) {
- if ((u8_t)(dns_seqno - entry->seqno) > lseq) {
- lseq = dns_seqno - entry->seqno;
+ u8_t age = dns_seqno - entry->seqno;
+ if (age > lseq) {
+ lseq = age;
lseqi = i;
}
}
@@ -1310,6 +1386,10 @@
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
#endif
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ entry->is_mdns = is_mdns;
+#endif
+
dns_seqno++;
/* force to send query without waiting timer */
@@ -1365,6 +1445,9 @@
void *callback_arg, u8_t dns_addrtype)
{
size_t hostnamelen;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ u8_t is_mdns;
+#endif
/* not initialized or no valid server yet, or invalid addr pointer
* or invalid hostname or invalid hostname length */
if ((addr == NULL) ||
@@ -1421,13 +1504,25 @@
LWIP_UNUSED_ARG(dns_addrtype);
#endif /* LWIP_IPV4 && LWIP_IPV6 */
- /* prevent calling found callback if no server is set, return error instead */
- if (ip_addr_isany_val(dns_servers[0])) {
- return ERR_VAL;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
+ is_mdns = 1;
+ } else {
+ is_mdns = 0;
+ }
+
+ if (!is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ /* prevent calling found callback if no server is set, return error instead */
+ if (ip_addr_isany_val(dns_servers[0])) {
+ return ERR_VAL;
+ }
}
/* queue query with specified callback */
- return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
+ return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
+ LWIP_DNS_ISMDNS_ARG(is_mdns));
}
#endif /* LWIP_DNS */
diff --git a/src/core/inet_chksum.c b/src/core/inet_chksum.c
index 8028952..917f3e4 100644
--- a/src/core/inet_chksum.c
+++ b/src/core/inet_chksum.c
@@ -51,7 +51,6 @@
#include "lwip/def.h"
#include "lwip/ip_addr.h"
-#include <stddef.h>
#include <string.h>
#ifndef LWIP_CHKSUM
diff --git a/src/core/init.c b/src/core/init.c
index 570d54a..ee415a9 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -339,6 +339,11 @@
void
lwip_init(void)
{
+#ifndef LWIP_SKIP_CONST_CHECK
+ int a;
+ LWIP_UNUSED_ARG(a);
+ LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a);
+#endif
#ifndef LWIP_SKIP_PACKING_CHECK
LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
#endif
diff --git a/src/core/ipv4/autoip.c b/src/core/ipv4/autoip.c
index 86a4aed..10db8a3 100644
--- a/src/core/ipv4/autoip.c
+++ b/src/core/ipv4/autoip.c
@@ -68,7 +68,6 @@
#include "lwip/etharp.h"
#include "lwip/prot/autoip.h"
-#include <stdlib.h>
#include <string.h>
/** Pseudo random macro based on netif informations.
@@ -95,8 +94,6 @@
static err_t autoip_arp_announce(struct netif *netif);
static void autoip_start_probing(struct netif *netif);
-#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
-
/**
* @ingroup autoip
* Set a statically allocated struct autoip to work with.
@@ -482,7 +479,8 @@
* ip.dst == llipaddr && hw.src != own hwaddr
*/
if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
- (ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
+ (ip4_addr_isany_val(sipaddr) &&
+ ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
!eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
("autoip_arp_reply(): Probe Conflict detected\n"));
diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index de5fc73..dd35471 100644
--- a/src/core/ipv4/dhcp.c
+++ b/src/core/ipv4/dhcp.c
@@ -103,26 +103,40 @@
#define REBOOT_TRIES 2
+#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
+#endif
+
/** Option handling: options are parsed in dhcp_parse_reply
* and saved in an array where other functions can load them from.
* This might be moved into the struct dhcp (not necessarily since
* lwIP is single-threaded and the array is only used while in recv
* callback). */
-#define DHCP_OPTION_IDX_OVERLOAD 0
-#define DHCP_OPTION_IDX_MSG_TYPE 1
-#define DHCP_OPTION_IDX_SERVER_ID 2
-#define DHCP_OPTION_IDX_LEASE_TIME 3
-#define DHCP_OPTION_IDX_T1 4
-#define DHCP_OPTION_IDX_T2 5
-#define DHCP_OPTION_IDX_SUBNET_MASK 6
-#define DHCP_OPTION_IDX_ROUTER 7
-#define DHCP_OPTION_IDX_DNS_SERVER 8
+enum dhcp_option_idx {
+ DHCP_OPTION_IDX_OVERLOAD = 0,
+ DHCP_OPTION_IDX_MSG_TYPE,
+ DHCP_OPTION_IDX_SERVER_ID,
+ DHCP_OPTION_IDX_LEASE_TIME,
+ DHCP_OPTION_IDX_T1,
+ DHCP_OPTION_IDX_T2,
+ DHCP_OPTION_IDX_SUBNET_MASK,
+ DHCP_OPTION_IDX_ROUTER,
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ DHCP_OPTION_IDX_DNS_SERVER,
+ DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
#if LWIP_DHCP_GET_NTP_SRV
-#define DHCP_OPTION_IDX_NTP_SERVER (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
-#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS)
-#else /* LWIP_DHCP_GET_NTP_SRV */
-#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+ DHCP_OPTION_IDX_NTP_SERVER,
+ DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1,
#endif /* LWIP_DHCP_GET_NTP_SRV */
+ DHCP_OPTION_IDX_MAX
+};
/** Holds the decoded option values, only valid while in dhcp_recv.
@todo: move this into struct dhcp? */
@@ -135,8 +149,10 @@
static u8_t dhcp_discover_request_options[] = {
DHCP_OPTION_SUBNET_MASK,
DHCP_OPTION_ROUTER,
- DHCP_OPTION_BROADCAST,
- DHCP_OPTION_DNS_SERVER
+ DHCP_OPTION_BROADCAST
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ , DHCP_OPTION_DNS_SERVER
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
#if LWIP_DHCP_GET_NTP_SRV
, DHCP_OPTION_NTP
#endif /* LWIP_DHCP_GET_NTP_SRV */
@@ -193,8 +209,6 @@
/* always add the DHCP options trailer to end and pad */
static void dhcp_option_trailer(struct dhcp *dhcp);
-#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP))
-
/** Ensure DHCP PCB is allocated and bound */
static err_t
dhcp_inc_pcb_refcount(void)
@@ -572,9 +586,9 @@
{
struct dhcp *dhcp = netif_dhcp_data(netif);
-#if LWIP_DNS || LWIP_DHCP_GET_NTP_SRV
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV
u8_t n;
-#endif /* LWIP_DNS || LWIP_DHCP_GET_NTP_SRV */
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */
#if LWIP_DHCP_GET_NTP_SRV
ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
#endif
@@ -640,14 +654,14 @@
dhcp_set_ntp_servers(n, ntp_server_addrs);
#endif /* LWIP_DHCP_GET_NTP_SRV */
-#if LWIP_DNS
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
/* DNS servers */
- for (n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+ 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;
ip_addr_set_ip4_u32(&dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
dns_setserver(n, &dns_addr);
}
-#endif /* LWIP_DNS */
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
}
/**
@@ -1517,6 +1531,7 @@
LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_ROUTER;
break;
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
case(DHCP_OPTION_DNS_SERVER):
/* special case: there might be more than one server */
LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
@@ -1525,6 +1540,7 @@
LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
break;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
case(DHCP_OPTION_LEASE_TIME):
LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
@@ -1541,6 +1557,8 @@
#endif /* LWIP_DHCP_GET_NTP_SRV*/
case(DHCP_OPTION_OVERLOAD):
LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ /* decode overload only in options, not in file/sname: invalid packet */
+ LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_OVERLOAD;
break;
case(DHCP_OPTION_MESSAGE_TYPE):
diff --git a/src/core/ipv4/etharp.c b/src/core/ipv4/etharp.c
index bf4dad5..b599bcc 100644
--- a/src/core/ipv4/etharp.c
+++ b/src/core/ipv4/etharp.c
@@ -840,7 +840,7 @@
if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
!ip4_addr_islinklocal(ipaddr)) {
#if LWIP_AUTOIP
- struct ip_hdr *iphdr = (struct ip_hdr*)(size_t)q->payload;
+ struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr*, q->payload);
/* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
a link-local source address must always be "directly to its destination
on the same physical link. The host MUST NOT send the packet to any
diff --git a/src/core/ipv4/icmp.c b/src/core/ipv4/icmp.c
index e60e448..e030ecc 100644
--- a/src/core/ipv4/icmp.c
+++ b/src/core/ipv4/icmp.c
@@ -81,7 +81,7 @@
#endif /* LWIP_DEBUG */
struct icmp_echo_hdr *iecho;
const struct ip_hdr *iphdr_in;
- s16_t hlen;
+ u16_t hlen;
const ip4_addr_t* src;
ICMP_STATS_INC(icmp.recv);
@@ -148,7 +148,7 @@
}
#endif
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
- if (pbuf_header(p, (hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+ if (pbuf_header(p, (s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
/* p is not big enough to contain link headers
* allocate a new one and copy p into it
*/
@@ -167,7 +167,7 @@
/* copy the ip header */
MEMCPY(r->payload, iphdr_in, hlen);
/* switch r->payload back to icmp header (cannot fail) */
- if (pbuf_header(r, -hlen)) {
+ if (pbuf_header(r, (s16_t)-hlen)) {
LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
pbuf_free(r);
goto icmperr;
@@ -194,7 +194,7 @@
/* We generate an answer by switching the dest and src ip addresses,
* setting the icmp type to ECHO_RESPONSE and updating the checksum. */
iecho = (struct icmp_echo_hdr *)p->payload;
- if (pbuf_header(p, hlen)) {
+ if (pbuf_header(p, (s16_t)hlen)) {
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
} else {
err_t ret;
@@ -247,7 +247,7 @@
if (type == ICMP_DUR) {
MIB2_STATS_INC(mib2.icmpindestunreachs);
} else if (type == ICMP_TE) {
- MIB2_STATS_INC(mib2.icmpindestunreachs);
+ MIB2_STATS_INC(mib2.icmpintimeexcds);
} else if (type == ICMP_PP) {
MIB2_STATS_INC(mib2.icmpinparmprobs);
} else if (type == ICMP_SQ) {
diff --git a/src/core/ipv4/igmp.c b/src/core/ipv4/igmp.c
index 5ae7d9c..561448e 100644
--- a/src/core/ipv4/igmp.c
+++ b/src/core/ipv4/igmp.c
@@ -244,6 +244,7 @@
igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
{
struct igmp_group *group;
+ struct igmp_group *list_head = netif_igmp_data(ifp);
/* Search if the group already exists */
group = igmp_lookfor_group(ifp, addr);
@@ -251,7 +252,7 @@
/* 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) {
@@ -260,9 +261,21 @@
group->group_state = IGMP_GROUP_NON_MEMBER;
group->last_reporter_flag = 0;
group->use = 0;
- group->next = netif_igmp_data(ifp);
- netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+ /* 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",
+ (ip4_addr_cmp(addr, &allsystems) != 0));
+ group->next = NULL;
+ netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+ } else {
+ /* append _after_ first entry */
+ LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
+ (ip4_addr_cmp(addr, &allsystems) == 0));
+ group->next = list_head->next;
+ list_head->next = group;
+ }
}
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
@@ -282,23 +295,18 @@
igmp_remove_group(struct netif* netif, struct igmp_group *group)
{
err_t err = ERR_OK;
+ struct igmp_group *tmp_group;
- /* Is it the first group? */
- if (netif_igmp_data(netif) == group) {
- netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group->next);
- } else {
- /* look for group further down the list */
- struct igmp_group *tmpGroup;
- for (tmpGroup = netif_igmp_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
- if (tmpGroup->next == group) {
- tmpGroup->next = group->next;
- break;
- }
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
+ if (tmp_group->next == group) {
+ tmp_group->next = group->next;
+ break;
}
- /* Group not found in the global igmp_group_list */
- if (tmpGroup == NULL) {
- err = ERR_ARG;
- }
+ }
+ /* Group not found in the global igmp_group_list */
+ if (tmp_group == NULL) {
+ err = ERR_ARG;
}
return err;
diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c
index c783677..2e2ce4b 100644
--- a/src/core/ipv4/ip4.c
+++ b/src/core/ipv4/ip4.c
@@ -177,7 +177,7 @@
/* loopif is disabled, looopback traffic is passed through any netif */
if (ip4_addr_isloopback(dest)) {
/* don't check for link on loopback traffic */
- if (netif_is_up(netif_default)) {
+ if (netif_default != NULL && netif_is_up(netif_default)) {
return netif_default;
}
/* default netif is not up, just use any netif for loopback traffic */
@@ -518,6 +518,15 @@
#endif /* LWIP_AUTOIP */
}
if (first) {
+#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;
+ }
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
first = 0;
netif = netif_list;
} else {
@@ -589,6 +598,7 @@
} else
#endif /* IP_FORWARD */
{
+ IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinaddrerrors);
MIB2_STATS_INC(mib2.ipindiscards);
}
@@ -853,7 +863,7 @@
IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
#if CHECKSUM_GEN_IP_INLINE
- chk_sum += LWIP_MAKE_U16(proto, ttl);
+ chk_sum += PP_NTOHS(proto | (ttl << 8));
#endif /* CHECKSUM_GEN_IP_INLINE */
/* dest cannot be NULL here */
@@ -866,7 +876,7 @@
IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
IPH_TOS_SET(iphdr, tos);
#if CHECKSUM_GEN_IP_INLINE
- chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl);
+ chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
#endif /* CHECKSUM_GEN_IP_INLINE */
IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
#if CHECKSUM_GEN_IP_INLINE
diff --git a/src/core/ipv4/ip4_addr.c b/src/core/ipv4/ip4_addr.c
index eb812af..2d47992 100644
--- a/src/core/ipv4/ip4_addr.c
+++ b/src/core/ipv4/ip4_addr.c
@@ -183,10 +183,10 @@
}
for (;;) {
if (isdigit(c)) {
- val = (val * base) + (int)(c - '0');
+ val = (val * base) + (u32_t)(c - '0');
c = *++cp;
} else if (base == 16 && isxdigit(c)) {
- val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+ val = (val << 4) | (u32_t)(c + 10 - (islower(c) ? 'a' : 'A'));
c = *++cp;
} else {
break;
@@ -310,7 +310,7 @@
do {
rem = *ap % (u8_t)10;
*ap /= (u8_t)10;
- inv[i++] = '0' + rem;
+ inv[i++] = (char)('0' + rem);
} while (*ap);
while (i--) {
if (len++ >= buflen) {
diff --git a/src/core/ipv4/ip4_frag.c b/src/core/ipv4/ip4_frag.c
index b6f9094..57fb44c 100644
--- a/src/core/ipv4/ip4_frag.c
+++ b/src/core/ipv4/ip4_frag.c
@@ -687,6 +687,8 @@
struct pbuf *rambuf;
#if !LWIP_NETIF_TX_SINGLE_PBUF
struct pbuf *newpbuf;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
#endif
struct ip_hdr *original_iphdr;
struct ip_hdr *iphdr;
@@ -696,10 +698,6 @@
int last;
u16_t poff = IP_HLEN;
u16_t tmp;
-#if !LWIP_NETIF_TX_SINGLE_PBUF
- u16_t newpbuflen = 0;
- u16_t left_to_copy;
-#endif
original_iphdr = (struct ip_hdr *)p->payload;
iphdr = original_iphdr;
diff --git a/src/core/ipv6/ethip6.c b/src/core/ipv6/ethip6.c
index 509fc1c..8f9a91b 100644
--- a/src/core/ipv6/ethip6.c
+++ b/src/core/ipv6/ethip6.c
@@ -62,7 +62,9 @@
* For IPv6 multicast, corresponding Ethernet addresses
* are selected and the packet is transmitted on the link.
*
- * For unicast addresses, ...
+ * For unicast addresses, ask the ND6 module what to do. It will either let us
+ * send the the packet right away, or queue the packet for later itself, unless
+ * an error occurs.
*
* @todo anycast addresses
*
@@ -71,14 +73,14 @@
* @param ip6addr The IP address of the packet destination.
*
* @return
- * - ERR_RTE No route to destination (no gateway to external networks),
- * or the return type of either nd6_queue_packet() or ethernet_output().
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
*/
err_t
ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
{
struct eth_addr dest;
- s8_t i;
+ const u8_t *hwaddr;
+ err_t result;
/* multicast destination IP address? */
if (ip6_addr_ismulticast(ip6addr)) {
@@ -91,36 +93,26 @@
dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
/* Send out. */
- return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
}
/* We have a unicast destination IP address */
/* @todo anycast? */
- /* Get next hop record. */
- i = nd6_get_next_hop_entry(ip6addr, netif);
- if (i < 0) {
- /* failed to get a next hop neighbor record. */
- return ERR_MEM;
+
+ /* Ask ND6 what to do with the packet. */
+ result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+ if (result != ERR_OK) {
+ return result;
}
- /* Now that we have a destination record, send or queue the packet. */
- if (neighbor_cache[i].state == ND6_STALE) {
- /* Switch to delay state. */
- neighbor_cache[i].state = ND6_DELAY;
- neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
- }
- /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
- if ((neighbor_cache[i].state == ND6_REACHABLE) ||
- (neighbor_cache[i].state == ND6_DELAY) ||
- (neighbor_cache[i].state == ND6_PROBE)) {
-
- /* Send out. */
- SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6);
- return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
}
- /* We should queue packet on this interface. */
- return nd6_queue_packet(i, q);
+ /* Send out the packet using the returned hardware address. */
+ SMEMCPY(dest.addr, hwaddr, 6);
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
}
#endif /* LWIP_IPV6 && LWIP_ETHERNET */
diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c
index 8cc9c01..a542a86 100644
--- a/src/core/ipv6/ip6.c
+++ b/src/core/ipv6/ip6.c
@@ -94,7 +94,8 @@
if (ip6_addr_islinklocal(dest)) {
if (ip6_addr_isany(src)) {
/* Use default netif, if Up. */
- if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+ if (netif_default == NULL || !netif_is_up(netif_default) ||
+ !netif_is_link_up(netif_default)) {
return NULL;
}
return netif_default;
@@ -114,7 +115,8 @@
}
/* netif not found, use default netif, if up */
- if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+ if (netif_default == NULL || !netif_is_up(netif_default) ||
+ !netif_is_link_up(netif_default)) {
return NULL;
}
return netif_default;
@@ -142,15 +144,9 @@
}
/* Get the netif for a suitable router. */
- i = nd6_select_router(dest, NULL);
- if (i >= 0) {
- if (default_router_list[i].neighbor_entry != NULL) {
- if (default_router_list[i].neighbor_entry->netif != NULL) {
- if (netif_is_up(default_router_list[i].neighbor_entry->netif) && netif_is_link_up(default_router_list[i].neighbor_entry->netif)) {
- return default_router_list[i].neighbor_entry->netif;
- }
- }
- }
+ netif = nd6_find_route(dest);
+ if ((netif != NULL) && netif_is_up(netif) && netif_is_link_up(netif)) {
+ return netif;
}
/* try with the netif that matches the source address. */
@@ -172,7 +168,7 @@
/* loopif is disabled, loopback traffic is passed through any netif */
if (ip6_addr_isloopback(dest)) {
/* don't check for link on loopback traffic */
- if (netif_is_up(netif_default)) {
+ if (netif_default != NULL && netif_is_up(netif_default)) {
return netif_default;
}
/* default netif is not up, just use any netif for loopback traffic */
@@ -290,8 +286,9 @@
{
struct netif *netif;
- /* do not forward link-local addresses */
- if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
+ /* do not forward link-local or loopback addresses */
+ if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+ ip6_addr_isloopback(ip6_current_dest_addr())) {
LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n"));
IP6_STATS_INC(ip6.rterr);
IP6_STATS_INC(ip6.drop);
@@ -509,12 +506,21 @@
}
}
}
- if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
- /* Do not match link-local addresses to other netifs. */
- netif = NULL;
- break;
- }
if (first) {
+ if (ip6_addr_islinklocal(ip6_current_dest_addr())
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+ || ip6_addr_isloopback(ip6_current_dest_addr())
+#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;
+ break;
+ }
first = 0;
netif = netif_list;
} else {
@@ -709,7 +715,7 @@
options_done:
/* p points to IPv6 header again. */
- pbuf_header_force(p, ip_data.current_ip_header_tot_len);
+ pbuf_header_force(p, (s16_t)ip_data.current_ip_header_tot_len);
/* send to upper layers */
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
diff --git a/src/core/ipv6/ip6_addr.c b/src/core/ipv6/ip6_addr.c
index 964291e..aa06659 100644
--- a/src/core/ipv6/ip6_addr.c
+++ b/src/core/ipv6/ip6_addr.c
@@ -132,8 +132,8 @@
} else if (isxdigit(*s)) {
/* add current digit */
current_block_value = (current_block_value << 4) +
- (isdigit(*s) ? *s - '0' :
- 10 + (islower(*s) ? *s - 'a' : *s - 'A'));
+ (isdigit(*s) ? (u32_t)(*s - '0') :
+ (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A')));
} else {
/* unexpected digit, space? CRLF? */
break;
diff --git a/src/core/ipv6/ip6_frag.c b/src/core/ipv6/ip6_frag.c
index b374f69..ff07f71 100644
--- a/src/core/ipv6/ip6_frag.c
+++ b/src/core/ipv6/ip6_frag.c
@@ -379,6 +379,7 @@
/* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
This cannot fail since we already checked when receiving this fragment. */
u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
+ LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
}
#else /* IPV6_FRAG_COPYHEADER */
@@ -530,6 +531,7 @@
if (IPV6_FRAG_REQROOM > 0) {
/* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
+ LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
}
#endif
@@ -546,6 +548,7 @@
if (IPV6_FRAG_REQROOM > 0) {
/* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
+ LWIP_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);
@@ -610,6 +613,7 @@
#if LWIP_IPV6 && LWIP_IPV6_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
/** Allocate a new struct pbuf_custom_ref */
static struct pbuf_custom_ref*
ip6_frag_alloc_pbuf_custom_ref(void)
@@ -638,6 +642,7 @@
}
ip6_frag_free_pbuf_custom_ref(pcr);
}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
/**
* Fragment an IPv6 datagram if too large for the netif or path MTU.
@@ -658,7 +663,11 @@
struct ip6_hdr *ip6hdr;
struct ip6_frag_hdr *frag_hdr;
struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
struct pbuf *newpbuf;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
static u32_t identification;
u16_t nfb;
u16_t left, cop;
@@ -666,8 +675,6 @@
u16_t fragment_offset = 0;
u16_t last;
u16_t poff = IP6_HLEN;
- u16_t newpbuflen = 0;
- u16_t left_to_copy;
identification++;
@@ -686,6 +693,26 @@
/* Fill this fragment */
cop = last ? left : nfb;
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
+ /* make room for the IP header */
+ if (pbuf_header(rambuf, IP6_HLEN)) {
+ pbuf_free(rambuf);
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+ ip6hdr = (struct ip6_hdr *)rambuf->payload;
+ frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+#else
/* When not using a static buffer, create a chain of pbufs.
* The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
* The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
@@ -744,6 +771,7 @@
}
}
poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
/* Set headers */
frag_hdr->_nexth = original_ip6hdr->_nexth;
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index 82394f6..7d7a957 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -46,6 +46,7 @@
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
#include "lwip/prot/nd6.h"
#include "lwip/prot/icmp6.h"
#include "lwip/pbuf.h"
@@ -59,6 +60,7 @@
#include "lwip/mld6.h"
#include "lwip/ip.h"
#include "lwip/stats.h"
+#include "lwip/dns.h"
#include <string.h>
@@ -93,10 +95,13 @@
static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr);
static s8_t nd6_new_destination_cache_entry(void);
static s8_t 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);
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_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
@@ -155,25 +160,6 @@
* link-layer changed?
* part of DAD mechanism? */
- /* Check that link-layer address option also fits in packet. */
- if (p->len < (sizeof(struct na_header) + 2)) {
- /* @todo debug message */
- pbuf_free(p);
- ND6_STATS_INC(nd6.lenerr);
- ND6_STATS_INC(nd6.drop);
- return;
- }
-
- lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
-
- if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
- /* @todo debug message */
- pbuf_free(p);
- ND6_STATS_INC(nd6.lenerr);
- ND6_STATS_INC(nd6.drop);
- return;
- }
-
/* Create an aligned copy. */
ip6_addr_set(&target_address, &(na_hdr->target_address));
@@ -185,12 +171,6 @@
/* We are using a duplicate address. */
netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
-#if LWIP_IPV6_MLD
- /* Leave solicited node multicast group. */
- ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]);
- mld6_leavegroup_netif(inp, &multicast_address);
-#endif /* LWIP_IPV6_MLD */
-
#if LWIP_IPV6_AUTOCONFIG
/* Check to see if this address was autoconfigured. */
if (!ip6_addr_islinklocal(&target_address)) {
@@ -209,6 +189,25 @@
}
#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
+ /* Check that link-layer address option also fits in packet. */
+ if (p->len < (sizeof(struct na_header) + 2)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
/* This is an unsolicited NA, most likely there was a LLADDR change. */
i = nd6_find_neighbor_cache_entry(&target_address);
if (i >= 0) {
@@ -390,6 +389,10 @@
struct ra_header *ra_hdr;
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 */
+ u8_t rdnss_server_idx = 0;
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
/* Check that RA header fits in packet. */
if (p->len < sizeof(struct ra_header)) {
@@ -534,6 +537,37 @@
route_opt = (struct route_option *)buffer;*/
break;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+ case ND6_OPTION_TYPE_RDNSS:
+ {
+ u8_t num, n;
+ struct rdnss_option * rdnss_opt;
+
+ 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);
+ }
+ }
+ }
+ }
+ break;
+ }
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
default:
/* Unrecognized option, abort. */
ND6_STATS_INC(nd6.proterr);
@@ -549,6 +583,7 @@
{
struct redirect_header *redir_hdr;
struct lladdr_option *lladdr_opt;
+ ip6_addr_t tmp;
/* Check that Redir header fits in packet. */
if (p->len < sizeof(struct redirect_header)) {
@@ -571,10 +606,10 @@
}
/* Copy original destination address to current source address, to have an aligned copy. */
- ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address));
+ ip6_addr_set(&tmp, &(redir_hdr->destination_address));
/* Find dest address in cache */
- i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+ i = nd6_find_destination_cache_entry(&tmp);
if (i < 0) {
/* Destination not in cache, drop packet. */
pbuf_free(p);
@@ -588,15 +623,15 @@
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(ip6_current_src_addr(), &(redir_hdr->target_address));
+ ip6_addr_set(&tmp, &(redir_hdr->target_address));
- i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+ i = nd6_find_neighbor_cache_entry(&tmp);
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), ip6_current_src_addr());
+ ip6_addr_set(&(neighbor_cache[i].next_hop_address), &tmp);
/* Receiving a message does not prove reachability: only in one direction.
* Delay probe in case we get confirmation of reachability from upper layer (TCP). */
@@ -622,6 +657,7 @@
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;
/* Check that ICMPv6 header + IPv6 header fit in payload */
if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
@@ -636,10 +672,10 @@
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(ip6_current_src_addr(), &(ip6hdr->dest));
+ ip6_addr_set(&tmp, &(ip6hdr->dest));
/* Look for entry in destination cache. */
- i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+ i = nd6_find_destination_cache_entry(&tmp);
if (i < 0) {
/* Destination not in cache, drop packet. */
pbuf_free(p);
@@ -829,13 +865,6 @@
netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
/* @todo implement preferred and valid lifetimes. */
} else if (netif->flags & NETIF_FLAG_UP) {
-#if LWIP_IPV6_MLD
- if ((addr_state & 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(netif, &multicast_address);
- }
-#endif /* LWIP_IPV6_MLD */
/* Send a NS for this address. */
nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
/* tentative: set next state by increasing by one */
@@ -1294,6 +1323,22 @@
}
/**
+ * Clear the destination cache.
+ *
+ * This operation may be necessary for consistency in the light of changing
+ * local addresses and/or use of the gateway hook.
+ */
+void
+nd6_clear_destination_cache(void)
+{
+ int i;
+
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ ip6_addr_set_any(&destination_cache[i].destination_addr);
+ }
+}
+
+/**
* Determine whether an address matches an on-link prefix.
*
* @param ip6addr the IPv6 address to match
@@ -1328,7 +1373,7 @@
* @return the default router entry index, or -1 if no suitable
* router is found
*/
-s8_t
+static s8_t
nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
{
s8_t i;
@@ -1381,6 +1426,30 @@
}
/**
+ * Find a router-announced route to the given destination.
+ *
+ * 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.
+ *
+ * @param ip6addr the destination IPv6 address
+ * @return the netif to use for the destination, or NULL if none found
+ */
+struct netif *
+nd6_find_route(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+
+ 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 */
+ }
+ }
+
+ return NULL;
+}
+
+/**
* Find an entry for a default router.
*
* @param router_addr the IPv6 address of the router
@@ -1416,6 +1485,7 @@
nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
{
s8_t router_index;
+ s8_t free_router_index;
s8_t neighbor_index;
/* Do we have a neighbor entry for this router? */
@@ -1439,12 +1509,22 @@
neighbor_cache[neighbor_index].isrouter = 1;
/* Look for empty entry. */
- for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+ free_router_index = LWIP_ND6_NUM_ROUTERS;
+ for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) {
+ /* check if router already exists (this is a special case for 2 netifs on the same subnet
+ - e.g. wifi and cable) */
+ if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){
+ return router_index;
+ }
if (default_router_list[router_index].neighbor_entry == NULL) {
- default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
- return router_index;
+ /* remember lowest free index to create a new entry */
+ free_router_index = router_index;
}
}
+ if (free_router_index < LWIP_ND6_NUM_ROUTERS) {
+ default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
+ return free_router_index;
+ }
/* Could not create a router entry. */
@@ -1521,9 +1601,12 @@
* suitable next hop was found, ERR_MEM if no cache entry
* could be created
*/
-s8_t
+static s8_t
nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
{
+#ifdef LWIP_HOOK_ND6_GET_GW
+ const ip6_addr_t *next_hop_addr;
+#endif /* LWIP_HOOK_ND6_GET_GW */
s8_t i;
#if LWIP_NETIF_HWADDRHINT
@@ -1567,6 +1650,12 @@
/* Destination in local link. */
destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
+#ifdef LWIP_HOOK_ND6_GET_GW
+ } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) {
+ /* Next hop for destination provided by hook function. */
+ destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+ ip6_addr_set(&destination_cache[nd6_cached_destination_index].next_hop_addr, next_hop_addr);
+#endif /* LWIP_HOOK_ND6_GET_GW */
} else {
/* We need to select a router. */
i = nd6_select_router(ip6addr, netif);
@@ -1634,7 +1723,7 @@
* @param q packet to be queued
* @return ERR_OK if succeeded, ERR_MEM if out of memory
*/
-err_t
+static err_t
nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
{
err_t result = ERR_MEM;
@@ -1811,6 +1900,61 @@
#endif /* LWIP_ND6_QUEUEING */
}
+/**
+ * A packet is to be transmitted to a specific IPv6 destination on a specific
+ * interface. Check if we can find the hardware address of the next hop to use
+ * for the packet. If so, give the hardware address to the caller, which should
+ * use it to send the packet right away. Otherwise, enqueue the packet for
+ * later transmission while looking up the hardware address, if possible.
+ *
+ * As such, this function returns one of three different possible results:
+ *
+ * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now.
+ * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later.
+ * - not ERR_OK: something went wrong; forward the error upward in the stack.
+ *
+ * @param netif The lwIP network interface on which the IP packet will be sent.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The destination IPv6 address of the packet.
+ * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning
+ * the packet has been queued).
+ * @return
+ * - ERR_OK on success, ERR_RTE if no route was found for the packet,
+ * or ERR_MEM if low memory conditions prohibit sending the packet at all.
+ */
+err_t
+nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp)
+{
+ s8_t i;
+
+ /* Get next hop record. */
+ i = nd6_get_next_hop_entry(ip6addr, netif);
+ if (i < 0) {
+ /* failed to get a next hop neighbor record. */
+ return i;
+ }
+
+ /* Now that we have a destination record, send or queue the packet. */
+ if (neighbor_cache[i].state == ND6_STALE) {
+ /* Switch to delay state. */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+ }
+ /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
+ if ((neighbor_cache[i].state == ND6_REACHABLE) ||
+ (neighbor_cache[i].state == ND6_DELAY) ||
+ (neighbor_cache[i].state == ND6_PROBE)) {
+
+ /* Tell the caller to send out the packet now. */
+ *hwaddrp = neighbor_cache[i].lladdr;
+ return ERR_OK;
+ }
+
+ /* We should queue packet on this interface. */
+ *hwaddrp = NULL;
+ return nd6_queue_packet(i, q);
+}
+
/**
* Get the Path MTU for a destination.
@@ -1917,4 +2061,38 @@
}
}
+#if LWIP_IPV6_MLD
+/**
+ * The state of a local IPv6 address entry is about to change. If needed, join
+ * or leave the solicited-node multicast group for the address.
+ *
+ * @param netif The netif that owns the address.
+ * @param addr_idx The index of the address.
+ * @param new_state The new (IP6_ADDR_) state for the address.
+ */
+void
+nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
+{
+ u8_t old_state, old_member, new_member;
+
+ old_state = netif_ip6_addr_state(netif, addr_idx);
+
+ /* 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);
+
+ if (old_member != new_member) {
+ ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+
+ if (new_member) {
+ mld6_joingroup_netif(netif, &multicast_address);
+ } else {
+ mld6_leavegroup_netif(netif, &multicast_address);
+ }
+ }
+}
+#endif /* LWIP_IPV6_MLD */
+
#endif /* LWIP_IPV6 */
diff --git a/src/core/mem.c b/src/core/mem.c
index 40f4cb9..db3b7cc 100644
--- a/src/core/mem.c
+++ b/src/core/mem.c
@@ -61,9 +61,13 @@
#include "lwip/err.h"
#include <string.h>
-#include <stdlib.h>
+
+#if MEM_LIBC_MALLOC
+#include <stdlib.h> /* for malloc()/free() */
+#endif
#if MEM_LIBC_MALLOC || MEM_USE_POOLS
+
/** mem_init is not used when using pools instead of a heap or using
* C library malloc().
*/
@@ -162,7 +166,7 @@
mem_malloc(mem_size_t size)
{
void *ret;
- struct memp_malloc_helper *element;
+ struct memp_malloc_helper *element = NULL;
memp_t poolnr;
mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
diff --git a/src/core/memp.c b/src/core/memp.c
index 2e3db28..3162449 100644
--- a/src/core/memp.c
+++ b/src/core/memp.c
@@ -71,7 +71,7 @@
#include "netif/ppp/ppp_opts.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
-#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
#include "lwip/ip6_frag.h"
#include "lwip/mld6.h"
@@ -209,7 +209,7 @@
for (j = 0; j < memp_pools[i]->num; ++j) {
memp_overflow_check_element_overflow(p, memp_pools[i]);
memp_overflow_check_element_underflow(p, memp_pools[i]);
- p = (struct memp*)(size_t)((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ p = LWIP_ALIGNMENT_CAST(struct memp*, ((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED));
}
}
SYS_ARCH_UNPROTECT(old_level);
@@ -301,15 +301,15 @@
SYS_ARCH_PROTECT(old_level);
memp = *desc->tab;
-
-#if MEMP_OVERFLOW_CHECK == 1
- memp_overflow_check_element_overflow(memp, desc);
- memp_overflow_check_element_underflow(memp, desc);
-#endif /* MEMP_OVERFLOW_CHECK */
#endif /* MEMP_MEM_MALLOC */
if (memp != NULL) {
#if !MEMP_MEM_MALLOC
+#if MEMP_OVERFLOW_CHECK == 1
+ memp_overflow_check_element_overflow(memp, desc);
+ memp_overflow_check_element_underflow(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
*desc->tab = memp->next;
#if MEMP_OVERFLOW_CHECK
memp->next = NULL;
diff --git a/src/core/netif.c b/src/core/netif.c
index fc9e50b..428b148 100644
--- a/src/core/netif.c
+++ b/src/core/netif.c
@@ -180,7 +180,7 @@
#endif /* NO_SYS */
#if LWIP_IPV6
- IP_ADDR6(loop_netif.ip6_addr, 0, 0, 0, PP_HTONL(0x00000001UL));
+ IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);
loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
#endif /* LWIP_IPV6 */
@@ -415,7 +415,7 @@
udp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
#endif /* LWIP_UDP */
#if LWIP_RAW
- raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+ raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
#endif /* LWIP_RAW */
}
}
@@ -478,7 +478,7 @@
return NULL;
}
- num = name[2] - '0';
+ num = (u8_t)(name[2] - '0');
for (netif = netif_list; netif != NULL; netif = netif->next) {
if (num == netif->num &&
@@ -904,7 +904,6 @@
void
netif_poll(struct netif *netif)
{
- struct pbuf *in;
/* If we have a loopif, SNMP counters are adjusted for it,
* if not they are adjusted for 'netif'. */
#if MIB2_STATS
@@ -916,56 +915,52 @@
#endif /* MIB2_STATS */
SYS_ARCH_DECL_PROTECT(lev);
- do {
- /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
- SYS_ARCH_PROTECT(lev);
- in = netif->loop_first;
- if (in != NULL) {
- struct pbuf *in_end = in;
+ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+ SYS_ARCH_PROTECT(lev);
+ while (netif->loop_first != NULL) {
+ struct pbuf *in, *in_end;
#if LWIP_LOOPBACK_MAX_PBUFS
- u8_t clen = 1;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
- while (in_end->len != in_end->tot_len) {
- LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
- in_end = in_end->next;
-#if LWIP_LOOPBACK_MAX_PBUFS
- clen++;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
- }
-#if LWIP_LOOPBACK_MAX_PBUFS
- /* 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;
+ u8_t clen = 1;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
- /* 'in_end' now points to the last pbuf from 'in' */
- if (in_end == netif->loop_last) {
- /* this was the last pbuf in the list */
- netif->loop_first = netif->loop_last = NULL;
- } else {
- /* pop the pbuf off the list */
- netif->loop_first = in_end->next;
- LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
- }
- /* De-queue the pbuf from its successors on the 'loop_' list. */
- in_end->next = NULL;
+ in = in_end = netif->loop_first;
+ while (in_end->len != in_end->tot_len) {
+ LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+ in_end = in_end->next;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen++;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
}
+#if LWIP_LOOPBACK_MAX_PBUFS
+ /* 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;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* 'in_end' now points to the last pbuf from 'in' */
+ if (in_end == netif->loop_last) {
+ /* this was the last pbuf in the list */
+ netif->loop_first = netif->loop_last = NULL;
+ } else {
+ /* pop the pbuf off the list */
+ netif->loop_first = in_end->next;
+ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+ }
+ /* De-queue the pbuf from its successors on the 'loop_' list. */
+ in_end->next = NULL;
SYS_ARCH_UNPROTECT(lev);
- if (in != NULL) {
- LINK_STATS_INC(link.recv);
- MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
- MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
- /* loopback packets are always IP packets! */
- if (ip_input(in, netif) != ERR_OK) {
- pbuf_free(in);
- }
- /* Don't reference the packet any more! */
- in = NULL;
+ LINK_STATS_INC(link.recv);
+ MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
+ MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
+ /* loopback packets are always IP packets! */
+ if (ip_input(in, netif) != ERR_OK) {
+ pbuf_free(in);
}
- /* go on while there is a packet on the list */
- } while (netif->loop_first != NULL);
+ SYS_ARCH_PROTECT(lev);
+ }
+ SYS_ARCH_UNPROTECT(lev);
}
#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
@@ -1058,7 +1053,7 @@
udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
#endif /* LWIP_UDP */
#if LWIP_RAW
- raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+ raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
#endif /* LWIP_RAW */
}
/* @todo: remove/readd mib2 ip6 entries? */
@@ -1101,6 +1096,13 @@
u8_t new_valid = state & IP6_ADDR_VALID;
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n"));
+#if LWIP_IPV6_MLD
+ /* Reevaluate solicited-node multicast group membership. */
+ if (netif->flags & NETIF_FLAG_MLD6) {
+ nd6_adjust_mld_membership(netif, addr_idx, state);
+ }
+#endif /* LWIP_IPV6_MLD */
+
if (old_valid && !new_valid) {
/* address about to be removed by setting invalid */
#if LWIP_TCP
@@ -1110,7 +1112,7 @@
udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
#endif /* LWIP_UDP */
#if LWIP_RAW
- raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+ raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
#endif /* LWIP_RAW */
/* @todo: remove mib2 ip6 entries? */
}
@@ -1200,10 +1202,10 @@
/* Set address state. */
#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
/* Will perform duplicate address detection (DAD). */
- netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE;
+ netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE);
#else
/* Consider address valid. */
- netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED;
+ netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED);
#endif /* LWIP_IPV6_AUTOCONFIG */
}
@@ -1233,7 +1235,7 @@
/* Find a free slot -- musn't be the first one (reserved for link local) */
for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (!ip6_addr_isvalid(netif->ip6_addr_state[i])) {
+ if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr);
netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE);
if (chosen_idx != NULL) {
diff --git a/src/core/pbuf.c b/src/core/pbuf.c
index 887dc8a..af63e60 100644
--- a/src/core/pbuf.c
+++ b/src/core/pbuf.c
@@ -568,11 +568,11 @@
}
if (header_size_increment < 0) {
- increment_magnitude = -header_size_increment;
+ 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 = header_size_increment;
+ 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. */
@@ -1119,7 +1119,8 @@
struct pbuf*
pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)
{
- return (struct pbuf*)(size_t)pbuf_skip_const(in, in_offset, out_offset);
+ const struct pbuf* out = pbuf_skip_const(in, in_offset, out_offset);
+ return LWIP_CONST_CAST(struct pbuf*, out);
}
/**
@@ -1227,6 +1228,7 @@
return p;
}
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;
diff --git a/src/core/raw.c b/src/core/raw.c
index f692c9c..93c6547 100644
--- a/src/core/raw.c
+++ b/src/core/raw.c
@@ -209,7 +209,7 @@
err_t
raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
{
- if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
}
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
@@ -233,7 +233,7 @@
err_t
raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
{
- if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
}
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
@@ -320,7 +320,13 @@
}
}
- netif = ip_route(&pcb->local_ip, ipaddr);
+ 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);
diff --git a/src/core/sys.c b/src/core/sys.c
index f177737..7059b4d 100644
--- a/src/core/sys.c
+++ b/src/core/sys.c
@@ -36,6 +36,44 @@
*
*/
+/**
+ * @defgroup sys_layer Porting (system abstraction layer)
+ * @ingroup lwip
+ * @verbinclude "sys_arch.txt"
+ *
+ * @defgroup sys_os OS abstraction layer
+ * @ingroup sys_layer
+ * No need to implement functions in this section in NO_SYS mode.
+ *
+ * @defgroup sys_sem Semaphores
+ * @ingroup sys_os
+ *
+ * @defgroup sys_mutex Mutexes
+ * @ingroup sys_os
+ * Mutexes are recommended to correctly handle priority inversion,
+ * especially if you use LWIP_CORE_LOCKING .
+ *
+ * @defgroup sys_mbox Mailboxes
+ * @ingroup sys_os
+ *
+ * @defgroup sys_time Time
+ * @ingroup sys_layer
+ *
+ * @defgroup sys_prot Critical sections
+ * @ingroup sys_layer
+ * Used to protect short regions of code against concurrent access.
+ * - Your system is a bare-metal system (probably with an RTOS)
+ * and interrupts are under your control:
+ * Implement this as LockInterrupts() / UnlockInterrupts()
+ * - Your system uses an RTOS with deferred interrupt handling from a
+ * worker thread: Implement as a global mutex or lock/unlock scheduler
+ * - Your system uses a high-level OS with e.g. POSIX signals:
+ * Implement as a global mutex
+ *
+ * @defgroup sys_misc Misc
+ * @ingroup sys_os
+ */
+
#include "lwip/opt.h"
#include "lwip/sys.h"
diff --git a/src/core/tcp.c b/src/core/tcp.c
index 1d70098..75be86b 100644
--- a/src/core/tcp.c
+++ b/src/core/tcp.c
@@ -551,7 +551,7 @@
#endif /* LWIP_IPV4 */
/* still need to check for ipaddr == NULL in IPv6 only case */
- if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
}
@@ -636,19 +636,44 @@
*
* @note The original tcp_pcb is freed. This function therefore has to be
* called like this:
- * tpcb = tcp_listen(tpcb);
+ * tpcb = tcp_listen_with_backlog(tpcb, backlog);
*/
struct tcp_pcb *
tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
{
- struct tcp_pcb_listen *lpcb;
+ return tcp_listen_with_backlog_and_err(pcb, backlog, NULL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @param err when NULL is returned, this contains the error reason
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ * called like this:
+ * tpcb = tcp_listen_with_backlog_and_err(tpcb, backlog, &err);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
+{
+ struct tcp_pcb_listen *lpcb = NULL;
+ err_t res;
LWIP_UNUSED_ARG(backlog);
- LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
+ LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
/* already listening? */
if (pcb->state == LISTEN) {
- return pcb;
+ lpcb = (struct tcp_pcb_listen*)pcb;
+ res = ERR_ALREADY;
+ goto done;
}
#if SO_REUSE
if (ip_get_option(pcb, SOF_REUSEADDR)) {
@@ -659,14 +684,17 @@
if ((lpcb->local_port == pcb->local_port) &&
ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
/* this address/port is already used */
- return NULL;
+ lpcb = NULL;
+ res = ERR_USE;
+ goto done;
}
}
}
#endif /* SO_REUSE */
lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
if (lpcb == NULL) {
- return NULL;
+ res = ERR_MEM;
+ goto done;
}
lpcb->callback_arg = pcb->callback_arg;
lpcb->local_port = pcb->local_port;
@@ -691,6 +719,11 @@
tcp_backlog_set(lpcb, backlog);
#endif /* TCP_LISTEN_BACKLOG */
TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+ res = ERR_OK;
+done:
+ if (err != NULL) {
+ *err = res;
+ }
return (struct tcp_pcb *)lpcb;
}
@@ -826,7 +859,7 @@
u32_t iss;
u16_t old_local_port;
- if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
}
@@ -880,10 +913,11 @@
#endif /* SO_REUSE */
}
- iss = tcp_next_iss();
+ iss = tcp_next_iss(pcb);
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
pcb->lastack = iss - 1;
+ pcb->snd_wl2 = iss - 1;
pcb->snd_lbb = iss - 1;
/* Start with a window that does not need scaling. When window scaling is
enabled and used, the window is enlarged when both sides agree on scaling. */
@@ -1491,7 +1525,6 @@
tcp_alloc(u8_t prio)
{
struct tcp_pcb *pcb;
- u32_t iss;
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
@@ -1554,11 +1587,6 @@
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = -1;
pcb->cwnd = 1;
- iss = tcp_next_iss();
- pcb->snd_wl2 = iss;
- pcb->snd_nxt = iss;
- pcb->lastack = iss;
- pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
pcb->last_timer = tcp_timer_ctr;
@@ -1826,12 +1854,18 @@
* @return u32_t pseudo random sequence number
*/
u32_t
-tcp_next_iss(void)
+tcp_next_iss(struct tcp_pcb *pcb)
{
+#ifdef LWIP_HOOK_TCP_ISN
+ return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port);
+#else /* LWIP_HOOK_TCP_ISN */
static u32_t iss = 6510;
+ LWIP_UNUSED_ARG(pcb);
+
iss += tcp_ticks; /* XXX */
return iss;
+#endif /* LWIP_HOOK_TCP_ISN */
}
#if TCP_CALCULATE_EFF_SEND_MSS
diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c
index 93268b3..fbbdc72 100644
--- a/src/core/tcp_in.c
+++ b/src/core/tcp_in.c
@@ -358,6 +358,11 @@
((pcb->refused_data != NULL) && (tcplen > 0))) {
/* pcb has been aborted or refused data is still refused and the new
segment contains data */
+ if (pcb->rcv_ann_wnd == 0) {
+ /* this is a zero-window probe, we respond to it with current RCV.NXT
+ and drop the data segment */
+ tcp_send_empty_ack(pcb);
+ }
TCP_STATS_INC(tcp.drop);
MIB2_STATS_INC(mib2.tcpinerrs);
goto aborted;
@@ -542,6 +547,7 @@
tcp_listen_input(struct tcp_pcb_listen *pcb)
{
struct tcp_pcb *npcb;
+ u32_t iss;
err_t rc;
if (flags & TCP_RST) {
@@ -589,6 +595,11 @@
npcb->state = SYN_RCVD;
npcb->rcv_nxt = seqno + 1;
npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+ iss = tcp_next_iss(npcb);
+ npcb->snd_wl2 = iss;
+ npcb->snd_nxt = iss;
+ npcb->lastack = iss;
+ npcb->snd_lbb = iss;
npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
npcb->callback_arg = pcb->callback_arg;
#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
@@ -602,7 +613,7 @@
/* Parse any options in the SYN. */
tcp_parseopt(npcb);
- npcb->snd_wnd = SND_WND_SCALE(npcb, tcphdr->wnd);
+ npcb->snd_wnd = tcphdr->wnd;
npcb->snd_wnd_max = npcb->snd_wnd;
npcb->ssthresh = LWIP_TCP_INITIAL_SSTHRESH(npcb);
@@ -751,7 +762,7 @@
pcb->rcv_nxt = seqno + 1;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->lastack = ackno;
- pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);
+ pcb->snd_wnd = tcphdr->wnd;
pcb->snd_wnd_max = pcb->snd_wnd;
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
pcb->state = ESTABLISHED;
diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c
index 3c2bc81..8ccdffb 100644
--- a/src/core/tcp_out.c
+++ b/src/core/tcp_out.c
@@ -377,6 +377,7 @@
u16_t oversize = 0;
u16_t oversize_used = 0;
#endif /* TCP_OVERSIZE */
+ u16_t extendlen = 0;
#if TCP_CHECKSUM_ON_COPY
u16_t concat_chksum = 0;
u8_t concat_chksum_swapped = 0;
@@ -480,6 +481,10 @@
/*
* Phase 2: Chain a new pbuf to the end of pcb->unsent.
*
+ * As an exception when NOT copying the data, if the given data buffer
+ * directly follows the last unsent data buffer in memory, extend the last
+ * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation.
+ *
* We don't extend segments containing SYN/FIN flags or options
* (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
* the end.
@@ -506,12 +511,24 @@
#if TCP_CHECKSUM_ON_COPY
concat_chksummed += seglen;
#endif /* TCP_CHECKSUM_ON_COPY */
+ queuelen += pbuf_clen(concat_p);
} else {
/* Data is not copied */
- if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
- ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
- goto memerr;
+ /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */
+ struct pbuf *p;
+ for (p = last_unsent->p; p->next != NULL; p = p->next);
+ if (p->type == PBUF_ROM && (const u8_t *)p->payload + p->len == (const u8_t *)arg) {
+ LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0);
+ extendlen = seglen;
+ } else {
+ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+ /* reference the non-volatile payload data */
+ ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
+ queuelen += pbuf_clen(concat_p);
}
#if TCP_CHECKSUM_ON_COPY
/* calculate the checksum of nocopy-data */
@@ -519,12 +536,9 @@
&concat_chksum, &concat_chksum_swapped);
concat_chksummed += seglen;
#endif /* TCP_CHECKSUM_ON_COPY */
- /* reference the non-volatile payload data */
- ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
}
pos += seglen;
- queuelen += pbuf_clen(concat_p);
}
} else {
#if TCP_OVERSIZE
@@ -669,26 +683,40 @@
#endif /* TCP_OVERSIZE */
/*
- * Phase 2: concat_p can be concatenated onto last_unsent->p
+ * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we
+ * determined that the last ROM pbuf can be extended to include the new data.
*/
if (concat_p != NULL) {
LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
(last_unsent != NULL));
pbuf_cat(last_unsent->p, concat_p);
last_unsent->len += concat_p->tot_len;
-#if TCP_CHECKSUM_ON_COPY
- if (concat_chksummed) {
- /*if concat checksumm swapped - swap it back */
- if (concat_chksum_swapped) {
- concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
- }
- tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
- &last_unsent->chksum_swapped);
- last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ } else if (extendlen > 0) {
+ struct pbuf *p;
+ LWIP_ASSERT("tcp_write: extension of reference requires reference",
+ last_unsent != NULL && last_unsent->p != NULL);
+ for (p = last_unsent->p; p->next != NULL; p = p->next) {
+ p->tot_len += extendlen;
}
-#endif /* TCP_CHECKSUM_ON_COPY */
+ p->tot_len += extendlen;
+ p->len += extendlen;
+ last_unsent->len += extendlen;
}
+#if TCP_CHECKSUM_ON_COPY
+ if (concat_chksummed) {
+ LWIP_ASSERT("tcp_write: concat checksum needs concatenated data",
+ concat_p != NULL || extendlen > 0);
+ /*if concat checksumm swapped - swap it back */
+ if (concat_chksum_swapped) {
+ concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
+ }
+ tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+ &last_unsent->chksum_swapped);
+ last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
/*
* Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
* is harmless
@@ -1033,6 +1061,24 @@
lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
}
#endif /* TCP_CWND_DEBUG */
+ /* Check if we need to start the persistent timer when the next unsent segment
+ * does not fit within the remaining send window and RTO timer is not running (we
+ * have no in-flight data). A traditional approach would fill the remaining window
+ * with part of the unsent segment (which will engage zero-window probing upon
+ * reception of the zero window update from the receiver). This ensures the
+ * subsequent window update is reliably received. With the goal of being lightweight,
+ * we avoid splitting the unsent segment and treat the window as already zero.
+ */
+ if (seg != NULL &&
+ lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd &&
+ wnd > 0 && wnd == pcb->snd_wnd && pcb->unacked == NULL) {
+ /* Start the persist timer */
+ if (pcb->persist_backoff == 0) {
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ }
+ goto output_done;
+ }
/* data available and window allows it to be sent? */
while (seg != NULL &&
lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
@@ -1112,6 +1158,7 @@
}
seg = pcb->unsent;
}
+output_done:
#if TCP_OVERSIZE
if (pcb->unsent == NULL) {
/* last unsent has been removed, reset unsent_oversize */
diff --git a/src/core/timeouts.c b/src/core/timeouts.c
index e2dc0fc..227d71f 100644
--- a/src/core/timeouts.c
+++ b/src/core/timeouts.c
@@ -179,7 +179,7 @@
for (i = 1; i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
/* we have to cast via size_t to get rid of const warning
(this is OK as cyclic_timer() casts back to const* */
- sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, (void*)(size_t)&lwip_cyclic_timers[i]);
+ sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i]));
}
/* Initialise timestamp for sys_check_timeouts */
diff --git a/src/core/udp.c b/src/core/udp.c
index e0ab186..a916414 100644
--- a/src/core/udp.c
+++ b/src/core/udp.c
@@ -116,24 +116,6 @@
}
}
return udp_port;
-#if 0
- struct udp_pcb *ipcb = udp_pcbs;
- while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) {
- if (ipcb->local_port == udp_port) {
- /* port is already used by another udp_pcb */
- udp_port++;
- /* restart scanning all udp pcbs */
- ipcb = udp_pcbs;
- } else {
- /* go on with next udp pcb */
- ipcb = ipcb->next;
- }
- }
- if (ipcb != NULL) {
- return 0;
- }
- return udp_port;
-#endif
}
/** Common code to see if the current input packet matches the pcb
@@ -178,15 +160,8 @@
}
} else
#endif /* LWIP_IPV4 */
- /* Handle IPv4 and IPv6: all, multicast or exact match */
- if (ip_addr_isany(&pcb->local_ip) ||
-#if LWIP_IPV6_MLD
- (ip_current_is_v6() && ip6_addr_ismulticast(ip6_current_dest_addr())) ||
-#endif /* LWIP_IPV6_MLD */
-#if LWIP_IGMP
- (!ip_current_is_v6() && ip4_addr_ismulticast(ip4_current_dest_addr())) ||
-#endif /* LWIP_IGMP */
- ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+ /* Handle IPv4 and IPv6: all or exact match */
+ if (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
return 1;
}
}
@@ -429,7 +404,7 @@
destination address was broadcast/multicast. */
if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
/* move payload pointer back to ip header */
- pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN);
+ pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
icmp_port_unreach(ip_current_is_v6(), p);
}
#endif /* LWIP_ICMP || LWIP_ICMP6 */
@@ -571,7 +546,12 @@
#endif /* LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) */
/* find the outgoing network interface for this packet */
- netif = ip_route(&pcb->local_ip, dst_ip_route);
+ 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(dst_ip_route)), dst_ip_route);
+ } else {
+ netif = ip_route(&pcb->local_ip, dst_ip_route);
+ }
/* no outgoing network interface could be found? */
if (netif == NULL) {
@@ -912,7 +892,7 @@
#endif /* LWIP_IPV4 */
/* still need to check for ipaddr == NULL in IPv6 only case */
- if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
}
diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h
index e8620dd..516bd16 100644
--- a/src/include/lwip/api.h
+++ b/src/include/lwip/api.h
@@ -43,8 +43,7 @@
/* Note: Netconn API is always available when sockets are enabled -
* sockets are implemented on top of them */
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
#include "lwip/netbuf.h"
#include "lwip/sys.h"
#include "lwip/ip_addr.h"
@@ -90,6 +89,7 @@
#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF3) == NETCONN_UDPLITE)
#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
#else /* LWIP_IPV6 */
+#define NETCONNTYPE_ISIPV6(t) (0)
#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE)
#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
#endif /* LWIP_IPV6 */
diff --git a/src/include/lwip/apps/mqtt.h b/src/include/lwip/apps/mqtt.h
new file mode 100644
index 0000000..ee88040
--- /dev/null
+++ b/src/include/lwip/apps/mqtt.h
@@ -0,0 +1,243 @@
+/**
+ * @file
+ * MQTT client
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * 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: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_CLIENT_H
+#define LWIP_HDR_APPS_MQTT_CLIENT_H
+
+#include "lwip/apps/mqtt_opts.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mqtt_client_t mqtt_client_t;
+
+/** @ingroup mqtt
+ * Default MQTT port */
+#define MQTT_PORT 1883
+
+/*---------------------------------------------------------------------------------------------- */
+/* Connection with server */
+
+/**
+ * @ingroup mqtt
+ * Client information and connection parameters */
+struct mqtt_connect_client_info_t {
+ /** Client identifier, must be set by caller */
+ const char *client_id;
+ /** User name and password, set to NULL if not used */
+ const char* client_user;
+ const char* client_pass;
+ /** keep alive time in seconds, 0 to disable keep alive functionality*/
+ u16_t keep_alive;
+ /** will topic, set to NULL if will is not to be used,
+ will_msg, will_qos and will retain are then ignored */
+ const char* will_topic;
+ const char* will_msg;
+ u8_t will_qos;
+ u8_t will_retain;
+};
+
+/**
+ * @ingroup mqtt
+ * Connection status codes */
+typedef enum
+{
+ MQTT_CONNECT_ACCEPTED = 0,
+ MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1,
+ MQTT_CONNECT_REFUSED_IDENTIFIER = 2,
+ MQTT_CONNECT_REFUSED_SERVER = 3,
+ MQTT_CONNECT_REFUSED_USERNAME_PASS = 4,
+ MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_ = 5,
+ MQTT_CONNECT_DISCONNECTED = 256,
+ MQTT_CONNECT_TIMEOUT = 257
+} mqtt_connection_status_t;
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt connection status callback. Called when
+ * client has connected to the server after initiating a mqtt connection attempt by
+ * calling mqtt_connect() or when connection is closed by server or an error
+ *
+ * @param client MQTT client itself
+ * @param arg Additional argument to pass to the callback function
+ * @param status Connect result code or disconnection notification @see mqtt_connection_status_t
+ *
+ */
+typedef void (*mqtt_connection_cb_t)(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
+
+
+/**
+ * @ingroup mqtt
+ * Data callback flags */
+enum {
+ /** Flag set when last fragment of data arrives in data callback */
+ MQTT_DATA_FLAG_LAST = 1
+};
+
+/**
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish data callback function. Called when data
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param data User data, pointed object, data may not be referenced after callback return,
+ NULL is passed when all publish data are delivered
+ * @param len Length of publish data fragment
+ * @param flags MQTT_DATA_FLAG_LAST set when this call contains the last part of data from publish message
+ *
+ */
+typedef void (*mqtt_incoming_data_cb_t)(void *arg, const u8_t *data, u16_t len, u8_t flags);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish function. Called when an incoming publish
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param topic Zero terminated Topic text string, topic may not be referenced after callback return
+ * @param tot_len Total length of publish data, if set to 0 (no publish payload) data callback will not be invoked
+ */
+typedef void (*mqtt_incoming_publish_cb_t)(void *arg, const char *topic, u32_t tot_len);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt request callback. Called when a subscribe, unsubscribe
+ * or publish request has completed
+ * @param arg Pointer to user data supplied when invoking request
+ * @param err ERR_OK on success
+ * ERR_TIMEOUT if no response was received within timeout,
+ * ERR_ABRT if (un)subscribe was denied
+ */
+typedef void (*mqtt_request_cb_t)(void *arg, err_t err);
+
+
+/**
+ * Pending request item, binds application callback to pending server requests
+ */
+struct mqtt_request_t
+{
+ /** Next item in list, NULL means this is the last in chain,
+ next pointing at itself means request is unallocated */
+ struct mqtt_request_t *next;
+ /** Callback to upper layer */
+ mqtt_request_cb_t cb;
+ void *arg;
+ /** MQTT packet identifier */
+ u16_t pkt_id;
+ /** Expire time relative to element before this */
+ u16_t timeout_diff;
+};
+
+/** Ring buffer */
+struct mqtt_ringbuf_t {
+ u16_t put;
+ u16_t get;
+ u8_t buf[MQTT_OUTPUT_RINGBUF_SIZE];
+};
+
+/** MQTT client */
+struct mqtt_client_t
+{
+ /** Timers and timeouts */
+ u16_t cyclic_tick;
+ u16_t keep_alive;
+ u16_t server_watchdog;
+ /** Packet identifier generator*/
+ u16_t pkt_id_seq;
+ /** Packet identifier of pending incoming publish */
+ u16_t inpub_pkt_id;
+ /** Connection state */
+ u8_t conn_state;
+ struct tcp_pcb *conn;
+ /** Connection callback */
+ void *connect_arg;
+ mqtt_connection_cb_t connect_cb;
+ /** Pending requests to server */
+ struct mqtt_request_t *pend_req_queue;
+ struct mqtt_request_t req_list[MQTT_REQ_MAX_IN_FLIGHT];
+ void *inpub_arg;
+ /** Incoming data callback */
+ mqtt_incoming_data_cb_t data_cb;
+ mqtt_incoming_publish_cb_t pub_cb;
+ /** Input */
+ u32_t msg_idx;
+ u8_t rx_buffer[MQTT_VAR_HEADER_BUFFER_LEN];
+ /** Output ring-buffer */
+ struct mqtt_ringbuf_t output;
+};
+
+
+/** Connect to server */
+err_t mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ipaddr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+ const struct mqtt_connect_client_info_t *client_info);
+
+/** Disconnect from server */
+void mqtt_disconnect(mqtt_client_t *client);
+
+/** Create new client */
+mqtt_client_t *mqtt_client_new(void);
+
+/** Check connection status */
+u8_t mqtt_client_is_connected(mqtt_client_t *client);
+
+/** Set callback to call for incoming publish */
+void mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t,
+ mqtt_incoming_data_cb_t data_cb, void *arg);
+
+/** Common function for subscribe and unsubscribe */
+err_t mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub);
+
+/** @ingroup mqtt
+ *Subscribe to topic */
+#define mqtt_subscribe(client, topic, qos, cb, arg) mqtt_sub_unsub(client, topic, qos, cb, arg, 1)
+/** @ingroup mqtt
+ * Unsubscribe to topic */
+#define mqtt_unsubscribe(client, topic, cb, arg) mqtt_sub_unsub(client, topic, 0, cb, arg, 0)
+
+
+/** Publish data to topic */
+err_t mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+ mqtt_request_cb_t cb, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_CLIENT_H */
diff --git a/src/include/lwip/apps/mqtt_opts.h b/src/include/lwip/apps/mqtt_opts.h
new file mode 100644
index 0000000..ffefacd
--- /dev/null
+++ b/src/include/lwip/apps/mqtt_opts.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * MQTT client options
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * 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: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_OPTS_H
+#define LWIP_HDR_APPS_MQTT_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup mqtt_opts Options
+ * @ingroup mqtt
+ * @{
+ */
+
+/**
+ * Output ring-buffer size, must be able to fit largest outgoing publish message topic+payloads
+ */
+#ifndef MQTT_OUTPUT_RINGBUF_SIZE
+#define MQTT_OUTPUT_RINGBUF_SIZE 256
+#endif
+
+/**
+ * Number of bytes in receive buffer, must be at least the size of the longest incoming topic + 8
+ * If one wants to avoid fragmented incoming publish, set length to max incoming topic length + max payload length + 8
+ */
+#ifndef MQTT_VAR_HEADER_BUFFER_LEN
+#define MQTT_VAR_HEADER_BUFFER_LEN 128
+#endif
+
+/**
+ * Maximum number of pending subscribe, unsubscribe and publish requests to server .
+ */
+#ifndef MQTT_REQ_MAX_IN_FLIGHT
+#define MQTT_REQ_MAX_IN_FLIGHT 4
+#endif
+
+/**
+ * Seconds between each cyclic timer call.
+ */
+#ifndef MQTT_CYCLIC_TIMER_INTERVAL
+#define MQTT_CYCLIC_TIMER_INTERVAL 5
+#endif
+
+/**
+ * Publish, subscribe and unsubscribe request timeout in seconds.
+ */
+#ifndef MQTT_REQ_TIMEOUT
+#define MQTT_REQ_TIMEOUT 30
+#endif
+
+/**
+ * Seconds for MQTT connect response timeout after sending connect request
+ */
+#ifndef MQTT_CONNECT_TIMOUT
+#define MQTT_CONNECT_TIMOUT 100
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_OPTS_H */
diff --git a/src/include/lwip/arch.h b/src/include/lwip/arch.h
index 473e4a4..b59f521 100644
--- a/src/include/lwip/arch.h
+++ b/src/include/lwip/arch.h
@@ -47,12 +47,67 @@
#include "arch/cc.h"
+/**
+ * @defgroup compiler_abstraction Compiler/platform abstraction
+ * @ingroup sys_layer
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ * @{
+ */
+
+/** Define the byte order of the system.
+ * Needed for conversion of network data to host byte order.
+ * Allowed values: LITTLE_ENDIAN and BIG_ENDIAN
+ */
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+/** Define random number generator function of your system */
+#ifndef LWIP_RAND
+#define LWIP_RAND() ((u32_t)rand())
+#endif
+
+/** Platform specific diagnostic output.\n
+ * Note the default implementation pulls in printf, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_DIAG
+#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Platform specific assertion handling.\n
+ * Note the default implementation pulls in printf, fflush and abort, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_ASSERT
+#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
+ x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if you do not want to
+ * include stddef.h header to get size_t. You need to typedef size_t
+ * by yourself in this case.
+ */
+#ifndef LWIP_NO_STDDEF_H
+#define LWIP_NO_STDDEF_H 0
+#endif
+
+#if !LWIP_NO_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif
+
/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
- * the stdint.h header. This cannot be \#defined in lwipopts.h since
- * this is not an option of lwIP itself, but an option of the lwIP port
- * to your system.
- * Additionally, this header is meant to be \#included in lwipopts.h
- * (you may need to declare function prototypes in there).
+ * the stdint.h header. You need to typedef the generic types listed in
+ * lwip/arch.h yourself in this case (u8_t, u16_t...).
*/
#ifndef LWIP_NO_STDINT_H
#define LWIP_NO_STDINT_H 0
@@ -71,11 +126,8 @@
#endif
/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
- * the inttypes.h header. This cannot be \#defined in lwipopts.h since
- * this is not an option of lwIP itself, but an option of the lwIP port
- * to your system.
- * Additionally, this header is meant to be \#included in lwipopts.h
- * (you may need to declare function prototypes in there).
+ * the inttypes.h header. You need to define the format strings listed in
+ * lwip/arch.h yourself in this case (X8_F, U16_F...).
*/
#ifndef LWIP_NO_INTTYPES_H
#define LWIP_NO_INTTYPES_H 0
@@ -110,14 +162,31 @@
#endif
#endif
+/** C++ const_cast<target_type>(val) equivalent to remove constness from a value (GCC -Wcast-qual) */
+#ifndef LWIP_CONST_CAST
+#define LWIP_CONST_CAST(target_type, val) ((target_type)((ptrdiff_t)val))
+#endif
+
+/** Get rid of alignment cast warnings (GCC -Wcast-align) */
+#ifndef LWIP_ALIGNMENT_CAST
+#define LWIP_ALIGNMENT_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Get rid of warnings related to pointer-to-numeric and vice-versa casts,
+ * e.g. "conversion from 'u8_t' to 'void *' of greater size"
+ */
+#ifndef LWIP_PTR_NUMERIC_CAST
+#define LWIP_PTR_NUMERIC_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
/** Allocates a memory buffer of specified size that is of sufficient size to align
* its start address using LWIP_MEM_ALIGN.
* You can declare your own version here e.g. to enforce alignment without adding
* trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement
- * requirements.
- * e.g. if you use gcc and need 32 bit alignment:
- * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] __attribute__((aligned(4)))
- * or more portable:
+ * requirements.\n
+ * e.g. if you use gcc and need 32 bit alignment:\n
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))\n
+ * or more portable:\n
* \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)]
*/
#ifndef LWIP_DECLARE_MEMORY_ALIGNED
@@ -151,39 +220,84 @@
extern "C" {
#endif
+/** Packed structs support.
+ * Placed BEFORE declaration of a packed struct.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
#ifndef PACK_STRUCT_BEGIN
#define PACK_STRUCT_BEGIN
#endif /* PACK_STRUCT_BEGIN */
+/** Packed structs support.
+ * Placed AFTER declaration of a packed struct.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
#ifndef PACK_STRUCT_END
#define PACK_STRUCT_END
#endif /* PACK_STRUCT_END */
+/** Packed structs support.
+ * Placed between end of declaration of a packed struct and trailing semicolon.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
#ifndef PACK_STRUCT_STRUCT
+#if defined(__GNUC__) || defined(__clang__)
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#else
#define PACK_STRUCT_STRUCT
+#endif
#endif /* PACK_STRUCT_STRUCT */
+/** Packed structs support.
+ * Wraps u32_t and u16_t members.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
#ifndef PACK_STRUCT_FIELD
#define PACK_STRUCT_FIELD(x) x
#endif /* PACK_STRUCT_FIELD */
-/* Used for struct fields of u8_t,
- * where some compilers warn that packing is not necessary */
+/** Packed structs support.
+ * Wraps u8_t members, where some compilers warn that packing is not necessary.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
#ifndef PACK_STRUCT_FLD_8
#define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x)
#endif /* PACK_STRUCT_FLD_8 */
-/* Used for struct fields of that are packed structs themself,
- * where some compilers warn that packing is not necessary */
+/** Packed structs support.
+ * Wraps members that are packed structs themselves, where some compilers warn that packing is not necessary.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
#ifndef PACK_STRUCT_FLD_S
#define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x)
#endif /* PACK_STRUCT_FLD_S */
+/** Packed structs support using \#include files before and after struct to be packed.\n
+ * The file included BEFORE the struct is "arch/bpstruct.h".\n
+ * The file included AFTER the struct is "arch/epstruct.h".\n
+ * This can be used to implement struct packing on MS Visual C compilers, see
+ * the Win32 port in the lwIP contrib repository for reference.
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifdef __DOXYGEN__
+#define PACK_STRUCT_USE_INCLUDES
+#endif
+/** Eliminates compiler warning about unused arguments (GCC -Wextra -Wunused). */
#ifndef LWIP_UNUSED_ARG
#define LWIP_UNUSED_ARG(x) (void)x
#endif /* LWIP_UNUSED_ARG */
+/**
+ * @}
+ */
#ifdef __cplusplus
}
diff --git a/src/include/lwip/autoip.h b/src/include/lwip/autoip.h
index 2f66698..1d85bcc 100644
--- a/src/include/lwip/autoip.h
+++ b/src/include/lwip/autoip.h
@@ -88,6 +88,8 @@
/* for lwIP internal use by ip4.c */
u8_t autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr);
+#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
+
#ifdef __cplusplus
}
#endif
diff --git a/src/include/lwip/debug.h b/src/include/lwip/debug.h
index c676612..a142f1c 100644
--- a/src/include/lwip/debug.h
+++ b/src/include/lwip/debug.h
@@ -40,24 +40,45 @@
#include "lwip/arch.h"
#include "lwip/opt.h"
-/** lower two bits indicate debug level
- * - 0 all
- * - 1 warning
- * - 2 serious
- * - 3 severe
+/**
+ * @defgroup debugging_levels LWIP_DBG_MIN_LEVEL and LWIP_DBG_TYPES_ON values
+ * @ingroup lwip_opts_debugmsg
+ * @{
*/
-#define LWIP_DBG_LEVEL_ALL 0x00
-#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */
-#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
-#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
-#define LWIP_DBG_LEVEL_SEVERE 0x03
-#define LWIP_DBG_MASK_LEVEL 0x03
+/** @name Debug level (LWIP_DBG_MIN_LEVEL)
+ * @{
+ */
+/** Debug level: ALL messages*/
+#define LWIP_DBG_LEVEL_ALL 0x00
+/** Debug level: Warnings. bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_WARNING 0x01
+/** Debug level: Serious. memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02
+/** Debug level: Severe */
+#define LWIP_DBG_LEVEL_SEVERE 0x03
+/**
+ * @}
+ */
+
+#define LWIP_DBG_MASK_LEVEL 0x03
+/* compatibility define only */
+#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL
+
+/** @name Enable/disable debug messages completely (LWIP_DBG_TYPES_ON)
+ * @{
+ */
/** flag for LWIP_DEBUGF to enable that debug message */
#define LWIP_DBG_ON 0x80U
/** flag for LWIP_DEBUGF to disable that debug message */
#define LWIP_DBG_OFF 0x00U
+/**
+ * @}
+ */
+/** @name Debug message types (LWIP_DBG_TYPES_ON)
+ * @{
+ */
/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
#define LWIP_DBG_TRACE 0x40U
/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
@@ -66,11 +87,31 @@
#define LWIP_DBG_FRESH 0x10U
/** flag for LWIP_DEBUGF to halt after printing this debug message */
#define LWIP_DBG_HALT 0x08U
+/**
+ * @}
+ */
/**
- * LWIP_NOASSERT: Disable LWIP_ASSERT checks.
- * -- To disable assertions define LWIP_NOASSERT in arch/cc.h.
+ * @}
*/
+
+/**
+ * @defgroup lwip_assertions Assertion handling
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_NOASSERT: Disable LWIP_ASSERT checks:
+ * To disable assertions define LWIP_NOASSERT in arch/cc.h.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_NOASSERT
+#undef LWIP_NOASSERT
+#endif
+/**
+ * @}
+ */
+
#ifndef LWIP_NOASSERT
#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \
LWIP_PLATFORM_ASSERT(message); }} while(0)
@@ -81,7 +122,6 @@
#define LWIP_ASSERT(message, assertion)
#endif /* LWIP_NOASSERT */
-/** if "expression" isn't true, then print "message" and execute "handler" expression */
#ifndef LWIP_ERROR
#ifndef LWIP_NOASSERT
#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_ASSERT(message)
@@ -91,17 +131,23 @@
#define LWIP_PLATFORM_ERROR(message)
#endif
+/* if "expression" isn't true, then print "message" and execute "handler" expression */
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
LWIP_PLATFORM_ERROR(message); handler;}} while(0)
#endif /* LWIP_ERROR */
+/** Enable debug message printing, but only if debug message type is enabled
+ * AND is of correct type AND is at least LWIP_DBG_LEVEL.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_DEBUG
+#undef LWIP_DEBUG
+#endif
+
#ifdef LWIP_DEBUG
#ifndef LWIP_PLATFORM_DIAG
#error "If you want to use LWIP_DEBUG, LWIP_PLATFORM_DIAG(message) needs to be defined in your arch/cc.h"
#endif
-/** print debug message only if debug message type is enabled...
- * AND is of correct type AND is at least LWIP_DBG_LEVEL
- */
#define LWIP_DEBUGF(debug, message) do { \
if ( \
((debug) & LWIP_DBG_ON) && \
@@ -119,4 +165,3 @@
#endif /* LWIP_DEBUG */
#endif /* LWIP_HDR_DEBUG_H */
-
diff --git a/src/include/lwip/def.h b/src/include/lwip/def.h
index bb07009..aaa64c7 100644
--- a/src/include/lwip/def.h
+++ b/src/include/lwip/def.h
@@ -57,6 +57,12 @@
/* Get the number of entries in an array ('x' must NOT be a pointer!) */
#define LWIP_ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
+/** Create u32_t value from bytes */
+#define LWIP_MAKEU32(a,b,c,d) (((u32_t)((a) & 0xff) << 24) | \
+ ((u32_t)((b) & 0xff) << 16) | \
+ ((u32_t)((c) & 0xff) << 8) | \
+ (u32_t)((d) & 0xff))
+
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
@@ -65,13 +71,6 @@
#endif
#endif
-/* Endianess-optimized shifting of two u8_t to create one u16_t */
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define LWIP_MAKE_U16(a, b) ((a << 8) | b)
-#else
-#define LWIP_MAKE_U16(a, b) ((b << 8) | a)
-#endif
-
#if BYTE_ORDER == BIG_ENDIAN
#define lwip_htons(x) (x)
#define lwip_ntohs(x) (x)
@@ -103,11 +102,11 @@
/* These macros should be calculated by the preprocessor and are used
with compile-time constants only (so that there is no little-endian
overhead at runtime). */
-#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
+#define PP_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
#define PP_NTOHS(x) PP_HTONS(x)
-#define PP_HTONL(x) ((((x) & 0xff) << 24) | \
- (((x) & 0xff00) << 8) | \
- (((x) & 0xff0000UL) >> 8) | \
+#define PP_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \
+ (((x) & 0x0000ff00UL) << 8) | \
+ (((x) & 0x00ff0000UL) >> 8) | \
(((x) & 0xff000000UL) >> 24))
#define PP_NTOHL(x) PP_HTONL(x)
diff --git a/src/include/lwip/dhcp.h b/src/include/lwip/dhcp.h
index f750328..ac1b18e 100644
--- a/src/include/lwip/dhcp.h
+++ b/src/include/lwip/dhcp.h
@@ -132,6 +132,8 @@
extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, const ip4_addr_t* ntp_server_addrs);
#endif /* LWIP_DHCP_GET_NTP_SRV */
+#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP))
+
#ifdef __cplusplus
}
#endif
diff --git a/src/include/lwip/dns.h b/src/include/lwip/dns.h
index 00d5a78..2d147af 100644
--- a/src/include/lwip/dns.h
+++ b/src/include/lwip/dns.h
@@ -61,7 +61,7 @@
#ifndef LWIP_DNS_ADDRTYPE_DEFAULT
#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV4_IPV6
#endif
-#elif defined(LWIP_IPV4)
+#elif LWIP_IPV4
#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV4
#else
#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV6
@@ -84,6 +84,13 @@
#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
#endif /* DNS_LOCAL_HOSTLIST */
+#if LWIP_IPV4
+extern const ip_addr_t dns_mquery_v4group;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+extern const ip_addr_t dns_mquery_v6group;
+#endif /* LWIP_IPV6 */
+
/** Callback which is invoked when a hostname is found.
* A function of this type must be implemented by the application using the DNS resolver.
* @param name pointer to the name that was looked up.
diff --git a/src/include/lwip/errno.h b/src/include/lwip/errno.h
index 25bc1f8..641cffb 100644
--- a/src/include/lwip/errno.h
+++ b/src/include/lwip/errno.h
@@ -45,100 +45,100 @@
#ifdef LWIP_PROVIDE_ERRNO
-#define EPERM 1 /* Operation not permitted */
-#define ENOENT 2 /* No such file or directory */
-#define ESRCH 3 /* No such process */
-#define EINTR 4 /* Interrupted system call */
-#define EIO 5 /* I/O error */
-#define ENXIO 6 /* No such device or address */
-#define E2BIG 7 /* Arg list too long */
-#define ENOEXEC 8 /* Exec format error */
-#define EBADF 9 /* Bad file number */
-#define ECHILD 10 /* No child processes */
-#define EAGAIN 11 /* Try again */
-#define ENOMEM 12 /* Out of memory */
-#define EACCES 13 /* Permission denied */
-#define EFAULT 14 /* Bad address */
-#define ENOTBLK 15 /* Block device required */
-#define EBUSY 16 /* Device or resource busy */
-#define EEXIST 17 /* File exists */
-#define EXDEV 18 /* Cross-device link */
-#define ENODEV 19 /* No such device */
-#define ENOTDIR 20 /* Not a directory */
-#define EISDIR 21 /* Is a directory */
-#define EINVAL 22 /* Invalid argument */
-#define ENFILE 23 /* File table overflow */
-#define EMFILE 24 /* Too many open files */
-#define ENOTTY 25 /* Not a typewriter */
-#define ETXTBSY 26 /* Text file busy */
-#define EFBIG 27 /* File too large */
-#define ENOSPC 28 /* No space left on device */
-#define ESPIPE 29 /* Illegal seek */
-#define EROFS 30 /* Read-only file system */
-#define EMLINK 31 /* Too many links */
-#define EPIPE 32 /* Broken pipe */
-#define EDOM 33 /* Math argument out of domain of func */
-#define ERANGE 34 /* Math result not representable */
-#define EDEADLK 35 /* Resource deadlock would occur */
-#define ENAMETOOLONG 36 /* File name too long */
-#define ENOLCK 37 /* No record locks available */
-#define ENOSYS 38 /* Function not implemented */
-#define ENOTEMPTY 39 /* Directory not empty */
-#define ELOOP 40 /* Too many symbolic links encountered */
-#define EWOULDBLOCK EAGAIN /* Operation would block */
-#define ENOMSG 42 /* No message of desired type */
-#define EIDRM 43 /* Identifier removed */
-#define ECHRNG 44 /* Channel number out of range */
-#define EL2NSYNC 45 /* Level 2 not synchronized */
-#define EL3HLT 46 /* Level 3 halted */
-#define EL3RST 47 /* Level 3 reset */
-#define ELNRNG 48 /* Link number out of range */
-#define EUNATCH 49 /* Protocol driver not attached */
-#define ENOCSI 50 /* No CSI structure available */
-#define EL2HLT 51 /* Level 2 halted */
-#define EBADE 52 /* Invalid exchange */
-#define EBADR 53 /* Invalid request descriptor */
-#define EXFULL 54 /* Exchange full */
-#define ENOANO 55 /* No anode */
-#define EBADRQC 56 /* Invalid request code */
-#define EBADSLT 57 /* Invalid slot */
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+#define ENOSYS 38 /* Function not implemented */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
-#define EDEADLOCK EDEADLK
+#define EDEADLOCK EDEADLK
-#define EBFONT 59 /* Bad font file format */
-#define ENOSTR 60 /* Device not a stream */
-#define ENODATA 61 /* No data available */
-#define ETIME 62 /* Timer expired */
-#define ENOSR 63 /* Out of streams resources */
-#define ENONET 64 /* Machine is not on the network */
-#define ENOPKG 65 /* Package not installed */
-#define EREMOTE 66 /* Object is remote */
-#define ENOLINK 67 /* Link has been severed */
-#define EADV 68 /* Advertise error */
-#define ESRMNT 69 /* Srmount error */
-#define ECOMM 70 /* Communication error on send */
-#define EPROTO 71 /* Protocol error */
-#define EMULTIHOP 72 /* Multihop attempted */
-#define EDOTDOT 73 /* RFS specific error */
-#define EBADMSG 74 /* Not a data message */
-#define EOVERFLOW 75 /* Value too large for defined data type */
-#define ENOTUNIQ 76 /* Name not unique on network */
-#define EBADFD 77 /* File descriptor in bad state */
-#define EREMCHG 78 /* Remote address changed */
-#define ELIBACC 79 /* Can not access a needed shared library */
-#define ELIBBAD 80 /* Accessing a corrupted shared library */
-#define ELIBSCN 81 /* .lib section in a.out corrupted */
-#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
-#define ELIBEXEC 83 /* Cannot exec a shared library directly */
-#define EILSEQ 84 /* Illegal byte sequence */
-#define ERESTART 85 /* Interrupted system call should be restarted */
-#define ESTRPIPE 86 /* Streams pipe error */
-#define EUSERS 87 /* Too many users */
-#define ENOTSOCK 88 /* Socket operation on non-socket */
-#define EDESTADDRREQ 89 /* Destination address required */
-#define EMSGSIZE 90 /* Message too long */
-#define EPROTOTYPE 91 /* Protocol wrong type for socket */
-#define ENOPROTOOPT 92 /* Protocol not available */
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
diff --git a/src/include/lwip/icmp6.h b/src/include/lwip/icmp6.h
index e66557d..a29dc8c 100644
--- a/src/include/lwip/icmp6.h
+++ b/src/include/lwip/icmp6.h
@@ -45,99 +45,12 @@
#include "lwip/pbuf.h"
#include "lwip/ip6_addr.h"
#include "lwip/netif.h"
-
+#include "lwip/prot/icmp6.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** ICMP type */
-enum icmp6_type {
- /** Destination unreachable */
- ICMP6_TYPE_DUR = 1,
- /** Packet too big */
- ICMP6_TYPE_PTB = 2,
- /** Time exceeded */
- ICMP6_TYPE_TE = 3,
- /** Parameter problem */
- ICMP6_TYPE_PP = 4,
- /** Private experimentation */
- ICMP6_TYPE_PE1 = 100,
- /** Private experimentation */
- ICMP6_TYPE_PE2 = 101,
- /** Reserved for expansion of error messages */
- ICMP6_TYPE_RSV_ERR = 127,
-
- /** Echo request */
- ICMP6_TYPE_EREQ = 128,
- /** Echo reply */
- ICMP6_TYPE_EREP = 129,
- /** Multicast listener query */
- ICMP6_TYPE_MLQ = 130,
- /** Multicast listener report */
- ICMP6_TYPE_MLR = 131,
- /** Multicast listener done */
- ICMP6_TYPE_MLD = 132,
- /** Router solicitation */
- ICMP6_TYPE_RS = 133,
- /** Router advertisement */
- ICMP6_TYPE_RA = 134,
- /** Neighbor solicitation */
- ICMP6_TYPE_NS = 135,
- /** Neighbor advertisement */
- ICMP6_TYPE_NA = 136,
- /** Redirect */
- ICMP6_TYPE_RD = 137,
- /** Multicast router advertisement */
- ICMP6_TYPE_MRA = 151,
- /** Multicast router solicitation */
- ICMP6_TYPE_MRS = 152,
- /** Multicast router termination */
- ICMP6_TYPE_MRT = 153,
- /** Private experimentation */
- ICMP6_TYPE_PE3 = 200,
- /** Private experimentation */
- ICMP6_TYPE_PE4 = 201,
- /** Reserved for expansion of informational messages */
- ICMP6_TYPE_RSV_INF = 255
-};
-
-/** ICMP destination unreachable codes */
-enum icmp6_dur_code {
- /** No route to destination */
- ICMP6_DUR_NO_ROUTE = 0,
- /** Communication with destination administratively prohibited */
- ICMP6_DUR_PROHIBITED = 1,
- /** Beyond scope of source address */
- ICMP6_DUR_SCOPE = 2,
- /** Address unreachable */
- ICMP6_DUR_ADDRESS = 3,
- /** Port unreachable */
- ICMP6_DUR_PORT = 4,
- /** Source address failed ingress/egress policy */
- ICMP6_DUR_POLICY = 5,
- /** Reject route to destination */
- ICMP6_DUR_REJECT_ROUTE = 6
-};
-
-/** ICMP time exceeded codes */
-enum icmp6_te_code {
- /** Hop limit exceeded in transit */
- ICMP6_TE_HL = 0,
- /** Fragment reassembly time exceeded */
- ICMP6_TE_FRAG = 1
-};
-
-/** ICMP parameter code */
-enum icmp6_pp_code {
- /** Erroneous header field encountered */
- ICMP6_PP_FIELD = 0,
- /** Unrecognized next header type encountered */
- ICMP6_PP_HEADER = 1,
- /** Unrecognized IPv6 option encountered */
- ICMP6_PP_OPTION = 2
-};
-
#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
void icmp6_input(struct pbuf *p, struct netif *inp);
diff --git a/src/include/lwip/inet.h b/src/include/lwip/inet.h
index 17edef3..4a34f02 100644
--- a/src/include/lwip/inet.h
+++ b/src/include/lwip/inet.h
@@ -132,10 +132,10 @@
#if LWIP_IPV4
-#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
-#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
+#define inet_addr_from_ip4addr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
+#define inet_addr_to_ip4addr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
/* ATTENTION: the next define only works because both s_addr and ip4_addr_t are an u32_t effectively! */
-#define inet_addr_to_ipaddr_p(target_ip4addr_p, source_inaddr) ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr))
+#define inet_addr_to_ip4addr_p(target_ip4addr_p, source_inaddr) ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr))
/* directly map this to the lwip internal functions */
#define inet_addr(cp) ipaddr_addr(cp)
diff --git a/src/include/lwip/ip4_addr.h b/src/include/lwip/ip4_addr.h
index 166acfa..51b46b8 100644
--- a/src/include/lwip/ip4_addr.h
+++ b/src/include/lwip/ip4_addr.h
@@ -52,24 +52,9 @@
u32_t addr;
};
-/** This is the packed version of ip4_addr_t,
- used in network headers that are itself packed */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip4_addr_packed {
- PACK_STRUCT_FIELD(u32_t addr);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
/** ip4_addr_t uses a struct for convenience only, so that the same defines can
* operate both on ip4_addr_t as well as on ip4_addr_p_t. */
typedef struct ip4_addr ip4_addr_t;
-typedef struct ip4_addr_packed ip4_addr_p_t;
/**
* struct ipaddr2 is used in the definition of the ARP packet format in
@@ -131,23 +116,8 @@
#define IP_LOOPBACKNET 127 /* official! */
-
-#if BYTE_ORDER == BIG_ENDIAN
/** Set an IP address given by the four byte-parts */
-#define IP4_ADDR(ipaddr, a,b,c,d) \
- (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \
- ((u32_t)((b) & 0xff) << 16) | \
- ((u32_t)((c) & 0xff) << 8) | \
- (u32_t)((d) & 0xff)
-#else
-/** Set an IP address given by the four byte-parts.
- Little-endian version that prevents the use of lwip_htonl. */
-#define IP4_ADDR(ipaddr, a,b,c,d) \
- (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \
- ((u32_t)((c) & 0xff) << 16) | \
- ((u32_t)((b) & 0xff) << 8) | \
- (u32_t)((a) & 0xff)
-#endif
+#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->addr = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
/** MEMCPY-like copying of IP addresses where addresses are known to be
* 16-bit-aligned if the port is correctly configured (so a port could define
diff --git a/src/include/lwip/ip6_addr.h b/src/include/lwip/ip6_addr.h
index 14d0f7c..d04472c 100644
--- a/src/include/lwip/ip6_addr.h
+++ b/src/include/lwip/ip6_addr.h
@@ -43,6 +43,7 @@
#define LWIP_HDR_IP6_ADDR_H
#include "lwip/opt.h"
+#include "def.h"
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
@@ -58,41 +59,12 @@
u32_t addr[4];
};
-/** This is the packed version of ip6_addr_t,
- used in network headers that are itself packed */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip6_addr_packed {
- PACK_STRUCT_FIELD(u32_t addr[4]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
/** IPv6 address */
typedef struct ip6_addr ip6_addr_t;
-typedef struct ip6_addr_packed ip6_addr_p_t;
-
-#if BYTE_ORDER == BIG_ENDIAN
-/** Set an IPv6 partial address given by byte-parts. */
+/** Set an IPv6 partial address given by byte-parts */
#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \
- (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \
- ((u32_t)((b) & 0xff) << 16) | \
- ((u32_t)((c) & 0xff) << 8) | \
- (u32_t)((d) & 0xff)
-#else
-/** Set an IPv6 partial address given by byte-parts.
-Little-endian version, stored in network order (no lwip_htonl). */
-#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \
- (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \
- ((u32_t)((c) & 0xff) << 16) | \
- ((u32_t)((b) & 0xff) << 8) | \
- (u32_t)((a) & 0xff)
-#endif
+ (ip6addr)->addr[index] = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
/** Set a full IPv6 address by passing the 4 u32_t indices in network byte order
(use PP_HTONL() for constants) */
diff --git a/src/include/lwip/ip_addr.h b/src/include/lwip/ip_addr.h
index c1df746..def2d08 100644
--- a/src/include/lwip/ip_addr.h
+++ b/src/include/lwip/ip_addr.h
@@ -66,7 +66,7 @@
* A union struct for both IP version's addresses.
* ATTENTION: watch out for its size when adding IPv6 address scope!
*/
-typedef struct _ip_addr {
+typedef struct ip_addr {
union {
ip6_addr_t ip6;
ip4_addr_t ip4;
@@ -79,8 +79,12 @@
/** @ingroup ip4addr */
#define IPADDR4_INIT(u32val) { { { { u32val, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_V4 }
+/** @ingroup ip4addr */
+#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
/** @ingroup ip6addr */
#define IPADDR6_INIT(a, b, c, d) { { { { a, b, c, d } } }, IPADDR_TYPE_V6 }
+/** @ingroup ip6addr */
+#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } }, IPADDR_TYPE_V6 }
/** @ingroup ipaddr */
#define IP_IS_ANY_TYPE_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY)
@@ -118,6 +122,8 @@
/** @ingroup ip6addr */
#define IP_ADDR6(ipaddr,i0,i1,i2,i3) do { IP6_ADDR(ip_2_ip6(ipaddr),i0,i1,i2,i3); \
IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V6); } while(0)
+/** @ingroup ip6addr */
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
/** @ingroup ipaddr */
#define ip_addr_copy(dest, src) do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \
@@ -215,6 +221,19 @@
/** @ingroup ipaddr */
#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX
+/** @ingroup ipaddr */
+#define ip4_2_ipv6_mapped_ipv4(ip6addr, ip4addr) do { \
+ (ip6addr)->addr[3] = (ip4addr)->addr; \
+ (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[0] = 0; } while(0);
+
+/** @ingroup ipaddr */
+#define unmap_ipv6_mapped_ipv4(ip4addr, ip6addr) \
+ (ip4addr)->addr = (ip6addr)->addr[3];
+
+#define IP46_ADDR_ANY(type) (((type) == IPADDR_TYPE_V6)? IP6_ADDR_ANY : IP4_ADDR_ANY)
+
#else /* LWIP_IPV4 && LWIP_IPV6 */
#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1
@@ -224,6 +243,7 @@
typedef ip4_addr_t ip_addr_t;
#define IPADDR4_INIT(u32val) { u32val }
+#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
#define IP_IS_V4_VAL(ipaddr) 1
#define IP_IS_V6_VAL(ipaddr) 0
#define IP_IS_V4(ipaddr) 1
@@ -263,10 +283,13 @@
#define IPADDR_STRLEN_MAX IP4ADDR_STRLEN_MAX
+#define IP46_ADDR_ANY(type) (IP4_ADDR_ANY)
+
#else /* LWIP_IPV4 */
typedef ip6_addr_t ip_addr_t;
#define IPADDR6_INIT(a, b, c, d) { { a, b, c, d } }
+#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } }
#define IP_IS_V4_VAL(ipaddr) 0
#define IP_IS_V6_VAL(ipaddr) 1
#define IP_IS_V4(ipaddr) 0
@@ -277,6 +300,7 @@
#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V6
#define ip_2_ip6(ipaddr) (ipaddr)
#define IP_ADDR6(ipaddr,i0,i1,i2,i3) IP6_ADDR(ipaddr,i0,i1,i2,i3)
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
#define ip_addr_copy(dest, src) ip6_addr_copy(dest, src)
#define ip_addr_copy_from_ip6(dest, src) ip6_addr_copy(dest, src)
@@ -304,6 +328,8 @@
#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX
+#define IP46_ADDR_ANY(type) (IP6_ADDR_ANY)
+
#endif /* LWIP_IPV4 */
#endif /* LWIP_IPV4 && LWIP_IPV6 */
@@ -314,7 +340,13 @@
/**
* @ingroup ip4addr
- * Provided for compatibility. Use IP4_ADDR_ANY for better readability.
+ * Can be used as a fixed/const ip_addr_t
+ * for the IP wildcard.
+ * Defined to @ref IP4_ADDR_ANY when IPv4 is enabled.
+ * Defined to @ref IP6_ADDR_ANY in IPv6 only systems.
+ * Use this if you can handle IPv4 _AND_ IPv6 addresses.
+ * Use @ref IP4_ADDR_ANY or @ref IP6_ADDR_ANY when the IP
+ * type matters.
*/
#define IP_ADDR_ANY IP4_ADDR_ANY
/**
@@ -355,7 +387,7 @@
#define IP6_ADDR_ANY6 (ip_2_ip6(&ip6_addr_any))
#if !LWIP_IPV4
-/** Just a little upgrade-helper for IPv6-only configurations: */
+/** IPv6-only configurations */
#define IP_ADDR_ANY IP6_ADDR_ANY
#endif /* !LWIP_IPV4 */
diff --git a/src/include/lwip/mem.h b/src/include/lwip/mem.h
index e4f6a64..ff208d2 100644
--- a/src/include/lwip/mem.h
+++ b/src/include/lwip/mem.h
@@ -45,7 +45,8 @@
#if MEM_LIBC_MALLOC
-#include <stddef.h> /* for size_t */
+#include "lwip/arch.h"
+
typedef size_t mem_size_t;
#define MEM_SIZE_F SZT_F
diff --git a/src/include/lwip/nd6.h b/src/include/lwip/nd6.h
index e4715ad..8204fa4 100644
--- a/src/include/lwip/nd6.h
+++ b/src/include/lwip/nd6.h
@@ -48,107 +48,32 @@
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
-#include "lwip/pbuf.h"
-#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
-#include "lwip/netif.h"
-
+#include "lwip/err.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** Struct for tables. */
-struct nd6_neighbor_cache_entry {
- ip6_addr_t next_hop_address;
- struct netif *netif;
- u8_t lladdr[NETIF_MAX_HWADDR_LEN];
- /*u32_t pmtu;*/
-#if LWIP_ND6_QUEUEING
- /** Pointer to queue of pending outgoing packets on this entry. */
- struct nd6_q_entry *q;
-#else /* LWIP_ND6_QUEUEING */
- /** Pointer to a single pending outgoing packet on this entry. */
- struct pbuf *q;
-#endif /* LWIP_ND6_QUEUEING */
- u8_t state;
- u8_t isrouter;
- union {
- u32_t reachable_time; /* in ms since value may originate from network packet */
- u32_t delay_time; /* ticks (ND6_TMR_INTERVAL) */
- u32_t probes_sent;
- u32_t stale_time; /* ticks (ND6_TMR_INTERVAL) */
- } counter;
-};
-
-struct nd6_destination_cache_entry {
- ip6_addr_t destination_addr;
- ip6_addr_t next_hop_addr;
- u16_t pmtu;
- u32_t age;
-};
-
-struct nd6_prefix_list_entry {
- ip6_addr_t prefix;
- struct netif *netif;
- u32_t invalidation_timer; /* in ms since value may originate from network packet */
-#if LWIP_IPV6_AUTOCONFIG
- u8_t flags;
-#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01
-#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02
-#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04
-#endif /* LWIP_IPV6_AUTOCONFIG */
-};
-
-struct nd6_router_list_entry {
- struct nd6_neighbor_cache_entry *neighbor_entry;
- u32_t invalidation_timer; /* in ms since value may originate from network packet */
- u8_t flags;
-};
-
-enum nd6_neighbor_cache_entry_state {
- ND6_NO_ENTRY = 0,
- ND6_INCOMPLETE,
- ND6_REACHABLE,
- ND6_STALE,
- ND6_DELAY,
- ND6_PROBE
-};
-
-#if LWIP_ND6_QUEUEING
-/** struct for queueing outgoing packets for unknown address
- * defined here to be accessed by memp.h
- */
-struct nd6_q_entry {
- struct nd6_q_entry *next;
- struct pbuf *p;
-};
-#endif /* LWIP_ND6_QUEUEING */
-
/** 1 second period */
#define ND6_TMR_INTERVAL 1000
-/* Router tables. */
-/* @todo make these static? and entries accessible through API? */
-extern struct nd6_neighbor_cache_entry neighbor_cache[];
-extern struct nd6_destination_cache_entry destination_cache[];
-extern struct nd6_prefix_list_entry prefix_list[];
-extern struct nd6_router_list_entry default_router_list[];
-
-/* Default values, can be updated by a RA message. */
-extern u32_t reachable_time;
-extern u32_t retrans_timer;
+struct pbuf;
+struct netif;
void nd6_tmr(void);
void nd6_input(struct pbuf *p, struct netif *inp);
-s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
-s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
+void nd6_clear_destination_cache(void);
+struct netif *nd6_find_route(const ip6_addr_t *ip6addr);
+err_t nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp);
u16_t nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif);
-err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *p);
#if LWIP_ND6_TCP_REACHABILITY_HINTS
void nd6_reachability_hint(const ip6_addr_t *ip6addr);
#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
void nd6_cleanup_netif(struct netif *netif);
+#if LWIP_IPV6_MLD
+void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state);
+#endif /* LWIP_IPV6_MLD */
#ifdef __cplusplus
}
diff --git a/src/include/lwip/netdb.h b/src/include/lwip/netdb.h
index 21688c6..d3d15df 100644
--- a/src/include/lwip/netdb.h
+++ b/src/include/lwip/netdb.h
@@ -38,8 +38,7 @@
#if LWIP_DNS && LWIP_SOCKET
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
#include "lwip/inet.h"
#include "lwip/sockets.h"
diff --git a/src/include/lwip/netifapi.h b/src/include/lwip/netifapi.h
index 20f8bca..8bd2b4f 100644
--- a/src/include/lwip/netifapi.h
+++ b/src/include/lwip/netifapi.h
@@ -93,13 +93,17 @@
netifapi_errt_fn errtfunc);
/** @ingroup netifapi_netif */
-#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
+#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
/** @ingroup netifapi_netif */
-#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
+#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
/** @ingroup netifapi_netif */
-#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
+#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
/** @ingroup netifapi_netif */
-#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_link_up(n) netifapi_netif_common(n, netif_set_link_up, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_link_down(n) netifapi_netif_common(n, netif_set_link_down, NULL)
/**
* @defgroup netifapi_dhcp4 DHCPv4
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 6130501..5abac30 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -882,6 +882,15 @@
#if !defined LWIP_DHCP_MAX_NTP_SERVERS || defined __DOXYGEN__
#define LWIP_DHCP_MAX_NTP_SERVERS 1
#endif
+
+/**
+ * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select.
+ * DHCP servers received in the response are passed to DNS via @ref dns_setserver()
+ * (up to the maximum limit defined here).
+ */
+#if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP_MAX_DNS_SERVERS DNS_MAX_SERVERS
+#endif
/**
* @}
*/
@@ -1057,6 +1066,12 @@
#if !defined DNS_LOCAL_HOSTLIST_IS_DYNAMIC || defined __DOXYGEN__
#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0
#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Set this to 1 to enable querying ".local" names via mDNS
+ * using a One-Shot Multicast DNS Query */
+#if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__
+#define LWIP_DNS_SUPPORT_MDNS_QUERIES 0
+#endif
/**
* @}
*/
@@ -1348,7 +1363,7 @@
* for an additional encapsulation header before ethernet headers (e.g. 802.11)
*/
#if !defined PBUF_LINK_ENCAPSULATION_HLEN || defined __DOXYGEN__
-#define PBUF_LINK_ENCAPSULATION_HLEN 0
+#define PBUF_LINK_ENCAPSULATION_HLEN 0u
#endif
/**
@@ -2164,7 +2179,7 @@
/**
* LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented
*/
-#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__ || defined __DOXYGEN__
+#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__
#define LWIP_IPV6_REASS (LWIP_IPV6)
#endif
@@ -2230,13 +2245,18 @@
*/
/**
* LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol.
+ * If LWIP_IPV6 is enabled but this setting is disabled, the MAC layer must
+ * indiscriminately pass all inbound IPv6 multicast traffic to lwIP.
*/
#if !defined LWIP_IPV6_MLD || defined __DOXYGEN__
#define LWIP_IPV6_MLD (LWIP_IPV6)
#endif
/**
- * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined.
+ * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast groups that can be joined.
+ * There must be enough groups so that each netif can join the solicited-node
+ * multicast group for each of its local addresses, plus one for MDNS if
+ * applicable, plus any number of groups to be joined on UDP sockets.
*/
#if !defined MEMP_NUM_MLD6_GROUP || defined __DOXYGEN__
#define MEMP_NUM_MLD6_GROUP 4
@@ -2342,7 +2362,7 @@
* LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation
* message is sent, during neighbor reachability detection.
*/
-#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__s
+#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__
#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000
#endif
@@ -2359,9 +2379,18 @@
* with reachability hints for connected destinations. This helps avoid sending
* unicast neighbor solicitation messages.
*/
-#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__ || defined __DOXYGEN__
+#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__
#define LWIP_ND6_TCP_REACHABILITY_HINTS 1
#endif
+
+/**
+ * LWIP_ND6_RDNSS_MAX_DNS_SERVERS > 0: Use IPv6 Router Advertisement Recursive
+ * DNS Server Option (as per RFC 6106) to copy a defined maximum number of DNS
+ * servers to the DNS module.
+ */
+#if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0
+#endif
/**
* @}
*/
@@ -2387,6 +2416,29 @@
*/
/**
+ * LWIP_HOOK_TCP_ISN:
+ * Hook for generation of the Initial Sequence Number (ISN) for a new TCP
+ * connection. The default lwIP ISN generation algorithm is very basic and may
+ * allow for TCP spoofing attacks. This hook provides the means to implement
+ * the standardized ISN generation algorithm from RFC 6528 (see contrib/adons/tcp_isn),
+ * or any other desired algorithm as a replacement.
+ * Called from tcp_connect() and tcp_listen_input() when an ISN is needed for
+ * a new TCP connection, if TCP support (@ref LWIP_TCP) is enabled.\n
+ * Signature: u32_t my_hook_tcp_isn(const ip_addr_t* local_ip, u16_t local_port, const ip_addr_t* remote_ip, u16_t remote_port);
+ * - it may be necessary to use "struct ip_addr" (ip4_addr, ip6_addr) instead of "ip_addr_t" in function declarations\n
+ * Arguments:
+ * - local_ip: pointer to the local IP address of the connection
+ * - local_port: local port number of the connection (host-byte order)
+ * - remote_ip: pointer to the remote IP address of the connection
+ * - remote_port: remote port number of the connection (host-byte order)\n
+ * Return value:
+ * - the 32-bit Initial Sequence Number to use for the new TCP connection.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_ISN(local_ip, local_port, remote_ip, remote_port)
+#endif
+
+/**
* LWIP_HOOK_IP4_INPUT(pbuf, input_netif):
* - called from ip_input() (IPv4)
* - pbuf: received struct pbuf passed to ip_input()
@@ -2427,7 +2479,7 @@
* - dest: the destination IPv4 address
* Returns the IPv4 address of the gateway to handle the specified destination
* IPv4 address. If NULL is returned, the netif's default gateway is used.
- * The returned address MUST be reachable on the specified netif!
+ * The returned address MUST be directly reachable on the specified netif!
* This function is meant to implement advanced IPv4 routing together with
* LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is
* not part of lwIP but can e.g. be hidden in the netif's state argument.
@@ -2464,6 +2516,22 @@
#endif
/**
+ * LWIP_HOOK_ND6_GET_GW(netif, dest):
+ * - called from nd6_get_next_hop_entry() (IPv6)
+ * - netif: the netif used for sending
+ * - dest: the destination IPv6 address
+ * Returns the IPv6 address of the next hop to handle the specified destination
+ * IPv6 address. If NULL is returned, a NDP-discovered router is used instead.
+ * The returned address MUST be directly reachable on the specified netif!
+ * This function is meant to implement advanced IPv6 routing together with
+ * LWIP_HOOK_IP6_ROUTE(). The actual routing/gateway table implementation is
+ * not part of lwIP but can e.g. be hidden in the netif's state argument.
+*/
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_ND6_GET_GW(netif, dest)
+#endif
+
+/**
* LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr):
* - called from ethernet_input() if VLAN support is enabled
* - netif: struct netif on which the packet has been received
@@ -2526,7 +2594,7 @@
---------------------------------------
*/
/**
- * @defgroup lwip_opts_debugmsg Debugging
+ * @defgroup lwip_opts_debugmsg Debug messages
* @ingroup lwip_opts_debug
* @{
*/
@@ -2534,14 +2602,16 @@
* LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is
* compared against this value. If it is smaller, then debugging
* messages are written.
+ * @see debugging_levels
*/
-#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__ || defined __DOXYGEN__
+#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
#endif
/**
* LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
* debug messages of certain types.
+ * @see debugging_levels
*/
#if !defined LWIP_DBG_TYPES_ON || defined __DOXYGEN__
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
diff --git a/src/include/lwip/priv/api_msg.h b/src/include/lwip/priv/api_msg.h
index ad38345..f12b8b7 100644
--- a/src/include/lwip/priv/api_msg.h
+++ b/src/include/lwip/priv/api_msg.h
@@ -43,8 +43,7 @@
/* Note: Netconn API is always available when sockets are enabled -
* sockets are implemented on top of them */
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#include "lwip/sys.h"
diff --git a/src/include/lwip/priv/nd6_priv.h b/src/include/lwip/priv/nd6_priv.h
new file mode 100644
index 0000000..4bda0b7
--- /dev/null
+++ b/src/include/lwip/priv/nd6_priv.h
@@ -0,0 +1,144 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * 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: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ND6_PRIV_H
+#define LWIP_HDR_ND6_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ND6_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+ * defined here to be accessed by memp.h
+ */
+struct nd6_q_entry {
+ struct nd6_q_entry *next;
+ struct pbuf *p;
+};
+#endif /* LWIP_ND6_QUEUEING */
+
+/** Struct for tables. */
+struct nd6_neighbor_cache_entry {
+ ip6_addr_t next_hop_address;
+ struct netif *netif;
+ u8_t lladdr[NETIF_MAX_HWADDR_LEN];
+ /*u32_t pmtu;*/
+#if LWIP_ND6_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this entry. */
+ struct nd6_q_entry *q;
+#else /* LWIP_ND6_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this entry. */
+ struct pbuf *q;
+#endif /* LWIP_ND6_QUEUEING */
+ u8_t state;
+ u8_t isrouter;
+ union {
+ u32_t reachable_time; /* in ms since value may originate from network packet */
+ u32_t delay_time; /* ticks (ND6_TMR_INTERVAL) */
+ u32_t probes_sent;
+ u32_t stale_time; /* ticks (ND6_TMR_INTERVAL) */
+ } counter;
+};
+
+struct nd6_destination_cache_entry {
+ ip6_addr_t destination_addr;
+ ip6_addr_t next_hop_addr;
+ u16_t pmtu;
+ u32_t age;
+};
+
+struct nd6_prefix_list_entry {
+ ip6_addr_t prefix;
+ struct netif *netif;
+ u32_t invalidation_timer; /* in ms since value may originate from network packet */
+#if LWIP_IPV6_AUTOCONFIG
+ u8_t flags;
+#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01
+#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02
+#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04
+#endif /* LWIP_IPV6_AUTOCONFIG */
+};
+
+struct nd6_router_list_entry {
+ struct nd6_neighbor_cache_entry *neighbor_entry;
+ u32_t invalidation_timer; /* in ms since value may originate from network packet */
+ u8_t flags;
+};
+
+enum nd6_neighbor_cache_entry_state {
+ ND6_NO_ENTRY = 0,
+ ND6_INCOMPLETE,
+ ND6_REACHABLE,
+ ND6_STALE,
+ ND6_DELAY,
+ ND6_PROBE
+};
+
+/* Router tables. */
+/* @todo make these static? and entries accessible through API? */
+extern struct nd6_neighbor_cache_entry neighbor_cache[];
+extern struct nd6_destination_cache_entry destination_cache[];
+extern struct nd6_prefix_list_entry prefix_list[];
+extern struct nd6_router_list_entry default_router_list[];
+
+/* Default values, can be updated by a RA message. */
+extern u32_t reachable_time;
+extern u32_t retrans_timer;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_ND6_PRIV_H */
diff --git a/src/include/lwip/priv/tcp_priv.h b/src/include/lwip/priv/tcp_priv.h
index 51747e0..f68a856 100644
--- a/src/include/lwip/priv/tcp_priv.h
+++ b/src/include/lwip/priv/tcp_priv.h
@@ -34,8 +34,8 @@
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef LWIP_HDR_TCP_IMPL_H
-#define LWIP_HDR_TCP_IMPL_H
+#ifndef LWIP_HDR_TCP_PRIV_H
+#define LWIP_HDR_TCP_PRIV_H
#include "lwip/opt.h"
@@ -452,7 +452,7 @@
const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
u16_t local_port, u16_t remote_port);
-u32_t tcp_next_iss(void);
+u32_t tcp_next_iss(struct tcp_pcb *pcb);
err_t tcp_keepalive(struct tcp_pcb *pcb);
err_t tcp_zero_window_probe(struct tcp_pcb *pcb);
@@ -501,4 +501,4 @@
#endif /* LWIP_TCP */
-#endif /* LWIP_HDR_TCP_H */
+#endif /* LWIP_HDR_TCP_PRIV_H */
diff --git a/src/include/lwip/prot/dns.h b/src/include/lwip/prot/dns.h
index 0a99ab0..94782d6 100644
--- a/src/include/lwip/prot/dns.h
+++ b/src/include/lwip/prot/dns.h
@@ -115,6 +115,24 @@
#endif
#define SIZEOF_DNS_HDR 12
+
+/* Multicast DNS definitions */
+
+/** UDP port for multicast DNS queries */
+#ifndef DNS_MQUERY_PORT
+#define DNS_MQUERY_PORT 5353
+#endif
+
+/* IPv4 group for multicast DNS queries: 224.0.0.251 */
+#ifndef DNS_MQUERY_IPV4_GROUP_INIT
+#define DNS_MQUERY_IPV4_GROUP_INIT IPADDR4_INIT_BYTES(224,0,0,251)
+#endif
+
+/* IPv6 group for multicast DNS queries: FF02::FB */
+#ifndef DNS_MQUERY_IPV6_GROUP_INIT
+#define DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB)
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/include/lwip/prot/icmp6.h b/src/include/lwip/prot/icmp6.h
index a0a713b..3461120 100644
--- a/src/include/lwip/prot/icmp6.h
+++ b/src/include/lwip/prot/icmp6.h
@@ -43,6 +43,93 @@
extern "C" {
#endif
+/** ICMP type */
+enum icmp6_type {
+ /** Destination unreachable */
+ ICMP6_TYPE_DUR = 1,
+ /** Packet too big */
+ ICMP6_TYPE_PTB = 2,
+ /** Time exceeded */
+ ICMP6_TYPE_TE = 3,
+ /** Parameter problem */
+ ICMP6_TYPE_PP = 4,
+ /** Private experimentation */
+ ICMP6_TYPE_PE1 = 100,
+ /** Private experimentation */
+ ICMP6_TYPE_PE2 = 101,
+ /** Reserved for expansion of error messages */
+ ICMP6_TYPE_RSV_ERR = 127,
+
+ /** Echo request */
+ ICMP6_TYPE_EREQ = 128,
+ /** Echo reply */
+ ICMP6_TYPE_EREP = 129,
+ /** Multicast listener query */
+ ICMP6_TYPE_MLQ = 130,
+ /** Multicast listener report */
+ ICMP6_TYPE_MLR = 131,
+ /** Multicast listener done */
+ ICMP6_TYPE_MLD = 132,
+ /** Router solicitation */
+ ICMP6_TYPE_RS = 133,
+ /** Router advertisement */
+ ICMP6_TYPE_RA = 134,
+ /** Neighbor solicitation */
+ ICMP6_TYPE_NS = 135,
+ /** Neighbor advertisement */
+ ICMP6_TYPE_NA = 136,
+ /** Redirect */
+ ICMP6_TYPE_RD = 137,
+ /** Multicast router advertisement */
+ ICMP6_TYPE_MRA = 151,
+ /** Multicast router solicitation */
+ ICMP6_TYPE_MRS = 152,
+ /** Multicast router termination */
+ ICMP6_TYPE_MRT = 153,
+ /** Private experimentation */
+ ICMP6_TYPE_PE3 = 200,
+ /** Private experimentation */
+ ICMP6_TYPE_PE4 = 201,
+ /** Reserved for expansion of informational messages */
+ ICMP6_TYPE_RSV_INF = 255
+};
+
+/** ICMP destination unreachable codes */
+enum icmp6_dur_code {
+ /** No route to destination */
+ ICMP6_DUR_NO_ROUTE = 0,
+ /** Communication with destination administratively prohibited */
+ ICMP6_DUR_PROHIBITED = 1,
+ /** Beyond scope of source address */
+ ICMP6_DUR_SCOPE = 2,
+ /** Address unreachable */
+ ICMP6_DUR_ADDRESS = 3,
+ /** Port unreachable */
+ ICMP6_DUR_PORT = 4,
+ /** Source address failed ingress/egress policy */
+ ICMP6_DUR_POLICY = 5,
+ /** Reject route to destination */
+ ICMP6_DUR_REJECT_ROUTE = 6
+};
+
+/** ICMP time exceeded codes */
+enum icmp6_te_code {
+ /** Hop limit exceeded in transit */
+ ICMP6_TE_HL = 0,
+ /** Fragment reassembly time exceeded */
+ ICMP6_TE_FRAG = 1
+};
+
+/** ICMP parameter code */
+enum icmp6_pp_code {
+ /** Erroneous header field encountered */
+ ICMP6_PP_FIELD = 0,
+ /** Unrecognized next header type encountered */
+ ICMP6_PP_HEADER = 1,
+ /** Unrecognized IPv6 option encountered */
+ ICMP6_PP_OPTION = 2
+};
+
/** This is the standard ICMP6 header. */
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
diff --git a/src/include/lwip/prot/ip4.h b/src/include/lwip/prot/ip4.h
index c3c3403..bd442c6 100644
--- a/src/include/lwip/prot/ip4.h
+++ b/src/include/lwip/prot/ip4.h
@@ -44,6 +44,22 @@
extern "C" {
#endif
+/** This is the packed version of ip4_addr_t,
+ used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip4_addr_packed {
+ PACK_STRUCT_FIELD(u32_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+typedef struct ip4_addr_packed ip4_addr_p_t;
+
/* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */
#define IP_HLEN 20
diff --git a/src/include/lwip/prot/ip6.h b/src/include/lwip/prot/ip6.h
index 4e3ca37..6e1e263 100644
--- a/src/include/lwip/prot/ip6.h
+++ b/src/include/lwip/prot/ip6.h
@@ -43,6 +43,21 @@
#ifdef __cplusplus
extern "C" {
#endif
+
+/** This is the packed version of ip6_addr_t,
+ used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_addr_packed {
+ PACK_STRUCT_FIELD(u32_t addr[4]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+typedef struct ip6_addr_packed ip6_addr_p_t;
#define IP6_HLEN 40
diff --git a/src/include/lwip/prot/mld6.h b/src/include/lwip/prot/mld6.h
index 2664829..be3a006 100644
--- a/src/include/lwip/prot/mld6.h
+++ b/src/include/lwip/prot/mld6.h
@@ -38,7 +38,7 @@
#define LWIP_HDR_PROT_MLD6_H
#include "lwip/arch.h"
-#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/include/lwip/prot/nd6.h b/src/include/lwip/prot/nd6.h
index eae3d28..2d4903d 100644
--- a/src/include/lwip/prot/nd6.h
+++ b/src/include/lwip/prot/nd6.h
@@ -39,6 +39,7 @@
#include "lwip/arch.h"
#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
#ifdef __cplusplus
extern "C" {
@@ -246,6 +247,29 @@
# include "arch/epstruct.h"
#endif
+/** Recursive DNS Server Option. */
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+#define LWIP_RDNSS_OPTION_MAX_SERVERS LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+#else
+#define LWIP_RDNSS_OPTION_MAX_SERVERS 1
+#endif
+#define ND6_OPTION_TYPE_RDNSS (25)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rdnss_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FIELD(u16_t reserved);
+ PACK_STRUCT_FIELD(u32_t lifetime);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t rdnss_address[LWIP_RDNSS_OPTION_MAX_SERVERS]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/include/lwip/prot/tcp.h b/src/include/lwip/prot/tcp.h
index c2c03aa..67fe7b9 100644
--- a/src/include/lwip/prot/tcp.h
+++ b/src/include/lwip/prot/tcp.h
@@ -85,7 +85,7 @@
#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | TCPH_FLAGS(phdr))
#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS(~TCP_FLAGS)) | lwip_htons(flags))
-#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | (flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = (u16_t)(lwip_htons((u16_t)((len) << 12) | (flags)))
#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | lwip_htons(flags))
#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~lwip_htons(flags))
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 9d76776..2522056 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -43,8 +43,6 @@
#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
-#include <stddef.h> /* for size_t */
-
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#include "lwip/inet.h"
diff --git a/src/include/lwip/sys.h b/src/include/lwip/sys.h
index bb06a40..d12bae0 100644
--- a/src/include/lwip/sys.h
+++ b/src/include/lwip/sys.h
@@ -34,44 +34,6 @@
* Author: Adam Dunkels <adam@sics.se>
*/
-/**
- * @defgroup sys_layer System abstraction layer
- * @ingroup infrastructure
- * @verbinclude "sys_arch.txt"
- *
- * @defgroup sys_os OS abstraction layer
- * @ingroup sys_layer
- * No need to implement functions in this section in NO_SYS mode.
- *
- * @defgroup sys_sem Semaphores
- * @ingroup sys_os
- *
- * @defgroup sys_mutex Mutexes
- * @ingroup sys_os
- * Mutexes are recommended to correctly handle priority inversion,
- * especially if you use LWIP_CORE_LOCKING .
- *
- * @defgroup sys_mbox Mailboxes
- * @ingroup sys_os
- *
- * @defgroup sys_time Time
- * @ingroup sys_layer
- *
- * @defgroup sys_prot Critical sections
- * @ingroup sys_layer
- * Used to protect short regions of code against concurrent access.
- * - Your system is a bare-metal system (probably with an RTOS)
- * and interrupts are under your control:
- * Implement this as LockInterrupts() / UnlockInterrupts()
- * - Your system uses an RTOS with deferred interrupt handling from a
- * worker thread: Implement as a global mutex or lock/unlock scheduler
- * - Your system uses a high-level OS with e.g. POSIX signals:
- * Implement as a global mutex
- *
- * @defgroup sys_misc Misc
- * @ingroup sys_os
- */
-
#ifndef LWIP_HDR_SYS_H
#define LWIP_HDR_SYS_H
diff --git a/src/include/lwip/tcp.h b/src/include/lwip/tcp.h
index 35584b4..3fd4467 100644
--- a/src/include/lwip/tcp.h
+++ b/src/include/lwip/tcp.h
@@ -387,6 +387,7 @@
err_t tcp_connect (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
u16_t port, tcp_connected_fn connected);
+struct tcp_pcb * tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err);
struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
/** @ingroup tcp_raw */
#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
diff --git a/src/netif/lowpan6.c b/src/netif/lowpan6.c
index eca9a45..9a84cbc 100644
--- a/src/netif/lowpan6.c
+++ b/src/netif/lowpan6.c
@@ -616,7 +616,8 @@
err_t
lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
{
- s8_t i;
+ err_t result;
+ const u8_t *hwaddr;
struct ieee_802154_addr src, dest;
#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
ip6_addr_t ip6_src;
@@ -663,35 +664,23 @@
}
#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
-
- /* Get next hop record. */
- i = nd6_get_next_hop_entry(ip6addr, netif);
- if (i < 0) {
+ /* Ask ND6 what to do with the packet. */
+ result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+ if (result != ERR_OK) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
- /* failed to get a next hop neighbor record. */
- return ERR_MEM;
+ return result;
}
- /* Now that we have a destination record, send or queue the packet. */
- if (neighbor_cache[i].state == ND6_STALE) {
- /* Switch to delay state. */
- neighbor_cache[i].state = ND6_DELAY;
- neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
- }
- /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
- if ((neighbor_cache[i].state == ND6_REACHABLE) ||
- (neighbor_cache[i].state == ND6_DELAY) ||
- (neighbor_cache[i].state == ND6_PROBE)) {
-
- /* Send out. */
- dest.addr_len = netif->hwaddr_len;
- SMEMCPY(dest.addr, neighbor_cache[i].lladdr, netif->hwaddr_len);
- MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
- return lowpan6_frag(netif, q, &src, &dest);
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
}
- /* We should queue packet on this interface. */
- return nd6_queue_packet(i, q);
+ /* Send out the packet using the returned hardware address. */
+ dest.addr_len = netif->hwaddr_len;
+ SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len);
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ return lowpan6_frag(netif, q, &src, &dest);
}
static struct pbuf *
diff --git a/src/netif/ppp/ppp.c b/src/netif/ppp/ppp.c
index 0e37eeb..d9b5077 100644
--- a/src/netif/ppp/ppp.c
+++ b/src/netif/ppp/ppp.c
@@ -1145,12 +1145,12 @@
nsa = dns_getserver(0);
ip_addr_set_ip4_u32(&nsb, ns1);
if (ip_addr_cmp(nsa, &nsb)) {
- dns_setserver(0, IP4_ADDR_ANY);
+ dns_setserver(0, IP_ADDR_ANY);
}
nsa = dns_getserver(1);
ip_addr_set_ip4_u32(&nsb, ns2);
if (ip_addr_cmp(nsa, &nsb)) {
- dns_setserver(1, IP4_ADDR_ANY);
+ dns_setserver(1, IP_ADDR_ANY);
}
return 1;
}
diff --git a/src/netif/ppp/pppapi.c b/src/netif/ppp/pppapi.c
index e2bedc7..947f7ba 100644
--- a/src/netif/ppp/pppapi.c
+++ b/src/netif/ppp/pppapi.c
@@ -222,6 +222,7 @@
msg->msg.msg.l2tpcreate.secret_len,
#else /* PPPOL2TP_AUTH_SUPPORT */
NULL,
+ 0,
#endif /* PPPOL2TP_AUTH_SUPPORT */
msg->msg.msg.l2tpcreate.link_status_cb, msg->msg.msg.l2tpcreate.ctx_cb);
return ERR_OK;
@@ -239,6 +240,10 @@
ppp_pcb* result;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+#if !PPPOL2TP_AUTH_SUPPORT
+ LWIP_UNUSED_ARG(secret);
+ LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
PPPAPI_VAR_REF(msg).msg.ppp = NULL;
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.pppif = pppif;
diff --git a/src/netif/ppp/pppol2tp.c b/src/netif/ppp/pppol2tp.c
index c215cff..90cf108 100644
--- a/src/netif/ppp/pppol2tp.c
+++ b/src/netif/ppp/pppol2tp.c
@@ -113,6 +113,10 @@
ppp_pcb *ppp;
pppol2tp_pcb *l2tp;
struct udp_pcb *udp;
+#if !PPPOL2TP_AUTH_SUPPORT
+ LWIP_UNUSED_ARG(secret);
+ LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
if (ipaddr == NULL) {
goto ipaddr_check_failed;
@@ -303,7 +307,7 @@
udp_bind(l2tp->udp, IP6_ADDR_ANY, 0);
} else
#endif /* LWIP_IPV6 */
- udp_bind(l2tp->udp, IP4_ADDR_ANY, 0);
+ udp_bind(l2tp->udp, IP_ADDR_ANY, 0);
#if PPPOL2TP_AUTH_SUPPORT
/* Generate random vector */
diff --git a/src/netif/ppp/pppos.c b/src/netif/ppp/pppos.c
index 5220ed3..5416429 100644
--- a/src/netif/ppp/pppos.c
+++ b/src/netif/ppp/pppos.c
@@ -35,8 +35,8 @@
#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include <string.h>
-#include <stddef.h>
+#include "lwip/arch.h"
#include "lwip/err.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
@@ -471,18 +471,20 @@
u8_t escaped;
PPPOS_DECL_PROTECT(lev);
- PPPOS_PROTECT(lev);
- if (!pppos->open) {
- PPPOS_UNPROTECT(lev);
- return;
- }
- PPPOS_UNPROTECT(lev);
-
PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", ppp->netif->num, l));
while (l-- > 0) {
cur_char = *s++;
PPPOS_PROTECT(lev);
+ /* ppp_input can disconnect the interface, we need to abort to prevent a memory
+ * leak if there are remaining bytes because pppos_connect and pppos_listen
+ * functions expect input buffer to be free. Furthermore there are no real
+ * reason to continue reading bytes if we are disconnected.
+ */
+ if (!pppos->open) {
+ PPPOS_UNPROTECT(lev);
+ return;
+ }
escaped = ESCAPE_P(pppos->in_accm, cur_char);
PPPOS_UNPROTECT(lev);
/* Handle special characters. */
diff --git a/src/netif/ppp/utils.c b/src/netif/ppp/utils.c
index a5ae86f..008c633 100644
--- a/src/netif/ppp/utils.c
+++ b/src/netif/ppp/utils.c
@@ -247,11 +247,13 @@
val = va_arg(args, unsigned int);
base = 16;
break;
+#if 0 /* unused (and wrong on LLP64 systems) */
case 'p':
val = (unsigned long) va_arg(args, void *);
base = 16;
neg = 2;
break;
+#endif /* unused (and wrong on LLP64 systems) */
case 's':
str = va_arg(args, char *);
break;
diff --git a/test/fuzz/Makefile b/test/fuzz/Makefile
new file mode 100644
index 0000000..67ffb19
--- /dev/null
+++ b/test/fuzz/Makefile
@@ -0,0 +1,53 @@
+#
+# Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+# 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: Adam Dunkels <adam@sics.se>
+#
+
+all compile: lwip_fuzz
+.PHONY: all clean
+
+CC=afl-gcc
+LDFLAGS=-lm
+CFLAGS=-O0
+
+CONTRIBDIR=../../../lwip-contrib
+include $(CONTRIBDIR)/ports/unix/Common.mk
+
+clean:
+ rm -f *.o $(LWIPLIBCOMMON) lwip_fuzz *.s .depend* *.core core
+
+depend dep: .depend
+
+include .depend
+
+.depend: fuzz.c $(LWIPFILES) $(APPFILES)
+ $(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend
+
+lwip_fuzz: .depend $(LWIPLIBCOMMON) fuzz.o
+ $(CC) $(CFLAGS) -o lwip_fuzz fuzz.o $(LWIPLIBCOMMON) $(LDFLAGS)
diff --git a/test/fuzz/README b/test/fuzz/README
new file mode 100644
index 0000000..1d1e3d8
--- /dev/null
+++ b/test/fuzz/README
@@ -0,0 +1,34 @@
+
+Fuzzing the lwIP stack (afl-fuzz requires linux/unix or similar)
+
+This directory contains a small app that reads Ethernet frames from stdin and
+processes them. It is used together with the 'american fuzzy lop' tool (found
+at http://lcamtuf.coredump.cx/afl/) and the sample inputs to test how
+unexpected inputs are handled. The afl tool will read the known inputs, and
+try to modify them to exercise as many code paths as possible, by instrumenting
+the code and keeping track of which code is executed.
+
+Just running make will produce the test program.
+
+Then run afl with:
+
+afl-fuzz -i inputs/<INPUT> -o output ./lwip_fuzz
+
+and it should start working. It will probably complain about CPU scheduler,
+set AFL_SKIP_CPUFREQ=1 to ignore it.
+If it complains about invalid "/proc/sys/kernel/core_pattern" setting, try
+executing "sudo bash -c 'echo core > /proc/sys/kernel/core_pattern'".
+
+The input is split into different subdirectories since they test different
+parts of the code, and since you want to run one instance of afl-fuzz on each
+core.
+
+When afl finds a crash or a hang, the input that caused it will be placed in
+the output directory. If you have hexdump and text2pcap tools installed,
+running output_to_pcap.sh <outputdir> will create pcap files for each input
+file to simplify viewing in wireshark.
+
+The lwipopts.h file needs to have checksum checking off, otherwise almost every
+packet will be discarded because of that. The other options can be tuned to
+expose different parts of the code.
+
diff --git a/test/fuzz/config.h b/test/fuzz/config.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/fuzz/config.h
diff --git a/test/fuzz/fuzz.c b/test/fuzz/fuzz.c
new file mode 100644
index 0000000..a9157f1
--- /dev/null
+++ b/test/fuzz/fuzz.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * 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: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#include "lwip/init.h"
+#include "lwip/netif.h"
+#include "netif/etharp.h"
+#if LWIP_IPV6
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#endif
+#include <string.h>
+#include <stdio.h>
+
+/* no-op send function */
+static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(p);
+ return ERR_OK;
+}
+
+static err_t testif_init(struct netif *netif)
+{
+ netif->name[0] = 'f';
+ netif->name[1] = 'z';
+ netif->output = etharp_output;
+ netif->linkoutput = lwip_tx_func;
+ netif->mtu = 1500;
+ netif->hwaddr_len = 6;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
+
+ netif->hwaddr[0] = 0x00;
+ netif->hwaddr[1] = 0x23;
+ netif->hwaddr[2] = 0xC1;
+ netif->hwaddr[3] = 0xDE;
+ netif->hwaddr[4] = 0xD0;
+ netif->hwaddr[5] = 0x0D;
+
+#if LWIP_IPV6
+ netif->output_ip6 = ethip6_output;
+ netif->ip6_autoconfig_enabled = 1;
+ netif_create_ip6_linklocal_address(netif, 1);
+ netif->flags |= NETIF_FLAG_MLD6;
+#endif
+
+ return ERR_OK;
+}
+
+static void input_pkt(struct netif *netif, const u8_t *data, size_t len)
+{
+ struct pbuf *p, *q;
+ err_t err;
+
+ LWIP_ASSERT("pkt too big", len <= 0xFFFF);
+ p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
+ LWIP_ASSERT("alloc failed", p);
+ for(q = p; q != NULL; q = q->next) {
+ MEMCPY(q->payload, data, q->len);
+ data += q->len;
+ }
+ err = netif->input(p, netif);
+ if (err != ERR_OK) {
+ pbuf_free(p);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ struct netif net_test;
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
+ u8_t pktbuf[2000];
+ size_t len;
+
+ lwip_init();
+
+ IP4_ADDR(&addr, 172, 30, 115, 84);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ IP4_ADDR(&gw, 172, 30, 115, 1);
+
+ netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
+
+#if LWIP_IPV6
+ nd6_tmr(); /* tick nd to join multicast groups */
+#endif
+
+ if(argc > 1) {
+ FILE* f;
+ const char* filename;
+ printf("reading input from file... ");
+ fflush(stdout);
+ filename = argv[1];
+ LWIP_ASSERT("invalid filename", filename != NULL);
+ f = fopen(filename, "rb");
+ LWIP_ASSERT("open failed", f != NULL);
+ len = fread(pktbuf, 1, sizeof(pktbuf), f);
+ fclose(f);
+ printf("testing file: \"%s\"...\r\n", filename);
+ } else {
+ len = fread(pktbuf, 1, sizeof(pktbuf), stdin);
+ }
+ input_pkt(&net_test, pktbuf, len);
+
+ return 0;
+}
diff --git a/test/fuzz/inputs/arp/arp_req.bin b/test/fuzz/inputs/arp/arp_req.bin
new file mode 100644
index 0000000..b317334
--- /dev/null
+++ b/test/fuzz/inputs/arp/arp_req.bin
Binary files differ
diff --git a/test/fuzz/inputs/icmp/icmp_ping.bin b/test/fuzz/inputs/icmp/icmp_ping.bin
new file mode 100644
index 0000000..87e1ea7
--- /dev/null
+++ b/test/fuzz/inputs/icmp/icmp_ping.bin
Binary files differ
diff --git a/test/fuzz/inputs/ipv6/neighbor_solicitation.bin b/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
new file mode 100644
index 0000000..d2f921c
--- /dev/null
+++ b/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
Binary files differ
diff --git a/test/fuzz/inputs/ipv6/router_adv.bin b/test/fuzz/inputs/ipv6/router_adv.bin
new file mode 100644
index 0000000..3aa9615
--- /dev/null
+++ b/test/fuzz/inputs/ipv6/router_adv.bin
Binary files differ
diff --git a/test/fuzz/inputs/tcp/tcp_syn.bin b/test/fuzz/inputs/tcp/tcp_syn.bin
new file mode 100644
index 0000000..d77f6d2
--- /dev/null
+++ b/test/fuzz/inputs/tcp/tcp_syn.bin
Binary files differ
diff --git a/test/fuzz/inputs/udp/udp_port_5000.bin b/test/fuzz/inputs/udp/udp_port_5000.bin
new file mode 100644
index 0000000..d77e267
--- /dev/null
+++ b/test/fuzz/inputs/udp/udp_port_5000.bin
Binary files differ
diff --git a/test/fuzz/lwipopts.h b/test/fuzz/lwipopts.h
new file mode 100644
index 0000000..4aff09b
--- /dev/null
+++ b/test/fuzz/lwipopts.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * 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
+ *
+ */
+#ifndef LWIP_HDR_LWIPOPTS_H__
+#define LWIP_HDR_LWIPOPTS_H__
+
+/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
+#define NO_SYS 1
+#define LWIP_NETCONN 0
+#define LWIP_SOCKET 0
+#define SYS_LIGHTWEIGHT_PROT 0
+
+#define LWIP_IPV6 1
+#define IPV6_FRAG_COPYHEADER 1
+#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 0
+
+/* Enable DHCP to test it */
+#define LWIP_DHCP 1
+
+/* Turn off checksum verification of fuzzed data */
+#define CHECKSUM_CHECK_IP 0
+#define CHECKSUM_CHECK_UDP 0
+#define CHECKSUM_CHECK_TCP 0
+#define CHECKSUM_CHECK_ICMP 0
+#define CHECKSUM_CHECK_ICMP6 0
+
+/* Minimal changes to opt.h required for tcp unit tests: */
+#define MEM_SIZE 16000
+#define TCP_SND_QUEUELEN 40
+#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
+#define TCP_SND_BUF (12 * TCP_MSS)
+#define TCP_WND (10 * TCP_MSS)
+#define LWIP_WND_SCALE 1
+#define TCP_RCV_SCALE 0
+#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
+
+/* Minimal changes to opt.h required for etharp unit tests: */
+#define ETHARP_SUPPORT_STATIC_ENTRIES 1
+
+#endif /* LWIP_HDR_LWIPOPTS_H__ */
diff --git a/test/fuzz/output_to_pcap.sh b/test/fuzz/output_to_pcap.sh
new file mode 100644
index 0000000..c999ff0
--- /dev/null
+++ b/test/fuzz/output_to_pcap.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+if [ -z "$1" ]
+then
+ echo "This script will make pcap files from the afl-fuzz crash/hang files"
+ echo "It needs hexdump and text2pcap"
+ echo "Please give output directory as argument"
+ exit 2
+fi
+
+for i in `ls $1/crashes/id*`
+do
+ PCAPNAME=`echo $i | grep pcap`
+ if [ -z "$PCAPNAME" ]; then
+ hexdump -C $i > $1/$$.tmp
+ text2pcap $1/$$.tmp ${i}.pcap
+ fi
+done
+for i in `ls $1/hangs/id*`
+do
+ PCAPNAME=`echo $i | grep pcap`
+ if [ -z "$PCAPNAME" ]; then
+ hexdump -C $i > $1/$$.tmp
+ text2pcap $1/$$.tmp ${i}.pcap
+ fi
+done
+rm -f $1/$$.tmp
+
+echo
+echo "Created pcap files:"
+ls $1/*/*.pcap
diff --git a/test/unit/dhcp/test_dhcp.c b/test/unit/dhcp/test_dhcp.c
index 68f2881..3ff42cc 100644
--- a/test/unit/dhcp/test_dhcp.c
+++ b/test/unit/dhcp/test_dhcp.c
@@ -120,7 +120,8 @@
TEST_LWIP_DHCP,
TEST_LWIP_DHCP_NAK,
TEST_LWIP_DHCP_RELAY,
- TEST_LWIP_DHCP_NAK_NO_ENDMARKER
+ TEST_LWIP_DHCP_NAK_NO_ENDMARKER,
+ TEST_LWIP_DHCP_INVALID_OVERLOAD
} tcase;
static int debug = 0;
@@ -904,6 +905,111 @@
}
END_TEST
+START_TEST(test_dhcp_invalid_overload)
+{
+ u8_t dhcp_offer_invalid_overload[] = {
+ 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */
+ 0x00, 0x0F, 0xEE, 0x30, 0xAB, 0x22, /* From Remote host */
+ 0x08, 0x00, /* Protocol: IP */
+ 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */
+ 0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */
+
+ 0x02, /* Type == Boot reply */
+ 0x01, 0x06, /* Hw Ethernet, 6 bytes addrlen */
+ 0x00, /* 0 hops */
+ 0xAA, 0xAA, 0xAA, 0xAA, /* Transaction id, will be overwritten */
+ 0x00, 0x00, /* 0 seconds elapsed */
+ 0x00, 0x00, /* Flags (unicast) */
+ 0x00, 0x00, 0x00, 0x00, /* Client ip */
+ 0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */
+ 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server ip */
+ 0x00, 0x00, 0x00, 0x00, /* relay agent */
+ 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MAC addr + padding */
+
+ /* Empty server name */
+ 0x34, 0x01, 0x02, 0xff, /* Overload: SNAME + END */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Empty boot file name */
+ 0x34, 0x01, 0x01, 0xff, /* Overload FILE + END */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x63, 0x82, 0x53, 0x63, /* Magic cookie */
+ 0x35, 0x01, 0x02, /* Message type: Offer */
+ 0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Server identifier (IP) */
+ 0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */
+ 0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */
+ 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Subnet mask */
+ 0x34, 0x01, 0x03, /* Overload: FILE + SNAME */
+ 0xff, /* End option */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Padding */
+ };
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
+ u32_t xid;
+ LWIP_UNUSED_ARG(_i);
+
+ tcase = TEST_LWIP_DHCP_INVALID_OVERLOAD;
+ setdebug(0);
+
+ IP4_ADDR(&addr, 0, 0, 0, 0);
+ IP4_ADDR(&netmask, 0, 0, 0, 0);
+ IP4_ADDR(&gw, 0, 0, 0, 0);
+
+ netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
+
+ dhcp_start(&net_test);
+
+ fail_unless(txpacket == 1); /* DHCP discover sent */
+ xid = htonl(netif_dhcp_data(&net_test)->xid);
+ memcpy(&dhcp_offer_invalid_overload[46], &xid, 4); /* insert correct transaction id */
+ dhcp_offer_invalid_overload[311] = 3;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+ /* IP addresses should be zero */
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ fail_unless(txpacket == 1); /* Nothing more sent */
+
+ dhcp_offer_invalid_overload[311] = 2;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+ /* IP addresses should be zero */
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ fail_unless(txpacket == 1); /* Nothing more sent */
+
+ dhcp_offer_invalid_overload[311] = 1;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+ /* IP addresses should be zero */
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ fail_unless(txpacket == 1); /* Nothing more sent */
+
+ dhcp_offer_invalid_overload[311] = 0;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer));
+
+ fail_unless(netif_dhcp_data(&net_test)->state == DHCP_STATE_REQUESTING);
+
+ fail_unless(txpacket == 2); /* No more sent */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
+
+ netif_remove(&net_test);
+}
+END_TEST
/** Create the suite including all tests for this module */
Suite *
@@ -913,7 +1019,8 @@
TESTFUNC(test_dhcp),
TESTFUNC(test_dhcp_nak),
TESTFUNC(test_dhcp_relayed),
- TESTFUNC(test_dhcp_nak_no_endmarker)
+ TESTFUNC(test_dhcp_nak_no_endmarker),
+ TESTFUNC(test_dhcp_invalid_overload)
};
return create_suite("DHCP", tests, sizeof(tests)/sizeof(testfunc), dhcp_setup, dhcp_teardown);
}
diff --git a/test/unit/mdns/test_mdns.c b/test/unit/mdns/test_mdns.c
index 223520c..ca9be64 100644
--- a/test/unit/mdns/test_mdns.c
+++ b/test/unit/mdns/test_mdns.c
@@ -42,6 +42,7 @@
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -60,6 +61,7 @@
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -78,6 +80,7 @@
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -89,18 +92,20 @@
END_TEST
START_TEST(readname_long_label)
-{ static const u8_t data[] = {
+{
+ static const u8_t data[] = {
0x05, 'm', 'u', 'l', 't', 'i',
0x52, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
- 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00,
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -151,11 +156,12 @@
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
- 0x00,
+ 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -172,14 +178,15 @@
/* Some padding needed, not supported to jump to bytes containing dns header */
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 10 */ 0x0f, 0x0e, 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
- /* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c,
+ /* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c
};
static const u8_t fullname[] = {
- 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -200,14 +207,15 @@
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
/* 0x10 */ 0x04, 'c', 'a', 's', 't', 0x00, 0xc0, 0x10,
- /* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16,
+ /* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16
};
static const u8_t fullname[] = {
- 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00,
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -231,16 +239,17 @@
/* 0x18 */ 0x03, 'd', 'n', 's', 0xc0, 0x10, 0xc0, 0x10,
/* 0x20 */ 0x04, 'd', 'e', 'e', 'p', 0xc0, 0x18, 0x00,
/* 0x28 */ 0x04, 'c', 'a', 's', 't', 0xc0, 0x20, 0xb0,
- /* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28,
+ /* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28
};
static const u8_t fullname[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x04, 'd', 'e', 'e', 'p', 0x03, 'd', 'n', 's',
- 0x04, 'n', 'a', 'm', 'e', 0x00,
+ 0x04, 'n', 'a', 'm', 'e', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -258,14 +267,15 @@
{
static const u8_t data[] = {
/* 0x00 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10, 0x00, 0x01, 0x40,
- /* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
+ /* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab
};
static const u8_t fullname[] = {
- 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -282,11 +292,12 @@
START_TEST(readname_half_jump)
{
static const u8_t data[] = {
- 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0,
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -300,11 +311,12 @@
START_TEST(readname_jump_toolong)
{
static const u8_t data[] = {
- 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40,
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -319,11 +331,12 @@
{
static const u8_t data[] = {
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10,
+ /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -338,11 +351,12 @@
{
static const u8_t data[] = {
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15,
+ /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -358,6 +372,7 @@
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
struct mdns_domain domain;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "multi", 5);
@@ -376,6 +391,7 @@
static const char *toolong = "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-";
struct mdns_domain domain;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "multi", 5);
@@ -390,6 +406,7 @@
static const char *label = "0123456789abcdef0123456789abcdef";
struct mdns_domain domain;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
@@ -437,10 +454,11 @@
START_TEST(domain_eq_basic)
{
static const u8_t data[] = {
- 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00,
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
};
struct mdns_domain domain1, domain2;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, "multi", 5);
@@ -467,6 +485,7 @@
{
struct mdns_domain domain1, domain2;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, "multi", 5);
@@ -492,6 +511,7 @@
{
struct mdns_domain domain1, domain2;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, "multi", 5);
@@ -519,6 +539,7 @@
static const u8_t data2[] = { 0x7f, 0x8c, 0x01, 0xff, 0xcf };
struct mdns_domain domain1, domain2;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, (const char*)data1, sizeof(data1));
@@ -548,6 +569,7 @@
{
struct mdns_domain domain1, domain2;
err_t res;
+ LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
memset(domain1.name, 0xAA, sizeof(MDNS_DOMAIN_MAXLEN));
@@ -578,6 +600,7 @@
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -612,6 +635,7 @@
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -641,13 +665,14 @@
/* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
/* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
- /* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15,
+ /* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -682,6 +707,7 @@
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -715,6 +741,7 @@
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -749,6 +776,7 @@
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -778,13 +806,14 @@
/* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
/* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
- /* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15,
+ /* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
@@ -824,6 +853,7 @@
u16_t offset;
u16_t length;
err_t res;
+ LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
p->payload = (void *)(size_t)data;
diff --git a/test/unit/tcp/tcp_helper.c b/test/unit/tcp/tcp_helper.c
index fc7e67f..aa7c84a 100644
--- a/test/unit/tcp/tcp_helper.c
+++ b/test/unit/tcp/tcp_helper.c
@@ -141,9 +141,18 @@
tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
{
+ u32_t iss;
+
/* @todo: are these all states? */
/* @todo: remove from previous list */
pcb->state = state;
+
+ iss = tcp_next_iss(pcb);
+ pcb->snd_wl2 = iss;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss;
+ pcb->snd_lbb = iss;
+
if (state == ESTABLISHED) {
TCP_REG(&tcp_active_pcbs, pcb);
pcb->local_ip.addr = local_ip->addr;
diff --git a/test/unit/tcp/test_tcp.c b/test/unit/tcp/test_tcp.c
index bfcfa1d..1e5ef03 100644
--- a/test/unit/tcp/test_tcp.c
+++ b/test/unit/tcp/test_tcp.c
@@ -35,8 +35,8 @@
{
/* reset iss to default (6510) */
tcp_ticks = 0;
- tcp_ticks = 0 - (tcp_next_iss() - 6510);
- tcp_next_iss();
+ tcp_ticks = 0 - (tcp_next_iss(NULL) - 6510);
+ tcp_next_iss(NULL);
tcp_ticks = 0;
test_tcp_timer = 0;
@@ -418,7 +418,6 @@
tcp_ticks = SEQNO1 - ISS;
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- EXPECT(pcb->lastack == SEQNO1);
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
pcb->mss = TCP_MSS;
/* disable initial congestion window (we don't send a SYN here...) */
@@ -513,11 +512,10 @@
/* create and initialize the pcb */
tcp_ticks = 0;
- tcp_ticks = 0 - tcp_next_iss();
- tcp_ticks = SEQNO1 - tcp_next_iss();
+ tcp_ticks = 0 - tcp_next_iss(NULL);
+ tcp_ticks = SEQNO1 - tcp_next_iss(NULL);
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- EXPECT(pcb->lastack == SEQNO1);
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
pcb->mss = TCP_MSS;
/* disable initial congestion window (we don't send a SYN here...) */