diff --git a/.gitignore b/.gitignore index 0092ea6f..cb0ed534 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 8f030fde..051f7ae4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,74 @@ HISTORY (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 c42c67d1..d9b9d4ab 100644 --- a/UPGRADING +++ b/UPGRADING @@ -8,7 +8,12 @@ with newer versions. * [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 99afb124..89344b0e 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 125d3239..177d4c4e 100644 --- a/doc/doxygen/lwip.Doxyfile +++ b/doc/doxygen/lwip.Doxyfile @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # 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 31cc0df6..4f43d60a 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 00000000..3e67defd --- /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 333946da..4dc727b6 100644 --- a/doc/sys_arch.txt +++ b/doc/sys_arch.txt @@ -29,7 +29,7 @@ in a mailbox is just a pointer, nothing more. 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 ac79422c..7d30bb8f 100644 --- a/src/Filelists.mk +++ b/src/Filelists.mk @@ -167,6 +167,9 @@ NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c # 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 @@ LWIPAPPFILES=$(SNMPFILES) \ $(SNTPFILES) \ $(MDNSFILES) \ $(NETBIOSNSFILES) \ - $(TFTPFILES) + $(TFTPFILES) \ + $(MQTTFILES) diff --git a/src/api/api_lib.c b/src/api/api_lib.c index b4fc403d..3c1d6a6c 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -254,10 +254,22 @@ netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port) 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 @@ netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port) 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 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn) #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 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn) *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 @@ netconn_recv_data(struct netconn *conn, void **new_buf) { 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 @@ netconn_recv_data(struct netconn *conn, void **new_buf) #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 @@ netconn_recv_data(struct netconn *conn, void **new_buf) #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 @@ netconn_send(struct netconn *conn, struct netbuf *buf) 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 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, 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 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, 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 @@ netconn_join_leave_group(struct netconn *conn, 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 @@ netconn_join_leave_group(struct netconn *conn, 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 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr) 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 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr) } #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 86546c29..4927ee50 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 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) 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 @@ pcb_new(struct api_msg *msg) #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 @@ pcb_new(struct api_msg *msg) #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 @@ pcb_new(struct api_msg *msg) 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 @@ lwip_netconn_do_bind(void *m) } 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 @@ lwip_netconn_do_listen(void *m) /* 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 @@ lwip_netconn_do_listen(void *m) } #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 @@ lwip_netconn_do_send(void *m) 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 @@ err_mem: 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_mem: 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 @@ lwip_netconn_do_getaddr(void *m) 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 1593e70a..35e9c025 100644 --- a/src/api/err.c +++ b/src/api/err.c @@ -64,6 +64,15 @@ static const int err_to_errno_table[] = { 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 @@ lwip_strerr(err_t err) } #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 ef0c79b1..ccd9586f 100644 --- a/src/api/netdb.c +++ b/src/api/netdb.c @@ -46,8 +46,8 @@ #include "lwip/api.h" #include "lwip/dns.h" -#include -#include +#include /* memset */ +#include /* atoi */ /** helper struct for gethostbyname_r to access the char* buffer */ struct gethostbyname_r_helper { @@ -382,7 +382,7 @@ lwip_getaddrinfo(const char *nodename, const char *servname, #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 9968a9ae..24b3b5fa 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 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) 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 @@ lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) 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 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) 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 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, 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 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, 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 @@ lwip_sendmsg(int s, const struct msghdr *msg, int flags) #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 @@ lwip_sendto(int s, const void *data, size_t size, int flags, #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 @@ lwip_sendto(int s, const void *data, size_t size, int flags, 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 @@ lwip_socket(int domain, int type, int protocol) 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 @@ lwip_writev(int s, const struct iovec *iov, int iovcnt) 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 @@ lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) } /* 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 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt 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 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt 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 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt 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 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ { 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 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ 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 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ 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 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ 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); @@ -2770,7 +2813,7 @@ lwip_socket_drop_registered_memberships(int s) ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr); ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr); - netconn_join_leave_group(sockets[s].conn, &multi_addr, &if_addr, NETCONN_LEAVE); + netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE); } } } diff --git a/src/apps/httpd/httpd.c b/src/apps/httpd/httpd.c index 103f9acb..aa2e9b7e 100644 --- a/src/apps/httpd/httpd.c +++ b/src/apps/httpd/httpd.c @@ -98,8 +98,8 @@ #include "lwip/ip.h" #include "lwip/tcp.h" -#include -#include +#include /* memset */ +#include /* atoi */ #include #if LWIP_TCP @@ -2563,6 +2563,7 @@ httpd_init(void) 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 1996cd1a..54bf2bca 100644 --- a/src/apps/lwiperf/lwiperf.c +++ b/src/apps/lwiperf/lwiperf.c @@ -294,7 +294,7 @@ lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn) } 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 @@ lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) 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 @@ lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) 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 e0c528c3..14334fc8 100644 --- a/src/apps/mdns/mdns.c +++ b/src/apps/mdns/mdns.c @@ -52,10 +52,7 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Erik Ekman - * - * Please coordinate changes and requests with Erik Ekman - * + * Author: Erik Ekman * */ @@ -68,7 +65,6 @@ #include "lwip/prot/dns.h" #include -#include #if LWIP_MDNS_RESPONDER @@ -85,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 @@ -573,7 +569,7 @@ mdns_build_dnssd_domain(struct mdns_domain *domain) 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); } @@ -598,7 +594,7 @@ mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *servi } 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); } @@ -1824,6 +1820,7 @@ mdns_resp_init(void) 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); @@ -2022,7 +2019,7 @@ mdns_resp_add_service(struct netif *netif, const char *name, const char *service 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 00000000..5f4a9eff --- /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 + * 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 + * + * + * @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 + * + */ +#include +#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 913d97f9..4f05180a 100644 --- a/src/apps/snmp/snmp_mib2_ip.c +++ b/src/apps/snmp/snmp_mib2_ip.c @@ -581,7 +581,7 @@ ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct 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 @@ ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct 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 070b41c3..24c3e265 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 #include "lwip/api.h" #include "lwip/ip.h" #include "lwip/udp.h" @@ -52,7 +53,7 @@ snmp_netconn_thread(void *arg) 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 858c40e6..204f265d 100644 --- a/src/apps/snmp/snmp_threadsync.c +++ b/src/apps/snmp/snmp_threadsync.c @@ -211,6 +211,7 @@ void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_thread 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..71b2abed 100644 --- a/src/apps/sntp/sntp.c +++ b/src/apps/sntp/sntp.c @@ -573,6 +573,7 @@ sntp_stop(void) { 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 @@ sntp_getserver(u8_t idx) 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 99f3d62e..8125313f 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 @@ char* 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 2aa1dd99..056672b7 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 @@ static u16_t dns_txid; #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 @@ static u16_t dns_txid; #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 @@ static u16_t dns_txid; #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 @@ static u16_t dns_txid; #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 @@ struct dns_table_entry { #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_table_entry dns_table[DNS_TABLE_SIZE]; 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 @@ dns_setserver(u8_t numdns, const ip_addr_t *dnsserver) if (dnsserver != NULL) { dns_servers[numdns] = (*dnsserver); } else { - dns_servers[numdns] = *IP4_ADDR_ANY; + dns_servers[numdns] = *IP_ADDR_ANY; } } } @@ -348,7 +385,7 @@ dns_getserver(u8_t numdns) if (numdns < DNS_MAX_SERVERS) { return &dns_servers[numdns]; } else { - return IP4_ADDR_ANY; + return IP_ADDR_ANY; } } @@ -665,7 +702,11 @@ dns_send(u8_t idx) 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 @@ dns_send(u8_t idx) 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 @@ dns_send(u8_t idx) /* 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 @@ dns_check_entry(u8_t i) 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 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, 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 @@ memerr: */ 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 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, } /* check if this is the oldest completed entry */ if (entry->state == DNS_STATE_DONE) { - 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 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, 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 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call 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 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call 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 80289528..917f3e4f 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 #include #ifndef LWIP_CHKSUM diff --git a/src/core/init.c b/src/core/init.c index 0eb1e44c..ee415a97 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -141,7 +141,7 @@ PACK_STRUCT_END #if (LWIP_TCP && (TCP_WND > 0xffffffff)) #error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h" #endif -#if (LWIP_TCP && LWIP_WND_SCALE && (TCP_RCV_SCALE > 14)) +#if (LWIP_TCP && (TCP_RCV_SCALE > 14)) #error "The maximum valid window scale value is 14!" #endif #if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE))) @@ -339,6 +339,11 @@ PACK_STRUCT_END 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 86a4aed3..10db8a34 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 #include /** 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 @@ autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) * 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 de5fc73b..dd354710 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 @@ u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; 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 @@ static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); /* 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 @@ dhcp_handle_ack(struct netif *netif) { 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_handle_ack(struct netif *netif) 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 @@ again: 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 @@ again: 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 @@ again: #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 bf4dad53..b599bcc2 100644 --- a/src/core/ipv4/etharp.c +++ b/src/core/ipv4/etharp.c @@ -840,7 +840,7 @@ etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) 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 e60e4481..e030ecc9 100644 --- a/src/core/ipv4/icmp.c +++ b/src/core/ipv4/icmp.c @@ -81,7 +81,7 @@ icmp_input(struct pbuf *p, struct netif *inp) #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 @@ icmp_input(struct pbuf *p, struct netif *inp) } #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 @@ icmp_input(struct pbuf *p, struct netif *inp) /* 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 @@ icmp_input(struct pbuf *p, struct netif *inp) /* 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 @@ icmp_input(struct pbuf *p, struct netif *inp) 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 5ae7d9c8..561448e0 100644 --- a/src/core/ipv4/igmp.c +++ b/src/core/ipv4/igmp.c @@ -244,6 +244,7 @@ struct igmp_group * 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 @@ igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr) /* Group already exists. */ return group; } - + /* Group doesn't exist yet, create a new one */ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); if (group != NULL) { @@ -260,9 +261,21 @@ igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr) 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,24 +295,19 @@ static err_t 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; - } - } - /* Group not found in the global igmp_group_list */ - if (tmpGroup == NULL) { - err = ERR_ARG; + /* 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 (tmp_group == NULL) { + err = ERR_ARG; + } return err; } diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c index c7836774..2e2ce4bd 100644 --- a/src/core/ipv4/ip4.c +++ b/src/core/ipv4/ip4.c @@ -177,7 +177,7 @@ ip4_route(const ip4_addr_t *dest) /* 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 @@ ip4_input(struct pbuf *p, struct netif *inp) #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 @@ ip4_input(struct pbuf *p, struct netif *inp) } else #endif /* IP_FORWARD */ { + IP_STATS_INC(ip.drop); MIB2_STATS_INC(mib2.ipinaddrerrors); MIB2_STATS_INC(mib2.ipindiscards); } @@ -853,7 +863,7 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d 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 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d 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 eb812afb..2d479923 100644 --- a/src/core/ipv4/ip4_addr.c +++ b/src/core/ipv4/ip4_addr.c @@ -183,10 +183,10 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr) } 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 @@ ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen) 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 b6f90945..57fb44cb 100644 --- a/src/core/ipv4/ip4_frag.c +++ b/src/core/ipv4/ip4_frag.c @@ -687,6 +687,8 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) 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 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) 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 509fc1c5..8f9a91b5 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 @@ ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) 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 8cc9c010..a542a869 100644 --- a/src/core/ipv6/ip6.c +++ b/src/core/ipv6/ip6.c @@ -94,7 +94,8 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) 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 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) } /* 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 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) } /* 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 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) /* 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 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) { 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 @@ ip6_input(struct pbuf *p, struct netif *inp) } } } - 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 @@ netif_found: 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 964291ee..aa06659a 100644 --- a/src/core/ipv6/ip6_addr.c +++ b/src/core/ipv6/ip6_addr.c @@ -132,8 +132,8 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr) } 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 b374f691..ff07f71c 100644 --- a/src/core/ipv6/ip6_frag.c +++ b/src/core/ipv6/ip6_frag.c @@ -379,6 +379,7 @@ ip6_reass(struct pbuf *p) /* 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 @@ ip6_reass(struct pbuf *p) 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 @@ ip6_reass(struct pbuf *p) 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 @@ nullreturn: #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(struct pbuf *p) } 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 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest) 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 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest) 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 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest) /* 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 @@ ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest) } } 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 82394f6e..7d7a9570 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 @@ -93,10 +95,13 @@ static void nd6_free_neighbor_cache_entry(s8_t i); 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 @@ nd6_input(struct pbuf *p, struct netif *inp) * 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 @@ nd6_input(struct pbuf *p, struct netif *inp) /* 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 @@ nd6_input(struct pbuf *p, struct netif *inp) } #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 @@ nd6_input(struct pbuf *p, struct netif *inp) 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 @@ nd6_input(struct pbuf *p, struct netif *inp) 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 @@ nd6_input(struct pbuf *p, struct netif *inp) { 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 @@ nd6_input(struct pbuf *p, struct netif *inp) } /* 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 @@ nd6_input(struct pbuf *p, struct netif *inp) 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 @@ nd6_input(struct pbuf *p, struct netif *inp) struct icmp6_hdr *icmp6hdr; /* Packet too big message */ struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */ u32_t pmtu; + ip6_addr_t tmp; /* Check that ICMPv6 header + IPv6 header fit in payload */ if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { @@ -636,10 +672,10 @@ nd6_input(struct pbuf *p, struct netif *inp) 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 @@ nd6_tmr(void) 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 */ @@ -1293,6 +1322,22 @@ nd6_new_destination_cache_entry(void) return j; } +/** + * 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. * @@ -1328,7 +1373,7 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif) * @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; @@ -1380,6 +1425,30 @@ nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif) return -1; } +/** + * 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. * @@ -1416,6 +1485,7 @@ static s8_t 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 @@ nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif) 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 @@ nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif) * 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 @@ nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) /* 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 @@ nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) * @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 @@ nd6_send_q(s8_t i) #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 @@ nd6_cleanup_netif(struct netif *netif) } } +#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 40f4cb9e..db3b7cc5 100644 --- a/src/core/mem.c +++ b/src/core/mem.c @@ -61,9 +61,13 @@ #include "lwip/err.h" #include -#include + +#if MEM_LIBC_MALLOC +#include /* 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 @@ void * 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 2e3db28e..31624492 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 @@ memp_overflow_check_all(void) 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 @@ do_memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int 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 fc9e50b0..428b1484 100644 --- a/src/core/netif.c +++ b/src/core/netif.c @@ -180,7 +180,7 @@ netif_init(void) #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 @@ netif_remove(struct netif *netif) 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 @@ netif_find(const char *name) 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 @@ netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* ad 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 @@ netif_poll(struct netif *netif) #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 @@ netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, 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 @@ netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state) 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 @@ netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state) 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 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) /* 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 @@ netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chos /* 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 887dc8aa..af63e60d 100644 --- a/src/core/pbuf.c +++ b/src/core/pbuf.c @@ -568,11 +568,11 @@ pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force) } 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 @@ pbuf_skip_const(const struct pbuf* in, u16_t in_offset, u16_t* out_offset) 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 @@ pbuf_coalesce(struct pbuf *p, pbuf_layer layer) 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 f692c9c2..93c65473 100644 --- a/src/core/raw.c +++ b/src/core/raw.c @@ -209,7 +209,7 @@ raw_input(struct pbuf *p, struct netif *inp) 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 @@ raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr) 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 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) } } - 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 f1777372..7059b4de 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 1d700980..75be86bb 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -551,7 +551,7 @@ tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) #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 @@ tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) * * @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 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) 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_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) 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 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, 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 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, #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 @@ struct tcp_pcb * 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 @@ tcp_alloc(u8_t prio) 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 @@ tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) * @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 93268b34..fbbdc729 100644 --- a/src/core/tcp_in.c +++ b/src/core/tcp_in.c @@ -358,6 +358,11 @@ tcp_input(struct pbuf *p, struct netif *inp) ((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 @@ static void 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 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) 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 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) /* 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 @@ tcp_process(struct tcp_pcb *pcb) 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 3c2bc81a..8ccdffb9 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -377,6 +377,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) 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 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) /* * 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 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) #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 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) &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 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) #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 @@ tcp_output(struct tcp_pcb *pcb) 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 @@ tcp_output(struct tcp_pcb *pcb) } 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 e2dc0fc7..227d71fc 100644 --- a/src/core/timeouts.c +++ b/src/core/timeouts.c @@ -179,7 +179,7 @@ void sys_timeouts_init(void) 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 f9aee732..a9164149 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -116,24 +116,6 @@ again: } } 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 @@ udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast) } } 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 @@ udp_input(struct pbuf *p, struct netif *inp) 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 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, #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) { @@ -662,7 +642,7 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_i * this could be an old address if netif->ip_addr has changed */ if (!ip4_addr_cmp(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) { /* local_ip doesn't match, drop the packet */ - return ERR_VAL; + return ERR_RTE; } /* use UDP PCB local IP address as source address */ src_ip = &pcb->local_ip; @@ -912,7 +892,7 @@ udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) #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 e8620dd8..516bd163 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 /* for size_t */ - +#include "lwip/arch.h" #include "lwip/netbuf.h" #include "lwip/sys.h" #include "lwip/ip_addr.h" @@ -90,6 +89,7 @@ extern "C" { #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/mdns.h b/src/include/lwip/apps/mdns.h index f57b182f..d0368161 100644 --- a/src/include/lwip/apps/mdns.h +++ b/src/include/lwip/apps/mdns.h @@ -31,7 +31,7 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Erik Ekman + * Author: Erik Ekman * */ #ifndef LWIP_HDR_MDNS_H diff --git a/src/include/lwip/apps/mdns_opts.h b/src/include/lwip/apps/mdns_opts.h index a298c5aa..bf186bcc 100644 --- a/src/include/lwip/apps/mdns_opts.h +++ b/src/include/lwip/apps/mdns_opts.h @@ -31,7 +31,7 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Erik Ekman + * Author: Erik Ekman * */ diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h index 9ae2cef9..8ee6db86 100644 --- a/src/include/lwip/apps/mdns_priv.h +++ b/src/include/lwip/apps/mdns_priv.h @@ -31,7 +31,7 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Erik Ekman + * Author: Erik Ekman * */ #ifndef LWIP_HDR_MDNS_PRIV_H diff --git a/src/include/lwip/apps/mqtt.h b/src/include/lwip/apps/mqtt.h new file mode 100644 index 00000000..ee880405 --- /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 00000000..ffefacd2 --- /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 473e4a4a..b59f521b 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 +#include +#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 +#include +#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 /* 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 @@ typedef uintptr_t mem_ptr_t; #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 @@ typedef uintptr_t mem_ptr_t; #endif #endif +/** C++ const_cast(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 @@ typedef uintptr_t mem_ptr_t; 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 2f666989..1d85bccf 100644 --- a/src/include/lwip/autoip.h +++ b/src/include/lwip/autoip.h @@ -88,6 +88,8 @@ u8_t autoip_supplied_address(const struct netif *netif); /* 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 c6766120..a142f1cf 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 bb07009c..aaa64c77 100644 --- a/src/include/lwip/def.h +++ b/src/include/lwip/def.h @@ -57,6 +57,12 @@ extern "C" { /* 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 @@ extern "C" { #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 @@ u32_t lwip_htonl(u32_t x); /* 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 f7503280..ac1b18e9 100644 --- a/src/include/lwip/dhcp.h +++ b/src/include/lwip/dhcp.h @@ -132,6 +132,8 @@ void dhcp_fine_tmr(void); 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 00d5a78c..2d147afa 100644 --- a/src/include/lwip/dns.h +++ b/src/include/lwip/dns.h @@ -61,7 +61,7 @@ extern "C" { #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 @@ struct local_hostlist_entry { #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 25bc1f89..641cffb0 100644 --- a/src/include/lwip/errno.h +++ b/src/include/lwip/errno.h @@ -45,100 +45,100 @@ extern "C" { #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 e66557d4..a29dc8c1 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 17edef33..4a34f026 100644 --- a/src/include/lwip/inet.h +++ b/src/include/lwip/inet.h @@ -132,10 +132,10 @@ extern const struct in6_addr in6addr_any; #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/init.h b/src/include/lwip/init.h index b707c0ed..c87d2cce 100644 --- a/src/include/lwip/init.h +++ b/src/include/lwip/init.h @@ -54,11 +54,11 @@ extern "C" { /** x.X.x: Minor version of the stack */ #define LWIP_VERSION_MINOR 0 /** x.x.X: Revision of the stack */ -#define LWIP_VERSION_REVISION 0 +#define LWIP_VERSION_REVISION 1 /** For release candidates, this is set to 1..254 * For official releases, this is set to 255 (LWIP_RC_RELEASE) * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */ -#define LWIP_VERSION_RC LWIP_RC_RELEASE +#define LWIP_VERSION_RC LWIP_RC_DEVELOPMENT /** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ #define LWIP_RC_RELEASE 255 diff --git a/src/include/lwip/ip4_addr.h b/src/include/lwip/ip4_addr.h index 166acfab..51b46b8d 100644 --- a/src/include/lwip/ip4_addr.h +++ b/src/include/lwip/ip4_addr.h @@ -52,24 +52,9 @@ struct ip4_addr { 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 @@ struct netif; #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 14d0f7cf..d04472c3 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 @@ struct ip6_addr { 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 c1df7464..def2d08c 100644 --- a/src/include/lwip/ip_addr.h +++ b/src/include/lwip/ip_addr.h @@ -66,7 +66,7 @@ enum lwip_ip_addr_type { * 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 @@ extern const ip_addr_t ip_addr_any_type; /** @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 @@ extern const ip_addr_t ip_addr_any_type; /** @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 @@ int ipaddr_aton(const char *cp, ip_addr_t *addr); /** @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 @@ int ipaddr_aton(const char *cp, ip_addr_t *addr); 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 @@ typedef ip4_addr_t ip_addr_t; #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 @@ typedef ip6_addr_t ip_addr_t; #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 @@ typedef ip6_addr_t ip_addr_t; #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 @@ extern const ip_addr_t ip_addr_broadcast; /** * @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 @@ extern const ip_addr_t ip6_addr_any; #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 e4f6a64d..ff208d25 100644 --- a/src/include/lwip/mem.h +++ b/src/include/lwip/mem.h @@ -45,7 +45,8 @@ extern "C" { #if MEM_LIBC_MALLOC -#include /* 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 e4715ad3..8204fa4c 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 21688c65..d3d15dfa 100644 --- a/src/include/lwip/netdb.h +++ b/src/include/lwip/netdb.h @@ -38,8 +38,7 @@ #if LWIP_DNS && LWIP_SOCKET -#include /* 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 20f8bcaa..8bd2b4f7 100644 --- a/src/include/lwip/netifapi.h +++ b/src/include/lwip/netifapi.h @@ -93,13 +93,17 @@ err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, 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 0db03b50..5abac30c 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 /** * @} */ @@ -1128,7 +1143,10 @@ /** * TCP_WND: The size of a TCP window. This must be at least - * (2 * TCP_MSS) for things to work well + * (2 * TCP_MSS) for things to work well. + * ATTENTION: when using TCP_RCV_SCALE, TCP_WND is the total size + * with scaling applied. Maximum window value in the TCP header + * will be TCP_WND >> TCP_RCV_SCALE */ #if !defined TCP_WND || defined __DOXYGEN__ #define TCP_WND (4 * TCP_MSS) @@ -1345,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 /** @@ -2161,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 @@ -2227,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 @@ -2339,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 @@ -2356,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 /** * @} */ @@ -2383,6 +2415,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) @@ -2424,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. @@ -2460,6 +2515,22 @@ #define LWIP_HOOK_IP6_ROUTE(src, dest) #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 @@ -2523,7 +2594,7 @@ --------------------------------------- */ /** - * @defgroup lwip_opts_debugmsg Debugging + * @defgroup lwip_opts_debugmsg Debug messages * @ingroup lwip_opts_debug * @{ */ @@ -2531,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 ad383456..f12b8b7d 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 /* 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 00000000..4bda0b79 --- /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 + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#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 51747e0f..f68a856a 100644 --- a/src/include/lwip/priv/tcp_priv.h +++ b/src/include/lwip/priv/tcp_priv.h @@ -34,8 +34,8 @@ * Author: Adam Dunkels * */ -#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 @@ void tcp_rst(u32_t seqno, u32_t ackno, 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 @@ void tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_a #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 0a99ab0c..94782d6e 100644 --- a/src/include/lwip/prot/dns.h +++ b/src/include/lwip/prot/dns.h @@ -115,6 +115,24 @@ PACK_STRUCT_END #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 a0a713ba..34611204 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 c3c34038..bd442c68 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 4e3ca37d..6e1e2632 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 2664829b..be3a006a 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 eae3d28e..2d4903d1 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 @@ PACK_STRUCT_END # 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 c2c03aa4..67fe7b9e 100644 --- a/src/include/lwip/prot/tcp.h +++ b/src/include/lwip/prot/tcp.h @@ -85,7 +85,7 @@ PACK_STRUCT_END #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 9d76776f..2522056d 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 /* 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 bb06a404..d12bae0f 100644 --- a/src/include/lwip/sys.h +++ b/src/include/lwip/sys.h @@ -34,44 +34,6 @@ * Author: Adam Dunkels */ -/** - * @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 35584b45..3fd44678 100644 --- a/src/include/lwip/tcp.h +++ b/src/include/lwip/tcp.h @@ -387,6 +387,7 @@ err_t tcp_bind (struct tcp_pcb *pcb, const ip_addr_t *ipaddr, 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 eca9a453..9a84cbcc 100644 --- a/src/netif/lowpan6.c +++ b/src/netif/lowpan6.c @@ -616,7 +616,8 @@ lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) 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 @@ lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) } #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 0e37eeb2..d9b50777 100644 --- a/src/netif/ppp/ppp.c +++ b/src/netif/ppp/ppp.c @@ -1145,12 +1145,12 @@ int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) { 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 e2bedc75..947f7ba8 100644 --- a/src/netif/ppp/pppapi.c +++ b/src/netif/ppp/pppapi.c @@ -222,6 +222,7 @@ pppapi_do_pppol2tp_create(struct tcpip_api_call_data *m) 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 @@ pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipad 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 c215cff6..90cf1082 100644 --- a/src/netif/ppp/pppol2tp.c +++ b/src/netif/ppp/pppol2tp.c @@ -113,6 +113,10 @@ ppp_pcb *pppol2tp_create(struct netif *pppif, 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 @@ static err_t pppol2tp_connect(ppp_pcb *ppp, void *ctx) { 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 5220ed30..54164298 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 -#include +#include "lwip/arch.h" #include "lwip/err.h" #include "lwip/pbuf.h" #include "lwip/sys.h" @@ -471,18 +471,20 @@ pppos_input(ppp_pcb *ppp, u8_t *s, int l) 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 a5ae86f0..008c6337 100644 --- a/src/netif/ppp/utils.c +++ b/src/netif/ppp/utils.c @@ -247,11 +247,13 @@ int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args) { 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 00000000..67ffb195 --- /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 +# + +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 00000000..1d1e3d8d --- /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/ -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 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 00000000..e69de29b diff --git a/test/fuzz/fuzz.c b/test/fuzz/fuzz.c new file mode 100644 index 00000000..a9157f13 --- /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 + * + */ + +#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 +#include + +/* 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 00000000..b317334f Binary files /dev/null and b/test/fuzz/inputs/arp/arp_req.bin differ diff --git a/test/fuzz/inputs/icmp/icmp_ping.bin b/test/fuzz/inputs/icmp/icmp_ping.bin new file mode 100644 index 00000000..87e1ea79 Binary files /dev/null and b/test/fuzz/inputs/icmp/icmp_ping.bin differ diff --git a/test/fuzz/inputs/ipv6/neighbor_solicitation.bin b/test/fuzz/inputs/ipv6/neighbor_solicitation.bin new file mode 100644 index 00000000..d2f921c3 Binary files /dev/null and b/test/fuzz/inputs/ipv6/neighbor_solicitation.bin differ diff --git a/test/fuzz/inputs/ipv6/router_adv.bin b/test/fuzz/inputs/ipv6/router_adv.bin new file mode 100644 index 00000000..3aa96156 Binary files /dev/null and b/test/fuzz/inputs/ipv6/router_adv.bin differ diff --git a/test/fuzz/inputs/tcp/tcp_syn.bin b/test/fuzz/inputs/tcp/tcp_syn.bin new file mode 100644 index 00000000..d77f6d23 Binary files /dev/null and b/test/fuzz/inputs/tcp/tcp_syn.bin 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 00000000..d77e2675 Binary files /dev/null and b/test/fuzz/inputs/udp/udp_port_5000.bin differ diff --git a/test/fuzz/lwipopts.h b/test/fuzz/lwipopts.h new file mode 100644 index 00000000..4aff09ba --- /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 00000000..c999ff03 --- /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 68f28819..3ff42cc4 100644 --- a/test/unit/dhcp/test_dhcp.c +++ b/test/unit/dhcp/test_dhcp.c @@ -120,7 +120,8 @@ static enum tcase { 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 @@ START_TEST(test_dhcp_nak_no_endmarker) } 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 @@ dhcp_suite(void) 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 9cff0fd7..ca9be64e 100644 --- a/test/unit/mdns/test_mdns.c +++ b/test/unit/mdns/test_mdns.c @@ -26,10 +26,7 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Erik Ekman - * - * Please coordinate changes and requests with Erik Ekman - * + * Author: Erik Ekman * */ @@ -45,6 +42,7 @@ START_TEST(readname_basic) 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; @@ -63,6 +61,7 @@ START_TEST(readname_anydata) 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; @@ -81,6 +80,7 @@ START_TEST(readname_short_buf) 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; @@ -92,18 +92,20 @@ START_TEST(readname_short_buf) 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; @@ -154,11 +156,12 @@ START_TEST(readname_overflow) 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; @@ -175,14 +178,15 @@ START_TEST(readname_jump_earlier) /* 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; @@ -203,14 +207,15 @@ START_TEST(readname_jump_earlier_jump) /* 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; @@ -234,16 +239,17 @@ START_TEST(readname_jump_maxdepth) /* 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; @@ -261,14 +267,15 @@ START_TEST(readname_jump_later) { 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; @@ -285,11 +292,12 @@ END_TEST 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; @@ -303,11 +311,12 @@ END_TEST 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; @@ -322,11 +331,12 @@ START_TEST(readname_jump_loop_label) { 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; @@ -341,11 +351,12 @@ START_TEST(readname_jump_loop_jump) { 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; @@ -361,6 +372,7 @@ START_TEST(add_label_basic) 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); @@ -379,6 +391,7 @@ START_TEST(add_label_long_label) 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); @@ -393,6 +406,7 @@ START_TEST(add_label_full) 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)); @@ -440,10 +454,11 @@ END_TEST 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); @@ -470,6 +485,7 @@ START_TEST(domain_eq_diff) { 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); @@ -495,6 +511,7 @@ START_TEST(domain_eq_case) { 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); @@ -522,6 +539,7 @@ START_TEST(domain_eq_anydata) 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)); @@ -551,6 +569,7 @@ START_TEST(domain_eq_length) { 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)); @@ -581,6 +600,7 @@ START_TEST(compress_full_match) 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; @@ -615,6 +635,7 @@ START_TEST(compress_full_match_subset) 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; @@ -644,13 +665,14 @@ START_TEST(compress_full_match_jump) /* 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; @@ -685,6 +707,7 @@ START_TEST(compress_no_match) 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; @@ -718,6 +741,7 @@ START_TEST(compress_2nd_label) 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; @@ -752,6 +776,7 @@ START_TEST(compress_2nd_label_short) 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; @@ -781,13 +806,14 @@ START_TEST(compress_jump_to_jump) /* 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; @@ -827,6 +853,7 @@ START_TEST(compress_long_match) 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 fc7e67f8..aa7c84a9 100644 --- a/test/unit/tcp/tcp_helper.c +++ b/test/unit/tcp/tcp_helper.c @@ -141,9 +141,18 @@ void 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 bfcfa1d9..1e5ef03d 100644 --- a/test/unit/tcp/test_tcp.c +++ b/test/unit/tcp/test_tcp.c @@ -35,8 +35,8 @@ tcp_setup(void) { /* 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 @@ START_TEST(test_tcp_fast_rexmit_wraparound) 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 @@ START_TEST(test_tcp_rto_rexmit_wraparound) /* 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...) */