From: Ambarisha B Date: Wed, 4 Jul 2012 23:32:06 +0000 (+0530) Subject: LIBLWIP - updated to LwIP trunk X-Git-Tag: v3.3.0~1070 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zlib_tech.html?a=commitdiff_plain;h=7e55dbac3353579a74c883f67d1d02ccbd0890ae;p=minix.git LIBLWIP - updated to LwIP trunk commit a4a41b9023ef5b3a7c4a1cd82fb167fc63e706df Author: goldsimon Date: Wed Sep 26 21:50:42 2012 +0200 - This also brings in LwIP's IPv6 codebase Signed-off-by: Tomas Hruby --- diff --git a/lib/liblwip/Makefile b/lib/liblwip/Makefile index 5840444ca..810b0612d 100644 --- a/lib/liblwip/Makefile +++ b/lib/liblwip/Makefile @@ -1,6 +1,6 @@ LIB = lwip -CPPFLAGS += -I${.CURDIR}/include/ -D_SYSTEM +CPPFLAGS += -I${.CURDIR}/include/ -I${.CURDIR}/include/ipv4 -I${.CURDIR}/include/ipv6 -D_SYSTEM #.include "${.CURDIR}/api/Makefile.inc" .include "${.CURDIR}/core/Makefile.inc" @@ -9,3 +9,4 @@ CPPFLAGS += -I${.CURDIR}/include/ -D_SYSTEM SRCS += sys_arch.c .include +.include diff --git a/lib/liblwip/api/api_lib.c b/lib/liblwip/api/api_lib.c index 04f6d306d..adaaad435 100644 --- a/lib/liblwip/api/api_lib.c +++ b/lib/liblwip/api/api_lib.c @@ -72,10 +72,11 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal conn = netconn_alloc(t, callback); if (conn != NULL) { - msg.function = do_newconn; + err_t err; msg.msg.msg.n.proto = proto; msg.msg.conn = conn; - if (TCPIP_APIMSG(&msg) != ERR_OK) { + TCPIP_APIMSG((&msg), lwip_netconn_do_newconn, err); + if (err != ERR_OK) { LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); @@ -109,13 +110,13 @@ netconn_delete(struct netconn *conn) return ERR_OK; } - msg.function = do_delconn; + msg.function = lwip_netconn_do_delconn; msg.msg.conn = conn; tcpip_apimsg(&msg); netconn_free(conn); - /* don't care for return value of do_delconn since it only calls void functions */ + /* don't care for return value of lwip_netconn_do_delconn since it only calls void functions */ return ERR_OK; } @@ -141,12 +142,11 @@ netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local) LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); - msg.function = do_getaddr; msg.msg.conn = conn; - msg.msg.msg.ad.ipaddr = addr; + msg.msg.msg.ad.ipaddr = ip_2_ipX(addr); msg.msg.msg.ad.port = port; msg.msg.msg.ad.local = local; - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err); NETCONN_SET_SAFE_ERR(conn, err); return err; @@ -170,11 +170,10 @@ netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port) LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); - msg.function = do_bind; msg.msg.conn = conn; msg.msg.msg.bc.ipaddr = addr; msg.msg.msg.bc.port = port; - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_bind, err); NETCONN_SET_SAFE_ERR(conn, err); return err; @@ -196,12 +195,29 @@ netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port) LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); - msg.function = do_connect; msg.msg.conn = conn; msg.msg.msg.bc.ipaddr = addr; msg.msg.msg.bc.port = port; - /* This is the only function which need to not block tcpip_thread */ - err = tcpip_apimsg(&msg); +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + /* The TCP version waits for the connect to succeed, + so always needs to use message passing. */ + msg.function = lwip_netconn_do_connect; + err = tcpip_apimsg(&msg); + } +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP + else +#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + { + /* UDP and RAW only set flags, so we can use core-locking. */ + TCPIP_APIMSG(&msg, lwip_netconn_do_connect, err); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ NETCONN_SET_SAFE_ERR(conn, err); return err; @@ -221,9 +237,8 @@ netconn_disconnect(struct netconn *conn) LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); - msg.function = do_disconnect; msg.msg.conn = conn; - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_disconnect, err); NETCONN_SET_SAFE_ERR(conn, err); return err; @@ -240,6 +255,7 @@ netconn_disconnect(struct netconn *conn) err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) { +#if LWIP_TCP struct api_msg msg; err_t err; @@ -248,15 +264,19 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); - msg.function = do_listen; msg.msg.conn = conn; #if TCP_LISTEN_BACKLOG msg.msg.msg.lb.backlog = backlog; #endif /* TCP_LISTEN_BACKLOG */ - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_listen, err); NETCONN_SET_SAFE_ERR(conn, err); return err; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(backlog); + return ERR_ARG; +#endif /* LWIP_TCP */ } /** @@ -301,16 +321,15 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn) API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); if (newconn == NULL) { - /* connection has been closed */ - NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); - return ERR_CLSD; + /* connection has been aborted */ + NETCONN_SET_SAFE_ERR(conn, ERR_ABRT); + return ERR_ABRT; } #if TCP_LISTEN_BACKLOG /* Let the stack know that we have accepted the connection. */ - msg.function = do_recv; msg.msg.conn = conn; - /* don't care for the return value of do_recv */ - TCPIP_APIMSG(&msg); + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); #endif /* TCP_LISTEN_BACKLOG */ *new_conn = newconn; @@ -366,20 +385,22 @@ netconn_recv_data(struct netconn *conn, void **new_buf) #endif /* LWIP_SO_RCVTIMEO*/ #if LWIP_TCP - if (conn->type == NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { if (!netconn_get_noautorecved(conn) || (buf == NULL)) { /* Let the stack know that we have taken the data. */ /* TODO: Speedup: Don't block and wait for the answer here (to prevent multiple thread-switches). */ - msg.function = do_recv; msg.msg.conn = conn; if (buf != NULL) { msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len; } else { msg.msg.msg.r.len = 1; } - /* don't care for the return value of do_recv */ - TCPIP_APIMSG(&msg); + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); } /* If we are closed, we indicate that we no longer wish to use the socket */ @@ -402,7 +423,9 @@ netconn_recv_data(struct netconn *conn, void **new_buf) } #endif /* (LWIP_UDP || LWIP_RAW) */ +#if LWIP_SO_RCVBUF SYS_ARCH_DEC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); @@ -426,7 +449,7 @@ err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) { LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && - netconn_type(conn) == NETCONN_TCP, return ERR_ARG;); + NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;); return netconn_recv_data(conn, (void **)new_buf); } @@ -453,7 +476,10 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf) LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); #if LWIP_TCP - if (conn->type == NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { struct pbuf *p = NULL; /* This is not a listening netconn, since recvmbox is set */ @@ -473,12 +499,15 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf) buf->p = p; buf->ptr = p; buf->port = 0; - ip_addr_set_any(&buf->addr); + ipX_addr_set_any(LWIP_IPV6, &buf->addr); *new_buf = buf; /* don't set conn->last_err: it's only ERR_OK, anyway */ return ERR_OK; - } else + } #endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ { #if (LWIP_UDP || LWIP_RAW) return netconn_recv_data(conn, (void **)new_buf); @@ -499,18 +528,22 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf) void netconn_recved(struct netconn *conn, u32_t length) { - if ((conn != NULL) && (conn->type == NETCONN_TCP) && +#if LWIP_TCP + if ((conn != NULL) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (netconn_get_noautorecved(conn))) { struct api_msg msg; /* Let the stack know that we have taken the data. */ /* TODO: Speedup: Don't block and wait for the answer here (to prevent multiple thread-switches). */ - msg.function = do_recv; msg.msg.conn = conn; msg.msg.msg.r.len = length; - /* don't care for the return value of do_recv */ - TCPIP_APIMSG(&msg); + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); } +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(length); +#endif /* LWIP_TCP */ } /** @@ -527,7 +560,7 @@ err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port) { if (buf != NULL) { - ip_addr_set(&buf->addr, addr); + ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &buf->addr, addr); buf->port = port; return netconn_send(conn, buf); } @@ -550,10 +583,9 @@ 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)); - msg.function = do_send; msg.msg.conn = conn; msg.msg.msg.b = buf; - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_send, err); NETCONN_SET_SAFE_ERR(conn, err); return err; @@ -569,31 +601,61 @@ netconn_send(struct netconn *conn, struct netbuf *buf) * - NETCONN_COPY: data will be copied into memory belonging to the stack * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once + * @param bytes_written pointer to a location that receives the number of written bytes * @return ERR_OK if data was sent, any other err_t on error */ err_t -netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags) +netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written) { struct api_msg msg; err_t err; + u8_t dontblock; LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); - LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); + LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;); if (size == 0) { return ERR_OK; } + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); + if (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. */ + return ERR_VAL; + } - /* @todo: for non-blocking write, check if 'size' would ever fit into - snd_queue or snd_buf */ - msg.function = do_write; + /* non-blocking write sends as much */ msg.msg.conn = conn; msg.msg.msg.w.dataptr = dataptr; msg.msg.msg.w.apiflags = apiflags; msg.msg.msg.w.len = size; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + msg.msg.msg.w.time_started = sys_now(); + } else { + msg.msg.msg.w.time_started = 0; + } +#endif /* LWIP_SO_SNDTIMEO */ + /* For locking the core: this _can_ be delayed on low memory/low send buffer, but if it is, this is done inside api_msg.c:do_write(), so we can use the non-blocking version here. */ - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_write, err); + if ((err == ERR_OK) && (bytes_written != NULL)) { + if (dontblock +#if LWIP_SO_SNDTIMEO + || (conn->send_timeout != 0) +#endif /* LWIP_SO_SNDTIMEO */ + ) { + /* nonblocking write: maybe the data has been sent partly */ + *bytes_written = msg.msg.msg.w.len; + } else { + /* blocking call succeeded: all data has been sent if it */ + *bytes_written = size; + } + } NETCONN_SET_SAFE_ERR(conn, err); return err; @@ -614,11 +676,11 @@ netconn_close_shutdown(struct netconn *conn, u8_t how) LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); - msg.function = do_close; + msg.function = lwip_netconn_do_close; msg.msg.conn = conn; /* shutting down both ends is the same as closing */ msg.msg.msg.sd.shut = how; - /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close, + /* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close, don't use TCPIP_APIMSG here */ err = tcpip_apimsg(&msg); @@ -651,7 +713,7 @@ netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); } -#if LWIP_IGMP +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) /** * Join multicast groups for UDP netconns. * @@ -673,17 +735,16 @@ netconn_join_leave_group(struct netconn *conn, LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); - msg.function = do_join_leave_group; msg.msg.conn = conn; - msg.msg.msg.jl.multiaddr = multiaddr; - msg.msg.msg.jl.netif_addr = netif_addr; + msg.msg.msg.jl.multiaddr = ip_2_ipX(multiaddr); + msg.msg.msg.jl.netif_addr = ip_2_ipX(netif_addr); msg.msg.msg.jl.join_or_leave = join_or_leave; - err = TCPIP_APIMSG(&msg); + TCPIP_APIMSG(&msg, lwip_netconn_do_join_leave_group, err); NETCONN_SET_SAFE_ERR(conn, err); return err; } -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS /** @@ -716,7 +777,7 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr) msg.err = &err; msg.sem = &sem; - tcpip_callback(do_gethostbyname, &msg); + tcpip_callback(lwip_netconn_do_gethostbyname, &msg); sys_sem_wait(&sem); sys_sem_free(&sem); diff --git a/lib/liblwip/api/api_msg.c b/lib/liblwip/api/api_msg.c index 366f5d9a2..04e9bdd73 100644 --- a/lib/liblwip/api/api_msg.c +++ b/lib/liblwip/api/api_msg.c @@ -51,6 +51,7 @@ #include "lwip/tcpip.h" #include "lwip/igmp.h" #include "lwip/dns.h" +#include "lwip/mld6.h" #include @@ -62,8 +63,8 @@ /* forward declarations */ #if LWIP_TCP -static err_t do_writemore(struct netconn *conn); -static void do_close_internal(struct netconn *conn); +static err_t lwip_netconn_do_writemore(struct netconn *conn); +static void lwip_netconn_do_close_internal(struct netconn *conn); #endif #if LWIP_RAW @@ -112,7 +113,7 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, buf->p = q; buf->ptr = q; - ip_addr_copy(buf->addr, *ip_current_src_addr()); + ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_current_src_addr()); buf->port = pcb->protocol; len = q->tot_len; @@ -120,7 +121,9 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, netbuf_delete(buf); return 0; } else { +#if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } @@ -173,17 +176,16 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, } else { buf->p = p; buf->ptr = p; - ip_addr_set(&buf->addr, addr); + ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr); buf->port = port; #if LWIP_NETBUF_RECVINFO { - const struct ip_hdr* iphdr = ip_current_header(); /* get the UDP header - always in the first pbuf, ensured by udp_input */ - const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); + const struct udp_hdr* udphdr = ipX_next_header_ptr(); #if LWIP_CHECKSUM_ON_COPY buf->flags = NETBUF_FLAG_DESTADDR; #endif /* LWIP_CHECKSUM_ON_COPY */ - ip_addr_set(&buf->toaddr, ip_current_dest_addr()); + ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_current_dest_addr()); buf->toport_chksum = udphdr->dest; } #endif /* LWIP_NETBUF_RECVINFO */ @@ -194,7 +196,9 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, netbuf_delete(buf); return; } else { +#if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } @@ -248,7 +252,9 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ return ERR_MEM; } else { +#if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } @@ -276,9 +282,9 @@ poll_tcp(void *arg, struct tcp_pcb *pcb) LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { - do_writemore(conn); + lwip_netconn_do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { - do_close_internal(conn); + lwip_netconn_do_close_internal(conn); } /* @todo: implement connect timeout here? */ @@ -312,9 +318,9 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { - do_writemore(conn); + lwip_netconn_do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { - do_close_internal(conn); + lwip_netconn_do_close_internal(conn); } if (conn) { @@ -379,7 +385,7 @@ err_tcp(void *arg, err_t err) if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || (old_state == NETCONN_CONNECT)) { - /* calling do_writemore/do_close_internal is not necessary + /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary since the pcb has already been deleted! */ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); SET_NONBLOCKING_CONNECT(conn, 0); @@ -450,6 +456,14 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { /* When returning != ERR_OK, the pcb is aborted in tcp_process(), so do nothing here! */ + /* remove all references to this netconn from the pcb */ + struct tcp_pcb* pcb = newconn->pcb.tcp; + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 4); + tcp_err(pcb, NULL); + /* remove reference from to the pcb from this netconn */ newconn->pcb.tcp = NULL; /* no need to drain since we know the recvmbox is empty. */ sys_mbox_free(&newconn->recvmbox); @@ -467,7 +481,7 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) /** * Create a new pcb of a specific type. - * Called from do_newconn(). + * Called from lwip_netconn_do_newconn(). * * @param msg the api_msg_msg describing the connection type * @return msg->conn->err, but the return value is currently ignored @@ -482,46 +496,50 @@ pcb_new(struct api_msg_msg *msg) #if LWIP_RAW case NETCONN_RAW: msg->conn->pcb.raw = raw_new(msg->msg.n.proto); - if(msg->conn->pcb.raw == NULL) { - msg->err = ERR_MEM; - break; + if(msg->conn->pcb.raw != NULL) { + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); } - raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: msg->conn->pcb.udp = udp_new(); - if(msg->conn->pcb.udp == NULL) { - msg->err = ERR_MEM; - break; - } + if(msg->conn->pcb.udp != NULL) { #if LWIP_UDPLITE - if (msg->conn->type==NETCONN_UDPLITE) { - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); - } + if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } #endif /* LWIP_UDPLITE */ - if (msg->conn->type==NETCONN_UDPNOCHKSUM) { - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); } - udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: msg->conn->pcb.tcp = tcp_new(); - if(msg->conn->pcb.tcp == NULL) { - msg->err = ERR_MEM; - break; + if(msg->conn->pcb.tcp != NULL) { + setup_tcp(msg->conn); } - setup_tcp(msg->conn); break; #endif /* LWIP_TCP */ default: /* Unsupported netconn type, e.g. protocol disabled */ msg->err = ERR_VAL; - break; + return; } + if (msg->conn->pcb.ip == NULL) { + msg->err = ERR_MEM; + } +#if LWIP_IPV6 + else { + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + ip_set_v6(msg->conn->pcb.ip, 1); + } + } +#endif /* LWIP_IPV6 */ } /** @@ -531,7 +549,7 @@ pcb_new(struct api_msg_msg *msg) * @param msg the api_msg_msg describing the connection type */ void -do_newconn(struct api_msg_msg *msg) +lwip_netconn_do_newconn(struct api_msg_msg *msg) { msg->err = ERR_OK; if(msg->conn->pcb.tcp == NULL) { @@ -569,10 +587,7 @@ netconn_alloc(enum netconn_type t, netconn_callback callback) conn->type = t; conn->pcb.tcp = NULL; -#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ - (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) - size = DEFAULT_RAW_RECVMBOX_SIZE; -#else + /* If all sizes are the same, every compiler should optimize this switch to nothing, */ switch(NETCONNTYPE_GROUP(t)) { #if LWIP_RAW case NETCONN_RAW: @@ -591,18 +606,15 @@ netconn_alloc(enum netconn_type t, netconn_callback callback) #endif /* LWIP_TCP */ default: LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); - break; + goto free_and_return; } -#endif if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { - memp_free(MEMP_NETCONN, conn); - return NULL; + goto free_and_return; } if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { sys_sem_free(&conn->op_completed); - memp_free(MEMP_NETCONN, conn); - return NULL; + goto free_and_return; } #if LWIP_TCP @@ -614,19 +626,25 @@ netconn_alloc(enum netconn_type t, netconn_callback callback) conn->socket = -1; #endif /* LWIP_SOCKET */ conn->callback = callback; - conn->recv_avail = 0; #if LWIP_TCP conn->current_msg = NULL; conn->write_offset = 0; #endif /* LWIP_TCP */ +#if LWIP_SO_SNDTIMEO + conn->send_timeout = 0; +#endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO conn->recv_timeout = 0; #endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVBUF conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; + conn->recv_avail = 0; #endif /* LWIP_SO_RCVBUF */ conn->flags = 0; return conn; +free_and_return: + memp_free(MEMP_NETCONN, conn); + return NULL; } /** @@ -674,7 +692,7 @@ netconn_drain(struct netconn *conn) if (sys_mbox_valid(&conn->recvmbox)) { while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { #if LWIP_TCP - if (conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) { if(mem != NULL) { p = (struct pbuf*)mem; /* pcb might be set to NULL already by err_tcp() */ @@ -726,13 +744,13 @@ netconn_drain(struct netconn *conn) * @param conn the TCP netconn to close */ static void -do_close_internal(struct netconn *conn) +lwip_netconn_do_close_internal(struct netconn *conn) { err_t err; u8_t shut, shut_rx, shut_tx, close; LWIP_ASSERT("invalid conn", (conn != NULL)); - LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); + LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)); LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); @@ -764,21 +782,21 @@ do_close_internal(struct netconn *conn) } } /* Try to close the connection */ - if (shut == NETCONN_SHUT_RDWR) { + if (close) { err = tcp_close(conn->pcb.tcp); } else { - err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR); + err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx); } if (err == ERR_OK) { /* Closing succeeded */ conn->current_msg->err = ERR_OK; conn->current_msg = NULL; conn->state = NETCONN_NONE; - /* Set back some callback pointers as conn is going away */ - conn->pcb.tcp = NULL; - /* Trigger select() in socket layer. Make sure everybody notices activity - on the connection, error first! */ if (close) { + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ API_EVENT(conn, NETCONN_EVT_ERROR, 0); } if (shut_rx) { @@ -811,14 +829,15 @@ do_close_internal(struct netconn *conn) * @param msg the api_msg_msg pointing to the connection */ void -do_delconn(struct api_msg_msg *msg) +lwip_netconn_do_delconn(struct api_msg_msg *msg) { /* @todo TCP: abort running write/connect? */ if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN) && (msg->conn->state != NETCONN_CONNECT)) { /* this only happens for TCP netconns */ - LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); msg->err = ERR_INPROGRESS; } else { LWIP_ASSERT("blocking connect in progress", @@ -847,8 +866,8 @@ do_delconn(struct api_msg_msg *msg) msg->conn->state = NETCONN_CLOSE; msg->msg.sd.shut = NETCONN_SHUT_RDWR; msg->conn->current_msg = msg; - do_close_internal(msg->conn); - /* API_EVENT is called inside do_close_internal, before releasing + lwip_netconn_do_close_internal(msg->conn); + /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing the application thread, so we can return at this point! */ return; #endif /* LWIP_TCP */ @@ -877,7 +896,7 @@ do_delconn(struct api_msg_msg *msg) * the IP address and port to bind to */ void -do_bind(struct api_msg_msg *msg) +lwip_netconn_do_bind(struct api_msg_msg *msg) { if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; @@ -910,13 +929,13 @@ do_bind(struct api_msg_msg *msg) #if LWIP_TCP /** - * TCP callback function if a connection (opened by tcp_connect/do_connect) has + * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has * been established (or reset by the remote host). * * @see tcp.h (struct tcp_pcb.connected) for parameters and return values */ static err_t -do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) { struct netconn *conn; int was_blocking; @@ -936,7 +955,7 @@ do_connected(void *arg, struct tcp_pcb *pcb, err_t err) if (conn->current_msg != NULL) { conn->current_msg->err = err; } - if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { + if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) { setup_tcp(conn); } was_blocking = !IN_NONBLOCKING_CONNECT(conn); @@ -944,12 +963,7 @@ do_connected(void *arg, struct tcp_pcb *pcb, err_t err) conn->current_msg = NULL; conn->state = NETCONN_NONE; if (!was_blocking) { - SYS_ARCH_DECL_PROTECT(lev); - SYS_ARCH_PROTECT(lev); - if (conn->last_err == ERR_INPROGRESS) { - conn->last_err = ERR_OK; - } - SYS_ARCH_UNPROTECT(lev); + NETCONN_SET_SAFE_ERR(conn, ERR_OK); } API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); @@ -968,54 +982,63 @@ do_connected(void *arg, struct tcp_pcb *pcb, err_t err) * the IP address and port to connect to */ void -do_connect(struct api_msg_msg *msg) +lwip_netconn_do_connect(struct api_msg_msg *msg) { if (msg->conn->pcb.tcp == NULL) { /* This may happen when calling netconn_connect() a second time */ msg->err = ERR_CLSD; + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(&msg->conn->op_completed); + return; + } } else { switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW - case NETCONN_RAW: - msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); - break; + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; #endif /* LWIP_RAW */ #if LWIP_UDP - case NETCONN_UDP: - msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); - break; + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; #endif /* LWIP_UDP */ #if LWIP_TCP - case NETCONN_TCP: - /* Prevent connect while doing any other action. */ - if (msg->conn->state != NETCONN_NONE) { - msg->err = ERR_ISCONN; - } else { - setup_tcp(msg->conn); - msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, - msg->msg.bc.port, do_connected); - if (msg->err == ERR_OK) { - u8_t non_blocking = netconn_is_nonblocking(msg->conn); - msg->conn->state = NETCONN_CONNECT; - SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); - if (non_blocking) { - msg->err = ERR_INPROGRESS; - } else { - msg->conn->current_msg = msg; - /* sys_sem_signal() is called from do_connected (or err_tcp()), - * when the connection is established! */ - return; + case NETCONN_TCP: + /* Prevent connect while doing any other action. */ + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; + } else { + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, + msg->msg.bc.port, lwip_netconn_do_connected); + if (msg->err == ERR_OK) { + u8_t non_blocking = netconn_is_nonblocking(msg->conn); + msg->conn->state = NETCONN_CONNECT; + SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); + if (non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()), + * when the connection is established! */ + return; + } } } - } - break; + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(&msg->conn->op_completed); + return; #endif /* LWIP_TCP */ - default: - LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); - break; + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; } } - sys_sem_signal(&msg->conn->op_completed); + /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(), + so use TCPIP_APIMSG_ACK() here. */ + TCPIP_APIMSG_ACK(msg); } /** @@ -1026,7 +1049,7 @@ do_connect(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection to disconnect */ void -do_disconnect(struct api_msg_msg *msg) +lwip_netconn_do_disconnect(struct api_msg_msg *msg) { #if LWIP_UDP if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { @@ -1040,6 +1063,7 @@ do_disconnect(struct api_msg_msg *msg) TCPIP_APIMSG_ACK(msg); } +#if LWIP_TCP /** * Set a TCP pcb contained in a netconn into listen mode * Called from netconn_listen. @@ -1047,21 +1071,32 @@ do_disconnect(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection */ void -do_listen(struct api_msg_msg *msg) +lwip_netconn_do_listen(struct api_msg_msg *msg) { -#if LWIP_TCP if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; } else { msg->err = ERR_CONN; if (msg->conn->pcb.tcp != NULL) { - if (msg->conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { if (msg->conn->state == NETCONN_NONE) { + struct tcp_pcb* lpcb; +#if LWIP_IPV6 + if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + lpcb = tcp_listen_dual(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + } else +#endif /* LWIP_IPV6 */ + { #if TCP_LISTEN_BACKLOG - struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); + lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); #else /* TCP_LISTEN_BACKLOG */ - struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); + lpcb = tcp_listen(msg->conn->pcb.tcp); #endif /* TCP_LISTEN_BACKLOG */ + } if (lpcb == NULL) { /* in this case, the old pcb is still allocated */ msg->err = ERR_MEM; @@ -1088,12 +1123,14 @@ do_listen(struct api_msg_msg *msg) } } } + } else { + msg->err = ERR_ARG; } } } -#endif /* LWIP_TCP */ TCPIP_APIMSG_ACK(msg); } +#endif /* LWIP_TCP */ /** * Send some data on a RAW or UDP pcb contained in a netconn @@ -1102,7 +1139,7 @@ do_listen(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection */ void -do_send(struct api_msg_msg *msg) +lwip_netconn_do_send(struct api_msg_msg *msg) { if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; @@ -1112,29 +1149,29 @@ do_send(struct api_msg_msg *msg) switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: - if (ip_addr_isany(&msg->msg.b->addr)) { + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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); + msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr)); } break; #endif #if LWIP_UDP case NETCONN_UDP: #if LWIP_CHECKSUM_ON_COPY - if (ip_addr_isany(&msg->msg.b->addr)) { + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); } else { msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, - &msg->msg.b->addr, msg->msg.b->port, + ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port, msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); } #else /* LWIP_CHECKSUM_ON_COPY */ - if (ip_addr_isany(&msg->msg.b->addr)) { + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); } else { - msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port); + msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port); } #endif /* LWIP_CHECKSUM_ON_COPY */ break; @@ -1147,6 +1184,7 @@ do_send(struct api_msg_msg *msg) TCPIP_APIMSG_ACK(msg); } +#if LWIP_TCP /** * Indicate data has been received from a TCP pcb contained in a netconn * Called from netconn_recv @@ -1154,12 +1192,11 @@ do_send(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection */ void -do_recv(struct api_msg_msg *msg) +lwip_netconn_do_recv(struct api_msg_msg *msg) { -#if LWIP_TCP msg->err = ERR_OK; if (msg->conn->pcb.tcp != NULL) { - if (msg->conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { #if TCP_LISTEN_BACKLOG if (msg->conn->pcb.tcp->state == LISTEN) { tcp_accepted(msg->conn->pcb.tcp); @@ -1175,14 +1212,12 @@ do_recv(struct api_msg_msg *msg) } } } -#endif /* LWIP_TCP */ TCPIP_APIMSG_ACK(msg); } -#if LWIP_TCP /** * See if more data needs to be written from a previous call to netconn_write. - * Called initially from do_write. If the first call can't send all data + * Called initially from lwip_netconn_do_write. If the first call can't send all data * (because of low memory or empty send-buffer), this function is called again * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the * blocking application thread (waiting in netconn_write) is released. @@ -1192,9 +1227,9 @@ do_recv(struct api_msg_msg *msg) * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished */ static err_t -do_writemore(struct netconn *conn) +lwip_netconn_do_writemore(struct netconn *conn) { - err_t err = ERR_OK; + err_t err; void *dataptr; u16_t len, available; u8_t write_finished = 0; @@ -1210,62 +1245,78 @@ do_writemore(struct netconn *conn) LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", conn->write_offset < conn->current_msg->msg.w.len); - dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; - diff = conn->current_msg->msg.w.len - conn->write_offset; - if (diff > 0xffffUL) { /* max_u16_t */ - len = 0xffff; +#if LWIP_SO_SNDTIMEO + if ((conn->send_timeout != 0) && + ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { + write_finished = 1; + if (conn->write_offset == 0) { + /* nothing has been written */ + err = ERR_WOULDBLOCK; + conn->current_msg->msg.w.len = 0; + } else { + /* partial write */ + err = ERR_OK; + conn->current_msg->msg.w.len = conn->write_offset; + } + } else +#endif /* LWIP_SO_SNDTIMEO */ + { + dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; + diff = conn->current_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; #if LWIP_TCPIP_CORE_LOCKING - conn->flags |= NETCONN_FLAG_WRITE_DELAYED; + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; #endif - apiflags |= TCP_WRITE_FLAG_MORE; - } else { - len = (u16_t)diff; - } - available = tcp_sndbuf(conn->pcb.tcp); - if (available < len) { - /* don't try to write more than sendbuf */ - len = available; + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; + IF (DONTBLOCK){ + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { #if LWIP_TCPIP_CORE_LOCKING - conn->flags |= NETCONN_FLAG_WRITE_DELAYED; + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; #endif - apiflags |= TCP_WRITE_FLAG_MORE; - } - if (dontblock && (len < conn->current_msg->msg.w.len)) { - /* failed to send all data at once -> nonblocking write not possible */ - err = ERR_MEM; - } - if (err == ERR_OK) { - LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); - } - if (dontblock && (err == ERR_MEM)) { - /* nonblocking write failed */ - write_finished = 1; - err = ERR_WOULDBLOCK; - /* let poll_tcp check writable space to mark the pcb - writable again */ - conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; - /* let select mark this pcb as non-writable. */ - API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); - } else { /* if OK or memory error, check available space */ - if (((err == ERR_OK) || (err == ERR_MEM)) && - ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || - (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) { - /* The queued byte- or pbuf-count exceeds the configured low-water limit, - let select mark this pcb as non-writable. */ - API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + if ((err == ERR_OK) || (err == ERR_MEM)) { +err_mem: + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* non-blocking write did not write everything: mark the pcb non-writable + and let poll_tcp check writable space to mark the pcb writable again */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } } if (err == ERR_OK) { conn->write_offset += len; - if (conn->write_offset == conn->current_msg->msg.w.len) { + if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { + /* return sent length */ + conn->current_msg->msg.w.len = conn->write_offset; /* everything was written */ write_finished = 1; conn->write_offset = 0; } tcp_output(conn->pcb.tcp); - } else if (err == ERR_MEM) { + } 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! */ @@ -1273,16 +1324,16 @@ do_writemore(struct netconn *conn) /* tcp_write returned ERR_MEM, try tcp_output anyway */ tcp_output(conn->pcb.tcp); - #if LWIP_TCPIP_CORE_LOCKING +#if LWIP_TCPIP_CORE_LOCKING conn->flags |= NETCONN_FLAG_WRITE_DELAYED; - #endif +#endif } else { /* On errors != ERR_MEM, we don't try writing any more but return the error to the application thread. */ write_finished = 1; + conn->current_msg->msg.w.len = 0; } } - if (write_finished) { /* everything was written: set back connection state and back to application task */ @@ -1311,19 +1362,19 @@ do_writemore(struct netconn *conn) * @param msg the api_msg_msg pointing to the connection */ void -do_write(struct api_msg_msg *msg) +lwip_netconn_do_write(struct api_msg_msg *msg) { if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; } else { - if (msg->conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { #if LWIP_TCP if (msg->conn->state != NETCONN_NONE) { /* netconn is connecting, closing or in blocking write */ msg->err = ERR_INPROGRESS; } else if (msg->conn->pcb.tcp != NULL) { msg->conn->state = NETCONN_WRITE; - /* set all the variables used by do_writemore */ + /* set all the variables used by lwip_netconn_do_writemore */ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && msg->conn->write_offset == 0); LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); @@ -1331,7 +1382,7 @@ do_write(struct api_msg_msg *msg) msg->conn->write_offset = 0; #if LWIP_TCPIP_CORE_LOCKING msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED; - if (do_writemore(msg->conn) != ERR_OK) { + if (lwip_netconn_do_writemore(msg->conn) != ERR_OK) { LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); UNLOCK_TCPIP_CORE(); sys_arch_sem_wait(&msg->conn->op_completed, 0); @@ -1339,10 +1390,10 @@ do_write(struct api_msg_msg *msg) LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); } #else /* LWIP_TCPIP_CORE_LOCKING */ - do_writemore(msg->conn); + lwip_netconn_do_writemore(msg->conn); #endif /* LWIP_TCPIP_CORE_LOCKING */ - /* for both cases: if do_writemore was called, don't ACK the APIMSG - since do_writemore ACKs it! */ + /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG + since lwip_netconn_do_writemore ACKs it! */ return; } else { msg->err = ERR_CONN; @@ -1366,12 +1417,16 @@ do_write(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection */ void -do_getaddr(struct api_msg_msg *msg) +lwip_netconn_do_getaddr(struct api_msg_msg *msg) { if (msg->conn->pcb.ip != NULL) { - *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip : - msg->conn->pcb.ip->remote_ip); - + if (msg->msg.ad.local) { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->local_ip); + } else { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->remote_ip); + } msg->err = ERR_OK; switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW @@ -1419,15 +1474,16 @@ do_getaddr(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection */ void -do_close(struct api_msg_msg *msg) +lwip_netconn_do_close(struct api_msg_msg *msg) { #if LWIP_TCP /* @todo: abort running write/connect? */ if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { /* this only happens for TCP netconns */ - LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); msg->err = ERR_INPROGRESS; - } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)) { if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) { /* LISTEN doesn't support half shutdown */ msg->err = ERR_CONN; @@ -1440,8 +1496,8 @@ do_close(struct api_msg_msg *msg) msg->conn->write_offset == 0); msg->conn->state = NETCONN_CLOSE; msg->conn->current_msg = msg; - do_close_internal(msg->conn); - /* for tcp netconns, do_close_internal ACKs the message */ + lwip_netconn_do_close_internal(msg->conn); + /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */ return; } } else @@ -1452,7 +1508,7 @@ do_close(struct api_msg_msg *msg) sys_sem_signal(&msg->conn->op_completed); } -#if LWIP_IGMP +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) /** * Join multicast groups for UDP netconns. * Called from netconn_join_leave_group @@ -1460,7 +1516,7 @@ do_close(struct api_msg_msg *msg) * @param msg the api_msg_msg pointing to the connection */ void -do_join_leave_group(struct api_msg_msg *msg) +lwip_netconn_do_join_leave_group(struct api_msg_msg *msg) { if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; @@ -1468,10 +1524,28 @@ do_join_leave_group(struct api_msg_msg *msg) if (msg->conn->pcb.tcp != NULL) { if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { #if LWIP_UDP - if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { - msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); - } else { - msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); +#if LWIP_IPV6 && LWIP_IPV6_MLD + if (PCB_ISIPV6(msg->conn->pcb.udp)) { + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mld6_joingroup(ipX_2_ip6(msg->msg.jl.netif_addr), + ipX_2_ip6(msg->msg.jl.multiaddr)); + } else { + msg->err = mld6_leavegroup(ipX_2_ip6(msg->msg.jl.netif_addr), + ipX_2_ip6(msg->msg.jl.multiaddr)); + } + } + else +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + { +#if LWIP_IGMP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(ipX_2_ip(msg->msg.jl.netif_addr), + ipX_2_ip(msg->msg.jl.multiaddr)); + } else { + msg->err = igmp_leavegroup(ipX_2_ip(msg->msg.jl.netif_addr), + ipX_2_ip(msg->msg.jl.multiaddr)); + } +#endif /* LWIP_IGMP */ } #endif /* LWIP_UDP */ #if (LWIP_TCP || LWIP_RAW) @@ -1485,7 +1559,7 @@ do_join_leave_group(struct api_msg_msg *msg) } TCPIP_APIMSG_ACK(msg); } -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS /** @@ -1494,7 +1568,7 @@ do_join_leave_group(struct api_msg_msg *msg) * signaling the semaphore. */ static void -do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { struct dns_api_msg *msg = (struct dns_api_msg*)arg; @@ -1520,11 +1594,11 @@ do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) * @param arg the dns_api_msg pointing to the query */ void -do_gethostbyname(void *arg) +lwip_netconn_do_gethostbyname(void *arg) { struct dns_api_msg *msg = (struct dns_api_msg*)arg; - *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg); + *msg->err = dns_gethostbyname(msg->name, msg->addr, lwip_netconn_do_dns_found, msg); if (*msg->err != ERR_INPROGRESS) { /* on error or immediate success, wake up the application * task waiting in netconn_gethostbyname */ diff --git a/lib/liblwip/api/err.c b/lib/liblwip/api/err.c index b0a4eb3e1..92fa8b7db 100644 --- a/lib/liblwip/api/err.c +++ b/lib/liblwip/api/err.c @@ -49,14 +49,14 @@ static const char *err_strerr[] = { "Operation in progress.", /* ERR_INPROGRESS -5 */ "Illegal value.", /* ERR_VAL -6 */ "Operation would block.", /* ERR_WOULDBLOCK -7 */ - "Connection aborted.", /* ERR_ABRT -8 */ - "Connection reset.", /* ERR_RST -9 */ - "Connection closed.", /* ERR_CLSD -10 */ - "Not connected.", /* ERR_CONN -11 */ - "Illegal argument.", /* ERR_ARG -12 */ - "Address in use.", /* ERR_USE -13 */ - "Low-level netif error.", /* ERR_IF -14 */ - "Already connected.", /* ERR_ISCONN -15 */ + "Address in use.", /* ERR_USE -8 */ + "Already connected.", /* ERR_ISCONN -9 */ + "Connection aborted.", /* ERR_ABRT -10 */ + "Connection reset.", /* ERR_RST -11 */ + "Connection closed.", /* ERR_CLSD -12 */ + "Not connected.", /* ERR_CONN -13 */ + "Illegal argument.", /* ERR_ARG -14 */ + "Low-level netif error.", /* ERR_IF -15 */ }; /** diff --git a/lib/liblwip/api/netbuf.c b/lib/liblwip/api/netbuf.c index 9390c9ee9..0ccd2bce3 100644 --- a/lib/liblwip/api/netbuf.c +++ b/lib/liblwip/api/netbuf.c @@ -61,7 +61,7 @@ netbuf *netbuf_new(void) if (buf != NULL) { buf->p = NULL; buf->ptr = NULL; - ip_addr_set_any(&buf->addr); + ipX_addr_set_any(LWIP_IPV6, &buf->addr); buf->port = 0; #if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY #if LWIP_CHECKSUM_ON_COPY @@ -69,7 +69,7 @@ netbuf *netbuf_new(void) #endif /* LWIP_CHECKSUM_ON_COPY */ buf->toport_chksum = 0; #if LWIP_NETBUF_RECVINFO - ip_addr_set_any(&buf->toaddr); + ipX_addr_set_any(LWIP_IPV6, &buf->toaddr); #endif /* LWIP_NETBUF_RECVINFO */ #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ return buf; diff --git a/lib/liblwip/api/netdb.c b/lib/liblwip/api/netdb.c index a7e4e06bc..6a4bac561 100644 --- a/lib/liblwip/api/netdb.c +++ b/lib/liblwip/api/netdb.c @@ -49,7 +49,7 @@ /** helper struct for gethostbyname_r to access the char* buffer */ struct gethostbyname_r_helper { - ip_addr_t *addrs; + ip_addr_t *addr_list[2]; ip_addr_t addr; char *aliases; }; @@ -180,7 +180,7 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, } /* first thing to do: set *result to nothing */ *result = NULL; - if ((name == NULL) || (ret == NULL) || (buf == 0)) { + if ((name == NULL) || (ret == NULL) || (buf == NULL)) { /* not all arguments given */ *h_errnop = EINVAL; return -1; @@ -197,10 +197,10 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); /* query host IP address */ - err = netconn_gethostbyname(name, &(h->addr)); + err = netconn_gethostbyname(name, &h->addr); if (err != ERR_OK) { LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); - *h_errnop = ENSRNOTFOUND; + *h_errnop = HOST_NOT_FOUND; return -1; } @@ -209,13 +209,14 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, hostname[namelen] = 0; /* fill hostent */ - h->addrs = &(h->addr); + h->addr_list[0] = &h->addr; + h->addr_list[1] = NULL; h->aliases = NULL; - ret->h_name = (char*)hostname; - ret->h_aliases = &(h->aliases); + ret->h_name = hostname; + ret->h_aliases = &h->aliases; ret->h_addrtype = AF_INET; ret->h_length = sizeof(ip_addr_t); - ret->h_addr_list = (char**)&(h->addrs); + ret->h_addr_list = (char**)&h->addr_list; /* set result != NULL */ *result = ret; diff --git a/lib/liblwip/api/netifapi.c b/lib/liblwip/api/netifapi.c index 43e47203a..81403f828 100644 --- a/lib/liblwip/api/netifapi.c +++ b/lib/liblwip/api/netifapi.c @@ -41,8 +41,8 @@ /** * Call netif_add() inside the tcpip_thread context. */ -void -do_netifapi_netif_add(struct netifapi_msg_msg *msg) +static void +netifapi_do_netif_add(struct netifapi_msg_msg *msg) { if (!netif_add( msg->netif, msg->msg.add.ipaddr, @@ -61,8 +61,8 @@ do_netifapi_netif_add(struct netifapi_msg_msg *msg) /** * Call netif_set_addr() inside the tcpip_thread context. */ -void -do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg) +static void +netifapi_do_netif_set_addr(struct netifapi_msg_msg *msg) { netif_set_addr( msg->netif, msg->msg.add.ipaddr, @@ -76,8 +76,8 @@ do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg) * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the * tcpip_thread context. */ -void -do_netifapi_netif_common(struct netifapi_msg_msg *msg) +static void +netifapi_do_netif_common(struct netifapi_msg_msg *msg) { if (msg->msg.common.errtfunc != NULL) { msg->err = msg->msg.common.errtfunc(msg->netif); @@ -104,7 +104,7 @@ netifapi_netif_add(struct netif *netif, netif_input_fn input) { struct netifapi_msg msg; - msg.function = do_netifapi_netif_add; + msg.function = netifapi_do_netif_add; msg.msg.netif = netif; msg.msg.msg.add.ipaddr = ipaddr; msg.msg.msg.add.netmask = netmask; @@ -129,7 +129,7 @@ netifapi_netif_set_addr(struct netif *netif, ip_addr_t *gw) { struct netifapi_msg msg; - msg.function = do_netifapi_netif_set_addr; + msg.function = netifapi_do_netif_set_addr; msg.msg.netif = netif; msg.msg.msg.add.ipaddr = ipaddr; msg.msg.msg.add.netmask = netmask; @@ -149,7 +149,7 @@ netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, netifapi_errt_fn errtfunc) { struct netifapi_msg msg; - msg.function = do_netifapi_netif_common; + msg.function = netifapi_do_netif_common; msg.msg.netif = netif; msg.msg.msg.common.voidfunc = voidfunc; msg.msg.msg.common.errtfunc = errtfunc; diff --git a/lib/liblwip/api/sockets.c b/lib/liblwip/api/sockets.c index 9919b6112..660367129 100644 --- a/lib/liblwip/api/sockets.c +++ b/lib/liblwip/api/sockets.c @@ -58,6 +58,66 @@ #include +#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \ + (sin)->sin_len = sizeof(struct sockaddr_in); \ + (sin)->sin_family = AF_INET; \ + (sin)->sin_port = htons((port)); \ + inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \ + memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0) +#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \ + inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \ + (port) = ntohs((sin)->sin_port); }while(0) + +#if LWIP_IPV6 +#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \ + ((namelen) == sizeof(struct sockaddr_in6))) +#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \ + ((name)->sa_family == AF_INET6)) +#define SOCK_ADDR_TYPE_MATCH(name, sock) \ + ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ + (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) +#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \ + (sin6)->sin6_len = sizeof(struct sockaddr_in6); \ + (sin6)->sin6_family = AF_INET6; \ + (sin6)->sin6_port = htons((port)); \ + (sin6)->sin6_flowinfo = 0; \ + inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0) +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \ + inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \ + (port) = ntohs((sin6)->sin6_port); }while(0) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \ + (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6)) +#else /* LWIP_IPV6 */ +#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in)) +#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET) +#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) +#endif /* LWIP_IPV6 */ + +#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \ + IS_SOCK_ADDR_TYPE_VALID(name)) +#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \ + SOCK_ADDR_TYPE_MATCH(name, sock)) +#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) + + + #define NUM_SOCKETS MEMP_NUM_NETCONN /** Contains all internal pointers and states used for a socket */ @@ -105,8 +165,10 @@ struct lwip_select_cb { struct lwip_setgetsockopt_data { /** socket struct for which to change options */ struct lwip_sock *sock; +#ifdef LWIP_DEBUG /** socket index for which to change options */ int s; +#endif /* LWIP_DEBUG */ /** level of the option to process */ int level; /** name of the option to process */ @@ -120,6 +182,18 @@ struct lwip_setgetsockopt_data { err_t err; }; +/** A struct sockaddr replacement that has the same alignment as sockaddr_in/ + * sockaddr_in6 if instantiated. + */ +union sockaddr_aligned { + struct sockaddr sa; +#if LWIP_IPV6 + struct sockaddr_in6 sin6; +#endif /* LWIP_IPV6 */ + struct sockaddr_in sin; +}; + + /** The global array of available sockets */ static struct lwip_sock sockets[NUM_SOCKETS]; /** The global list of tasks waiting for select */ @@ -139,14 +213,14 @@ static const int err_to_errno_table[] = { EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ EINVAL, /* ERR_VAL -6 Illegal value. */ EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ - ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */ - ECONNRESET, /* ERR_RST -9 Connection reset. */ - ESHUTDOWN, /* ERR_CLSD -10 Connection closed. */ - ENOTCONN, /* ERR_CONN -11 Not connected. */ - EIO, /* ERR_ARG -12 Illegal argument. */ - EADDRINUSE, /* ERR_USE -13 Address in use. */ - -1, /* ERR_IF -14 Low-level netif error */ - -1, /* ERR_ISCONN -15 Already connected. */ + EADDRINUSE, /* ERR_USE -8 Address in use. */ + EALREADY, /* ERR_ISCONN -9 Already connected. */ + ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */ + ECONNRESET, /* ERR_RST -11 Connection reset. */ + ENOTCONN, /* ERR_CLSD -12 Connection closed. */ + ENOTCONN, /* ERR_CONN -13 Not connected. */ + EIO, /* ERR_ARG -14 Illegal argument. */ + -1, /* ERR_IF -15 Low-level netif error */ }; #define ERR_TO_ERRNO_TABLE_SIZE \ @@ -257,7 +331,7 @@ alloc_socket(struct netconn *newconn, int accepted) sockets[i].rcvevent = 0; /* TCP sendbuf is empty, but the socket is not yet writable until connected * (unless it has been created by accept()). */ - sockets[i].sendevent = (newconn->type == NETCONN_TCP ? (accepted != 0) : 1); + sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1); sockets[i].errevent = 0; sockets[i].err = 0; sockets[i].select_waiting = 0; @@ -311,10 +385,9 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { struct lwip_sock *sock, *nsock; struct netconn *newconn; - ip_addr_t naddr; - u16_t port; + ipX_addr_t naddr; + u16_t port = 0; int newsock; - struct sockaddr_in sin; err_t err; SYS_ARCH_DECL_PROTECT(lev); @@ -334,6 +407,10 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) err = netconn_accept(sock->conn, &newconn); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } sock_set_errno(sock, err_to_errno(err)); return -1; } @@ -341,30 +418,26 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) /* Prevent automatic window updates, we do this on our own! */ netconn_set_noautorecved(newconn, 1); - /* get the IP address and port of the remote host */ - err = netconn_peer(newconn, &naddr, &port); - if (err != ERR_OK) { - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); - netconn_delete(newconn); - sock_set_errno(sock, err_to_errno(err)); - return -1; - } - /* Note that POSIX only requires us to check addr is non-NULL. addrlen must * not be NULL if addr is valid. */ - if (NULL != addr) { + if (addr != NULL) { + union sockaddr_aligned tempaddr; + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, ipX_2_ip(&naddr), &port); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - inet_addr_from_ipaddr(&sin.sin_addr, &naddr); - - if (*addrlen > sizeof(sin)) - *addrlen = sizeof(sin); - MEMCPY(addr, &sin, *addrlen); + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port); + if (*addrlen > tempaddr.sa.sa_len) { + *addrlen = tempaddr.sa.sa_len; + } + MEMCPY(addr, &tempaddr, *addrlen); } newsock = alloc_socket(newconn, 1); @@ -387,9 +460,12 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) newconn->socket = newsock; SYS_ARCH_UNPROTECT(lev); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); - ip_addr_debug_print(SOCKETS_DEBUG, &naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock)); + if (addr != NULL) { + LWIP_DEBUGF(SOCKETS_DEBUG, (" addr=")); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + } sock_set_errno(sock, 0); return newsock; @@ -399,30 +475,33 @@ int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) { struct lwip_sock *sock; - ip_addr_t local_addr; + ipX_addr_t local_addr; u16_t local_port; err_t err; - const struct sockaddr_in *name_in; sock = get_socket(s); if (!sock) { return -1; } + if (!SOCK_ADDR_TYPE_MATCH(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + /* check size, familiy and alignment of 'name' */ - LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && - ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)), + LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)), sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - name_in = (const struct sockaddr_in *)(void*)name; - - inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr); - local_port = name_in->sin_port; + LWIP_UNUSED_ARG(namelen); + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port))); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port)); - err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); + err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), local_port); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); @@ -449,7 +528,7 @@ lwip_close(int s) } if(sock->conn != NULL) { - is_tcp = netconn_type(sock->conn) == NETCONN_TCP; + is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP; } else { LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL); } @@ -466,34 +545,35 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) { struct lwip_sock *sock; err_t err; - const struct sockaddr_in *name_in; sock = get_socket(s); if (!sock) { return -1; } + if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + /* check size, familiy and alignment of 'name' */ - LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && - ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)), + LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name), sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - name_in = (const struct sockaddr_in *)(void*)name; - - if (name_in->sin_family == AF_UNSPEC) { + LWIP_UNUSED_ARG(namelen); + if (name->sa_family == AF_UNSPEC) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); err = netconn_disconnect(sock->conn); } else { - ip_addr_t remote_addr; + ipX_addr_t remote_addr; u16_t remote_port; - - inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr); - remote_port = name_in->sin_port; - + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port))); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port)); - err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); + err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), remote_port); } if (err != ERR_OK) { @@ -535,6 +615,10 @@ lwip_listen(int s, int backlog) if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } sock_set_errno(sock, err_to_errno(err)); return -1; } @@ -545,15 +629,13 @@ lwip_listen(int s, int backlog) int lwip_recvfrom(int s, void *mem, size_t len, int flags, - struct sockaddr *from, socklen_t *fromlen) + struct sockaddr *from, socklen_t *fromlen) { struct lwip_sock *sock; void *buf = NULL; struct pbuf *p; u16_t buflen, copylen; int off = 0; - ip_addr_t *addr; - u16_t port; u8_t done = 0; err_t err; @@ -586,7 +668,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, /* No data was left from the previous operation, so we try to get some from the network. */ - if (netconn_type(sock->conn) == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf); } else { err = netconn_recv(sock->conn, (struct netbuf **)&buf); @@ -616,7 +698,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, sock->lastdata = buf; } - if (netconn_type(sock->conn) == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { p = (struct pbuf *)buf; } else { p = ((struct netbuf *)buf)->p; @@ -639,7 +721,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, off += copylen; - if (netconn_type(sock->conn) == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); len -= copylen; if ( (len <= 0) || @@ -654,47 +736,37 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, /* Check to see from where the data was.*/ if (done) { - ip_addr_t fromaddr; - if (from && fromlen) { - struct sockaddr_in sin; - - if (netconn_type(sock->conn) == NETCONN_TCP) { - addr = &fromaddr; - netconn_getaddr(sock->conn, addr, &port, 0); +#if !SOCKETS_DEBUG + if (from && fromlen) +#endif /* !SOCKETS_DEBUG */ + { + u16_t port; + ipX_addr_t tmpaddr; + ipX_addr_t *fromaddr; + union sockaddr_aligned saddr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + fromaddr = &tmpaddr; + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0); } else { - addr = netbuf_fromaddr((struct netbuf *)buf); port = netbuf_fromport((struct netbuf *)buf); + fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf); } - - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - inet_addr_from_ipaddr(&sin.sin_addr, addr); - - if (*fromlen > sizeof(sin)) { - *fromlen = sizeof(sin); - } - - MEMCPY(from, &sin, *fromlen); - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, addr); + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, fromaddr, port); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, fromaddr); LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); - } else { #if SOCKETS_DEBUG - if (netconn_type(sock->conn) == NETCONN_TCP) { - addr = &fromaddr; - netconn_getaddr(sock->conn, addr, &port, 0); - } else { - addr = netbuf_fromaddr((struct netbuf *)buf); - port = netbuf_fromport((struct netbuf *)buf); + if (from && fromlen) +#endif /* SOCKETS_DEBUG */ + { + if (*fromlen > saddr.sa.sa_len) { + *fromlen = saddr.sa.sa_len; + } + MEMCPY(from, &saddr, *fromlen); } - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); -#endif /* SOCKETS_DEBUG */ } } @@ -703,7 +775,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, /* If this is a TCP socket, check if there is data left in the buffer. If so, it should be saved in the sock structure for next time around. */ - if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) { + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) { sock->lastdata = buf; sock->lastoffset += copylen; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf)); @@ -711,7 +783,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, sock->lastdata = NULL; sock->lastoffset = 0; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf)); - if (netconn_type(sock->conn) == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { pbuf_free((struct pbuf *)buf); } else { netbuf_delete((struct netbuf *)buf); @@ -720,7 +792,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, } } while (!done); - if (off > 0) { + if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) { /* update receive window */ netconn_recved(sock->conn, (u32_t)off); } @@ -746,6 +818,7 @@ lwip_send(int s, const void *data, size_t size, int flags) struct lwip_sock *sock; err_t err; u8_t write_flags; + size_t written; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", s, data, size, flags)); @@ -755,7 +828,7 @@ lwip_send(int s, const void *data, size_t size, int flags) return -1; } - if (sock->conn->type != NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { #if (LWIP_UDP || LWIP_RAW) return lwip_sendto(s, data, size, flags, NULL, 0); #else /* (LWIP_UDP || LWIP_RAW) */ @@ -764,22 +837,15 @@ lwip_send(int s, const void *data, size_t size, int flags) #endif /* (LWIP_UDP || LWIP_RAW) */ } - if ((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) { - if ((size > TCP_SND_BUF) || ((size / TCP_MSS) > TCP_SND_QUEUELEN)) { - /* too much data to ever send nonblocking! */ - sock_set_errno(sock, EMSGSIZE); - return -1; - } - } - write_flags = NETCONN_COPY | ((flags & MSG_MORE) ? NETCONN_MORE : 0) | ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); - err = netconn_write(sock->conn, data, size, write_flags); + written = 0; + err = netconn_write_partly(sock->conn, data, size, write_flags, &written); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); sock_set_errno(sock, err_to_errno(err)); - return (err == ERR_OK ? (int)size : -1); + return (err == ERR_OK ? (int)written : -1); } int @@ -789,7 +855,6 @@ lwip_sendto(int s, const void *data, size_t size, int flags, struct lwip_sock *sock; err_t err; u16_t short_size; - const struct sockaddr_in *to_in; u16_t remote_port; #if !LWIP_TCPIP_CORE_LOCKING struct netbuf buf; @@ -800,36 +865,45 @@ lwip_sendto(int s, const void *data, size_t size, int flags, return -1; } - if (sock->conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { #if LWIP_TCP return lwip_send(s, data, size, flags); #else /* LWIP_TCP */ + LWIP_UNUSED_ARG(flags); sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1; #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; LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || - ((tolen == sizeof(struct sockaddr_in)) && - ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))), + (IS_SOCK_ADDR_LEN_VALID(tolen) && + IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))), sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - to_in = (const struct sockaddr_in *)(void*)to; + LWIP_UNUSED_ARG(tolen); #if LWIP_TCPIP_CORE_LOCKING - /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ + /* Special speedup for fast UDP/RAW sending: call the raw API directly + instead of using the netconn functions. */ { struct pbuf* p; - ip_addr_t *remote_addr; + ipX_addr_t *remote_addr; + ipX_addr_t remote_addr_tmp; #if LWIP_NETIF_TX_SINGLE_PBUF p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM); if (p != NULL) { #if LWIP_CHECKSUM_ON_COPY u16_t chksum = 0; - if (sock->conn->type != NETCONN_RAW) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); } else #endif /* LWIP_CHECKSUM_ON_COPY */ @@ -840,25 +914,45 @@ lwip_sendto(int s, const void *data, size_t size, int flags, p->payload = (void*)data; #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ - if (to_in != NULL) { - inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr); - remote_port = ntohs(to_in->sin_port); + if (to != NULL) { + SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6, + to, &remote_addr_tmp, remote_port); + remote_addr = &remote_addr_tmp; } else { - remote_addr = IP_ADDR_ANY; - remote_port = 0; + remote_addr = &sock->conn->pcb.ip->remote_ip; +#if LWIP_UDP + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { + remote_port = sock->conn->pcb.udp->remote_port; + } else +#endif /* LWIP_UDP */ + { + remote_port = 0; + } } LOCK_TCPIP_CORE(); - if (sock->conn->type == NETCONN_RAW) { - err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr); - } else { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) { +#if LWIP_RAW + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr)); +#else /* LWIP_RAW */ + err = ERR_ARG; +#endif /* LWIP_RAW */ + } +#if LWIP_UDP && LWIP_RAW + else +#endif /* LWIP_UDP && LWIP_RAW */ + { +#if LWIP_UDP #if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p, - remote_addr, remote_port, 1, chksum); + ipX_2_ip(remote_addr), remote_port, 1, chksum); #else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p, - remote_addr, remote_port); + ipX_2_ip(remote_addr), remote_port); #endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ +#else /* LWIP_UDP */ + err = ERR_ARG; +#endif /* LWIP_UDP */ } UNLOCK_TCPIP_CORE(); @@ -874,18 +968,18 @@ lwip_sendto(int s, const void *data, size_t size, int flags, buf.flags = 0; #endif /* LWIP_CHECKSUM_ON_COPY */ if (to) { - inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr); - remote_port = ntohs(to_in->sin_port); - netbuf_fromport(&buf) = remote_port; + SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port); } else { - remote_port = 0; - ip_addr_set_any(&buf.addr); - netbuf_fromport(&buf) = 0; + remote_port = 0; + ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); } + netbuf_fromport(&buf) = remote_port; + - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%d"U16_F", flags=0x%x to=", + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=", s, data, short_size, flags)); - ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &buf.addr); LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); /* make the buffer point to the data that should be sent */ @@ -895,7 +989,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, err = ERR_MEM; } else { #if LWIP_CHECKSUM_ON_COPY - if (sock->conn->type != NETCONN_RAW) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); netbuf_set_chksum(&buf, chksum); err = ERR_OK; @@ -926,23 +1020,27 @@ lwip_socket(int domain, int type, int protocol) struct netconn *conn; int i; - LWIP_UNUSED_ARG(domain); +#if !LWIP_IPV6 + LWIP_UNUSED_ARG(domain); /* @todo: check this */ +#endif /* LWIP_IPV6 */ /* create a netconn */ switch (type) { case SOCK_RAW: - conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); + conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW), + (u8_t)protocol, event_callback); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; case SOCK_DGRAM: - conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? - NETCONN_UDPLITE : NETCONN_UDP, event_callback); + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, + ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) , + event_callback); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; case SOCK_STREAM: - conn = netconn_new_with_callback(NETCONN_TCP, event_callback); + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); if (conn != NULL) { @@ -1181,6 +1279,8 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); select_cb.prev->next = select_cb.next; } + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; SYS_ARCH_UNPROTECT(lev); sys_sem_free(&select_cb.sem); @@ -1208,8 +1308,6 @@ return_copy_fdsets: if (exceptset) { *exceptset = lexceptset; } - - return nready; } @@ -1223,6 +1321,7 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) int s; struct lwip_sock *sock; struct lwip_select_cb *scb; + int last_select_cb_ctr; SYS_ARCH_DECL_PROTECT(lev); LWIP_UNUSED_ARG(len); @@ -1285,56 +1384,51 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) return; } - SYS_ARCH_UNPROTECT(lev); - /* Now decide if anyone is waiting for this socket */ - /* NOTE: This code is written this way to protect the select link list - but to avoid a deadlock situation by releasing select_lock before - signalling for the select. This means we need to go through the list - multiple times ONLY IF a select was actually waiting. We go through - the list the number of waiting select calls + 1. This list is - expected to be small. */ - while (1) { - int last_select_cb_ctr; - SYS_ARCH_PROTECT(lev); - for (scb = select_cb_list; scb; scb = scb->next) { - /* @todo: unprotect with each loop and check for changes? */ - if (scb->sem_signalled == 0) { - /* Test this select call for our socket */ + /* NOTE: This code goes through the select_cb_list list multiple times + ONLY IF a select was actually waiting. We go through the list the number + of waiting select calls + 1. This list is expected to be small. */ + + /* At this point, SYS_ARCH is still protected! */ +again: + for (scb = select_cb_list; scb != NULL; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* semaphore not signalled yet */ + int do_signal = 0; + /* Test this select call for our socket */ + if (sock->rcvevent > 0) { if (scb->readset && FD_ISSET(s, scb->readset)) { - if (sock->rcvevent > 0) { - break; - } + do_signal = 1; } - if (scb->writeset && FD_ISSET(s, scb->writeset)) { - if (sock->sendevent != 0) { - break; - } + } + if (sock->sendevent != 0) { + if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { + do_signal = 1; } - if (scb->exceptset && FD_ISSET(s, scb->exceptset)) { - if (sock->errevent != 0) { - break; - } + } + if (sock->errevent != 0) { + if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { + do_signal = 1; } } - /* unlock interrupts with each step */ - last_select_cb_ctr = select_cb_ctr; - SYS_ARCH_UNPROTECT(lev); - SYS_ARCH_PROTECT(lev); - if (last_select_cb_ctr != select_cb_ctr) { - /* someone has changed select_cb_list, restart at the beginning */ - scb = select_cb_list; + if (do_signal) { + scb->sem_signalled = 1; + /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might + lead to the select thread taking itself off the list, invalidagin the semaphore. */ + sys_sem_signal(&scb->sem); } } - if (scb) { - scb->sem_signalled = 1; - sys_sem_signal(&scb->sem); - SYS_ARCH_UNPROTECT(lev); - } else { - SYS_ARCH_UNPROTECT(lev); - break; + /* unlock interrupts with each step */ + last_select_cb_ctr = select_cb_ctr; + SYS_ARCH_UNPROTECT(lev); + /* this makes sure interrupt protection time is short */ + SYS_ARCH_PROTECT(lev); + if (last_select_cb_ctr != select_cb_ctr) { + /* someone has changed select_cb_list, restart at the beginning */ + goto again; } } + SYS_ARCH_UNPROTECT(lev); } /** @@ -1356,7 +1450,7 @@ lwip_shutdown(int s, int how) } if (sock->conn != NULL) { - if (netconn_type(sock->conn) != NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { sock_set_errno(sock, EOPNOTSUPP); return EOPNOTSUPP; } @@ -1386,33 +1480,31 @@ static int lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) { struct lwip_sock *sock; - struct sockaddr_in sin; - ip_addr_t naddr; + union sockaddr_aligned saddr; + ipX_addr_t naddr; + u16_t port; sock = get_socket(s); if (!sock) { return -1; } - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - /* get the IP address and port */ - netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local); + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, &naddr, port); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, &naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port)); - - sin.sin_port = htons(sin.sin_port); - inet_addr_from_ipaddr(&sin.sin_addr, &naddr); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port)); - if (*namelen > sizeof(sin)) { - *namelen = sizeof(sin); + if (*namelen > saddr.sa.sa_len) { + *namelen = saddr.sa.sa_len; } + MEMCPY(name, &saddr, *namelen); - MEMCPY(name, &sin, *namelen); sock_set_errno(sock, 0); return 0; } @@ -1459,7 +1551,9 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) case SO_ERROR: case SO_KEEPALIVE: /* UNIMPL case SO_CONTIMEO: */ - /* UNIMPL case SO_SNDTIMEO: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO case SO_RCVTIMEO: #endif /* LWIP_SO_RCVTIMEO */ @@ -1486,7 +1580,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) err = EINVAL; } #if LWIP_UDP - if ((sock->conn->type != NETCONN_UDP) || + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP || ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { /* this flag is only available for UDP, not for UDP lite */ err = EAFNOSUPPORT; @@ -1528,7 +1622,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) if (*optlen < sizeof(u8_t)) { err = EINVAL; } - if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { err = EAFNOSUPPORT; } break; @@ -1550,7 +1644,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) } /* If this is no TCP socket, ignore any options. */ - if (sock->conn->type != NETCONN_TCP) + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) return 0; switch (optname) { @@ -1570,6 +1664,27 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) } /* switch (optname) */ break; #endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + #if LWIP_UDP && LWIP_UDPLITE /* Level: IPPROTO_UDPLITE */ case IPPROTO_UDPLITE: @@ -1579,7 +1694,7 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) } /* If this is no UDP lite socket, ignore any options. */ - if (sock->conn->type != NETCONN_UDPLITE) { + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { return 0; } @@ -1610,6 +1725,9 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) /* Now do the actual option processing */ data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ data.level = level; data.optname = optname; data.optval = optval; @@ -1664,13 +1782,13 @@ lwip_getsockopt_internal(void *arg) case SO_REUSEPORT: #endif /* SO_REUSE */ /*case SO_USELOOPBACK: UNIMPL */ - *(int*)optval = sock->conn->pcb.ip->so_options & optname; + *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", s, optname, (*(int*)optval?"on":"off"))); break; case SO_TYPE: - switch (NETCONNTYPE_GROUP(sock->conn->type)) { + switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) { case NETCONN_RAW: *(int*)optval = SOCK_RAW; break; @@ -1681,18 +1799,18 @@ lwip_getsockopt_internal(void *arg) *(int*)optval = SOCK_DGRAM; break; default: /* unrecognized socket type */ - *(int*)optval = sock->conn->type; + *(int*)optval = netconn_type(sock->conn); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", s, *(int *)optval)); - } /* switch (sock->conn->type) */ + } /* switch (netconn_type(sock->conn)) */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", s, *(int *)optval)); break; case SO_ERROR: - /* only overwrite if ERR_OK before */ - if (sock->err == 0) { + /* only overwrite ERR_OK or tempoary errors */ + if ((sock->err == 0) || (sock->err == EINPROGRESS)) { sock_set_errno(sock, err_to_errno(sock->conn->last_err)); } *(int *)optval = sock->err; @@ -1701,6 +1819,11 @@ lwip_getsockopt_internal(void *arg) s, *(int *)optval)); break; +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + *(int *)optval = netconn_get_sendtimeout(sock->conn); + break; +#endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO case SO_RCVTIMEO: *(int *)optval = netconn_get_recvtimeout(sock->conn); @@ -1800,6 +1923,23 @@ lwip_getsockopt_internal(void *arg) } /* switch (optname) */ break; #endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", + s, *(int *)optval)); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + #if LWIP_UDP && LWIP_UDPLITE /* Level: IPPROTO_UDPLITE */ case IPPROTO_UDPLITE: @@ -1855,7 +1995,9 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt /* UNIMPL case SO_DONTROUTE: */ case SO_KEEPALIVE: /* UNIMPL case case SO_CONTIMEO: */ - /* UNIMPL case case SO_SNDTIMEO: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO case SO_RCVTIMEO: #endif /* LWIP_SO_RCVTIMEO */ @@ -1880,7 +2022,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt err = EINVAL; } #if LWIP_UDP - if ((sock->conn->type != NETCONN_UDP) || + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) || ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { /* this flag is only available for UDP, not for UDP lite */ err = EAFNOSUPPORT; @@ -1911,7 +2053,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt if (optlen < sizeof(u8_t)) { err = EINVAL; } - if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { err = EAFNOSUPPORT; } break; @@ -1919,7 +2061,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt if (optlen < sizeof(struct in_addr)) { err = EINVAL; } - if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { err = EAFNOSUPPORT; } break; @@ -1927,7 +2069,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt if (optlen < sizeof(u8_t)) { err = EINVAL; } - if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { err = EAFNOSUPPORT; } break; @@ -1936,7 +2078,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt if (optlen < sizeof(struct ip_mreq)) { err = EINVAL; } - if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { err = EAFNOSUPPORT; } break; @@ -1957,7 +2099,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt } /* If this is no TCP socket, ignore any options. */ - if (sock->conn->type != NETCONN_TCP) + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) return 0; switch (optname) { @@ -1977,6 +2119,29 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt } /* switch (optname) */ break; #endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (optlen < sizeof(int)) { + err = EINVAL; + } + + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + #if LWIP_UDP && LWIP_UDPLITE /* Level: IPPROTO_UDPLITE */ case IPPROTO_UDPLITE: @@ -1986,7 +2151,7 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt } /* If this is no UDP lite socket, ignore any options. */ - if (sock->conn->type != NETCONN_UDPLITE) + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) return 0; switch (optname) { @@ -2017,6 +2182,9 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt /* Now do the actual option processing */ data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ data.level = level; data.optname = optname; data.optval = (void*)optval; @@ -2071,13 +2239,18 @@ lwip_setsockopt_internal(void *arg) #endif /* SO_REUSE */ /* UNIMPL case SO_USELOOPBACK: */ if (*(int*)optval) { - sock->conn->pcb.ip->so_options |= optname; + ip_set_option(sock->conn->pcb.ip, optname); } else { - sock->conn->pcb.ip->so_options &= ~optname; + ip_reset_option(sock->conn->pcb.ip, optname); } LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", s, optname, (*(int*)optval?"on":"off"))); break; +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval); + break; +#endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO case SO_RCVTIMEO: netconn_set_recvtimeout(sock->conn, *(int*)optval); @@ -2198,12 +2371,33 @@ lwip_setsockopt_internal(void *arg) } /* switch (optname) */ break; #endif /* LWIP_TCP*/ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (*(int*)optval) { + sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY; + } else { + sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n", + s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0))); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + #if LWIP_UDP && LWIP_UDPLITE /* Level: IPPROTO_UDPLITE */ case IPPROTO_UDPLITE: switch (optname) { case UDPLITE_SEND_CSCOV: - if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) { + if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { /* don't allow illegal values! */ sock->conn->pcb.udp->chksum_len_tx = 8; } else { @@ -2213,7 +2407,7 @@ lwip_setsockopt_internal(void *arg) s, (*(int*)optval)) ); break; case UDPLITE_RECV_CSCOV: - if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) { + if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { /* don't allow illegal values! */ sock->conn->pcb.udp->chksum_len_rx = 8; } else { @@ -2239,21 +2433,49 @@ int lwip_ioctl(int s, long cmd, void *argp) { struct lwip_sock *sock = get_socket(s); + u8_t val; +#if LWIP_SO_RCVBUF u16_t buflen = 0; s16_t recv_avail; - u8_t val; +#endif /* LWIP_SO_RCVBUF */ if (!sock) { return -1; } switch (cmd) { +#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE case FIONREAD: if (!argp) { sock_set_errno(sock, EINVAL); return -1; } +#if LWIP_FIONREAD_LINUXMODE + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + struct pbuf *p; + if (sock->lastdata) { + p = ((struct netbuf *)sock->lastdata)->p; + } else { + struct netbuf *rxbuf; + err_t err; + if (sock->rcvevent <= 0) { + *((u16_t*)argp) = 0; + } else { + err = netconn_recv(sock->conn, &rxbuf); + if (err != ERR_OK) { + *((u16_t*)argp) = 0; + } else { + sock->lastdata = rxbuf; + *((u16_t*)argp) = rxbuf->p->tot_len; + } + } + } + return 0; + } +#endif /* LWIP_FIONREAD_LINUXMODE */ +#if LWIP_SO_RCVBUF + /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */ SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); if (recv_avail < 0) { recv_avail = 0; @@ -2263,7 +2485,7 @@ lwip_ioctl(int s, long cmd, void *argp) /* Check if there is data left from the last recv operation. /maq 041215 */ if (sock->lastdata) { struct pbuf *p = (struct pbuf *)sock->lastdata; - if (netconn_type(sock->conn) != NETCONN_TCP) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { p = ((struct netbuf *)p)->p; } buflen = p->tot_len; @@ -2275,6 +2497,10 @@ lwip_ioctl(int s, long cmd, void *argp) LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); sock_set_errno(sock, 0); return 0; +#else /* LWIP_SO_RCVBUF */ + break; +#endif /* LWIP_SO_RCVBUF */ +#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */ case FIONBIO: val = 0; @@ -2287,10 +2513,11 @@ lwip_ioctl(int s, long cmd, void *argp) return 0; default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); - sock_set_errno(sock, ENOSYS); /* not yet implemented */ - return -1; + break; } /* switch (cmd) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; } /** A minimal implementation of fcntl. diff --git a/lib/liblwip/api/tcpip.c b/lib/liblwip/api/tcpip.c index 857e7d9bd..7c1c9cad1 100644 --- a/lib/liblwip/api/tcpip.c +++ b/lib/liblwip/api/tcpip.c @@ -103,6 +103,11 @@ tcpip_thread(void *arg) ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); } else #endif /* LWIP_ETHERNET */ +#if LWIP_IPV6 + if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) { + ip6_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_IPV6 */ { ip_input(msg->msg.inp.p, msg->msg.inp.netif); } @@ -117,12 +122,6 @@ tcpip_thread(void *arg) break; #endif /* LWIP_NETIF_API */ - case TCPIP_MSG_CALLBACK: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); - msg->msg.cb.function(msg->msg.cb.ctx); - memp_free(MEMP_TCPIP_MSG_API, msg); - break; - #if LWIP_TCPIP_TIMEOUT case TCPIP_MSG_TIMEOUT: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); @@ -136,6 +135,17 @@ tcpip_thread(void *arg) break; #endif /* LWIP_TCPIP_TIMEOUT */ + case TCPIP_MSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; + default: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); LWIP_ASSERT("tcpip_thread: invalid message", 0); @@ -172,22 +182,22 @@ tcpip_input(struct pbuf *p, struct netif *inp) #else /* LWIP_TCPIP_CORE_LOCKING_INPUT */ struct tcpip_msg *msg; - if (sys_mbox_valid(&mbox)) { - msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); - if (msg == NULL) { - return ERR_MEM; - } + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } - msg->type = TCPIP_MSG_INPKT; - msg->msg.inp.p = p; - msg->msg.inp.netif = inp; - if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { - memp_free(MEMP_TCPIP_MSG_INPKT, msg); - return ERR_MEM; - } - return ERR_OK; + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; } - return ERR_VAL; + return ERR_OK; #endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ } @@ -316,30 +326,6 @@ tcpip_apimsg(struct api_msg *apimsg) return ERR_VAL; } -#if LWIP_TCPIP_CORE_LOCKING -/** - * Call the lower part of a netconn_* function - * This function has exclusive access to lwIP core code by locking it - * before the function is called. - * - * @param apimsg a struct containing the function to call and its parameters - * @return ERR_OK (only for compatibility fo tcpip_apimsg()) - */ -err_t -tcpip_apimsg_lock(struct api_msg *apimsg) -{ -#ifdef LWIP_DEBUG - /* catch functions that don't set err */ - apimsg->msg.err = ERR_VAL; -#endif - - LOCK_TCPIP_CORE(); - apimsg->function(&(apimsg->msg)); - UNLOCK_TCPIP_CORE(); - return apimsg->msg.err; - -} -#endif /* LWIP_TCPIP_CORE_LOCKING */ #endif /* LWIP_NETCONN */ #if LWIP_NETIF_API @@ -392,6 +378,52 @@ tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) #endif /* !LWIP_TCPIP_CORE_LOCKING */ #endif /* LWIP_NETIF_API */ +/** + * Allocate a structure for a static callback message and initialize it. + * This is intended to be used to send "static" messages from interrupt context. + * + * @param function the function to call + * @param ctx parameter passed to function + * @return a struct pointer to pass to tcpip_trycallback(). + */ +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) +{ + struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return NULL; + } + msg->type = TCPIP_MSG_CALLBACK_STATIC; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + return (struct tcpip_callback_msg*)msg; +} + +/** + * Free a callback message allocated by tcpip_callbackmsg_new(). + * + * @param msg the message to free + */ +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) +{ + memp_free(MEMP_TCPIP_MSG_API, msg); +} + +/** + * Try to post a callback-message to the tcpip_thread mbox + * This is intended to be used to send "static" messages from interrupt context. + * + * @param msg pointer to the message to post + * @return sys_mbox_trypost() return code + */ +err_t +tcpip_trycallback(struct tcpip_callback_msg* msg) +{ + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + return sys_mbox_trypost(&mbox, msg); +} + /** * Initialize this module: * - initialize all sub modules diff --git a/lib/liblwip/core/Makefile.inc b/lib/liblwip/core/Makefile.inc index 50680909d..677a1f0b2 100644 --- a/lib/liblwip/core/Makefile.inc +++ b/lib/liblwip/core/Makefile.inc @@ -1,5 +1,5 @@ .PATH: \ - ${.CURDIR}/core ${.CURDIR}/core/ipv4 ${.CURDIR}/core/snmp + ${.CURDIR}/core ${.CURDIR}/core/ipv4 ${.CURDIR}/core/ipv6 ${.CURDIR}/core/snmp SRCS += \ def.c \ @@ -18,15 +18,24 @@ SRCS += \ memp.c \ pbuf.c \ stats.c \ + inet_chksum.c \ \ - ip.c \ + ip4.c \ igmp.c \ icmp.c \ - ip_addr.c \ + ip4_addr.c \ ip_frag.c \ - inet.c \ autoip.c \ - inet_chksum.c \ + \ + dhcp6.c \ + ethip6.c \ + icmp6.c \ + inet6.c \ + ip6.c \ + ip6_addr.c \ + ip6_frag.c \ + mld6.c \ + nd6.c \ \ mib_structs.c \ mib2.c \ diff --git a/lib/liblwip/core/dhcp.c b/lib/liblwip/core/dhcp.c index 37943ca64..279bcfb31 100644 --- a/lib/liblwip/core/dhcp.c +++ b/lib/liblwip/core/dhcp.c @@ -76,7 +76,6 @@ #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/def.h" -#include "lwip/sys.h" #include "lwip/dhcp.h" #include "lwip/autoip.h" #include "lwip/dns.h" @@ -84,6 +83,13 @@ #include +/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using + * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) + */ +#ifndef DHCP_CREATE_RAND_XID +#define DHCP_CREATE_RAND_XID 1 +#endif + /** Default for DHCP_GLOBAL_XID is 0xABCD0000 * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. * #define DHCP_GLOBAL_XID_HEADER "stdlib.h" @@ -115,7 +121,7 @@ #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 +#define DHCP_OPTION_IDX_DNS_SERVER 8 #define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS) /** Holds the decoded option values, only valid while in dhcp_recv. @@ -126,6 +132,11 @@ u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; @todo: move this into struct dhcp? */ u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; +#ifdef DHCP_GLOBAL_XID +static u32_t xid; +static u8_t xid_initialised; +#endif /* DHCP_GLOBAL_XID */ + #define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) #define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) #define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) @@ -164,6 +175,9 @@ static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); static void dhcp_option_short(struct dhcp *dhcp, u16_t value); static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +#if LWIP_NETIF_HOSTNAME +static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); +#endif /* LWIP_NETIF_HOSTNAME */ /* always add the DHCP options trailer to end and pad */ static void dhcp_option_trailer(struct dhcp *dhcp); @@ -295,17 +309,7 @@ dhcp_select(struct netif *netif) dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); #if LWIP_NETIF_HOSTNAME - if (netif->hostname != NULL) { - const char *p = (const char*)netif->hostname; - u8_t namelen = (u8_t)strlen(p); - if (namelen > 0) { - LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); - dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); - while (*p) { - dhcp_option_byte(dhcp, *p++); - } - } - } + dhcp_option_hostname(dhcp, netif); #endif /* LWIP_NETIF_HOSTNAME */ dhcp_option_trailer(dhcp); @@ -563,12 +567,10 @@ dhcp_handle_ack(struct netif *netif) #if LWIP_DNS /* DNS servers */ - n = 0; - while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) { + for(n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) { ip_addr_t dns_addr; ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n))); dns_setserver(n, &dns_addr); - n++; } #endif /* LWIP_DNS */ } @@ -592,6 +594,23 @@ dhcp_set_struct(struct netif *netif, struct dhcp *dhcp) netif->dhcp = dhcp; } +/** Removes a struct dhcp from a netif. + * + * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the + * struct dhcp since the memory is passed back to the heap. + * + * @param netif the netif from which to remove the struct dhcp + */ +void dhcp_cleanup(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif->dhcp != NULL) { + mem_free(netif->dhcp); + netif->dhcp = NULL; + } +} + /** * Start DHCP negotiation for a network interface. * @@ -659,7 +678,7 @@ dhcp_start(struct netif *netif) LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); return ERR_MEM; } - dhcp->pcb->so_options |= SOF_BROADCAST; + ip_set_option(dhcp->pcb, SOF_BROADCAST); /* set up local and remote port for the pcb */ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); @@ -709,7 +728,7 @@ dhcp_inform(struct netif *netif) return; } dhcp.pcb = pcb; - dhcp.pcb->so_options |= SOF_BROADCAST; + ip_set_option(dhcp.pcb, SOF_BROADCAST); udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); } @@ -762,7 +781,10 @@ dhcp_network_changed(struct netif *netif) default: dhcp->tries = 0; #if LWIP_DHCP_AUTOIP_COOP - dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } #endif /* LWIP_DHCP_AUTOIP_COOP */ dhcp_discover(netif); break; @@ -938,6 +960,11 @@ dhcp_bind(struct netif *netif) LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); } + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } + if (dhcp->subnet_mask_given) { /* copy offered network mask */ ip_addr_copy(sn_mask, dhcp->offered_sn_mask); @@ -945,11 +972,11 @@ dhcp_bind(struct netif *netif) /* subnet mask not given, choose a safe subnet mask given the network class */ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr); if (first_octet <= 127) { - ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000)); + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL)); } else if (first_octet >= 192) { - ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00)); + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL)); } else { - ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000)); + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL)); } } @@ -959,7 +986,7 @@ dhcp_bind(struct netif *netif) /* copy network address */ ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask); /* use first host address on network as gateway */ - ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001)); + ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL)); } #if LWIP_DHCP_AUTOIP_COOP @@ -1004,20 +1031,6 @@ dhcp_renew(struct netif *netif) dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); -#if LWIP_NETIF_HOSTNAME - if (netif->hostname != NULL) { - const char *p = (const char*)netif->hostname; - u8_t namelen = (u8_t)strlen(p); - if (namelen > 0) { - LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); - dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); - while (*p) { - dhcp_option_byte(dhcp, *p++); - } - } - } -#endif /* LWIP_NETIF_HOSTNAME */ - #if 0 dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); @@ -1027,6 +1040,11 @@ dhcp_renew(struct netif *netif) dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); #endif + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + /* append DHCP message trailer */ dhcp_option_trailer(dhcp); @@ -1068,17 +1086,7 @@ dhcp_rebind(struct netif *netif) dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); #if LWIP_NETIF_HOSTNAME - if (netif->hostname != NULL) { - const char *p = (const char*)netif->hostname; - u8_t namelen = (u8_t)strlen(p); - if (namelen > 0) { - LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); - dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); - while (*p) { - dhcp_option_byte(dhcp, *p++); - } - } - } + dhcp_option_hostname(dhcp, netif); #endif /* LWIP_NETIF_HOSTNAME */ #if 0 @@ -1161,6 +1169,9 @@ dhcp_release(struct netif *netif) err_t result; u16_t msecs; LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + if (dhcp == NULL) { + return ERR_ARG; + } /* idle DHCP client */ dhcp_set_state(dhcp, DHCP_OFF); @@ -1290,6 +1301,29 @@ dhcp_option_long(struct dhcp *dhcp, u32_t value) dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); } +#if LWIP_NETIF_HOSTNAME +static void +dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) +{ + if (netif->hostname != NULL) { + size_t namelen = strlen(netif->hostname); + if (namelen > 0) { + u8_t len; + const char *p = netif->hostname; + /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME + and 1 byte for trailer) */ + size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; + LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); + len = LWIP_MIN(namelen, available); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len); + while (len--) { + dhcp_option_byte(dhcp, *p++); + } + } + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + /** * Extract the DHCP message and the DHCP options. * @@ -1367,44 +1401,44 @@ again: offset--; break; case(DHCP_OPTION_SUBNET_MASK): - LWIP_ASSERT("len == 4", len == 4); + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; break; case(DHCP_OPTION_ROUTER): decode_len = 4; /* only copy the first given router */ - LWIP_ASSERT("len >= decode_len", len >= decode_len); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_ROUTER; break; case(DHCP_OPTION_DNS_SERVER): /* special case: there might be more than one server */ - LWIP_ASSERT("len % 4 == 0", len % 4 == 0); + LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;); /* limit number of DNS servers */ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); - LWIP_ASSERT("len >= decode_len", len >= decode_len); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_DNS_SERVER; break; case(DHCP_OPTION_LEASE_TIME): - LWIP_ASSERT("len == 4", len == 4); + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_LEASE_TIME; break; case(DHCP_OPTION_OVERLOAD): - LWIP_ASSERT("len == 1", len == 1); + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_OVERLOAD; break; case(DHCP_OPTION_MESSAGE_TYPE): - LWIP_ASSERT("len == 1", len == 1); + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_MSG_TYPE; break; case(DHCP_OPTION_SERVER_ID): - LWIP_ASSERT("len == 4", len == 4); + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_SERVER_ID; break; case(DHCP_OPTION_T1): - LWIP_ASSERT("len == 4", len == 4); + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_T1; break; case(DHCP_OPTION_T2): - LWIP_ASSERT("len == 4", len == 4); + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); decode_idx = DHCP_OPTION_IDX_T2; break; default: @@ -1418,32 +1452,39 @@ again: u16_t copy_len; decode_next: LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); - LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx)); - copy_len = LWIP_MIN(decode_len, 4); - pbuf_copy_partial(q, &value, copy_len, val_offset); - if (decode_len > 4) { - /* decode more than one u32_t */ - LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0); + if (!dhcp_option_given(dhcp, decode_idx)) { + copy_len = LWIP_MIN(decode_len, 4); + pbuf_copy_partial(q, &value, copy_len, val_offset); + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, htonl(value)); + decode_len -= 4; + val_offset += 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = ntohl(value); + } else { + LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); + value = ((u8_t*)&value)[0]; + } dhcp_got_option(dhcp, decode_idx); - dhcp_set_option_value(dhcp, decode_idx, htonl(value)); - decode_len -= 4; - val_offset += 4; - decode_idx++; - goto decode_next; - } else if (decode_len == 4) { - value = ntohl(value); - } else { - LWIP_ASSERT("invalid decode_len", decode_len == 1); - value = ((u8_t*)&value)[0]; + dhcp_set_option_value(dhcp, decode_idx, value); } - dhcp_got_option(dhcp, decode_idx); - dhcp_set_option_value(dhcp, decode_idx, value); } if (offset >= q->len) { offset -= q->len; offset_max -= q->len; - q = q->next; - options = (u8_t*)q->payload; + if ((offset < offset_max) && offset_max) { + q = q->next; + LWIP_ASSERT("next pbuf was null", q); + options = (u8_t*)q->payload; + } else { + // We've run out of bytes, probably no end marker. Don't proceed. + break; + } } } /* is this an overloaded message? */ @@ -1606,10 +1647,12 @@ dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) * with a packet analyser). We simply increment for each new request. * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + static u32_t xid; +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ static u32_t xid = 0xABCD0000; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ #else - static u32_t xid; - static u8_t xid_initialised = 0; if (!xid_initialised) { xid = DHCP_GLOBAL_XID; xid_initialised = !xid_initialised; @@ -1630,7 +1673,11 @@ dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) /* reuse transaction identifier in retransmissions */ if (dhcp->tries == 0) { - xid++; +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + xid = LWIP_RAND(); +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + xid++; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ } dhcp->xid = xid; LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, @@ -1660,7 +1707,7 @@ dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) ip_addr_set_zero(&dhcp->msg_out->giaddr); for (i = 0; i < DHCP_CHADDR_LEN; i++) { /* copy netif hardware address, pad with zeroes */ - dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/; + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/; } for (i = 0; i < DHCP_SNAME_LEN; i++) { dhcp->msg_out->sname[i] = 0; @@ -1714,9 +1761,8 @@ dhcp_option_trailer(struct dhcp *dhcp) LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; /* packet is too small, or not 4 byte aligned? */ - while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) { - /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */ - LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) && + (dhcp->options_out_len < DHCP_OPTIONS_LEN)) { /* add a fill/padding byte */ dhcp->msg_out->options[dhcp->options_out_len++] = 0; } diff --git a/lib/liblwip/core/dns.c b/lib/liblwip/core/dns.c index 2efc45176..788df7155 100644 --- a/lib/liblwip/core/dns.c +++ b/lib/liblwip/core/dns.c @@ -694,7 +694,7 @@ dns_check_entry(u8_t i) case DNS_STATE_DONE: { /* if the time to live is nul */ - if (--pEntry->ttl == 0) { + if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) { LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); /* flush this entry */ pEntry->state = DNS_STATE_UNUSED; @@ -816,6 +816,13 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t if (pEntry->found) { (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); } + if (pEntry->ttl == 0) { + /* RFC 883, page 29: "Zero values are + interpreted to mean that the RR can only be used for the + transaction in progress, and should not be cached." + -> flush this entry now */ + goto flushentry; + } /* deallocate memory and return */ goto memerr; } else { @@ -838,6 +845,7 @@ responseerr: if (pEntry->found) { (*pEntry->found)(pEntry->name, NULL, pEntry->arg); } +flushentry: /* flush this entry */ pEntry->state = DNS_STATE_UNUSED; pEntry->found = NULL; @@ -922,6 +930,7 @@ dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) * name is already in the local names table. * - ERR_INPROGRESS enqueue a request to be sent to the DNS server * for resolution if no errors are present. + * - ERR_ARG: dns client not initialized or invalid hostname * * @param hostname the hostname that is to be queried * @param addr pointer to a ip_addr_t where to store the address if it is already @@ -941,7 +950,7 @@ dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback foun if ((dns_pcb == NULL) || (addr == NULL) || (!hostname) || (!hostname[0]) || (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { - return ERR_VAL; + return ERR_ARG; } #if LWIP_HAVE_LOOPIF diff --git a/lib/liblwip/core/ipv4/inet_chksum.c b/lib/liblwip/core/inet_chksum.c similarity index 77% rename from lib/liblwip/core/ipv4/inet_chksum.c rename to lib/liblwip/core/inet_chksum.c index 960252f64..8bc42c147 100644 --- a/lib/liblwip/core/ipv4/inet_chksum.c +++ b/lib/liblwip/core/inet_chksum.c @@ -60,6 +60,7 @@ # ifndef LWIP_CHKSUM_ALGORITHM # define LWIP_CHKSUM_ALGORITHM 2 # endif +u16_t lwip_standard_chksum(void *dataptr, int len); #endif /* If none set: */ #ifndef LWIP_CHKSUM_ALGORITHM @@ -77,7 +78,7 @@ * @note accumulator size limits summable length to 64k * @note host endianess is irrelevant (p3 RFC1071) */ -static u16_t +u16_t lwip_standard_chksum(void *dataptr, u16_t len) { u32_t acc; @@ -131,7 +132,7 @@ lwip_standard_chksum(void *dataptr, u16_t len) * @return host order (!) lwip checksum (non-inverted Internet sum) */ -static u16_t +u16_t lwip_standard_chksum(void *dataptr, int len) { u8_t *pb = (u8_t *)dataptr; @@ -187,7 +188,7 @@ lwip_standard_chksum(void *dataptr, int len) * by Curt McDowell, Broadcom Corp. December 8th, 2005 */ -static u16_t +u16_t lwip_standard_chksum(void *dataptr, int len) { u8_t *pb = (u8_t *)dataptr; @@ -256,30 +257,13 @@ lwip_standard_chksum(void *dataptr, int len) } #endif -/* inet_chksum_pseudo: - * - * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. - * IP addresses are expected to be in network byte order. - * - * @param p chain of pbufs over that a checksum should be calculated (ip data part) - * @param src source ip address (used for checksum of pseudo header) - * @param dst destination ip address (used for checksum of pseudo header) - * @param proto ip protocol (used for checksum of pseudo header) - * @param proto_len length of the ip data part (used for checksum of pseudo header) - * @return checksum (as u16_t) to be saved directly in the protocol header - */ -u16_t -inet_chksum_pseudo(struct pbuf *p, - ip_addr_t *src, ip_addr_t *dest, - u8_t proto, u16_t proto_len) +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) { - u32_t acc; - u32_t addr; struct pbuf *q; - u8_t swapped; + u8_t swapped = 0; - acc = 0; - swapped = 0; /* iterate through all pbuf in chain */ for(q = p; q != NULL; q = q->next) { LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", @@ -299,12 +283,7 @@ inet_chksum_pseudo(struct pbuf *p, if (swapped) { acc = SWAP_BYTES_IN_WORD(acc); } - addr = ip4_addr_get_u32(src); - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); - addr = ip4_addr_get_u32(dest); - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); acc += (u32_t)htons(proto_len); @@ -329,18 +308,69 @@ inet_chksum_pseudo(struct pbuf *p, * @return checksum (as u16_t) to be saved directly in the protocol header */ u16_t -inet_chksum_pseudo_partial(struct pbuf *p, - ip_addr_t *src, ip_addr_t *dest, - u8_t proto, u16_t proto_len, u16_t chksum_len) +inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest) { u32_t acc; u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV6 */ + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, u32_t acc) +{ struct pbuf *q; - u8_t swapped; + u8_t swapped = 0; u16_t chklen; - acc = 0; - swapped = 0; /* iterate through all pbuf in chain */ for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", @@ -365,12 +395,7 @@ inet_chksum_pseudo_partial(struct pbuf *p, if (swapped) { acc = SWAP_BYTES_IN_WORD(acc); } - addr = ip4_addr_get_u32(src); - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); - addr = ip4_addr_get_u32(dest); - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); acc += (u32_t)htons(proto_len); @@ -382,6 +407,76 @@ inet_chksum_pseudo_partial(struct pbuf *p, return (u16_t)~(acc & 0xffffUL); } +/* inet_chksum_pseudo_partial: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. Will only compute for a + * portion of the payload. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param chksum_len number of payload bytes used to compute chksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV6 */ + /* inet_chksum: * * Calculates the Internet checksum over a portion of memory. Used primarily for IP diff --git a/lib/liblwip/core/init.c b/lib/liblwip/core/init.c index 949934ae5..65c3f06f4 100644 --- a/lib/liblwip/core/init.c +++ b/lib/liblwip/core/init.c @@ -56,6 +56,10 @@ #include "lwip/dns.h" #include "lwip/timers.h" #include "netif/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/api.h" /* Compile-time sanity checks for configuration errors. * These can be done independently of LWIP_DEBUG, without penalty. @@ -66,9 +70,6 @@ #if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" #endif -#if (!LWIP_ARP && ARP_QUEUEING) - #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h" -#endif #if (!LWIP_UDP && LWIP_UDPLITE) #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" #endif @@ -87,6 +88,7 @@ #if (!LWIP_UDP && LWIP_DNS) #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" #endif +#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ #if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" #endif @@ -99,30 +101,41 @@ #if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" #endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#endif /* !MEMP_MEM_MALLOC */ #if (LWIP_TCP && (TCP_WND > 0xffff)) #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" #endif #if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" #endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif #if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" #endif #if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" #endif -#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) - #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" -#endif #if (LWIP_NETIF_API && (NO_SYS==1)) #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" #endif #if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" #endif -#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) - #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" -#endif #if (!LWIP_NETCONN && LWIP_SOCKET) #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" #endif @@ -144,13 +157,6 @@ #if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" #endif -/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ -#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT)) - #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" -#endif -#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) - #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" -#endif #if (MEM_LIBC_MALLOC && MEM_USE_POOLS) #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" #endif @@ -160,9 +166,6 @@ #if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" #endif -#if (TCP_QUEUE_OOSEQ && !LWIP_TCP) - #error "TCP_QUEUE_OOSEQ requires LWIP_TCP" -#endif #if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" #endif @@ -172,8 +175,8 @@ #if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" #endif -#if LWIP_IGMP && !defined(LWIP_RAND) - #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" +#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND) + #error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" #endif #if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" @@ -184,6 +187,32 @@ #if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues" #endif +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_ACCEPTCONN != SOF_ACCEPTCONN + #error "SO_ACCEPTCONN != SOF_ACCEPTCONN" +#endif +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#if SO_LINGER != SOF_LINGER + #error "WARNING: SO_LINGER != SOF_LINGER" +#endif +#endif /* LWIP_SOCKET */ /* Compile-time checks for deprecated options. @@ -207,48 +236,54 @@ #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." #endif -#ifdef LWIP_DEBUG -static void -lwip_sanity_check(void) -{ - /* Warnings */ +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS #if LWIP_NETCONN - if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n")); +#if MEMP_MEM_MALLOC +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* MEMP_MEM_MALLOC */ #endif /* LWIP_NETCONN */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS #if LWIP_TCP - if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n")); - if (TCP_SND_BUF < 2 * TCP_MSS) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n")); - if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS))) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n")); - if (TCP_SNDLOWAT >= TCP_SND_BUF) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF.\n")); - if (TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN.\n")); - if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE)) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n")); - if (TCP_WND < TCP_MSS) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n")); +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif #endif /* LWIP_TCP */ -#if LWIP_SOCKET - /* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ - if (SO_ACCEPTCONN != SOF_ACCEPTCONN) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_ACCEPTCONN != SOF_ACCEPTCONN\n")); - if (SO_REUSEADDR != SOF_REUSEADDR) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_REUSEADDR != SOF_REUSEADDR\n")); - if (SO_KEEPALIVE != SOF_KEEPALIVE) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_KEEPALIVE != SOF_KEEPALIVE\n")); - if (SO_BROADCAST != SOF_BROADCAST) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_BROADCAST != SOF_BROADCAST\n")); - if (SO_LINGER != SOF_LINGER) - LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_LINGER != SOF_LINGER\n")); -#endif /* LWIP_SOCKET */ -} -#else /* LWIP_DEBUG */ -#define lwip_sanity_check() -#endif /* LWIP_DEBUG */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ /** * Perform Sanity check of user-configurable values, and initialize all modules. @@ -256,9 +291,6 @@ lwip_sanity_check(void) void lwip_init(void) { - /* Sanity check user-configurable values */ - lwip_sanity_check(); - /* Modules initialization */ stats_init(); #if !NO_SYS @@ -296,6 +328,13 @@ lwip_init(void) #if LWIP_DNS dns_init(); #endif /* LWIP_DNS */ +#if LWIP_IPV6 + ip6_init(); + nd6_init(); +#if LWIP_IPV6_MLD + mld6_init(); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ #if LWIP_TIMERS sys_timeouts_init(); diff --git a/lib/liblwip/core/ipv4/autoip.c b/lib/liblwip/core/ipv4/autoip.c index f5fbe34ef..b122da27e 100644 --- a/lib/liblwip/core/ipv4/autoip.c +++ b/lib/liblwip/core/ipv4/autoip.c @@ -122,14 +122,6 @@ static err_t autoip_bind(struct netif *netif); /* start sending probes for llipaddr */ static void autoip_start_probing(struct netif *netif); -/** - * Initialize this module - */ -void -autoip_init(void) -{ - LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n")); -} /** Set a statically allocated struct autoip to work with. * Using this prevents autoip_start to allocate it using mem_malloc. @@ -170,8 +162,8 @@ autoip_handle_arp_conflict(struct netif *netif) /* Somehow detect if we are defending or retreating */ unsigned char defend = 1; /* tbd */ - if(defend) { - if(netif->autoip->lastconflict > 0) { + if (defend) { + if (netif->autoip->lastconflict > 0) { /* retreat, there was a conflicting ARP in the last * DEFEND_INTERVAL seconds */ @@ -295,7 +287,7 @@ autoip_start(struct netif *netif) struct autoip *autoip = netif->autoip; err_t result = ERR_OK; - if(netif_is_up(netif)) { + if (netif_is_up(netif)) { netif_set_down(netif); } @@ -309,12 +301,12 @@ autoip_start(struct netif *netif) LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); - if(autoip == NULL) { + if (autoip == NULL) { /* no AutoIP client attached yet? */ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): starting new AUTOIP client\n")); autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); - if(autoip == NULL) { + if (autoip == NULL) { LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): could not allocate autoip\n")); return ERR_MEM; @@ -360,7 +352,7 @@ autoip_start_probing(struct netif *netif) * accquiring and probing address * compliant to RFC 3927 Section 2.2.1 */ - if(autoip->tried_llipaddr > MAX_CONFLICTS) { + if (autoip->tried_llipaddr > MAX_CONFLICTS) { autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; } } @@ -404,7 +396,7 @@ autoip_tmr() while (netif != NULL) { /* only act on AutoIP configured interfaces */ if (netif->autoip != NULL) { - if(netif->autoip->lastconflict > 0) { + if (netif->autoip->lastconflict > 0) { netif->autoip->lastconflict--; } @@ -414,10 +406,10 @@ autoip_tmr() switch(netif->autoip->state) { case AUTOIP_STATE_PROBING: - if(netif->autoip->ttw > 0) { + if (netif->autoip->ttw > 0) { netif->autoip->ttw--; } else { - if(netif->autoip->sent_num >= PROBE_NUM) { + if (netif->autoip->sent_num >= PROBE_NUM) { netif->autoip->state = AUTOIP_STATE_ANNOUNCING; netif->autoip->sent_num = 0; netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; @@ -439,10 +431,10 @@ autoip_tmr() break; case AUTOIP_STATE_ANNOUNCING: - if(netif->autoip->ttw > 0) { + if (netif->autoip->ttw > 0) { netif->autoip->ttw--; } else { - if(netif->autoip->sent_num == 0) { + if (netif->autoip->sent_num == 0) { /* We are here the first time, so we waited ANNOUNCE_WAIT seconds * Now we can bind to an IP address and use it. * @@ -458,7 +450,7 @@ autoip_tmr() netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; netif->autoip->sent_num++; - if(netif->autoip->sent_num >= ANNOUNCE_NUM) { + if (netif->autoip->sent_num >= ANNOUNCE_NUM) { netif->autoip->state = AUTOIP_STATE_BOUND; netif->autoip->sent_num = 0; netif->autoip->ttw = 0; @@ -494,12 +486,7 @@ autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) */ ip_addr_t sipaddr, dipaddr; struct eth_addr netifaddr; - netifaddr.addr[0] = netif->hwaddr[0]; - netifaddr.addr[1] = netif->hwaddr[1]; - netifaddr.addr[2] = netif->hwaddr[2]; - netifaddr.addr[3] = netif->hwaddr[3]; - netifaddr.addr[4] = netif->hwaddr[4]; - netifaddr.addr[5] = netif->hwaddr[5]; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without * structure packing (not using structure copy which breaks strict-aliasing rules). diff --git a/lib/liblwip/core/ipv4/icmp.c b/lib/liblwip/core/ipv4/icmp.c index 02e498d75..af471533b 100644 --- a/lib/liblwip/core/ipv4/icmp.c +++ b/lib/liblwip/core/ipv4/icmp.c @@ -70,7 +70,7 @@ static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); * Currently only processes icmp echo requests and sends * out the echo response. * - * @param p the icmp echo request packet, p->payload pointing to the ip header + * @param p the icmp echo request packet, p->payload pointing to the icmp header * @param inp the netif on which this packet was received */ void @@ -87,10 +87,9 @@ icmp_input(struct pbuf *p, struct netif *inp) ICMP_STATS_INC(icmp.recv); snmp_inc_icmpinmsgs(); - - iphdr = (struct ip_hdr *)p->payload; + iphdr = (struct ip_hdr *)ip_current_header(); hlen = IPH_HL(iphdr) * 4; - if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) { + if (p->len < sizeof(u16_t)*2) { LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); goto lenerr; } @@ -110,13 +109,13 @@ icmp_input(struct pbuf *p, struct netif *inp) int accepted = 1; #if !LWIP_MULTICAST_PING /* multicast destination address? */ - if (ip_addr_ismulticast(¤t_iphdr_dest)) { + if (ip_addr_ismulticast(ip_current_dest_addr())) { accepted = 0; } #endif /* LWIP_MULTICAST_PING */ #if !LWIP_BROADCAST_PING /* broadcast destination address? */ - if (ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { + if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { accepted = 0; } #endif /* LWIP_BROADCAST_PING */ @@ -190,12 +189,16 @@ icmp_input(struct pbuf *p, struct netif *inp) ip_addr_copy(iphdr->src, *ip_current_dest_addr()); ip_addr_copy(iphdr->dest, *ip_current_src_addr()); ICMPH_TYPE_SET(iecho, ICMP_ER); +#if CHECKSUM_GEN_ICMP /* adjust the checksum */ - if (iecho->chksum >= PP_HTONS(0xffff - (ICMP_ECHO << 8))) { + if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; } else { iecho->chksum += PP_HTONS(ICMP_ECHO << 8); } +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ /* Set the correct TTL and recalculate the header checksum. */ IPH_TTL_SET(iphdr, ICMP_TTL); diff --git a/lib/liblwip/core/ipv4/igmp.c b/lib/liblwip/core/ipv4/igmp.c index 390a5a741..bd52744f9 100644 --- a/lib/liblwip/core/ipv4/igmp.c +++ b/lib/liblwip/core/ipv4/igmp.c @@ -100,7 +100,7 @@ Steve Reynolds */ #define IGMP_TTL 1 #define IGMP_MINLEN 8 -#define ROUTER_ALERT 0x9404 +#define ROUTER_ALERT 0x9404U #define ROUTER_ALERTLEN 4 /* @@ -139,7 +139,6 @@ static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); static err_t igmp_remove_group(struct igmp_group *group); static void igmp_timeout( struct igmp_group *group); static void igmp_start_timer(struct igmp_group *group, u8_t max_time); -static void igmp_stop_timer(struct igmp_group *group); static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); static void igmp_send(struct igmp_group *group, u8_t type); @@ -382,14 +381,13 @@ igmp_remove_group(struct igmp_group *group) /** * Called from ip_input() if a new IGMP packet is received. * - * @param p received igmp packet, p->payload pointing to the ip header + * @param p received igmp packet, p->payload pointing to the igmp header * @param inp network interface on which the packet was received * @param dest destination ip address of the igmp packet */ void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) { - struct ip_hdr * iphdr; struct igmp_msg* igmp; struct igmp_group* group; struct igmp_group* groupref; @@ -397,8 +395,7 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) IGMP_STATS_INC(igmp.recv); /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ - iphdr = (struct ip_hdr *)p->payload; - if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { + if (p->len < IGMP_MINLEN) { pbuf_free(p); IGMP_STATS_INC(igmp.lenerr); LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); @@ -406,9 +403,9 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) } LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); - ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src)); LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); - ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest)); LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); /* Now calculate and check the checksum */ @@ -702,19 +699,10 @@ igmp_start_timer(struct igmp_group *group, u8_t max_time) if (max_time == 0) { max_time = 1; } +#ifdef LWIP_RAND /* ensure the random value is > 0 */ group->timer = (LWIP_RAND() % (max_time - 1)) + 1; -} - -/** - * Stop a timer for an igmp_group - * - * @param group the igmp_group for which to stop the timer - */ -static void -igmp_stop_timer(struct igmp_group *group) -{ - group->timer = 0; +#endif /* LWIP_RAND */ } /** diff --git a/lib/liblwip/core/ipv4/ip.c b/lib/liblwip/core/ipv4/ip4.c similarity index 86% rename from lib/liblwip/core/ipv4/ip.c rename to lib/liblwip/core/ipv4/ip4.c index 862c609da..642d32a68 100644 --- a/lib/liblwip/core/ipv4/ip.c +++ b/lib/liblwip/core/ipv4/ip4.c @@ -83,7 +83,7 @@ || (LWIP_IP_ACCEPT_UDP_PORT(port))) #elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ /* accept custom port only */ -#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port)) +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port)) #else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ /* accept DHCP client port only */ #define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) @@ -93,20 +93,8 @@ #define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 #endif /* LWIP_DHCP */ -/** - * The interface that provided the packet for the current callback - * invocation. - */ -struct netif *current_netif; - -/** - * Header of the input packet currently being processed. - */ -const struct ip_hdr *current_header; -/** Source IP address of current_header */ -ip_addr_t current_iphdr_src; -/** Destination IP address of current_header */ -ip_addr_t current_iphdr_dest; +/** Global data for both IPv4 and IPv6 */ +struct ip_globals ip_data; /** The IP header ID of the next outgoing IP packet */ static u16_t ip_id; @@ -125,8 +113,15 @@ ip_route(ip_addr_t *dest) { struct netif *netif; +#ifdef LWIP_HOOK_IP4_ROUTE + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + /* iterate through netifs */ - for(netif = netif_list; netif != NULL; netif = netif->next) { + for (netif = netif_list; netif != NULL; netif = netif->next) { /* network mask matches? */ if (netif_is_up(netif)) { if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { @@ -147,6 +142,41 @@ ip_route(ip_addr_t *dest) } #if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @param dest the destination IP address + * @return 1: can forward 0: discard + */ +static int +ip_canforward(struct pbuf *p) +{ + u32_t addr = ip4_addr_get_u32(ip_current_dest_addr()); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + /** * Forwards an IP packet. It finds an appropriate route for the * packet, decrements the TTL value of the packet, adjusts the @@ -163,28 +193,35 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) PERF_START; + if (!ip_canforward(p)) { + goto return_noroute; + } + /* RFC3927 2.7: do not forward link-local addresses */ - if (ip_addr_islinklocal(¤t_iphdr_dest)) { + if (ip_addr_islinklocal(ip_current_dest_addr())) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), - ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); goto return_noroute; } /* Find network interface where to forward this IP packet to. */ - netif = ip_route(¤t_iphdr_dest); + netif = ip_route(ip_current_dest_addr()); if (netif == NULL) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", - ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), - ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + /* @todo: send ICMP_DUR_NET? */ goto return_noroute; } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif == inp) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); goto return_noroute; } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ /* decrement TTL */ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); @@ -201,23 +238,37 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) } /* Incrementally update the IP checksum. */ - if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffff - 0x100)) { + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); } else { IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); } LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), - ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); IP_STATS_INC(ip.fw); IP_STATS_INC(ip.xmit); snmp_inc_ipforwdatagrams(); PERF_STOP("ip_forward"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip_frag(p, netif, ip_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { + /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); + } + return; + } /* transmit pbuf on chosen interface */ - netif->output(netif, p, ¤t_iphdr_dest); + netif->output(netif, p, ip_current_dest_addr()); return; return_noroute: snmp_inc_ipoutnoroutes(); @@ -264,6 +315,13 @@ ip_input(struct pbuf *p, struct netif *inp) return ERR_OK; } +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + /* obtain IP header length in number of 32-bit words */ iphdr_hlen = IPH_HL(iphdr); /* calculate IP header length in bytes */ @@ -311,13 +369,13 @@ ip_input(struct pbuf *p, struct netif *inp) pbuf_realloc(p, iphdr_len); /* copy IP addresses to aligned ip_addr_t */ - ip_addr_copy(current_iphdr_dest, iphdr->dest); - ip_addr_copy(current_iphdr_src, iphdr->src); + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest); + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src); /* match packet against an interface, i.e. is this packet for us? */ #if LWIP_IGMP - if (ip_addr_ismulticast(¤t_iphdr_dest)) { - if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ¤t_iphdr_dest))) { + if (ip_addr_ismulticast(ip_current_dest_addr())) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) { netif = inp; } else { netif = NULL; @@ -340,9 +398,9 @@ ip_input(struct pbuf *p, struct netif *inp) /* interface is up and configured? */ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { /* unicast to this interface address? */ - if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) || + if (ip_addr_cmp(ip_current_dest_addr(), &(netif->ip_addr)) || /* or broadcast on this interface network address? */ - ip_addr_isbroadcast(¤t_iphdr_dest, netif)) { + ip_addr_isbroadcast(ip_current_dest_addr(), netif)) { LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", netif->name[0], netif->name[1])); /* break out of for loop */ @@ -352,7 +410,7 @@ ip_input(struct pbuf *p, struct netif *inp) /* connections to link-local addresses must persist after changing the netif's address (RFC3927 ch. 1.9) */ if ((netif->autoip != NULL) && - ip_addr_cmp(¤t_iphdr_dest, &(netif->autoip->llipaddr))) { + ip_addr_cmp(ip_current_dest_addr(), &(netif->autoip->llipaddr))) { LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n", netif->name[0], netif->name[1])); /* break out of for loop */ @@ -400,10 +458,10 @@ ip_input(struct pbuf *p, struct netif *inp) /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ #if IP_ACCEPT_LINK_LAYER_ADDRESSING /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ - if (check_ip_src && !ip_addr_isany(¤t_iphdr_src)) + if (check_ip_src && !ip_addr_isany(ip_current_src_addr())) #endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ - { if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) || - (ip_addr_ismulticast(¤t_iphdr_src))) { + { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) || + (ip_addr_ismulticast(ip_current_src_addr()))) { /* packet source is not valid */ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); /* free (drop) packet pbufs */ @@ -421,7 +479,7 @@ ip_input(struct pbuf *p, struct netif *inp) LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); #if IP_FORWARD /* non-broadcast packet? */ - if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { /* try to forward IP packet on (other) interfaces */ ip_forward(p, iphdr, inp); } else @@ -480,14 +538,16 @@ ip_input(struct pbuf *p, struct netif *inp) ip_debug_print(p); LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); - current_netif = inp; - current_header = iphdr; + ip_data.current_netif = inp; + ip_data.current_ip4_header = iphdr; + ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; #if LWIP_RAW /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) #endif /* LWIP_RAW */ { + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ switch (IPH_PROTO(iphdr)) { #if LWIP_UDP @@ -513,14 +573,15 @@ ip_input(struct pbuf *p, struct netif *inp) #endif /* LWIP_ICMP */ #if LWIP_IGMP case IP_PROTO_IGMP: - igmp_input(p, inp, ¤t_iphdr_dest); + igmp_input(p, inp, ip_current_dest_addr()); break; #endif /* LWIP_IGMP */ default: #if LWIP_ICMP /* send ICMP destination protocol unreachable unless is was a broadcast */ - if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) && - !ip_addr_ismulticast(¤t_iphdr_dest)) { + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && + !ip_addr_ismulticast(ip_current_dest_addr())) { + pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */ p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } @@ -535,10 +596,12 @@ ip_input(struct pbuf *p, struct netif *inp) } } - current_netif = NULL; - current_header = NULL; - ip_addr_set_any(¤t_iphdr_src); - ip_addr_set_any(¤t_iphdr_dest); + /* @todo: this is not really necessary... */ + ip_data.current_netif = NULL; + ip_data.current_ip4_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip_addr_set_any(ip_current_src_addr()); + ip_addr_set_any(ip_current_dest_addr()); return ERR_OK; } @@ -625,7 +688,7 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); } #if CHECKSUM_GEN_IP_INLINE - for (i = 0; i < optlen_aligned; i += sizeof(u16_t)) { + for (i = 0; i < optlen_aligned/2; i++) { chk_sum += ((u16_t*)p->payload)[i]; } #endif /* CHECKSUM_GEN_IP_INLINE */ @@ -657,9 +720,10 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; #endif /* CHECKSUM_GEN_IP_INLINE */ - IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos); + IPH_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); #if CHECKSUM_GEN_IP_INLINE - chk_sum += iphdr->_v_hl_tos; + chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); #endif /* CHECKSUM_GEN_IP_INLINE */ IPH_LEN_SET(iphdr, htons(p->tot_len)); #if CHECKSUM_GEN_IP_INLINE @@ -801,9 +865,9 @@ ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, return ERR_RTE; } - netif->addr_hint = addr_hint; + NETIF_SET_HWADDRHINT(netif, addr_hint); err = ip_output_if(p, src, dest, ttl, tos, proto, netif); - netif->addr_hint = NULL; + NETIF_SET_HWADDRHINT(netif, NULL); return err; } @@ -817,9 +881,6 @@ void ip_debug_print(struct pbuf *p) { struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; - u8_t *payload; - - payload = (u8_t *)iphdr + IP_HLEN; LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); diff --git a/lib/liblwip/core/ipv4/ip_addr.c b/lib/liblwip/core/ipv4/ip4_addr.c similarity index 99% rename from lib/liblwip/core/ipv4/ip_addr.c rename to lib/liblwip/core/ipv4/ip4_addr.c index 7e953d375..8f633ff23 100644 --- a/lib/liblwip/core/ipv4/ip_addr.c +++ b/lib/liblwip/core/ipv4/ip4_addr.c @@ -93,7 +93,7 @@ ip4_addr_netmask_valid(u32_t netmask) u32_t nm_hostorder = lwip_htonl(netmask); /* first, check for the first zero */ - for (mask = 1U << 31 ; mask != 0; mask >>= 1) { + for (mask = 1UL << 31 ; mask != 0; mask >>= 1) { if ((nm_hostorder & mask) == 0) { break; } diff --git a/lib/liblwip/core/ipv6/README b/lib/liblwip/core/ipv6/README new file mode 100644 index 000000000..362000486 --- /dev/null +++ b/lib/liblwip/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/lib/liblwip/core/ipv4/inet.c b/lib/liblwip/core/ipv6/dhcp6.c similarity index 81% rename from lib/liblwip/core/ipv4/inet.c rename to lib/liblwip/core/ipv6/dhcp6.c index e283a5766..9656c3b2b 100644 --- a/lib/liblwip/core/ipv4/inet.c +++ b/lib/liblwip/core/ipv6/dhcp6.c @@ -1,11 +1,11 @@ /** * @file - * Functions common to all TCP/IPv4 modules, such as the byte order functions. * + * DHCPv6. */ /* - * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * Copyright (c) 2010 Inico Technologies Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -32,11 +32,19 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Adam Dunkels + * Author: Ivan Delamer * + * + * Please coordinate changes and requests with Ivan Delamer + * */ #include "lwip/opt.h" -#include "lwip/inet.h" +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +#endif /* LWIP_IPV6_DHCP6 */ diff --git a/lib/liblwip/core/ipv6/ethip6.c b/lib/liblwip/core/ipv6/ethip6.c new file mode 100644 index 000000000..ab9783a0d --- /dev/null +++ b/lib/liblwip/core/ipv6/ethip6.c @@ -0,0 +1,193 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * 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 + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET + +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" + +#include + +#define ETHTYPE_IPV6 0x86DD + +/** The ethernet address */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[6]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Ethernet header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +/** + * Send an IPv6 packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +ethip6_send(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be 6 for ethip6!", + (netif->hwaddr_len == 6)); + SMEMCPY(ðhdr->dest, dst, 6); + SMEMCPY(ðhdr->src, src, 6); + ethhdr->type = PP_HTONS(ETHTYPE_IPV6); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. + * + * For IPv6 multicast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, ... + * + * @TODO anycast addresses + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @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 etharp_query() or etharp_send_ip(). + */ +err_t +ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr) +{ + struct eth_addr dest; + s8_t i; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + return ERR_BUF; + } + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + /* Hash IP multicast address to MAC address.*/ + dest.addr[0] = 0x33; + dest.addr[1] = 0x33; + dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0]; + dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1]; + dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2]; + dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3]; + + /* Send out. */ + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* 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; + } + + /* 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. */ + SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We should queue packet on this interface. */ + pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR); + return nd6_queue_packet(i, q); +} + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/lib/liblwip/core/ipv6/icmp6.c b/lib/liblwip/core/ipv6/icmp6.c new file mode 100644 index 000000000..ea82682ef --- /dev/null +++ b/lib/liblwip/core/ipv6/icmp6.c @@ -0,0 +1,337 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * 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 + * + */ + +#include "lwip/opt.h" + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif +#if LWIP_ICMP6_DATASIZE == 0 +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/* Forward declarations */ +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); + + +/** + * Process an input ICMPv6 message. Called by ip6_input. + * + * Will generate a reply for echo requests. Other messages are forwarded + * to nd6_input, or mld6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +icmp6_input(struct pbuf *p, struct netif *inp) +{ + struct icmp6_hdr *icmp6hdr; + struct pbuf * r; + ip6_addr_t * reply_src; + + ICMP6_STATS_INC(icmp6.recv); + + /* Check that ICMPv6 header fits in payload */ + if (p->len < sizeof(struct icmp6_hdr)) { + /* drop short packets */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.lenerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + +#if LWIP_ICMP6_CHECKSUM_CHECK + if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), + ip6_current_dest_addr()) != 0) { + /* Checksum failed */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.chkerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_ICMP6_CHECKSUM_CHECK */ + + switch (icmp6hdr->type) { + case ICMP6_TYPE_NA: /* Neighbor advertisement */ + case ICMP6_TYPE_NS: /* Neighbor solicitation */ + case ICMP6_TYPE_RA: /* Router advertisement */ + case ICMP6_TYPE_RD: /* Redirect */ + case ICMP6_TYPE_PTB: /* Packet too big */ + nd6_input(p, inp); + return; + break; + case ICMP6_TYPE_RS: +#if LWIP_IPV6_FORWARD + /* TODO implement router functionality */ +#endif + break; +#if LWIP_IPV6_MLD + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: + mld6_input(p, inp); + return; + break; +#endif + case ICMP6_TYPE_EREQ: +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_MULTICAST_PING */ + + /* Allocate reply. */ + r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); + if (r == NULL) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + + /* Copy echo request. */ + if (pbuf_copy(r, p) != ERR_OK) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.err); + return; + } + + /* Determine reply source IPv6 address. */ +#if LWIP_MULTICAST_PING + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); + if (reply_src == NULL) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else +#endif /* LWIP_MULTICAST_PING */ + { + reply_src = ip6_current_dest_addr(); + } + + /* Set fields in reply. */ + ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, + IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); + + /* Send reply. */ + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(r, reply_src, ip6_current_src_addr(), + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); + pbuf_free(r); + + break; + default: + ICMP6_STATS_INC(icmp6.proterr); + ICMP6_STATS_INC(icmp6.drop); + break; + } + + pbuf_free(p); +} + + +/** + * Send an icmpv6 'destination unreachable' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the unreachable type + */ +void +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); +} + +/** + * Send an icmpv6 'packet too big' packet. + * + * @param p the input packet for which the 'packet too big' should be sent, + * p->payload pointing to the IPv6 header + * @param mtu the maximum mtu that we can accept + */ +void +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); +} + +/** + * Send an icmpv6 'time exceeded' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + */ +void +icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); +} + +/** + * Send an icmpv6 'parameter problem' packet. + * + * @param p the input packet for which the 'param problem' should be sent, + * p->payload pointing to the IP header + * @param c ICMPv6 code for the param problem type + * @param pointer the pointer to the byte where the parameter is found + */ +void +icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) +{ + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); +} + +/** + * Send an ICMPv6 packet in response to an incoming packet. + * + * @param p the input packet for which the response should be sent, + * p->payload pointing to the IPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + * @param type Type of the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +{ + struct pbuf *q; + struct icmp6_hdr *icmp6hdr; + ip6_addr_t *reply_src, *reply_dest; + ip6_addr_t reply_src_local, reply_dest_local; + struct ip6_hdr *ip6hdr; + struct netif *netif; + + /* ICMPv6 header + IPv6 header + data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); + + icmp6hdr = (struct icmp6_hdr *)q->payload; + icmp6hdr->type = type; + icmp6hdr->code = code; + icmp6hdr->data = data; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Get the destination address and netif for this ICMP message. */ + if ((ip_current_netif() == NULL) || + ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { + /* Special case, as ip6_current_xxx is either NULL, or points + * to a different packet than the one that expired. + * We must use the addresses that are stored in the expired packet. */ + ip6hdr = (struct ip6_hdr *)p->payload; + /* copy from packed address to aligned address */ + ip6_addr_copy(reply_dest_local, ip6hdr->src); + ip6_addr_copy(reply_src_local, ip6hdr->dest); + reply_dest = &reply_dest_local; + reply_src = &reply_src_local; + netif = ip6_route(reply_src, reply_dest); + if (netif == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else { + netif = ip_current_netif(); + reply_dest = ip6_current_src_addr(); + + /* Select an address to use as source. */ + reply_src = ip6_select_source_address(netif, reply_dest); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + + /* calculate checksum */ + icmp6hdr->chksum = 0; + icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, + reply_src, reply_dest); + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(q); +} + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/lib/liblwip/core/ipv6/inet6.c b/lib/liblwip/core/ipv6/inet6.c new file mode 100644 index 000000000..bdf4ff4ff --- /dev/null +++ b/lib/liblwip/core/ipv6/inet6.c @@ -0,0 +1,51 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * 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 + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/inet6.h" + +/** @see ip6_addr.c for implementation of functions. */ + +#endif /* LWIP_IPV6 */ diff --git a/lib/liblwip/core/ipv6/ip6.c b/lib/liblwip/core/ipv6/ip6.c new file mode 100644 index 000000000..6a8cafe06 --- /dev/null +++ b/lib/liblwip/core/ipv6/ip6.c @@ -0,0 +1,1028 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * 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 + * + */ + + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_frag.h" +#include "lwip/icmp6.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/dhcp6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + + +/** + * Finds the appropriate network interface for a given IPv6 address. It tries to select + * a netif following a sequence of heuristics: + * 1) if there is only 1 netif, return it + * 2) if the destination is a link-local address, try to match the src address to a netif. + * this is a tricky case because with multiple netifs, link-local addresses only have + * meaning within a particular subnet/link. + * 3) tries to match the destination subnet to a configured address + * 4) tries to find a router + * 5) tries to match the source address to the netif + * 6) returns the default netif, if configured + * + * @param src the source IPv6 address, if known + * @param dest the destination IPv6 address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip6_route(struct ip6_addr *src, struct ip6_addr *dest) +{ + struct netif *netif; + s8_t i; + + /* If single netif configuration, fast return. */ + if ((netif_list != NULL) && (netif_list->next == NULL)) { + return netif_list; + } + + /* Special processing for link-local addresses. */ + if (ip6_addr_islinklocal(dest)) { + if (ip6_addr_isany(src)) { + /* Use default netif. */ + return netif_default; + } + + /* Try to find the netif for the source address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* netif not found, use default netif */ + return netif_default; + } + + /* See if the destination subnet matches a configured address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* 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) { + return default_router_list[i].neighbor_entry->netif; + } + } + } + + /* try with the netif that matches the source address. */ + if (!ip6_addr_isany(src)) { + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + } + + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. + * + * @param netif the netif on which to send a packet + * @param dest the destination we are trying to reach + * @return the most suitable source address to use, or NULL if no suitable + * source address is found + */ +ip6_addr_t * +ip6_select_source_address(struct netif *netif, ip6_addr_t * dest) +{ + ip6_addr_t * src = NULL; + u8_t i; + + /* If dest is link-local, choose a link-local source. */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a site-local with matching prefix. */ + if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a unique-local with matching prefix. */ + if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a global with best matching prefix. */ + if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isglobal(netif_ip6_addr(netif, i))) { + if (src == NULL) { + src = netif_ip6_addr(netif, i); + } + else { + /* Replace src only if we find a prefix match. */ + /* TODO find longest matching prefix. */ + if ((!(ip6_addr_netcmp(src, dest))) && + ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { + src = netif_ip6_addr(netif, i); + } + } + } + } + if (src != NULL) { + return src; + } + } + + /* Last resort: see if arbitrary prefix matches. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + + return NULL; +} + +#if LWIP_IPV6_FORWARD +/** + * Forwards an IPv6 packet. It finds an appropriate route for the + * packet, decrements the HL value of the packet, and outputs + * the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IPv6 header of the input packet + * @param inp the netif on which this packet was received + */ +static void +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())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* decrement HL */ + IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); + /* send ICMP6 if HL == 0 */ + if (IP6H_HOPLIM(iphdr) == 0) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_time_exceeded(p, ICMP6_TE_HL); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + if (netif->mtu && (p->tot_len > netif->mtu)) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_packet_too_big(p, netif->mtu); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); + + /* transmit pbuf on chosen interface */ + netif->output_ip6(netif, p, ip6_current_dest_addr()); + IP6_STATS_INC(ip6.fw); + IP6_STATS_INC(ip6.xmit); + return; +} +#endif /* LWIP_IPV6_FORWARD */ + + +/** + * This function is called by the network interface device driver when + * an IPv6 packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip6_forward). + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IPv6 packet (p->payload points to IPv6 header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip6_input(struct pbuf *p, struct netif *inp) +{ + struct ip6_hdr *ip6hdr; + struct netif *netif; + u8_t nexth; + u16_t hlen; /* the current header length */ + u8_t i; +#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ + @todo + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP6_STATS_INC(ip6.recv); + + /* identify the IP header */ + ip6hdr = (struct ip6_hdr *)p->payload; + if (IP6H_V(ip6hdr) != 6) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U16_F"\n", + IP6H_V(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { + if (IP6_HLEN > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + IP6_HLEN, p->len)); + } + if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr)); + + /* copy IP addresses to aligned ip6_addr_t */ + ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); + ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); + + /* current header pointer. */ + ip_data.current_ip6_header = ip6hdr; + + /* In netif, used in case we need to send ICMPv6 packets back. */ + ip_data.current_netif = inp; + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* Always joined to multicast if-local and link-local all-nodes group. */ + if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || + ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { + netif = inp; + } +#if LWIP_IPV6_MLD + else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { + netif = inp; + } +#else /* LWIP_IPV6_MLD */ + else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { + /* Filter solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = NULL; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + netif = inp; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + break; + } + } + } +#endif /* LWIP_IPV6_MLD */ + else { + netif = NULL; + } + } + else { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + /* interface is up? */ + if (netif_is_up(netif)) { + /* unicast to this interface address? address configured? */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { + /* exit outer loop */ + goto netif_found; + } + } + } + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + /* Do not match link-local addresses to other netifs. */ + netif = NULL; + break; + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); +netif_found: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", + netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); + } + + /* "::" packet source address? (used in duplicate address detection) */ + if (ip6_addr_isany(ip6_current_src_addr()) && + (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { + /* packet source is not valid */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); +#if LWIP_IPV6_FORWARD + /* non-multicast packet? */ + if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* try to forward IP packet on (other) interfaces */ + ip6_forward(p, ip6hdr, inp); + } +#endif /* LWIP_IPV6_FORWARD */ + pbuf_free(p); + goto ip6_input_cleanup; + } + + /* current netif pointer. */ + ip_data.current_netif = netif; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Process known option extension headers, if present. */ + while (nexth != IP6_NEXTH_NONE) + { + switch (nexth) { + case IP6_NEXTH_HOPBYHOP: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_DESTOPTS: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_ROUTING: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + + case IP6_NEXTH_FRAGMENT: + { + struct ip6_frag_hdr * frag_hdr; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); + + frag_hdr = (struct ip6_frag_hdr *)p->payload; + + /* Get next header type. */ + nexth = frag_hdr->_nexth; + + /* Fragment Header length. */ + hlen = 8; + ip_data.current_ip_header_tot_len += hlen; + + /* Make sure this header fits in current pbuf. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_FRAG_STATS_INC(ip6_frag.lenerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto ip6_input_cleanup; + } + + /* Offset == 0 and more_fragments == 0? */ + if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) && + ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) { + + /* This is a 1-fragment packet, usually a packet that we have + * already reassembled. Skip this header anc continue. */ + pbuf_header(p, -hlen); + } + else { +#if LWIP_IPV6_REASS + + /* reassemble the packet */ + p = ip6_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + goto ip6_input_cleanup; + } + + /* Returned p point to IPv6 header. + * Update all our variables and pointers and continue. */ + ip6hdr = (struct ip6_hdr *)p->payload; + nexth = IP6H_NEXTH(ip6hdr); + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + pbuf_header(p, -IP6_HLEN); + +#else /* LWIP_IPV6_REASS */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.opterr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; +#endif /* LWIP_IPV6_REASS */ + } + break; + } + default: + goto options_done; + break; + } + } +options_done: + + /* p points to IPv6 header again. */ + pbuf_header(p, ip_data.current_ip_header_tot_len); + + /* send to upper layers */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); + ip6_debug_print(p); + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + switch (nexth) { + case IP6_NEXTH_NONE: + pbuf_free(p); + break; +#if LWIP_UDP + case IP6_NEXTH_UDP: +#if LWIP_UDPLITE + case IP6_NEXTH_UDPLITE: +#endif /* LWIP_UDPLITE */ + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + icmp6_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP6 + /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ + if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && + (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { + icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); + } +#endif /* LWIP_ICMP */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.proterr); + IP6_STATS_INC(ip6.drop); + break; + } + } + +ip6_input_cleanup: + ip_data.current_netif = NULL; + ip_data.current_ip6_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); + ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); + + return ERR_OK; +} + + +/** + * Sends an IPv6 packet on a network interface. This function constructs + * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is + * used as source (usually during network startup). If the source IPv6 address it + * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network + * interface is filled in as source address. If the destination IPv6 address is + * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points + * to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IPv6/LINK headers + * returns errors returned by netif->output + */ +err_t +ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /* generate IPv6 header */ + if (pbuf_header(p, IP6_HLEN)) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + ip6hdr = (struct ip6_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", + (p->len >= sizeof(struct ip6_hdr))); + + IP6H_HOPLIM_SET(ip6hdr, hl); + IP6H_NEXTH_SET(ip6hdr, nexth); + + /* dest cannot be NULL here */ + ip6_addr_copy(ip6hdr->dest, *dest); + + IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); + IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); + + if (src == NULL) { + src = IP6_ADDR_ANY; + } + else if (ip6_addr_isany(src)) { + src = ip6_select_source_address(netif, dest); + if ((src == NULL) || ip6_addr_isany(src)) { + /* No appropriate source address was found for this packet. */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + } + /* src cannot be NULL here */ + ip6_addr_copy(ip6hdr->src, *src); + + } else { + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; + } + + IP6_STATS_INC(ip6.xmit); + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip6_debug_print(p); + +#if ENABLE_LOOPBACK + /* TODO implement loopback for v6 + if (ip6_addr_cmp(dest, netif_ip6_addr(0))) { + return netif_loop_output(netif, p, dest); + }*/ +#endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { + return ip6_frag(p, netif, dest); + } +#endif /* LWIP_IPV6_FRAG */ + + LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()")); + return netif->output_ip6(netif, p, dest); +} + +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + + /* pbufs passed to IPv6 must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + return ip6_output_if(p, src, dest, hl, tc, nexth, netif); +} + + +#if LWIP_NETIF_HWADDRHINT +/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip6_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if LWIP_IPV6_MLD +/** + * Add a hop-by-hop options header with a router alert option and padding. + * + * Used by MLD when sending a Multicast listener report/done message. + * + * @param p the packet to which we will prepend the options header + * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) + * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) + * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise + */ +err_t +ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value) +{ + struct ip6_hbh_hdr * hbh_hdr; + + /* Move pointer to make room for hop-by-hop options header. */ + if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + hbh_hdr = (struct ip6_hbh_hdr *)p->payload; + + /* Set fields. */ + hbh_hdr->_nexth = nexth; + hbh_hdr->_hlen = 0; + hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; + hbh_hdr->_ra_opt_dlen = 2; + hbh_hdr->_ra_opt_data = value; + hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; + hbh_hdr->_padn_opt_dlen = 0; + + return ERR_OK; +} +#endif /* LWIP_IPV6_MLD */ + +#if IP6_DEBUG +/* Print an IPv6 header by using LWIP_DEBUGF + * @param p an IPv6 packet, p->payload pointing to the IPv6 header + */ +void +ip6_debug_print(struct pbuf *p) +{ + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + + LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", + IP6H_V(ip6hdr), + IP6H_TC(ip6hdr), + IP6H_FL(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", + ntohs(IP6H_PLEN(ip6hdr)), + ntohs(IP6H_NEXTH(ip6hdr)), + ntohs(IP6H_HOPLIM(ip6hdr)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->src)), + IP6_ADDR_BLOCK2(&(ip6hdr->src)), + IP6_ADDR_BLOCK3(&(ip6hdr->src)), + IP6_ADDR_BLOCK4(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->src)), + IP6_ADDR_BLOCK6(&(ip6hdr->src)), + IP6_ADDR_BLOCK7(&(ip6hdr->src)), + IP6_ADDR_BLOCK8(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->dest)), + IP6_ADDR_BLOCK2(&(ip6hdr->dest)), + IP6_ADDR_BLOCK3(&(ip6hdr->dest)), + IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->dest)), + IP6_ADDR_BLOCK6(&(ip6hdr->dest)), + IP6_ADDR_BLOCK7(&(ip6hdr->dest)), + IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP6_DEBUG */ + +#endif /* LWIP_IPV6 */ diff --git a/lib/liblwip/core/ipv6/ip6_addr.c b/lib/liblwip/core/ipv6/ip6_addr.c new file mode 100644 index 000000000..65d27980e --- /dev/null +++ b/lib/liblwip/core/ipv6/ip6_addr.c @@ -0,0 +1,251 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * 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 + * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +/* used by IP6_ADDR_ANY in ip6_addr.h */ +const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } }; + +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) +#endif + +/** + * Check whether "cp" is a valid ascii representation + * of an IPv6 address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * + * @param cp IPv6 address in ascii represenation (e.g. "FF01::1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ip6addr_aton(const char *cp, ip6_addr_t *addr) +{ + u32_t addr_index, zero_blocks, current_block_index, current_block_value; + const char * s; + + /* Count the number of colons, to count the number of blocks in a "::" sequence + zero_blocks may be 1 even if there are no :: sequences */ + zero_blocks = 8; + for (s = cp; *s != 0; s++) { + if (*s == ':') + zero_blocks--; + else if (!isxdigit(*s)) + break; + } + + /* parse each block */ + addr_index = 0; + current_block_index = 0; + current_block_value = 0; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + current_block_index++; + current_block_value = 0; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } if (s[1] == ':') { + s++; + /* "::" found, set zeros */ + while (zero_blocks-- > 0) { + if (current_block_index & 0x1) { + addr_index++; + } + else { + if (addr) { + addr->addr[addr_index] = 0; + } + } + current_block_index++; + } + } + } 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')); + } else { + /* unexpected digit, space? CRLF? */ + break; + } + } + + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + + /* convert to network byte order. */ + if (addr) { + for (addr_index = 0; addr_index < 4; addr_index++) { + addr->addr[addr_index] = htonl(addr->addr[addr_index]); + } + } + + if (current_block_index != 7) { + return 0; + } + + return 1; +} + +/** + * Convert numeric IPv6 address into ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip6 address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ip6addr_ntoa(const ip6_addr_t *addr) +{ + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip6 address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char * +ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) +{ + u32_t current_block_index, current_block_value; + s32_t zero_flag, i; + + i = 0; + zero_flag = 0; /* used to indicate a zero chain for "::' */ + + for (current_block_index = 0; current_block_index < 8; current_block_index++) { + /* get the current 16-bit block */ + current_block_value = htonl(addr->addr[current_block_index >> 1]); + if ((current_block_index & 0x1) == 0) { + current_block_value = current_block_value >> 16; + } + current_block_value &= 0xffff; + + if (current_block_value == 0) { + /* generate empty block "::" */ + if (!zero_flag) { + if (current_block_index > 0) { + zero_flag = 1; + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + } + } + else { + if (current_block_index > 0) { + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + + if ((current_block_value & 0xf000) == 0) { + zero_flag = 1; + } + else { + buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + buf[i++] = xchar((current_block_value & 0xf)); + if (i >= buflen) return NULL; + + zero_flag = 0; + } + } + + buf[i] = 0; + + return buf; +} +#endif /* LWIP_IPV6 */ diff --git a/lib/liblwip/core/ipv6/ip6_frag.c b/lib/liblwip/core/ipv6/ip6_frag.c new file mode 100644 index 000000000..a43fd6141 --- /dev/null +++ b/lib/liblwip/core/ipv6/ip6_frag.c @@ -0,0 +1,697 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * 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 + * + */ + +#include "lwip/opt.h" +#include "lwip/ip6_frag.h" +#include "lwip/ip6.h" +#include "lwip/icmp6.h" +#include "lwip/nd6.h" + +#include "lwip/pbuf.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IPv6 header, since it replaces + * the Fragment Header in memory in incoming fragments to keep + * track of the various fragments. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* static variables */ +static struct ip6_reassdata *reassdatagrams; +static u16_t ip6_reass_pbufcount; + +/* Forward declarations. */ +static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); +#if IP_REASS_FREE_OLDEST +static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); +#endif /* IP_REASS_FREE_OLDEST */ + +void +ip6_reass_tmr(void) +{ + struct ip6_reassdata *r, *tmp; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + r = r->next; + } else { + /* reassembly timed out */ + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip6_reass_free_complete_datagram(tmp); + } + } +} + +/** + * Free a datagram (struct ip6_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), + * sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + */ +static void +ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) +{ + struct ip6_reassdata *prev; + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip6_reass_helper *iprh; + +#if LWIP_ICMP6 + iprh = (struct ip6_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, move back to the original header (we are now pointing to Fragment header). */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); + } + else { + icmp6_time_exceeded(p, ICMP6_TE_FRAG); + } + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP6 */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip6_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + + /* Then, unchain the struct ip6_reassdata from the list and free it. */ + if (ipr == reassdatagrams) { + reassdatagrams = ipr->next; + } else { + prev = reassdatagrams; + while (prev != NULL) { + if (prev->next == ipr) { + break; + } + prev = prev->next; + } + if (prev != NULL) { + prev->next = ipr->next; + } + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* Finally, update number of pbufs in reassembly queue */ + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); + ip6_reass_pbufcount -= pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram ipr is not freed! + * + * @param ipr ip6_reassdata for the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + */ +static void +ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) +{ + struct ip6_reassdata *r, *oldest; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the current datagram! */ + do { + r = oldest = reassdatagrams; + while (r != NULL) { + if (r != ipr) { + if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + r = r->next; + } + if (oldest != NULL) { + ip6_reass_free_complete_datagram(oldest); + } + } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL)); +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Reassembles incoming IPv6 fragments into an IPv6 datagram. + * + * @param p points to the IPv6 Fragment Header + * @param len the length of the payload (after Fragment Header) + * @return NULL if reassembly is incomplete, pbuf pointing to + * IPv6 Header if reassembly is complete + */ +struct pbuf * +ip6_reass(struct pbuf *p) +{ + struct ip6_reassdata *ipr, *ipr_prev; + struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct ip6_frag_hdr * frag_hdr; + u16_t offset, len; + u8_t clen, valid = 1; + struct pbuf *q; + + IP6_FRAG_STATS_INC(ip6_frag.recv); + + frag_hdr = (struct ip6_frag_hdr *) p->payload; + + clen = pbuf_clen(p); + + offset = ntohs(frag_hdr->_fragment_offset); + + /* Calculate fragment length from IPv6 payload length. + * Adjust for headers before Fragment Header. + * And finally adjust by Fragment Header length. */ + len = ntohs(ip6_current_header()->_plen); + len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; + len -= IP6_FRAG_HLEN; + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if ((frag_hdr->_identification == ipr->identification) && + ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && + ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { + IP6_FRAG_STATS_INC(ip6_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + /* Make room and try again. */ + ip6_reass_remove_oldest_datagram(ipr, clen); + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + memset(ipr, 0, sizeof(struct ip6_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + + /* Use the current IPv6 header for src/dest address reference. + * Eventually, we will replace it when we get the first fragment + * (it might be this one, in any case, it is done later). */ + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + + /* copy the fragmented packet id. */ + ipr->identification = frag_hdr->_identification; + + /* copy the nexth field */ + ipr->nexth = frag_hdr->_nexth; + } + + /* Check if we are allowed to enqueue more datagrams. */ + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + ip6_reass_remove_oldest_datagram(ipr, clen); + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* @todo: send ICMPv6 time exceeded here? */ + /* drop this pbuf */ + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + /* Overwrite Fragment Header with our own helper struct. */ + iprh = (struct ip6_reass_helper *)p->payload; + iprh->next_pbuf = NULL; + iprh->start = (offset & IP6_FRAG_OFFSET_MASK); + iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; + + /* find the right place to insert this pbuf */ + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip6_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + if (iprh_prev != NULL) { + if (iprh->start < iprh_prev->end) { + /* fragment overlaps with previous, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ + iprh_prev->next_pbuf = p; + } else { + /* fragment with the lowest offset */ + ipr->p = p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no gaps. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = p; + } + } + + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip6_reass_pbufcount += clen; + + /* Remember IPv6 header if this is the first fragment. */ + if (iprh->start == 0) { + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + } + + /* If this is the last fragment, calculate total packet length. */ + if ((offset & IP6_FRAG_MORE_FLAG) == 0) { + ipr->datagram_len = iprh->end; + } + + /* Additional validity tests: we have received first and last fragment. */ + iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; + if (iprh_tmp->start != 0) { + valid = 0; + } + if (ipr->datagram_len == 0) { + valid = 0; + } + + /* Final validity test: no gaps between current and last fragment. */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while ((q != NULL) && valid) { + iprh = (struct ip6_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + + if (valid) { + /* All fragments have been received */ + + /* chain together the pbufs contained within the ip6_reassdata list. */ + iprh = (struct ip6_reass_helper*) ipr->p->payload; + while(iprh != NULL) { + + if (iprh->next_pbuf != NULL) { + /* Save next helper struct (will be hidden in next step). */ + iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; + + /* hide the fragment header for every succeding fragment */ + pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); + pbuf_cat(ipr->p, iprh->next_pbuf); + } + else { + iprh_tmp = NULL; + } + + iprh = iprh_tmp; + } + + /* Adjust datagram length by adding header lengths. */ + ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) + + IP6_FRAG_HLEN + - IP6_HLEN ; + + /* Set payload length in ip header. */ + ipr->iphdr->_plen = htons(ipr->datagram_len); + + /* Get the furst pbuf. */ + p = ipr->p; + + /* Restore Fragment Header in first pbuf. Mark as "single fragment" + * packet. Restore nexth. */ + frag_hdr = (struct ip6_frag_hdr *) p->payload; + frag_hdr->_nexth = ipr->nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = 0; + frag_hdr->_identification = 0; + + /* release the sources allocate for the fragment queue entry */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); + ipr_prev->next = ipr->next; + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* adjust the number of pbufs currently queued for reassembly. */ + ip6_reass_pbufcount -= pbuf_clen(p); + + /* Move pbuf back to IPv6 header. */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); + pbuf_free(p); + return NULL; + } + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + return NULL; + +nullreturn: + pbuf_free(p); + return NULL; +} + +#endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG + +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip6_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ip6_frag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip6_frag_free_pbuf_custom_ref(pcr); +} + +/** + * Fragment an IPv6 datagram if too large for the netif or path MTU. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p + * + * @param p ipv6 packet to send + * @param netif the netif on which to send + * @param dest destination ipv6 address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest) +{ + struct ip6_hdr *original_ip6hdr; + struct ip6_hdr *ip6hdr; + struct ip6_frag_hdr * frag_hdr; + struct pbuf *rambuf; + struct pbuf *newpbuf; + static u32_t identification; + u16_t nfb; + u16_t left, cop; + u16_t mtu; + u16_t fragment_offset = 0; + u16_t last; + u16_t poff = IP6_HLEN; + u16_t newpbuflen = 0; + u16_t left_to_copy; + + identification++; + + original_ip6hdr = (struct ip6_hdr *)p->payload; + + mtu = nd6_get_destination_mtu(dest, netif); + + /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */ + left = p->tot_len - IP6_HLEN; + + nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; + + while (left) { + last = (left <= nfb); + + /* Fill this fragment */ + cop = last ? left : nfb; + + /* 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, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + 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!", + (p->len >= (IP6_HLEN + IP6_FRAG_HLEN))); + 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); + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + p->tot_len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip6_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip6_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; + + /* Set headers */ + frag_hdr->_nexth = original_ip6hdr->_nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); + frag_hdr->_identification = htonl(identification); + + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); + IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + IP6_FRAG_STATS_INC(ip6_frag.xmit); + netif->output_ip6(netif, rambuf, dest); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); + left -= cop; + fragment_offset += cop; + } + return ERR_OK; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/lib/liblwip/core/ipv6/mld6.c b/lib/liblwip/core/ipv6/mld6.c new file mode 100644 index 000000000..1cb2dd9c3 --- /dev/null +++ b/lib/liblwip/core/ipv6/mld6.c @@ -0,0 +1,580 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * 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 + * + */ + +/* Based on igmp.c implementation of igmp v2 protocol */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mld6.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + + +/* + * MLD constants + */ +#define MLD6_HL 1 +#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) + +#define MLD6_GROUP_NON_MEMBER 0 +#define MLD6_GROUP_DELAYING_MEMBER 1 +#define MLD6_GROUP_IDLE_MEMBER 2 + + +/* The list of joined groups. */ +static struct mld_group* mld_group_list; + + +/* Forward declarations. */ +static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr); +static err_t mld6_free_group(struct mld_group *group); +static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); +static void mld6_send(struct mld_group *group, u8_t type); + + +/** + * Stop MLD processing on interface + * + * @param netif network interface on which stop MLD processing + */ +err_t +mld6_stop(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + struct mld_group *prev = NULL; + struct mld_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == mld_group_list) { + mld_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report MLD memberships for this interface + * + * @param netif network interface on which report MLD memberships + */ +void +mld6_report_groups(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->netif == netif) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + group = group->next; + } +} + +/** + * Search for a group that is joined on a netif + * + * @param ifp the network interface for which to look + * @param addr the group ipv6 address to search for + * @return a struct mld_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct mld_group * +mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + return NULL; +} + + +/** + * create a new group + * + * @param ifp the network interface for which to create + * @param addr the new group ipv6 + * @return a struct mld_group*, + * NULL on memory error. + */ +static struct mld_group * +mld6_new_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group; + + group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); + if (group != NULL) { + group->netif = ifp; + ip6_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = mld_group_list; + + mld_group_list = group; + } + + return group; +} + +/** + * Remove a group in the mld_group_list and free + * + * @param group the group to remove + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +mld6_free_group(struct mld_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (mld_group_list == group) { + mld_group_list = group->next; + } else { + /* look for group further down the list */ + struct mld_group *tmpGroup; + for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not find group */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + + return err; +} + + +/** + * Process an input MLD message. Called by icmp6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +mld6_input(struct pbuf *p, struct netif *inp) +{ + struct mld_header * mld_hdr; + struct mld_group* group; + + MLD6_STATS_INC(mld6.recv); + + /* Check that mld header fits in packet. */ + if (p->len < sizeof(struct mld_header)) { + /* TODO debug message */ + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + MLD6_STATS_INC(mld6.drop); + return; + } + + mld_hdr = (struct mld_header *)p->payload; + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ + { + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { + MLD6_STATS_INC(mld6.rx_general); + /* Report all groups, except all nodes group, and if-local groups. */ + group = mld_group_list; + while (group != NULL) { + if ((group->netif == inp) && + (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && + (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + group = group->next; + } + } + else { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_group); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* Schedule a report. */ + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + } + break; /* ICMP6_TYPE_MLQ */ + } + case ICMP6_TYPE_MLR: /* Multicast listener report. */ + { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_report); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* If we are waiting to report, cancel it. */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + group->timer = 0; /* stopped */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + } + break; /* ICMP6_TYPE_MLR */ + } + case ICMP6_TYPE_MLD: /* Multicast listener done. */ + { + /* Do nothing, router will query us. */ + break; /* ICMP6_TYPE_MLD */ + } + default: + MLD6_STATS_INC(mld6.proterr); + MLD6_STATS_INC(mld6.drop); + break; + } + + pbuf_free(p); +} + +/** + * Join a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * join a new group. If IP6_ADDR_ANY, join on all netifs + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group or create a new one if not found */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group == NULL) { + /* Joining a new group. Create a new group entry. */ + group = mld6_new_group(netif, groupaddr); + if (group == NULL) { + return ERR_MEM; + } + + /* Activate this address on the MAC layer. */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER); + } + + /* Report our membership. */ + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + + /* Increment group use */ + group->use++; + err = ERR_OK; + } + + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * leave the group. If IP6_ISANY, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Leave if there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(group, ICMP6_TYPE_MLD); + } + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER); + } + + /* Free the group */ + mld6_free_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + + +/** + * Periodic timer for mld processing. Must be called every + * MLD6_TMR_INTERVAL milliseconds (100). + * + * When a delaying member expires, a membership report is sent. + */ +void +mld6_tmr(void) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + group->group_state = MLD6_GROUP_IDLE_MEMBER; + } + } + } + group = group->next; + } +} + +/** + * Schedule a delayed membership report for a group + * + * @param group the mld_group for which "delaying" membership report + * should be sent + * @param maxresp the max resp delay provided in the query + */ +static void +mld6_delayed_report(struct mld_group *group, u16_t maxresp) +{ + /* Convert maxresp from milliseconds to tmr ticks */ + maxresp = maxresp / MLD6_TMR_INTERVAL; + if (maxresp == 0) { + maxresp = 1; + } + +#ifdef LWIP_RAND + /* Randomize maxresp. (if LWIP_RAND is supported) */ + maxresp = (LWIP_RAND() % (maxresp - 1)) + 1; +#endif /* LWIP_RAND */ + + /* Apply timer value if no report has been scheduled already. */ + if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || + ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + group->timer = maxresp; + group->group_state = MLD6_GROUP_DELAYING_MEMBER; + } +} + +/** + * Send a MLD message (report or done). + * + * An IPv6 hop-by-hop options header with a router alert option + * is prepended. + * + * @param group the group to report or quit + * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) + */ +static void +mld6_send(struct mld_group *group, u8_t type) +{ + struct mld_header * mld_hdr; + struct pbuf * p; + ip6_addr_t * src_addr; + + /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) { + /* We couldn't allocate a suitable pbuf. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + MLD6_STATS_INC(mld6.memerr); + return; + } + + /* Move to make room for Hop-by-hop options header. */ + if (pbuf_header(p, -IP6_HBH_HLEN)) { + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + return; + } + + /* Select our source address. */ + if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) { + /* This is a special case, when we are performing duplicate address detection. + * We must join the multicast group, but we don't have a valid address yet. */ + src_addr = IP6_ADDR_ANY; + } else { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(group->netif, 0); + } + + /* MLD message header pointer. */ + mld_hdr = (struct mld_header *)p->payload; + + /* Set fields. */ + mld_hdr->type = type; + mld_hdr->code = 0; + mld_hdr->chksum = 0; + mld_hdr->max_resp_delay = 0; + mld_hdr->reserved = 0; + ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + + mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, + src_addr, &(group->group_address)); + + /* Add hop-by-hop headers options: router alert with MLD value. */ + ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); + + /* Send the packet out. */ + MLD6_STATS_INC(mld6.xmit); + ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), + MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif); + pbuf_free(p); +} + + + +#endif /* LWIP_IPV6 */ diff --git a/lib/liblwip/core/ipv6/nd6.c b/lib/liblwip/core/ipv6/nd6.c new file mode 100644 index 000000000..44629dbe5 --- /dev/null +++ b/lib/liblwip/core/ipv6/nd6.c @@ -0,0 +1,1785 @@ +/** + * @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 + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + + +/* Router tables. */ +struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; +struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; +struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; +struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; + +/* Default values, can be updated by a RA message. */ +u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; +u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */ + +/* Index for cache entries. */ +static u8_t nd6_cached_neighbor_index; +static u8_t nd6_cached_destination_index; + +/* Multicast address holder. */ +static ip6_addr_t multicast_address; + +/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ +static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; + +/* Forward declarations. */ +static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_neighbor_cache_entry(void); +static void nd6_free_neighbor_cache_entry(s8_t i); +static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_destination_cache_entry(void); +static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif); +static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_new_router(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); + +#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 +#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 +static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +static void nd6_send_rs(struct netif * netif); +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +#if LWIP_ND6_QUEUEING +static void nd6_free_q(struct nd6_q_entry *q); +#else /* LWIP_ND6_QUEUEING */ +#define nd6_free_q(q) pbuf_free(q) +#endif /* LWIP_ND6_QUEUEING */ +static void nd6_send_q(s8_t i); + + +/** + * Process an incoming neighbor discovery message + * + * @param p the nd packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +nd6_input(struct pbuf *p, struct netif *inp) +{ + u8_t msg_type; + s8_t i; + + ND6_STATS_INC(nd6.recv); + + msg_type = *((u8_t *)p->payload); + switch (msg_type) { + case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ + { + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that na header fits in packet. */ + if (p->len < (sizeof(struct na_header))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + na_hdr = (struct na_header *)p->payload; + + /* Unsolicited NA?*/ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* This is an unsolicited NA. + * link-layer changed? + * part of DAD mechanism? */ + + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* 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)); + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* If the target address matches this netif, it is a DAD response. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + /* 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_ip6_addr(inp, i), &multicast_address); +#endif /* LWIP_IPV6_MLD */ + + + + +#if LWIP_IPV6_AUTOCONFIG + /* Check to see if this address was autoconfigured. */ + if (!ip6_addr_islinklocal(ip6_current_dest_addr())) { + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i >= 0) { + /* Mark this prefix as duplicate, so that we don't use it + * to generate this address again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + pbuf_free(p); + return; + } + } +#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ + + /* This is an unsolicited NA, most likely there was a LLADDR change. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i >= 0) { + if (na_hdr->flags & ND6_FLAG_OVERRIDE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + } + } + else { + /* This is a solicited NA. + * neighbor address resolution response? + * neighbor unreachability detection response? */ + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + + /* Find the cache entry corresponding to this na. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i < 0) { + /* We no longer care about this target address. drop it. */ + pbuf_free(p); + return; + } + + /* Update cache entry. */ + neighbor_cache[i].netif = inp; + neighbor_cache[i].counter.reachable_time = reachable_time; + if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || + (neighbor_cache[i].state == ND6_INCOMPLETE)) { + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* 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)); + + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + neighbor_cache[i].state = ND6_REACHABLE; + + /* Send queued packets, if any. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + } + + break; /* ICMP6_TYPE_NA */ + } + case ICMP6_TYPE_NS: /* Neighbor solicitation. */ + { + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + u8_t accepted; + + /* Check that ns header fits in packet. */ + if (p->len < sizeof(struct ns_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ns_hdr = (struct ns_header *)p->payload; + + /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ + lladdr_opt = NULL; + if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + } + + /* Check if the target address is configured on the receiving netif. */ + accepted = 0; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || + (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && + ip6_addr_isany(ip6_current_src_addr()))) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + accepted = 1; + break; + } + } + + /* NS not for us? */ + if (!accepted) { + pbuf_free(p); + return; + } + + /* Check for ANY address in src (DAD algorithm). */ + if (ip6_addr_isany(ip6_current_src_addr())) { + /* Sender is validating this address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + /* Send a NA back so that the sender does not use this address. */ + nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); + if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { + /* We shouldn't use this address either. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + } + } + } + } + else { + /* Sender is trying to resolve our address. */ + /* Verify that they included their own link-layer address. */ + if (lladdr_opt == NULL) { + /* Not a valid message. */ + pbuf_free(p); + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + return; + } + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if ( i>= 0) { + /* We already have a record for the solicitor. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + + /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + else + { + /* Add their IPv6 address and link-layer address to neighbor cache. + * We will need it at least to send a unicast NA message, but most + * likely we will also be communicating with this node soon. */ + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* We couldn't assign a cache entry for this neighbor. + * we won't be able to reply. drop it. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + 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()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address)); + + /* Send back a NA for us. Allocate the reply pbuf. */ + nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); + } + + break; /* ICMP6_TYPE_NS */ + } + case ICMP6_TYPE_RA: /* Router Advertisement. */ + { + struct ra_header * ra_hdr; + u8_t * buffer; /* Used to copy options. */ + u16_t offset; + + /* Check that RA header fits in packet. */ + if (p->len < sizeof(struct ra_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ra_hdr = (struct ra_header *)p->payload; + + /* If we are sending RS messages, stop. */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + inp->rs_count = 0; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + /* Get the matching default router entry. */ + i = nd6_get_router(ip6_current_src_addr(), inp); + if (i < 0) { + /* Create a new router entry. */ + i = nd6_new_router(ip6_current_src_addr(), inp); + } + + if (i < 0) { + /* Could not create a new router entry. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Re-set invalidation timer. */ + default_router_list[i].invalidation_timer = ra_hdr->router_lifetime; + + /* Re-set default timer values. */ +#if LWIP_ND6_ALLOW_RA_UPDATES + if (ra_hdr->retrans_timer > 0) { + retrans_timer = ra_hdr->retrans_timer; + } + if (ra_hdr->reachable_time > 0) { + reachable_time = ra_hdr->reachable_time; + } +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + + /* TODO set default hop limit... */ + /* ra_hdr->current_hop_limit;*/ + + /* Update flags in local entry (incl. preference). */ + default_router_list[i].flags = ra_hdr->flags; + + /* Offset to options. */ + offset = sizeof(struct ra_header); + + /* Process each option. */ + while ((p->tot_len - offset) > 0) { + if (p->len == p->tot_len) { + /* no need to copy from contiguous pbuf */ + buffer = &((u8_t*)p->payload)[offset]; + } else { + buffer = nd6_ra_buffer; + pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset); + } + switch (buffer[0]) { + case ND6_OPTION_TYPE_SOURCE_LLADDR: + { + struct lladdr_option * lladdr_opt; + lladdr_opt = (struct lladdr_option *)buffer; + if ((default_router_list[i].neighbor_entry != NULL) && + (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { + SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); + default_router_list[i].neighbor_entry->state = ND6_REACHABLE; + default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; + } + break; + } + case ND6_OPTION_TYPE_MTU: + { + struct mtu_option * mtu_opt; + mtu_opt = (struct mtu_option *)buffer; + if (mtu_opt->mtu >= 1280) { +#if LWIP_ND6_ALLOW_RA_UPDATES + inp->mtu = mtu_opt->mtu; +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + } + break; + } + case ND6_OPTION_TYPE_PREFIX_INFO: + { + struct prefix_option * prefix_opt; + prefix_opt = (struct prefix_option *)buffer; + + if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) { + /* Add to on-link prefix list. */ + + /* Get a memory-aligned copy of the prefix. */ + ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix)); + + /* find cache entry for this prefix. */ + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i < 0) { + /* Create a new cache entry. */ + i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp); + } + if (i >= 0) { + prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime; + +#if LWIP_IPV6_AUTOCONFIG + if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { + /* Mark prefix as autonomous, so that address autoconfiguration can take place. + * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + break; + } + case ND6_OPTION_TYPE_ROUTE_INFO: + { + /* TODO implement preferred routes. + struct route_option * route_opt; + route_opt = (struct route_option *)buffer;*/ + + break; + } + default: + /* Unrecognized option, abort. */ + ND6_STATS_INC(nd6.proterr); + break; + } + offset += 8 * ((u16_t)buffer[1]); + } + + break; /* ICMP6_TYPE_RA */ + } + case ICMP6_TYPE_RD: /* Redirect */ + { + struct redirect_header * redir_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that Redir header fits in packet. */ + if (p->len < sizeof(struct redirect_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + redir_hdr = (struct redirect_header *)p->payload; + + lladdr_opt = NULL; + if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); + } + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address)); + + /* Find dest address in cache */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Set the new target address. */ + ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + + /* If Link-layer address of other router is given, try to add to neighbor cache. */ + if (lladdr_opt != NULL) { + if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { + /* Copy target address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address)); + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + 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()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + if (i >= 0) { + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + } + } + break; /* ICMP6_TYPE_RD */ + } + case ICMP6_TYPE_PTB: /* Packet too big */ + { + struct icmp6_hdr *icmp6hdr; /* Packet too big message */ + struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */ + + /* Check that ICMPv6 header + IPv6 header fit in payload */ + if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { + /* drop short packets */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest)); + + /* Look for entry in destination cache. */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Change the Path MTU. */ + destination_cache[i].pmtu = icmp6hdr->data; + + break; /* ICMP6_TYPE_PTB */ + } + + default: + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + break; /* default */ + } + + pbuf_free(p); +} + + +/** + * Periodic timer for Neighbor discovery functions: + * + * - Update neighbor reachability states + * - Update destination cache entries age + * - Update invalidation timers of default routers and on-link prefixes + * - Perform duplicate address detection (DAD) for our addresses + * - Send router solicitations + */ +void +nd6_tmr(void) +{ + s8_t i, j; + struct netif * netif; + + /* Process neighbor entries. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + switch (neighbor_cache[i].state) { + case ND6_INCOMPLETE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST); + } + break; + case ND6_REACHABLE: + /* Send queued packets, if any are left. Should have been sent already. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { + /* Change to stale state. */ + neighbor_cache[i].state = ND6_STALE; + neighbor_cache[i].counter.stale_time = 0; + } + else { + neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_STALE: + neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL; + break; + case ND6_DELAY: + if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) { + /* Change to PROBE state. */ + neighbor_cache[i].state = ND6_PROBE; + neighbor_cache[i].counter.probes_sent = 0; + } + else { + neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_PROBE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0); + } + break; + case ND6_NO_ENTRY: + default: + /* Do nothing. */ + break; + } + } + + /* Process destination entries. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + destination_cache[i].age++; + } + + /* Process router entries. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (default_router_list[i].neighbor_entry != NULL) { + /* Active entry. */ + if (default_router_list[i].invalidation_timer > 0) { + default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + } + if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Less than 1 second remainig. Clear this entry. */ + default_router_list[i].neighbor_entry->isrouter = 0; + default_router_list[i].neighbor_entry = NULL; + default_router_list[i].invalidation_timer = 0; + default_router_list[i].flags = 0; + } + } + } + + /* Process prefix entries. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + prefix_list[i].invalidation_timer = 0; + } + if ((prefix_list[i].invalidation_timer > 0) && + (prefix_list[i].netif != NULL)) { + prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + +#if LWIP_IPV6_AUTOCONFIG + /* Initiate address autoconfiguration for this prefix, if conditions are met. */ + if (prefix_list[i].netif->ip6_autoconfig_enabled && + (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && + !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { + /* Try to get an address on this netif that is invalid. + * Skip 0 index (link-local address) */ + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) { + /* Generate an address using this prefix and interface ID from link-local address. */ + prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0]; + prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1]; + prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2]; + prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3]; + + /* Mark it as tentative (DAD will be performed if configured). */ + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); + + /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + + /* Process our own addresses, if DAD configured. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_istentative(netif->ip6_addr_state[i])) { + if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { + /* No NA received in response. Mark address as valid. */ + netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; + /* TODO implement preferred and valid lifetimes. */ + } + else if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_IPV6_MLD + if ((netif->ip6_addr_state[i] & 0x07) == 0) { + /* Join solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]); + mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address); + } +#endif /* LWIP_IPV6_MLD */ + /* Send a NS for this address. */ + nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); + (netif->ip6_addr_state[i])++; + /* TODO send max 1 NS per tmr call? enable return*/ + /*return;*/ + } + } + } + } + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send router solicitation messages, if necessary. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) { + nd6_send_rs(netif); + netif->rs_count--; + } + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +} + +/** + * Send a neighbor solicitation message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + + if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + } else { + src_addr = IP6_ADDR_ANY; + } + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + ns_hdr = (struct ns_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + + ns_hdr->type = ICMP6_TYPE_NS; + ns_hdr->code = 0; + ns_hdr->chksum = 0; + ns_hdr->reserved = 0; + ip6_addr_set(&(ns_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + target_addr = &multicast_address; + } + + ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + target_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +/** + * Send a neighbor advertisement message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + ip6_addr_t * dest_addr; + + /* Use link-local address as source address. */ + /* src_addr = &(netif->ip6_addr[0]); */ + /* Use target address as source address. */ + src_addr = target_addr; + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + na_hdr = (struct na_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + na_hdr->type = ICMP6_TYPE_NA; + na_hdr->code = 0; + na_hdr->chksum = 0; + na_hdr->flags = flags & 0xf0; + na_hdr->reserved[0] = 0; + na_hdr->reserved[1] = 0; + na_hdr->reserved[2] = 0; + ip6_addr_set(&(na_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + dest_addr = &multicast_address; + } + else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { + ip6_addr_set_allnodes_linklocal(&multicast_address); + dest_addr = &multicast_address; + } + else { + dest_addr = ip6_current_src_addr(); + } + + na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + dest_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, dest_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +/** + * Send a router solicitation message + * + * @param netif the netif on which to send the message + */ +static void +nd6_send_rs(struct netif * netif) +{ + struct rs_header * rs_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + u16_t packet_len; + + /* Link-local source address, or unspecified address? */ + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + src_addr = netif_ip6_addr(netif, 0); + } + else { + src_addr = IP6_ADDR_ANY; + } + + /* Generate the all routers target address. */ + ip6_addr_set_allrouters_linklocal(&multicast_address); + + /* Allocate a packet. */ + packet_len = sizeof(struct rs_header); + if (src_addr != IP6_ADDR_ANY) { + packet_len += sizeof(struct lladdr_option); + } + p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM); + if ((p == NULL) || (p->len < packet_len)) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + rs_hdr = (struct rs_header *)p->payload; + + rs_hdr->type = ICMP6_TYPE_RS; + rs_hdr->code = 0; + rs_hdr->chksum = 0; + rs_hdr->reserved = 0; + + if (src_addr != IP6_ADDR_ANY) { + /* Include our hw address. */ + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + + rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + &multicast_address); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, &multicast_address, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +/** + * Search for a neighbor cache entry + * + * @param ip6addr the IPv6 address of the neighbor + * @return The neighbor cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { + return i; + } + } + return -1; +} + +/** + * Create a new neighbor cache entry. + * + * If no unused entry is found, will try to recycle an old entry + * according to ad-hoc "age" heuristic. + * + * @return The neighbor cache entry index that was created, -1 if no + * entry could be created + */ +static s8_t +nd6_new_neighbor_cache_entry(void) +{ + s8_t i; + s8_t j; + u32_t time; + + + /* First, try to find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].state == ND6_NO_ENTRY) { + return i; + } + } + + /* We need to recycle an entry. in general, do not recycle if it is a router. */ + + /* Next, try to find a Stale entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_STALE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Probe entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_PROBE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Delayed entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_DELAY) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find the oldest reachable entry. */ + time = 0xfffffffful; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_REACHABLE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.reachable_time < time) { + j = i; + time = neighbor_cache[i].counter.reachable_time; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry without queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ( + (neighbor_cache[i].q == NULL) && + (neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry with queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* No more entries to try. */ + return -1; +} + +/** + * Will free any resources associated with a neighbor cache + * entry, and will mark it as unused. + * + * @param i the neighbor cache entry index to free + */ +static void +nd6_free_neighbor_cache_entry(s8_t i) +{ + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + + /* Free any queued packets. */ + if (neighbor_cache[i].q != NULL) { + nd6_free_q(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } + + neighbor_cache[i].state = ND6_NO_ENTRY; + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = NULL; + neighbor_cache[i].counter.reachable_time = 0; + ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); +} + +/** + * Search for a destination cache entry + * + * @param ip6addr the IPv6 address of the destination + * @return The destination cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_destination_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { + return i; + } + } + return -1; +} + +/** + * Create a new destination cache entry. If no unused entry is found, + * will recycle oldest entry. + * + * @return The destination cache entry index that was created, -1 if no + * entry was created + */ +static s8_t +nd6_new_destination_cache_entry(void) +{ + s8_t i, j; + u32_t age; + + /* Find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { + return i; + } + } + + /* Find oldest entry. */ + age = 0; + j = LWIP_ND6_NUM_DESTINATIONS - 1; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (destination_cache[i].age > age) { + j = i; + } + } + + return j; +} + +/** + * Determine whether an address matches an on-link prefix. + * + * @param ip6addr the IPv6 address to match + * @return 1 if the address is on-link, 0 otherwise + */ +static s8_t +nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if ((prefix_list[i].netif == netif) && + (prefix_list[i].invalidation_timer > 0) && + ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { + return 1; + } + } + /* Check to see if address prefix matches a (manually?) configured address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { + return 1; + } + } + return 0; +} + +/** + * Select a default router for a destination. + * + * @param ip6addr the destination address + * @param netif the netif for the outgoing packet, if known + * @return the default router entry index, or -1 if no suitable + * router is found + */ +s8_t +nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + /* last_router is used for round-robin router selection (as recommended + * in RFC). This is more robust in case one router is not reachable, + * we are not stuck trying to resolve it. */ + static s8_t last_router; + (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ + + /* TODO: implement default router preference */ + + /* Look for reachable routers. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0) && + (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { + return i; + } + } + + /* Look for router in other reachability states, but still valid according to timer. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0)) { + return i; + } + } + + /* Look for any router for which we have any information at all. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if (default_router_list[i].neighbor_entry != NULL && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { + return i; + } + } + + /* no suitable router found. */ + return -1; +} + +/** + * Find an entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is found, if known + * @return the index of the router entry, or -1 if not found + */ +static s8_t +nd6_get_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t i; + + /* Look for router. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if ((default_router_list[i].neighbor_entry != NULL) && + ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && + ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { + return i; + } + } + + /* router not found. */ + return -1; +} + +/** + * Create a new entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is connected, if known + * @return the index on the router table, or -1 if could not be created + */ +static s8_t +nd6_new_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t router_index; + s8_t neighbor_index; + + /* Do we have a neighbor entry for this router? */ + neighbor_index = nd6_find_neighbor_cache_entry(router_addr); + if (neighbor_index < 0) { + /* Create a neighbor entry for this router. */ + neighbor_index = nd6_new_neighbor_cache_entry(); + if (neighbor_index < 0) { + /* Could not create neighbor entry for this router. */ + return -1; + } + ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); + neighbor_cache[neighbor_index].netif = netif; + neighbor_cache[neighbor_index].q = NULL; + neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; + neighbor_cache[neighbor_index].counter.probes_sent = 0; + } + + /* Mark neighbor as router. */ + neighbor_cache[neighbor_index].isrouter = 1; + + /* Look for empty entry. */ + for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; 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; + } + } + + /* Could not create a router entry. */ + + /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ + neighbor_cache[neighbor_index].isrouter = 0; + + /* router not found. */ + return -1; +} + +/** + * Find the cached entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not found + */ +static s8_t +nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Look for prefix in list. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && + (prefix_list[i].netif == netif)) { + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Creates a new entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not created + */ +static s8_t +nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Create new entry. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((prefix_list[i].netif == NULL) || + (prefix_list[i].invalidation_timer == 0)) { + /* Found empty prefix entry. */ + prefix_list[i].netif = netif; + ip6_addr_set(&(prefix_list[i].prefix), prefix); + prefix_list[i].flags = 0; + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Determine the next hop for a destination. Will determine if the + * destination is on-link, else a suitable on-link router is selected. + * + * The last entry index is cached for fast entry search. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the neighbor cache entry for the next hop, ERR_RTE if no + * suitable next hop was found, ERR_MEM if no cache entry + * could be created + */ +s8_t +nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t addr_hint = *(netif->addr_hint); + if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { + nd6_cached_destination_index = addr_hint; + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look for ip6addr in destination cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + /* the cached entry index is the right one! */ + /* do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + /* Search destination cache. */ + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + /* found destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } + else { + /* Not found. Create a new destination entry. */ + i = nd6_new_destination_cache_entry(); + if (i >= 0) { + /* got new destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Could not create a destination cache entry. */ + return ERR_MEM; + } + + /* Copy dest address to destination cache. */ + ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); + + /* Now find the next hop. is it a neighbor? */ + if (ip6_addr_islinklocal(ip6addr) || + nd6_is_prefix_in_netif(ip6addr, 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); + } + else { + /* We need to select a router. */ + i = nd6_select_router(ip6addr, netif); + if (i < 0) { + /* No router found. */ + ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); + return ERR_RTE; + } + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); + } + } + } + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + *(netif->addr_hint) = nd6_cached_destination_index; + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look in neighbor cache for the next-hop address. */ + if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), + &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + /* Cache hit. */ + /* Do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); + if (i >= 0) { + /* Found a matching record, make it new cached entry. */ + nd6_cached_neighbor_index = i; + } + else { + /* Neighbor not in cache. Make a new entry. */ + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + /* got new neighbor entry. make it our new cached index. */ + nd6_cached_neighbor_index = i; + } else { + /* Could not create a neighbor cache entry. */ + return ERR_MEM; + } + + /* Initialize fields. */ + ip6_addr_copy(neighbor_cache[i].next_hop_address, + destination_cache[nd6_cached_destination_index].next_hop_addr); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = netif; + neighbor_cache[i].state = ND6_INCOMPLETE; + neighbor_cache[i].counter.probes_sent = 0; + } + } + + /* Reset this destination's age. */ + destination_cache[nd6_cached_destination_index].age = 0; + + return nd6_cached_neighbor_index; +} + +/** + * Queue a packet for a neighbor. + * + * @param neighbor_index the index in the neighbor cache table + * @param q packet to be queued + * @return ERR_OK if succeeded, ERR_MEM if out of memory + */ +err_t +nd6_queue_packet(s8_t neighbor_index, struct pbuf * q) +{ + err_t result = ERR_MEM; + struct pbuf *p; + int copy_needed = 0; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *new_entry, *r; +#endif /* LWIP_ND6_QUEUEING */ + + if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { + return ERR_ARG; + } + + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ +#if LWIP_ND6_QUEUEING + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); +#else /* LWIP_ND6_QUEUEING */ + pbuf_free(neighbor_cache[neighbor_index].q); + neighbor_cache[neighbor_index].q = NULL; +#endif /* LWIP_ND6_QUEUEING */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + } + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet was copied/ref'd? */ + if (p != NULL) { + /* queue packet ... */ +#if LWIP_ND6_QUEUEING + /* allocate a new nd6 queue entry */ + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + } + if (new_entry != NULL) { + new_entry->next = NULL; + new_entry->p = p; + if(neighbor_cache[neighbor_index].q != NULL) { + /* queue was already existent, append the new entry to the end */ + r = neighbor_cache[neighbor_index].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + neighbor_cache[neighbor_index].q = new_entry; + } + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; + } else { + /* the pool MEMP_ND6_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); + /* { result == ERR_MEM } through initialization */ + } +#else /* LWIP_ND6_QUEUEING */ + /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ + if (neighbor_cache[neighbor_index].q != NULL) { + pbuf_free(neighbor_cache[neighbor_index].q); + } + neighbor_cache[neighbor_index].q = p; + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; +#endif /* LWIP_ND6_QUEUEING */ + } else { + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + + return result; +} + +#if LWIP_ND6_QUEUEING +/** + * Free a complete queue of nd6 q entries + * + * @param q a queue of nd6_q_entry to free + */ +static void +nd6_free_q(struct nd6_q_entry *q) +{ + struct nd6_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ND6_QUEUE, r); + } +} +#endif /* LWIP_ND6_QUEUEING */ + +/** + * Send queued packets for a neighbor + * + * @param i the neighbor to send packets to + */ +static void +nd6_send_q(s8_t i) +{ + struct ip6_hdr *ip6hdr; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *q; +#endif /* LWIP_ND6_QUEUEING */ + + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + +#if LWIP_ND6_QUEUEING + while (neighbor_cache[i].q != NULL) { + /* remember first in queue */ + q = neighbor_cache[i].q; + /* pop first item off the queue */ + neighbor_cache[i].q = q->next; + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(q->p->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(q->p); + /* now queue entry can be freed */ + memp_free(MEMP_ND6_QUEUE, q); + } +#else /* LWIP_ND6_QUEUEING */ + if (neighbor_cache[i].q != NULL) { + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } +#endif /* LWIP_ND6_QUEUEING */ +} + + +/** + * Get the Path MTU for a destination. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the Path MTU, if known, or the netif default MTU + */ +u16_t +nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + if (destination_cache[i].pmtu > 0) { + return destination_cache[i].pmtu; + } + } + + if (netif != NULL) { + return netif->mtu; + } + + return 1280; /* Minimum MTU */ +} + + +#if LWIP_ND6_TCP_REACHABILITY_HINTS +/** + * Provide the Neighbor discovery process with a hint that a + * destination is reachable. Called by tcp_receive when ACKs are + * received or sent (as per RFC). This is useful to avoid sending + * NS messages every 30 seconds. + * + * @param ip6addr the destination address which is know to be reachable + * by an upper layer protocol (TCP) + */ +void +nd6_reachability_hint(ip6_addr_t * ip6addr) +{ + s8_t i; + + /* Find destination in cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + i = nd6_cached_destination_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_destination_cache_entry(ip6addr); + } + if (i < 0) { + return; + } + + /* Find next hop neighbor in cache. */ + if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + i = nd6_cached_neighbor_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); + } + if (i < 0) { + return; + } + + /* Set reachability state. */ + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; +} +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#endif /* LWIP_IPV6 */ diff --git a/lib/liblwip/core/mem.c b/lib/liblwip/core/mem.c index 98375553b..1659a2c7a 100644 --- a/lib/liblwip/core/mem.c +++ b/lib/liblwip/core/mem.c @@ -78,9 +78,10 @@ void * mem_malloc(mem_size_t size) { + void *ret; struct memp_malloc_helper *element; memp_t poolnr; - mem_size_t required_size = size + sizeof(struct memp_malloc_helper); + mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { #if MEM_USE_POOLS_TRY_BIGGER_POOL @@ -113,9 +114,9 @@ again: /* save the pool number this element came from */ element->poolnr = poolnr; /* and return a pointer to the memory directly after the struct memp_malloc_helper */ - element++; + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); - return element; + return ret; } /** @@ -128,13 +129,13 @@ again: void mem_free(void *rmem) { - struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem; + struct memp_malloc_helper *hmem; LWIP_ASSERT("rmem != NULL", (rmem != NULL)); LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); /* get the original struct memp_malloc_helper */ - hmem--; + hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); LWIP_ASSERT("hmem != NULL", (hmem != NULL)); LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); @@ -190,7 +191,9 @@ static struct mem *ram_end; static struct mem *lfree; /** concurrent access protection */ +#if !NO_SYS static sys_mutex_t mem_mutex; +#endif #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT @@ -518,7 +521,7 @@ mem_malloc(mem_size_t size) sys_mutex_lock(&mem_mutex); LWIP_MEM_ALLOC_PROTECT(); #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT - /* run as long as a mem_free disturbed mem_malloc */ + /* run as long as a mem_free disturbed mem_malloc or mem_trim */ do { local_mem_free_count = 0; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ @@ -532,12 +535,14 @@ mem_malloc(mem_size_t size) #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_free_count = 0; LWIP_MEM_ALLOC_UNPROTECT(); - /* allow mem_free to run */ + /* allow mem_free or mem_trim to run */ LWIP_MEM_ALLOC_PROTECT(); if (mem_free_count != 0) { - local_mem_free_count = mem_free_count; + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; } - mem_free_count = 0; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ if ((!mem->used) && @@ -581,15 +586,27 @@ mem_malloc(mem_size_t size) mem->used = 1; MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); } - +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ if (mem == lfree) { + struct mem *cur = lfree; /* Find next free block after mem and update lowest free pointer */ - while (lfree->used && lfree != ram_end) { + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; LWIP_MEM_ALLOC_UNPROTECT(); /* prevent high interrupt latency... */ LWIP_MEM_ALLOC_PROTECT(); - lfree = (struct mem *)(void *)&ram[lfree->next]; + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; } + lfree = cur; LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); } LWIP_MEM_ALLOC_UNPROTECT(); diff --git a/lib/liblwip/core/memp.c b/lib/liblwip/core/memp.c index 4da879a57..1323463e4 100644 --- a/lib/liblwip/core/memp.c +++ b/lib/liblwip/core/memp.c @@ -58,6 +58,9 @@ #include "lwip/snmp_msg.h" #include "lwip/dns.h" #include "netif/ppp_oe.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" #include @@ -176,19 +179,20 @@ static u8_t memp_memory[MEM_ALIGNMENT - 1 #if MEMP_SANITY_CHECK /** - * Check that memp-lists don't form a circle + * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm". */ static int memp_sanity(void) { - s16_t i, c; - struct memp *m, *n; + s16_t i; + struct memp *t, *h; for (i = 0; i < MEMP_MAX; i++) { - for (m = memp_tab[i]; m != NULL; m = m->next) { - c = 1; - for (n = memp_tab[i]; n != NULL; n = n->next) { - if (n == m && --c < 0) { + t = memp_tab[i]; + if(t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { + if (t == h) { return 0; } } @@ -282,17 +286,25 @@ memp_overflow_check_all(void) u16_t i, j; struct memp *p; +#if !MEMP_SEPARATE_POOLS p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ for (i = 0; i < MEMP_MAX; ++i) { - p = p; +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ for (j = 0; j < memp_num[i]; ++j) { memp_overflow_check_element_overflow(p, i); p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); } } +#if !MEMP_SEPARATE_POOLS p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ for (i = 0; i < MEMP_MAX; ++i) { - p = p; +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ for (j = 0; j < memp_num[i]; ++j) { memp_overflow_check_element_underflow(p, i); p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); @@ -310,9 +322,13 @@ memp_overflow_init(void) struct memp *p; u8_t *m; +#if !MEMP_SEPARATE_POOLS p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ for (i = 0; i < MEMP_MAX; ++i) { - p = p; +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ for (j = 0; j < memp_num[i]; ++j) { #if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; diff --git a/lib/liblwip/core/netif.c b/lib/liblwip/core/netif.c index f190b1f2e..f8133f76f 100644 --- a/lib/liblwip/core/netif.c +++ b/lib/liblwip/core/netif.c @@ -40,6 +40,7 @@ #include "lwip/def.h" #include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" #include "lwip/netif.h" #include "lwip/tcp_impl.h" #include "lwip/snmp.h" @@ -59,6 +60,12 @@ #if LWIP_DHCP #include "lwip/dhcp.h" #endif /* LWIP_DHCP */ +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_IPV6_MLD +#include "lwip/mld6.h" +#endif /* LWIP_IPV6_MLD */ #if LWIP_NETIF_STATUS_CALLBACK #define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0) @@ -75,6 +82,12 @@ struct netif *netif_list; struct netif *netif_default; +static u8_t netif_num; + +#if LWIP_IPV6 +static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + #if LWIP_HAVE_LOOPIF static struct netif loop_netif; @@ -137,7 +150,9 @@ struct netif * netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) { - static u8_t netifnum = 0; +#if LWIP_IPV6 + u32_t i; +#endif LWIP_ASSERT("No init function given", init != NULL); @@ -145,6 +160,13 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_set_zero(&netif->ip_addr); ip_addr_set_zero(&netif->netmask); ip_addr_set_zero(&netif->gw); +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip6_addr_set_zero(&netif->ip6_addr[i]); + netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); + } + netif->output_ip6 = netif_null_output_ip6; +#endif /* LWIP_IPV6 */ netif->flags = 0; #if LWIP_DHCP /* netif not under DHCP control by default */ @@ -154,6 +176,17 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, /* netif not under AutoIP control by default */ netif->autoip = NULL; #endif /* LWIP_AUTOIP */ +#if LWIP_IPV6_AUTOCONFIG + /* IPv6 address autoconfiguration not enabled by default */ + netif->ip6_autoconfig_enabled = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /* netif not under DHCPv6 control by default */ + netif->dhcp6 = NULL; +#endif /* LWIP_IPV6_DHCP6 */ #if LWIP_NETIF_STATUS_CALLBACK netif->status_callback = NULL; #endif /* LWIP_NETIF_STATUS_CALLBACK */ @@ -163,6 +196,9 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, #if LWIP_IGMP netif->igmp_mac_filter = NULL; #endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = NULL; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ #if ENABLE_LOOPBACK netif->loop_first = NULL; netif->loop_last = NULL; @@ -170,11 +206,9 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, /* remember netif specific state information data */ netif->state = state; - netif->num = netifnum++; + netif->num = netif_num++; netif->input = input; -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = NULL; -#endif /* LWIP_NETIF_HWADDRHINT*/ + NETIF_SET_HWADDRHINT(netif, NULL); #if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS netif->loop_cnt_current = 0; #endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ @@ -245,6 +279,10 @@ netif_remove(struct netif *netif) igmp_stop(netif); } #endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* stop MLD processing */ + mld6_stop(netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ if (netif_is_up(netif)) { /* set netif down before removing (call callback function) */ netif_set_down(netif); @@ -273,6 +311,11 @@ netif_remove(struct netif *netif) /* reset default netif */ netif_set_default(NULL); } +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); } @@ -325,16 +368,16 @@ netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) struct tcp_pcb_listen *lpcb; /* address is actually being changed? */ - if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { + if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); pcb = tcp_active_pcbs; while (pcb != NULL) { /* PCB bound to current local interface address? */ - if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr)) + if (ip_addr_cmp(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr)) #if LWIP_AUTOIP /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ - && !ip_addr_islinklocal(&(pcb->local_ip)) + && !ip_addr_islinklocal(ipX_2_ip(&pcb->local_ip)) #endif /* LWIP_AUTOIP */ ) { /* this connection must be aborted */ @@ -348,11 +391,11 @@ netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) } for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { /* PCB bound to current local interface address? */ - if ((!(ip_addr_isany(&(lpcb->local_ip)))) && - (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { + if ((!(ip_addr_isany(ipX_2_ip(&lpcb->local_ip)))) && + (ip_addr_cmp(ipX_2_ip(&lpcb->local_ip), &(netif->ip_addr)))) { /* The PCB is listening to the old ipaddr and * is set to listen to the new one instead */ - ip_addr_set(&(lpcb->local_ip), ipaddr); + ip_addr_set(ipX_2_ip(&lpcb->local_ip), ipaddr); } } } @@ -471,6 +514,16 @@ void netif_set_up(struct netif *netif) igmp_report_groups( netif); } #endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send Router Solicitation messages. */ + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + } } } @@ -491,6 +544,11 @@ void netif_set_down(struct netif *netif) snmp_get_sysuptime(&netif->ts); #endif +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_ARP */ NETIF_STATUS_CALLBACK(netif); } } @@ -507,6 +565,19 @@ void netif_set_status_callback(struct netif *netif, netif_status_callback_fn sta } #endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + /** * Called by a driver when its link goes up */ @@ -541,6 +612,10 @@ void netif_set_link_up(struct netif *netif ) igmp_report_groups( netif); } #endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ } NETIF_LINK_CALLBACK(netif); } @@ -750,3 +825,72 @@ netif_poll_all(void) } #endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ #endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +s8_t +netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + return i; + } + } + return -1; +} + +void +netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit) +{ + u8_t i, addr_index; + + /* Link-local prefix. */ + netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul); + netif->ip6_addr[0].addr[1] = 0; + + /* Generate interface ID. */ + if (from_mac_48bit) { + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | + ((u32_t)(netif->hwaddr[1]) << 16) | + ((u32_t)(netif->hwaddr[2]) << 8) | + (0xff)); + netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) | + ((u32_t)(netif->hwaddr[3]) << 16) | + ((u32_t)(netif->hwaddr[4]) << 8) | + (netif->hwaddr[5])); + } + else { + /* Use hwaddr directly as interface ID. */ + netif->ip6_addr[0].addr[2] = 0; + netif->ip6_addr[0].addr[3] = 0; + + addr_index = 3; + for (i = 0; i < 8; i++) { + if (i == 4) { + addr_index--; + } + netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); + } + } + + /* Set address state. */ +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* Will perform duplicate address detection (DAD). */ + netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; +#else + /* Consider address valid. */ + netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; +#endif /* LWIP_IPV6_AUTOCONFIG */ +} + +static err_t +netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + (void)netif; + (void)p; + (void)ipaddr; + + return ERR_IF; +} +#endif /* LWIP_IPV6 */ diff --git a/lib/liblwip/core/pbuf.c b/lib/liblwip/core/pbuf.c index dd9ff64ee..1e5e53b12 100644 --- a/lib/liblwip/core/pbuf.c +++ b/lib/liblwip/core/pbuf.c @@ -70,7 +70,7 @@ #include "lwip/pbuf.h" #include "lwip/sys.h" #include "arch/perf.h" -#if TCP_QUEUE_OOSEQ +#if LWIP_TCP && TCP_QUEUE_OOSEQ #include "lwip/tcp_impl.h" #endif #if LWIP_CHECKSUM_ON_COPY @@ -84,18 +84,25 @@ aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ #define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) -#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ #define PBUF_POOL_IS_EMPTY() -#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */ -/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ -#ifndef PBUF_POOL_FREE_OOSEQ -#define PBUF_POOL_FREE_OOSEQ 1 -#endif /* PBUF_POOL_FREE_OOSEQ */ +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ -#if PBUF_POOL_FREE_OOSEQ +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL #include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; #define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() -static u8_t pbuf_free_ooseq_queued; + /** * Attempt to reclaim some memory from queued out-of-sequence TCP segments * if we run out of pool pbufs. It's better to give priority to new packets @@ -104,15 +111,17 @@ static u8_t pbuf_free_ooseq_queued; * This must be done in the correct thread context therefore this function * can only be used with NO_SYS=0 and through tcpip_callback. */ -static void -pbuf_free_ooseq(void* arg) +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) { struct tcp_pcb* pcb; SYS_ARCH_DECL_PROTECT(old_level); - LWIP_UNUSED_ARG(arg); SYS_ARCH_PROTECT(old_level); - pbuf_free_ooseq_queued = 0; + pbuf_free_ooseq_pending = 0; SYS_ARCH_UNPROTECT(old_level); for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { @@ -126,29 +135,42 @@ pbuf_free_ooseq(void* arg) } } +#if !NO_SYS +/** + * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + /** Queue a call to pbuf_free_ooseq if not already queued. */ static void pbuf_pool_is_empty(void) { +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ u8_t queued; SYS_ARCH_DECL_PROTECT(old_level); - SYS_ARCH_PROTECT(old_level); - queued = pbuf_free_ooseq_queued; - pbuf_free_ooseq_queued = 1; + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; SYS_ARCH_UNPROTECT(old_level); if(!queued) { /* queue a call to pbuf_free_ooseq if not already queued */ - if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) { - SYS_ARCH_PROTECT(old_level); - pbuf_free_ooseq_queued = 0; - SYS_ARCH_UNPROTECT(old_level); - } + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ } -#endif /* PBUF_POOL_FREE_OOSEQ */ -#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */ +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ /** * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). @@ -190,21 +212,21 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ - offset = 0; switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ - offset += PBUF_TRANSPORT_HLEN; - /* FALLTHROUGH */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; case PBUF_IP: /* add room for IP layer header */ - offset += PBUF_IP_HLEN; - /* FALLTHROUGH */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; case PBUF_LINK: /* add room for link layer header */ - offset += PBUF_LINK_HLEN; + offset = PBUF_LINK_HLEN; break; case PBUF_RAW: + offset = 0; break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); @@ -336,7 +358,8 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) * @param p pointer to the custom pbuf to initialize (already allocated) * @param payload_mem pointer to the buffer that is used for payload and headers, * must be at least big enough to hold 'length' plus the header size, - * may be NULL if set later + * may be NULL if set later. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least * big enough to hold 'length' plus the header size */ @@ -348,35 +371,35 @@ pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_cust LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); /* determine header offset */ - offset = 0; switch (l) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ - offset += PBUF_TRANSPORT_HLEN; - /* FALLTHROUGH */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; case PBUF_IP: /* add room for IP layer header */ - offset += PBUF_IP_HLEN; - /* FALLTHROUGH */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; case PBUF_LINK: /* add room for link layer header */ - offset += PBUF_LINK_HLEN; + offset = PBUF_LINK_HLEN; break; case PBUF_RAW: + offset = 0; break; default: LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); return NULL; } - if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) { + if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); return NULL; } p->pbuf.next = NULL; if (payload_mem != NULL) { - p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset)); + p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); } else { p->pbuf.payload = NULL; } @@ -840,7 +863,6 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) /* iterate through pbuf chain */ do { - LWIP_ASSERT("p_to != NULL", p_to != NULL); /* copy one part of the original chain */ if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { /* complete current p_from fits into current p_to */ @@ -853,17 +875,18 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) offset_to += len; offset_from += len; LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); - if (offset_to == p_to->len) { - /* on to next p_to (if any) */ - offset_to = 0; - p_to = p_to->next; - } LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); if (offset_from >= p_from->len) { /* on to next p_from (if any) */ offset_from = 0; p_from = p_from->next; } + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } if((p_from != NULL) && (p_from->len == p_from->tot_len)) { /* don't copy more than one packet! */ diff --git a/lib/liblwip/core/raw.c b/lib/liblwip/core/raw.c index 325990249..6f50848c6 100644 --- a/lib/liblwip/core/raw.c +++ b/lib/liblwip/core/raw.c @@ -49,6 +49,8 @@ #include "lwip/raw.h" #include "lwip/stats.h" #include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" #include @@ -79,29 +81,47 @@ raw_input(struct pbuf *p, struct netif *inp) struct ip_hdr *iphdr; s16_t proto; u8_t eaten = 0; +#if LWIP_IPV6 + struct ip6_hdr *ip6hdr; +#endif /* LWIP_IPV6 */ + LWIP_UNUSED_ARG(inp); iphdr = (struct ip_hdr *)p->payload; - proto = IPH_PROTO(iphdr); +#if LWIP_IPV6 + if (IPH_V(iphdr) == 6) { + ip6hdr = (struct ip6_hdr *)p->payload; + proto = IP6H_NEXTH(ip6hdr); + } + else +#endif /* LWIP_IPV6 */ + { + proto = IPH_PROTO(iphdr); + } prev = NULL; pcb = raw_pcbs; /* loop through all raw pcbs until the packet is eaten by one */ /* this allows multiple pcbs to match against the packet by design */ while ((eaten == 0) && (pcb != NULL)) { - if ((pcb->protocol == IP_PROTO_ANY || pcb->protocol == proto) && - (ip_addr_isany(&pcb->local_ip) || - ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) { + if ((pcb->protocol == proto) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) { #if IP_SOF_BROADCAST_RECV /* broadcast filter? */ - if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp)) + if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) +#if LWIP_IPV6 + && !PCB_ISIPV6(pcb) +#endif /* LWIP_IPV6 */ + ) #endif /* IP_SOF_BROADCAST_RECV */ { /* receive callback function available? */ - if (pcb->recv != NULL) { + if (pcb->recv.ip4 != NULL) { /* the receive callback function did not eat the packet? */ - if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) { + eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr()); + if (eaten != 0) { /* receive function ate the packet */ p = NULL; eaten = 1; @@ -141,7 +161,7 @@ raw_input(struct pbuf *p, struct netif *inp) err_t raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) { - ip_addr_set(&pcb->local_ip, ipaddr); + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); return ERR_OK; } @@ -161,7 +181,7 @@ raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) err_t raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) { - ip_addr_set(&pcb->remote_ip, ipaddr); + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); return ERR_OK; } @@ -183,7 +203,7 @@ void raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) { /* remember recv() callback and user data */ - pcb->recv = recv; + pcb->recv.ip4 = recv; pcb->recv_arg = recv_arg; } @@ -204,13 +224,21 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) { err_t err; struct netif *netif; - ip_addr_t *src_ip; + ipX_addr_t *src_ip; struct pbuf *q; /* q will be sent down the stack */ - + s16_t header_size; + ipX_addr_t *dst_ip = ip_2_ipX(ipaddr); + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); - + + header_size = ( +#if LWIP_IPV6 + PCB_ISIPV6(pcb) ? IP6_HLEN : +#endif /* LWIP_IPV6 */ + IP_HLEN); + /* not enough space to add an IP header to first pbuf in given p chain? */ - if (pbuf_header(p, IP_HLEN)) { + if (pbuf_header(p, header_size)) { /* allocate header in new pbuf */ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); /* new header pbuf could not be allocated? */ @@ -218,22 +246,25 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); return ERR_MEM; } - /* chain header q in front of given pbuf p */ - pbuf_chain(q, p); + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + } /* { first pbuf q points to header pbuf } */ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { /* first pbuf q equals given pbuf */ q = p; - if(pbuf_header(q, -IP_HLEN)) { + if(pbuf_header(q, -header_size)) { LWIP_ASSERT("Can't restore header we just removed!", 0); return ERR_MEM; } } - if ((netif = ip_route(ipaddr)) == NULL) { - LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); + if (netif == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); @@ -242,32 +273,42 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) } #if IP_SOF_BROADCAST - /* broadcast filter? */ - if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) { - LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); - /* free any temporary header pbuf allocated by pbuf_header() */ - if (q != p) { - pbuf_free(q); +#if LWIP_IPV6 + /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */ + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; } - return ERR_VAL; } #endif /* IP_SOF_BROADCAST */ - if (ip_addr_isany(&pcb->local_ip)) { + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { /* use outgoing network interface IP address as source address */ - src_ip = &(netif->ip_addr); + src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip); +#if LWIP_IPV6 + if (src_ip == NULL) { + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } +#endif /* LWIP_IPV6 */ } else { /* use RAW PCB local IP address as source address */ - src_ip = &(pcb->local_ip); + src_ip = &pcb->local_ip; } -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = &(pcb->addr_hint); -#endif /* LWIP_NETIF_HWADDRHINT*/ - err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = NULL; -#endif /* LWIP_NETIF_HWADDRHINT*/ + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); + NETIF_SET_HWADDRHINT(netif, NULL); /* did we chain a header earlier? */ if (q != p) { @@ -287,7 +328,7 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) err_t raw_send(struct raw_pcb *pcb, struct pbuf *p) { - return raw_sendto(pcb, p, &pcb->remote_ip); + return raw_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip)); } /** @@ -349,4 +390,26 @@ raw_new(u8_t proto) return pcb; } +#if LWIP_IPV6 +/** + * Create a RAW PCB for IPv6. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number (next header) of the IPv6 packet payload + * (e.g. IP6_NEXTH_ICMP6) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new_ip6(u8_t proto) +{ + struct raw_pcb *pcb; + pcb = raw_new(proto); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + #endif /* LWIP_RAW */ diff --git a/lib/liblwip/core/snmp/mib2.c b/lib/liblwip/core/snmp/mib2.c index 5ed9fcd66..dcd3b62c1 100644 --- a/lib/liblwip/core/snmp/mib2.c +++ b/lib/liblwip/core/snmp/mib2.c @@ -43,10 +43,12 @@ #include "lwip/netif.h" #include "lwip/ip.h" #include "lwip/ip_frag.h" +#include "lwip/mem.h" #include "lwip/tcp_impl.h" #include "lwip/udp.h" #include "lwip/snmp_asn1.h" #include "lwip/snmp_structs.h" +#include "lwip/sys.h" #include "netif/etharp.h" /** @@ -71,7 +73,7 @@ #endif #ifndef SNMP_GET_SYSUPTIME -#define SNMP_GET_SYSUPTIME(sysuptime) +#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10)) #endif static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); @@ -1795,7 +1797,7 @@ void snmp_insert_udpidx_tree(struct udp_pcb *pcb) u8_t level; LWIP_ASSERT("pcb != NULL", pcb != NULL); - snmp_iptooid(&pcb->local_ip, &udpidx[0]); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); udpidx[4] = pcb->local_port; udp_rn = &udp_root; @@ -1848,7 +1850,7 @@ void snmp_delete_udpidx_tree(struct udp_pcb *pcb) u8_t bindings, fc, level, del_cnt; LWIP_ASSERT("pcb != NULL", pcb != NULL); - snmp_iptooid(&pcb->local_ip, &udpidx[0]); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); udpidx[4] = pcb->local_port; /* count PCBs for a given binding @@ -1857,7 +1859,7 @@ void snmp_delete_udpidx_tree(struct udp_pcb *pcb) npcb = udp_pcbs; while ((npcb != NULL)) { - if (ip_addr_cmp(&npcb->local_ip, &pcb->local_ip) && + if (ipX_addr_cmp(0, &npcb->local_ip, &pcb->local_ip) && (npcb->local_port == udpidx[4])) { bindings++; @@ -3879,17 +3881,17 @@ udpentry_get_value(struct obj_def *od, u16_t len, void *value) { u8_t id; struct udp_pcb *pcb; - ip_addr_t ip; + ipX_addr_t ip; u16_t port; LWIP_UNUSED_ARG(len); - snmp_oidtoip(&od->id_inst_ptr[1], &ip); + snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&ip); LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff)); port = (u16_t)od->id_inst_ptr[5]; pcb = udp_pcbs; while ((pcb != NULL) && - !(ip_addr_cmp(&pcb->local_ip, &ip) && + !(ipX_addr_cmp(0, &pcb->local_ip, &ip) && (pcb->local_port == port))) { pcb = pcb->next; @@ -3903,8 +3905,8 @@ udpentry_get_value(struct obj_def *od, u16_t len, void *value) { case 1: /* udpLocalAddress */ { - ip_addr_t *dst = (ip_addr_t*)value; - *dst = pcb->local_ip; + ipX_addr_t *dst = (ipX_addr_t*)value; + ipX_addr_copy(0, *dst, pcb->local_ip); } break; case 2: /* udpLocalPort */ diff --git a/lib/liblwip/core/snmp/msg_in.c b/lib/liblwip/core/snmp/msg_in.c index 2dfb55b25..be940c62d 100644 --- a/lib/liblwip/core/snmp/msg_in.c +++ b/lib/liblwip/core/snmp/msg_in.c @@ -90,7 +90,7 @@ snmp_init(void) trap_msg.pcb = snmp1_pcb; #ifdef SNMP_PRIVATE_MIB_INIT - /* If defined, rhis must be a function-like define to initialize the + /* If defined, this must be a function-like define to initialize the * private MIB after the stack has been initialized. * The private MIB can also be initialized in tcpip_callback (or after * the stack is initialized), this define is only for convenience. */ @@ -105,13 +105,28 @@ snmp_init(void) static void snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) { + /* move names back from outvb to invb */ + int v; + struct snmp_varbind *vbi = msg_ps->invb.head; + struct snmp_varbind *vbo = msg_ps->outvb.head; + for (v=0; vvb_idx; v++) { + vbi->ident_len = vbo->ident_len; + vbo->ident_len = 0; + vbi->ident = vbo->ident; + vbo->ident = NULL; + vbi = vbi->next; + vbo = vbo->next; + } + /* free outvb */ snmp_varbind_list_free(&msg_ps->outvb); + /* we send invb back */ msg_ps->outvb = msg_ps->invb; msg_ps->invb.head = NULL; msg_ps->invb.tail = NULL; msg_ps->invb.count = 0; msg_ps->error_status = error; - msg_ps->error_index = 1 + msg_ps->vb_idx; + /* error index must be 0 for error too big */ + msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0; snmp_send_response(msg_ps); snmp_varbind_list_free(&msg_ps->outvb); msg_ps->state = SNMP_MSG_EMPTY; @@ -182,7 +197,6 @@ snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) /* allocate output varbind */ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); - LWIP_ASSERT("vb != NULL",vb != NULL); if (vb != NULL) { vb->next = NULL; @@ -202,7 +216,6 @@ snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) { LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); vb->value = memp_malloc(MEMP_SNMP_VALUE); - LWIP_ASSERT("vb->value != NULL",vb->value != NULL); if (vb->value != NULL) { en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); @@ -297,7 +310,6 @@ snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; /* allocate output varbind */ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); - LWIP_ASSERT("vb != NULL",vb != NULL); if (vb != NULL) { vb->next = NULL; @@ -318,7 +330,6 @@ snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); vb->value = memp_malloc(MEMP_SNMP_VALUE); - LWIP_ASSERT("vb->value != NULL",vb->value != NULL); if (vb->value != NULL) { mn->get_value(&object_def, vb->value_len, vb->value); @@ -331,6 +342,8 @@ snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); msg_ps->vb_ptr->ident = vb->ident; msg_ps->vb_ptr->ident_len = vb->ident_len; + vb->ident = NULL; + vb->ident_len = 0; memp_free(MEMP_SNMP_VARBIND, vb); snmp_error_response(msg_ps,SNMP_ES_TOOBIG); } @@ -1305,7 +1318,6 @@ snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) struct snmp_varbind *vb; vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); - LWIP_ASSERT("vb != NULL",vb != NULL); if (vb != NULL) { u8_t i; @@ -1319,9 +1331,9 @@ snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH); /* allocate array of s32_t for our object identifier */ vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE); - LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL); if (vb->ident == NULL) { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n")); memp_free(MEMP_SNMP_VARBIND, vb); return NULL; } @@ -1343,9 +1355,9 @@ snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); /* allocate raw bytes for our object value */ vb->value = memp_malloc(MEMP_SNMP_VALUE); - LWIP_ASSERT("vb->value != NULL",vb->value != NULL); if (vb->value == NULL) { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n")); if (vb->ident != NULL) { memp_free(MEMP_SNMP_VALUE, vb->ident); @@ -1360,6 +1372,10 @@ snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) vb->value = NULL; } } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n")); + } return vb; } diff --git a/lib/liblwip/core/snmp/msg_out.c b/lib/liblwip/core/snmp/msg_out.c index 4778bee6f..fc0807c59 100644 --- a/lib/liblwip/core/snmp/msg_out.c +++ b/lib/liblwip/core/snmp/msg_out.c @@ -145,14 +145,7 @@ snmp_send_response(struct snmp_msg_pstat *m_stat) /* pass 1, size error, encode packet ino the pbuf(s) */ ofs = snmp_resp_header_enc(m_stat, p); - if (m_stat->error_status == SNMP_ES_TOOBIG) - { - snmp_varbind_list_enc(&emptyvb, p, ofs); - } - else - { - snmp_varbind_list_enc(&m_stat->outvb, p, ofs); - } + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); switch (m_stat->error_status) { @@ -224,6 +217,7 @@ snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) ip_addr_t dst_ip; struct pbuf *p; u16_t i,tot_len; + err_t err = ERR_OK; for (i=0, td = &trap_dst[0]; idip); /* lookup current source address for this dst */ dst_if = ip_route(&td->dip); - ip_addr_copy(dst_ip, dst_if->ip_addr); - /* @todo: what about IPv6? */ - trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); - trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); - trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); - trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); - trap_msg.gen_trap = generic_trap; - trap_msg.spc_trap = specific_trap; - if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) - { - /* enterprise-Specific trap */ - trap_msg.enterprise = eoid; - } - else - { - /* generic (MIB-II) trap */ - snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); - } - snmp_get_sysuptime(&trap_msg.ts); - - /* pass 0, calculate length fields */ - tot_len = snmp_varbind_list_sum(&trap_msg.outvb); - tot_len = snmp_trap_header_sum(&trap_msg, tot_len); - - /* allocate pbuf(s) */ - p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); - if (p != NULL) - { - u16_t ofs; - - /* pass 1, encode packet ino the pbuf(s) */ - ofs = snmp_trap_header_enc(&trap_msg, p); - snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); - - snmp_inc_snmpouttraps(); - snmp_inc_snmpoutpkts(); - - /** send to the TRAP destination */ - udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); - - pbuf_free(p); - } - else - { - return ERR_MEM; + if (dst_if != NULL) { + ip_addr_copy(dst_ip, dst_if->ip_addr); + /* @todo: what about IPv6? */ + trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); + trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); + trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); + trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** send to the TRAP destination */ + udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } else { + /* routing error */ + err = ERR_RTE; } } } - return ERR_OK; + return err; } void diff --git a/lib/liblwip/core/stats.c b/lib/liblwip/core/stats.c index 69f97d41f..06fbe0f2c 100644 --- a/lib/liblwip/core/stats.c +++ b/lib/liblwip/core/stats.c @@ -69,7 +69,7 @@ void stats_init(void) #if LWIP_STATS_DISPLAY void -stats_display_proto(struct stats_proto *proto, char *name) +stats_display_proto(struct stats_proto *proto, const char *name) { LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); @@ -88,9 +88,9 @@ stats_display_proto(struct stats_proto *proto, char *name) #if IGMP_STATS void -stats_display_igmp(struct stats_igmp *igmp) +stats_display_igmp(struct stats_igmp *igmp, const char *name) { - LWIP_PLATFORM_DIAG(("\nIGMP\n\t")); + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); @@ -99,8 +99,8 @@ stats_display_igmp(struct stats_igmp *igmp) LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); - LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group)); - LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general)); LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); @@ -110,7 +110,7 @@ stats_display_igmp(struct stats_igmp *igmp) #if MEM_STATS || MEMP_STATS void -stats_display_mem(struct stats_mem *mem, char *name) +stats_display_mem(struct stats_mem *mem, const char *name) { LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); @@ -159,9 +159,14 @@ stats_display(void) LINK_STATS_DISPLAY(); ETHARP_STATS_DISPLAY(); IPFRAG_STATS_DISPLAY(); + IP6_FRAG_STATS_DISPLAY(); IP_STATS_DISPLAY(); + ND6_STATS_DISPLAY(); + IP6_STATS_DISPLAY(); IGMP_STATS_DISPLAY(); + MLD6_STATS_DISPLAY(); ICMP_STATS_DISPLAY(); + ICMP6_STATS_DISPLAY(); UDP_STATS_DISPLAY(); TCP_STATS_DISPLAY(); MEM_STATS_DISPLAY(); diff --git a/lib/liblwip/core/sys.c b/lib/liblwip/core/sys.c index d3a77deb2..f17773726 100644 --- a/lib/liblwip/core/sys.c +++ b/lib/liblwip/core/sys.c @@ -45,6 +45,7 @@ #if !NO_SYS +#ifndef sys_msleep /** * Sleep for some ms. Timeouts are NOT processed while sleeping. * @@ -62,5 +63,6 @@ sys_msleep(u32_t ms) } } } +#endif /* sys_msleep */ #endif /* !NO_SYS */ diff --git a/lib/liblwip/core/tcp.c b/lib/liblwip/core/tcp.c index acb989bf3..a0cbb1595 100644 --- a/lib/liblwip/core/tcp.c +++ b/lib/liblwip/core/tcp.c @@ -52,9 +52,28 @@ #include "lwip/tcp_impl.h" #include "lwip/debug.h" #include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" #include +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + const char * const tcp_state_str[] = { "CLOSED", "LISTEN", @@ -69,6 +88,9 @@ const char * const tcp_state_str[] = { "TIME_WAIT" }; +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + /* Incremented every coarse grained timer shot (typically every 500 ms). */ u32_t tcp_ticks; const u8_t tcp_backoff[13] = @@ -91,19 +113,32 @@ struct tcp_pcb *tcp_tw_pcbs; #define NUM_TCP_PCB_LISTS 4 #define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 /** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ -struct tcp_pcb **tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, +struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; /** Only used for temporary storage. */ struct tcp_pcb *tcp_tmp_pcb; +u8_t tcp_active_pcbs_changed; + /** Timer counter to handle calling slow-timer from tcp_tmr() */ static u8_t tcp_timer; +static u8_t tcp_timer_ctr; static u16_t tcp_new_port(void); +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + /** * Called periodically to dispatch TCP timers. - * */ void tcp_tmr(void) @@ -139,7 +174,7 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) { err_t err; - if (rst_on_unacked_data && (pcb->state != LISTEN)) { + if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { /* Not all data received by application, send RST to tell the remote side about this. */ @@ -148,17 +183,18 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) /* don't call tcp_abort here: we must not deallocate the pcb since that might not be expected when calling tcp_close */ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, - pcb->local_port, pcb->remote_port); + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); tcp_pcb_purge(pcb); - - /* TODO: to which state do we move now? */ - - /* move to TIME_WAIT since we close actively */ - TCP_RMV(&tcp_active_pcbs, pcb); - pcb->state = TIME_WAIT; - TCP_REG(&tcp_tw_pcbs, pcb); - + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + memp_free(MEMP_TCP_PCB, pcb); + } return ERR_OK; } } @@ -173,8 +209,9 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) * is erroneous, but this should never happen as the pcb has in those cases * been freed, and so any remaining handles are bogus. */ err = ERR_OK; - if(pcb->local_port != 0) - TCP_RMV(&tcp_bound_pcbs, pcb); + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; break; @@ -186,7 +223,7 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) break; case SYN_SENT: err = ERR_OK; - tcp_pcb_remove(&tcp_active_pcbs, pcb); + TCP_PCB_REMOVE_ACTIVE(pcb); memp_free(MEMP_TCP_PCB, pcb); pcb = NULL; snmp_inc_tcpattemptfails(); @@ -265,7 +302,9 @@ tcp_close(struct tcp_pcb *pcb) /** * Causes all or part of a full-duplex connection of this PCB to be shut down. - * This doesn't deallocate the PCB! + * This doesn't deallocate the PCB unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds, + * the PCB should not be referenced any more. * * @param pcb PCB to shutdown * @param shut_rx shut down receive side if this is != 0 @@ -280,28 +319,32 @@ tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) return ERR_CONN; } if (shut_rx) { - /* shut down the receive side: free buffered data... */ + /* shut down the receive side: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ if (pcb->refused_data != NULL) { pbuf_free(pcb->refused_data); pcb->refused_data = NULL; } - /* ... and set a flag not to receive any more data */ - pcb->flags |= TF_RXCLOSED; } if (shut_tx) { /* This can't happen twice since if it succeeds, the pcb's state is changed. Only close in these states as the others directly deallocate the PCB */ switch (pcb->state) { - case SYN_RCVD: - case ESTABLISHED: - case CLOSE_WAIT: - return tcp_close_shutdown(pcb, 0); - default: - /* don't shut down other states */ - break; + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; } } - /* @todo: return another err_t if not in correct state or already shut? */ return ERR_OK; } @@ -317,8 +360,6 @@ void tcp_abandon(struct tcp_pcb *pcb, int reset) { u32_t seqno, ackno; - u16_t remote_port, local_port; - ip_addr_t remote_ip, local_ip; #if LWIP_CALLBACK_API tcp_err_fn errf; #endif /* LWIP_CALLBACK_API */ @@ -334,17 +375,14 @@ tcp_abandon(struct tcp_pcb *pcb, int reset) tcp_pcb_remove(&tcp_tw_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { + int send_rst = reset && (pcb->state != CLOSED); seqno = pcb->snd_nxt; ackno = pcb->rcv_nxt; - ip_addr_copy(local_ip, pcb->local_ip); - ip_addr_copy(remote_ip, pcb->remote_ip); - local_port = pcb->local_port; - remote_port = pcb->remote_port; #if LWIP_CALLBACK_API errf = pcb->errf; #endif /* LWIP_CALLBACK_API */ errf_arg = pcb->callback_arg; - tcp_pcb_remove(&tcp_active_pcbs, pcb); + TCP_PCB_REMOVE_ACTIVE(pcb); if (pcb->unacked != NULL) { tcp_segs_free(pcb->unacked); } @@ -356,12 +394,12 @@ tcp_abandon(struct tcp_pcb *pcb, int reset) tcp_segs_free(pcb->ooseq); } #endif /* TCP_QUEUE_OOSEQ */ - memp_free(MEMP_TCP_PCB, pcb); - TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); - if (reset) { + if (send_rst) { LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); - tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); } } @@ -392,6 +430,7 @@ tcp_abort(struct tcp_pcb *pcb) * to any local address * @param port the local port to bind to * @return ERR_USE if the port is already in use + * ERR_VAL if bind failed because the PCB is not in a valid state * ERR_OK if bound */ err_t @@ -401,7 +440,7 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) int max_pcb_list = NUM_TCP_PCB_LISTS; struct tcp_pcb *cpcb; - LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL); #if SO_REUSE /* Unless the REUSEADDR flag is set, @@ -409,15 +448,16 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) We do not dump TIME_WAIT pcb's; they can still be matched by incoming packets using both local and remote IP addresses and ports to distinguish. */ -#if SO_REUSE - if ((pcb->so_options & SOF_REUSEADDR) != 0) { + if (ip_get_option(pcb, SOF_REUSEADDR)) { max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; } -#endif /* SO_REUSE */ #endif /* SO_REUSE */ if (port == 0) { port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } } /* Check if the address already is in use (on all lists) */ @@ -428,13 +468,15 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) /* Omit checking for the same port if both pcbs have REUSEADDR set. For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in tcp_connect. */ - if (((pcb->so_options & SOF_REUSEADDR) == 0) || - ((cpcb->so_options & SOF_REUSEADDR) == 0)) + if (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) #endif /* SO_REUSE */ { - if (ip_addr_isany(&(cpcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + /* @todo: check accept_any_ip_version */ + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) || + ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) { return ERR_USE; } } @@ -442,8 +484,8 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) } } - if (!ip_addr_isany(ipaddr)) { - pcb->local_ip = *ipaddr; + if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr)); } pcb->local_port = port; TCP_REG(&tcp_bound_pcbs, pcb); @@ -492,13 +534,14 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) return pcb; } #if SO_REUSE - if ((pcb->so_options & SOF_REUSEADDR) != 0) { + if (ip_get_option(pcb, SOF_REUSEADDR)) { /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage is declared (listen-/connection-pcb), we have to make sure now that this port is only used once for every local IP. */ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { - if (lpcb->local_port == pcb->local_port) { - if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) { + if ((lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb)) { + if (ipX_addr_cmp(PCB_ISIPV6(pcb), &lpcb->local_ip, &pcb->local_ip)) { /* this address/port is already used */ return NULL; } @@ -515,11 +558,17 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) lpcb->state = LISTEN; lpcb->prio = pcb->prio; lpcb->so_options = pcb->so_options; - lpcb->so_options |= SOF_ACCEPTCONN; + ip_set_option(lpcb, SOF_ACCEPTCONN); lpcb->ttl = pcb->ttl; lpcb->tos = pcb->tos; - ip_addr_copy(lpcb->local_ip, pcb->local_ip); - TCP_RMV(&tcp_bound_pcbs, pcb); +#if LWIP_IPV6 + PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb); + lpcb->accept_any_ip_version = 0; +#endif /* LWIP_IPV6 */ + ipX_addr_copy(PCB_ISIPV6(pcb), lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } memp_free(MEMP_TCP_PCB, pcb); #if LWIP_CALLBACK_API lpcb->accept = tcp_accept_null; @@ -532,7 +581,29 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) return (struct tcp_pcb *)lpcb; } -/** +#if LWIP_IPV6 +/** + * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6 + * connections, if the pcb's local address is set to ANY. + */ +struct tcp_pcb * +tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb *lpcb; + + lpcb = tcp_listen_with_backlog(pcb, backlog); + if ((lpcb != NULL) && + ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* The default behavior is to accept connections on either + * IPv4 or IPv6, if not bound. */ + /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */ + ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1; + } + return lpcb; +} +#endif /* LWIP_IPV6 */ + +/** * Update the state that tracks the available window space to advertise. * * Returns how much extra window would be advertised if we sent an @@ -574,6 +645,9 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len) { int wnd_inflation; + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", len <= 0xffff - pcb->rcv_wnd ); @@ -598,35 +672,33 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len) } /** - * A nastly hack featuring 'goto' statements that allocates a - * new TCP local port. + * Allocate a new local TCP port. * * @return a new (free) local TCP port number */ static u16_t tcp_new_port(void) { - int i; + u8_t i; + u16_t n = 0; struct tcp_pcb *pcb; -#ifndef TCP_LOCAL_PORT_RANGE_START -#define TCP_LOCAL_PORT_RANGE_START 4096 -#define TCP_LOCAL_PORT_RANGE_END 0x7fff -#endif - static u16_t port = TCP_LOCAL_PORT_RANGE_START; - again: - if (++port > TCP_LOCAL_PORT_RANGE_END) { - port = TCP_LOCAL_PORT_RANGE_START; +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_port = TCP_LOCAL_PORT_RANGE_START; } /* Check all PCB lists. */ - for (i = 1; i < NUM_TCP_PCB_LISTS; i++) { + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { - if (pcb->local_port == port) { + if (pcb->local_port == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } goto again; } } } - return port; + return tcp_port; } /** @@ -647,46 +719,54 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, { err_t ret; u32_t iss; + u16_t old_local_port; - LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); if (ipaddr != NULL) { - pcb->remote_ip = *ipaddr; + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr)); } else { return ERR_VAL; } pcb->remote_port = port; /* check if we have a route to the remote host */ - if (ip_addr_isany(&(pcb->local_ip))) { + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { /* no local IP address set, yet. */ - struct netif *netif = ip_route(&(pcb->remote_ip)); - if (netif == NULL) { + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { /* Don't even try to send a SYN packet if we have no route since that will fail. */ return ERR_RTE; } - /* Use the netif's IP address as local address. */ - ip_addr_copy(pcb->local_ip, netif->ip_addr); + /* Use the address as local address of the pcb. */ + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); } + old_local_port = pcb->local_port; if (pcb->local_port == 0) { pcb->local_port = tcp_new_port(); + if (pcb->local_port == 0) { + return ERR_BUF; + } } #if SO_REUSE - if ((pcb->so_options & SOF_REUSEADDR) != 0) { + if (ip_get_option(pcb, SOF_REUSEADDR)) { /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure now that the 5-tuple is unique. */ struct tcp_pcb *cpcb; int i; - /* Don't check listen PCBs, check bound-, active- and TIME-WAIT PCBs. */ - for (i = 1; i < NUM_TCP_PCB_LISTS; i++) { + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { if ((cpcb->local_port == pcb->local_port) && (cpcb->remote_port == port) && - ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && - ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { + IP_PCB_IPVER_EQ(cpcb, pcb) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(ipaddr))) { /* linux returns EISCONN here, but ERR_USE should be OK for us */ return ERR_USE; } @@ -707,13 +787,13 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, The send MSS is updated when an MSS option is received. */ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; #if TCP_CALCULATE_EFF_SEND_MSS - pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ pcb->cwnd = 1; pcb->ssthresh = pcb->mss * 10; #if LWIP_CALLBACK_API pcb->connected = connected; -#else /* LWIP_CALLBACK_API */ +#else /* LWIP_CALLBACK_API */ LWIP_UNUSED_ARG(connected); #endif /* LWIP_CALLBACK_API */ @@ -722,8 +802,10 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, if (ret == ERR_OK) { /* SYN segment was enqueued, changed the pcbs state now */ pcb->state = SYN_SENT; - TCP_RMV(&tcp_bound_pcbs, pcb); - TCP_REG(&tcp_active_pcbs, pcb); + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(pcb); snmp_inc_tcpactiveopens(); tcp_output(pcb); @@ -741,7 +823,7 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, void tcp_slowtmr(void) { - struct tcp_pcb *pcb, *pcb2, *prev; + struct tcp_pcb *pcb, *prev; u16_t eff_wnd; u8_t pcb_remove; /* flag if a PCB should be removed */ u8_t pcb_reset; /* flag if a RST should be sent when removing */ @@ -750,7 +832,9 @@ tcp_slowtmr(void) err = ERR_OK; ++tcp_ticks; + ++tcp_timer_ctr; +tcp_slowtmr_start: /* Steps through all of the active PCBs. */ prev = NULL; pcb = tcp_active_pcbs; @@ -762,6 +846,12 @@ tcp_slowtmr(void) LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; pcb_remove = 0; pcb_reset = 0; @@ -787,8 +877,9 @@ tcp_slowtmr(void) } } else { /* Increase the retransmission timer if it is running */ - if(pcb->rtime >= 0) + if(pcb->rtime >= 0) { ++pcb->rtime; + } if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { /* Time for a retransmission. */ @@ -824,42 +915,35 @@ tcp_slowtmr(void) } /* Check if this PCB has stayed too long in FIN-WAIT-2 */ if (pcb->state == FIN_WAIT_2) { - if ((u32_t)(tcp_ticks - pcb->tmr) > - TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { - ++pcb_remove; - LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } } } /* Check if KEEPALIVE should be sent */ - if((pcb->so_options & SOF_KEEPALIVE) && + if(ip_get_option(pcb, SOF_KEEPALIVE) && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { -#if LWIP_TCP_KEEPALIVE if((u32_t)(tcp_ticks - pcb->tmr) > - (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl)) - / TCP_SLOW_INTERVAL) -#else - if((u32_t)(tcp_ticks - pcb->tmr) > - (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) -#endif /* LWIP_TCP_KEEPALIVE */ + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) { - LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", - ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), - ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); ++pcb_remove; ++pcb_reset; } -#if LWIP_TCP_KEEPALIVE - else if((u32_t)(tcp_ticks - pcb->tmr) > - (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl) - / TCP_SLOW_INTERVAL) -#else else if((u32_t)(tcp_ticks - pcb->tmr) > - (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT) + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) / TCP_SLOW_INTERVAL) -#endif /* LWIP_TCP_KEEPALIVE */ { tcp_keepalive(pcb); pcb->keep_cnt_sent++; @@ -897,6 +981,9 @@ tcp_slowtmr(void) /* If the PCB should be removed, do it. */ if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_err_fn err_fn; + void *err_arg; tcp_pcb_purge(pcb); /* Remove PCB from tcp_active_pcbs list. */ if (prev != NULL) { @@ -908,15 +995,22 @@ tcp_slowtmr(void) tcp_active_pcbs = pcb->next; } - TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); if (pcb_reset) { tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, - pcb->local_port, pcb->remote_port); + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); } - pcb2 = pcb->next; - memp_free(MEMP_TCP_PCB, pcb); - pcb = pcb2; + err_fn = pcb->errf; + err_arg = pcb->callback_arg; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } } else { /* get the 'next' element now and work with 'prev' below (in case of abort) */ prev = pcb; @@ -927,7 +1021,11 @@ tcp_slowtmr(void) if (prev->polltmr >= prev->pollinterval) { prev->polltmr = 0; LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + tcp_active_pcbs_changed = 0; TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } /* if err == ERR_ABRT, 'prev' is already deallocated */ if (err == ERR_OK) { tcp_output(prev); @@ -938,7 +1036,7 @@ tcp_slowtmr(void) /* Steps through all of the TIME-WAIT PCBs. */ - prev = NULL; + prev = NULL; pcb = tcp_tw_pcbs; while (pcb != NULL) { LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); @@ -953,6 +1051,7 @@ tcp_slowtmr(void) /* If the PCB should be removed, do it. */ if (pcb_remove) { + struct tcp_pcb *pcb2; tcp_pcb_purge(pcb); /* Remove PCB from tcp_tw_pcbs list. */ if (prev != NULL) { @@ -963,9 +1062,9 @@ tcp_slowtmr(void) LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); tcp_tw_pcbs = pcb->next; } - pcb2 = pcb->next; - memp_free(MEMP_TCP_PCB, pcb); - pcb = pcb2; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); } else { prev = pcb; pcb = pcb->next; @@ -982,34 +1081,78 @@ tcp_slowtmr(void) void tcp_fasttmr(void) { - struct tcp_pcb *pcb = tcp_active_pcbs; + struct tcp_pcb *pcb; + + ++tcp_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; while(pcb != NULL) { - struct tcp_pcb *next = pcb->next; - /* If there is data which was previously "refused" by upper layer */ - if (pcb->refused_data != NULL) { - /* Notify again application with data previously received. */ - err_t err; - LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n")); - TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); - if (err == ERR_OK) { - pcb->refused_data = NULL; - } else if (err == ERR_ABRT) { - /* if err == ERR_ABRT, 'pcb' is already deallocated */ - pcb = NULL; + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); } - } - /* send delayed ACKs */ - if (pcb && (pcb->flags & TF_ACK_DELAY)) { - LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); - tcp_ack_now(pcb); - tcp_output(pcb); - pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; } + } +} - pcb = next; +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; + pcb->refused_data = NULL; + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_FIN) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ + pcb->refused_data = refused_data; } + return ERR_OK; } /** @@ -1101,7 +1244,8 @@ tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) #endif /* LWIP_CALLBACK_API */ /** - * Kills the oldest active connection that has lower priority than prio. + * Kills the oldest active connection that has the same or lower priority than + * 'prio'. * * @param prio minimum priority */ @@ -1218,6 +1362,7 @@ tcp_alloc(u8_t prio) pcb->lastack = iss; pcb->snd_lbb = iss; pcb->tmr = tcp_ticks; + pcb->last_timer = tcp_timer_ctr; pcb->polltmr = 0; @@ -1256,6 +1401,24 @@ tcp_new(void) return tcp_alloc(TCP_PRIO_NORMAL); } +#if LWIP_IPV6 +/** + * Creates a new TCP-over-IPv6 protocol control block but doesn't + * place it on any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new_ip6(void) +{ + struct tcp_pcb * pcb; + pcb = tcp_alloc(TCP_PRIO_NORMAL); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + /** * Used to specify the argument that should be passed callback * functions. @@ -1265,7 +1428,9 @@ tcp_new(void) */ void tcp_arg(struct tcp_pcb *pcb, void *arg) -{ +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ pcb->callback_arg = arg; } #if LWIP_CALLBACK_API @@ -1280,6 +1445,7 @@ tcp_arg(struct tcp_pcb *pcb, void *arg) void tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) { + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); pcb->recv = recv; } @@ -1293,6 +1459,7 @@ tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) void tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) { + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); pcb->sent = sent; } @@ -1307,6 +1474,7 @@ tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) void tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) { + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); pcb->errf = err; } @@ -1321,6 +1489,8 @@ tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) { + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ pcb->accept = accept; } #endif /* LWIP_CALLBACK_API */ @@ -1335,6 +1505,7 @@ tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) void tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) { + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); #if LWIP_CALLBACK_API pcb->poll = poll; #else /* LWIP_CALLBACK_API */ @@ -1366,8 +1537,9 @@ tcp_pcb_purge(struct tcp_pcb *pcb) tcp_listen_pcbs.listen_pcbs != NULL); for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if ((lpcb->local_port == pcb->local_port) && - (ip_addr_isany(&lpcb->local_ip) || - ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) { + IP_PCB_IPVER_EQ(pcb, lpcb) && + (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) { /* port and address of the listen pcb match the timed-out pcb */ LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", lpcb->accepts_pending > 0); @@ -1466,14 +1638,36 @@ tcp_next_iss(void) * calculating the minimum of TCP_MSS and that netif's mtu (if set). */ u16_t -tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr) +tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) { u16_t mss_s; struct netif *outif; + s16_t mtu; + + outif = ipX_route(isipv6, src, dest); +#if LWIP_IPV6 + if (isipv6) { + /* First look in destination cache, to see if there is a Path MTU. */ + mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif); + } else +#endif /* LWIP_IPV6 */ + { + if (outif == NULL) { + return sendmss; + } + mtu = outif->mtu; + } - outif = ip_route(addr); - if ((outif != NULL) && (outif->mtu != 0)) { - mss_s = outif->mtu - IP_HLEN - TCP_HLEN; + if (mtu != 0) { + mss_s = mtu - IP_HLEN - TCP_HLEN; +#if LWIP_IPV6 + /* for IPv6, substract the difference in header size */ + mss_s -= (IP6_HLEN - IP_HLEN); +#endif /* LWIP_IPV6 */ /* RFC 1122, chap 4.2.2.6: * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize * We correct for TCP options in tcp_write(), and don't support IP options. diff --git a/lib/liblwip/core/tcp_in.c b/lib/liblwip/core/tcp_in.c index 1812fe36e..dacf5cf95 100644 --- a/lib/liblwip/core/tcp_in.c +++ b/lib/liblwip/core/tcp_in.c @@ -55,13 +55,18 @@ #include "lwip/stats.h" #include "lwip/snmp.h" #include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_ND6_TCP_REACHABILITY_HINTS +#include "lwip/nd6.h" +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ /* These variables are global to all functions involved in the input processing of TCP segments. They are set by the tcp_input() function. */ static struct tcp_seg inseg; static struct tcp_hdr *tcphdr; -static struct ip_hdr *iphdr; static u32_t seqno, ackno; static u8_t flags; static u16_t tcplen; @@ -85,7 +90,7 @@ static err_t tcp_timewait_input(struct tcp_pcb *pcb); * the TCP finite state machine. This function is called by the IP layer (in * ip_input()). * - * @param p received TCP segment to process (p->payload pointing to the IP header) + * @param p received TCP segment to process (p->payload pointing to the TCP header) * @param inp network interface on which this segment was received */ void @@ -99,57 +104,48 @@ tcp_input(struct pbuf *p, struct netif *inp) #endif /* SO_REUSE */ u8_t hdrlen; err_t err; +#if CHECKSUM_CHECK_TCP + u16_t chksum; +#endif /* CHECKSUM_CHECK_TCP */ PERF_START; TCP_STATS_INC(tcp.recv); snmp_inc_tcpinsegs(); - iphdr = (struct ip_hdr *)p->payload; - tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + tcphdr = (struct tcp_hdr *)p->payload; #if TCP_INPUT_DEBUG tcp_debug_print(tcphdr); #endif - /* remove header from payload */ - if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { + /* Check that TCP header fits in payload */ + if (p->len < sizeof(struct tcp_hdr)) { /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); TCP_STATS_INC(tcp.lenerr); - TCP_STATS_INC(tcp.drop); - snmp_inc_tcpinerrs(); - pbuf_free(p); - return; + goto dropped; } /* Don't even process incoming broadcasts/multicasts. */ - if (ip_addr_isbroadcast(¤t_iphdr_dest, inp) || - ip_addr_ismulticast(¤t_iphdr_dest)) { + if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) || + ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) { TCP_STATS_INC(tcp.proterr); - TCP_STATS_INC(tcp.drop); - snmp_inc_tcpinerrs(); - pbuf_free(p); - return; + goto dropped; } #if CHECKSUM_CHECK_TCP /* Verify TCP checksum. */ - if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), - IP_PROTO_TCP, p->tot_len) != 0) { + chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len, + ipX_current_src_addr(), ipX_current_dest_addr()); + if (chksum != 0) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", - inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), - IP_PROTO_TCP, p->tot_len))); -#if TCP_DEBUG + chksum)); tcp_debug_print(tcphdr); -#endif /* TCP_DEBUG */ TCP_STATS_INC(tcp.chkerr); - TCP_STATS_INC(tcp.drop); - snmp_inc_tcpinerrs(); - pbuf_free(p); - return; + goto dropped; } -#endif +#endif /* CHECKSUM_CHECK_TCP */ /* Move the payload pointer in the pbuf so that it points to the TCP data instead of the TCP header. */ @@ -158,10 +154,7 @@ tcp_input(struct pbuf *p, struct netif *inp) /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); TCP_STATS_INC(tcp.lenerr); - TCP_STATS_INC(tcp.drop); - snmp_inc_tcpinerrs(); - pbuf_free(p); - return; + goto dropped; } /* Convert fields in TCP header to host byte order. */ @@ -184,10 +177,10 @@ tcp_input(struct pbuf *p, struct netif *inp) LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); if (pcb->remote_port == tcphdr->src && - pcb->local_port == tcphdr->dest && - ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) && - ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) { - + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ @@ -209,9 +202,10 @@ tcp_input(struct pbuf *p, struct netif *inp) for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); if (pcb->remote_port == tcphdr->src && - pcb->local_port == tcphdr->dest && - ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) && - ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) { + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { /* We don't really care enough to move this PCB to the front of the list since we are not very likely to receive that many segments for connections in TIME-WAIT. */ @@ -227,22 +221,31 @@ tcp_input(struct pbuf *p, struct netif *inp) prev = NULL; for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if (lpcb->local_port == tcphdr->dest) { -#if SO_REUSE - if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest)) { - /* found an exact match */ - break; - } else if(ip_addr_isany(&(lpcb->local_ip))) { +#if LWIP_IPV6 + if (lpcb->accept_any_ip_version) { /* found an ANY-match */ +#if SO_REUSE lpcb_any = lpcb; lpcb_prev = prev; - } #else /* SO_REUSE */ - if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest) || - ip_addr_isany(&(lpcb->local_ip))) { - /* found a match */ break; - } #endif /* SO_REUSE */ + } else +#endif /* LWIP_IPV6 */ + if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) { + if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) { + /* found an exact match */ + break; + } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; + #endif /* SO_REUSE */ + } + } } prev = (struct tcp_pcb *)lpcb; } @@ -291,28 +294,25 @@ tcp_input(struct pbuf *p, struct netif *inp) /* Set up a tcp_seg structure. */ inseg.next = NULL; inseg.len = p->tot_len; - inseg.dataptr = p->payload; inseg.p = p; inseg.tcphdr = tcphdr; recv_data = NULL; recv_flags = 0; + if (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + /* If there is data which was previously "refused" by upper layer */ if (pcb->refused_data != NULL) { - /* Notify again application with data previously received. */ - LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); - TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); - if (err == ERR_OK) { - pcb->refused_data = NULL; - } else { - /* if err == ERR_ABRT, 'pcb' is already deallocated */ - /* drop incoming packets, because pcb is "full" */ - LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); - pbuf_free(p); - return; + goto aborted; } } tcp_input_pcb = pcb; @@ -331,6 +331,12 @@ tcp_input(struct pbuf *p, struct netif *inp) } else if (recv_flags & TF_CLOSED) { /* The connection has been closed and we will deallocate the PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); + } tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { @@ -346,6 +352,7 @@ tcp_input(struct pbuf *p, struct netif *inp) } if (recv_data != NULL) { + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); if (pcb->flags & TF_RXCLOSED) { /* received data although already closed -> abort (send RST) to notify the remote host that not all data has been processed */ @@ -353,9 +360,6 @@ tcp_input(struct pbuf *p, struct netif *inp) tcp_abort(pcb); goto aborted; } - if (flags & TCP_PSH) { - recv_data->flags |= PBUF_FLAG_PUSH; - } /* Notify application that data has been received. */ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); @@ -373,14 +377,19 @@ tcp_input(struct pbuf *p, struct netif *inp) /* If a FIN segment was received, we call the callback function with a NULL buffer to indicate EOF. */ if (recv_flags & TF_GOT_FIN) { - /* correct rcv_wnd as the application won't call tcp_recved() - for the FIN's seqno */ - if (pcb->rcv_wnd != TCP_WND) { - pcb->rcv_wnd++; - } - TCP_EVENT_CLOSED(pcb, err); - if (err == ERR_ABRT) { - goto aborted; + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } } } @@ -414,15 +423,19 @@ aborted: if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); - tcp_rst(ackno, seqno + tcplen, - ip_current_dest_addr(), ip_current_src_addr(), - tcphdr->dest, tcphdr->src); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); } pbuf_free(p); } LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); } /** @@ -443,15 +456,19 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) struct tcp_pcb *npcb; err_t rc; + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return ERR_OK; + } + /* In the LISTEN state, we check for incoming SYN segments, creates a new PCB, and responds with a SYN|ACK. */ if (flags & TCP_ACK) { /* For incoming segments with the ACK flag set, respond with a RST. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); - tcp_rst(ackno + 1, seqno + tcplen, - ip_current_dest_addr(), ip_current_src_addr(), - tcphdr->dest, tcphdr->src); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); } else if (flags & TCP_SYN) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); #if TCP_LISTEN_BACKLOG @@ -473,14 +490,18 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) pcb->accepts_pending++; #endif /* TCP_LISTEN_BACKLOG */ /* Set up the new PCB. */ - ip_addr_copy(npcb->local_ip, current_iphdr_dest); +#if LWIP_IPV6 + PCB_ISIPV6(npcb) = ip_current_is_v6(); +#endif /* LWIP_IPV6 */ + ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr()); + ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr()); npcb->local_port = pcb->local_port; - ip_addr_copy(npcb->remote_ip, current_iphdr_src); npcb->remote_port = tcphdr->src; npcb->state = SYN_RCVD; npcb->rcv_nxt = seqno + 1; npcb->rcv_ann_right_edge = npcb->rcv_nxt; npcb->snd_wnd = tcphdr->wnd; + npcb->snd_wnd_max = tcphdr->wnd; npcb->ssthresh = npcb->snd_wnd; npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ npcb->callback_arg = pcb->callback_arg; @@ -491,12 +512,13 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) npcb->so_options = pcb->so_options & SOF_INHERITED; /* Register the new PCB so that we can begin receiving segments for it. */ - TCP_REG(&tcp_active_pcbs, npcb); + TCP_REG_ACTIVE(npcb); /* Parse any options in the SYN. */ tcp_parseopt(npcb); #if TCP_CALCULATE_EFF_SEND_MSS - npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); + npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, + &npcb->remote_ip, PCB_ISIPV6(npcb)); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ snmp_inc_tcppassiveopens(); @@ -538,8 +560,8 @@ tcp_timewait_input(struct tcp_pcb *pcb) should be sent in reply */ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { /* If the SYN is in the window it is an error, send a reset */ - tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), - tcphdr->dest, tcphdr->src); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); return ERR_OK; } } else if (flags & TCP_FIN) { @@ -632,11 +654,13 @@ tcp_process(struct tcp_pcb *pcb) pcb->rcv_ann_right_edge = pcb->rcv_nxt; pcb->lastack = ackno; pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wnd_max = tcphdr->wnd; pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ pcb->state = ESTABLISHED; #if TCP_CALCULATE_EFF_SEND_MSS - pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, + PCB_ISIPV6(pcb)); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect @@ -649,6 +673,7 @@ tcp_process(struct tcp_pcb *pcb) LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); rseg = pcb->unacked; pcb->unacked = rseg->next; + tcp_seg_free(rseg); /* If there's nothing left to acknowledge, stop the retransmit timer, otherwise reset it to start again */ @@ -659,8 +684,6 @@ tcp_process(struct tcp_pcb *pcb) pcb->nrtx = 0; } - tcp_seg_free(rseg); - /* Call the user specified function to call when sucessfully * connected. */ TCP_EVENT_CONNECTED(pcb, ERR_OK, err); @@ -672,8 +695,8 @@ tcp_process(struct tcp_pcb *pcb) /* received ACK? possibly a half-open connection */ else if (flags & TCP_ACK) { /* send a RST to bring the other side in a non-synchronized state. */ - tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), - tcphdr->dest, tcphdr->src); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); } break; case SYN_RCVD: @@ -715,8 +738,8 @@ tcp_process(struct tcp_pcb *pcb) } } else { /* incorrect ACK number, send RST */ - tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), - tcphdr->dest, tcphdr->src); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); } } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { /* Looks like another copy of the SYN - retransmit our SYN-ACK */ @@ -740,7 +763,7 @@ tcp_process(struct tcp_pcb *pcb) ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); - TCP_RMV(&tcp_active_pcbs, pcb); + TCP_RMV_ACTIVE(pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); } else { @@ -757,7 +780,7 @@ tcp_process(struct tcp_pcb *pcb) LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); - TCP_RMV(&tcp_active_pcbs, pcb); + TCP_RMV_ACTIVE(pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); } @@ -767,7 +790,7 @@ tcp_process(struct tcp_pcb *pcb) if (flags & TCP_ACK && ackno == pcb->snd_nxt) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_pcb_purge(pcb); - TCP_RMV(&tcp_active_pcbs, pcb); + TCP_RMV_ACTIVE(pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); } @@ -832,7 +855,7 @@ tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) * data, and if so frees the memory of the buffered data. Next, is places the * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until - * i it has been removed from the buffer. + * it has been removed from the buffer. * * If the incoming segment constitutes an ACK for a segment that was used for RTT * estimation, the RTT is estimated here as well. @@ -852,6 +875,12 @@ tcp_receive(struct tcp_pcb *pcb) u32_t right_wnd_edge; u16_t new_tot_len; int found_dupack = 0; +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); if (flags & TCP_ACK) { right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; @@ -861,9 +890,21 @@ tcp_receive(struct tcp_pcb *pcb) (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { pcb->snd_wnd = tcphdr->wnd; + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < tcphdr->wnd) { + pcb->snd_wnd_max = tcphdr->wnd; + } pcb->snd_wl1 = seqno; pcb->snd_wl2 = ackno; - if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) { + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ pcb->persist_backoff = 0; } LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); @@ -910,8 +951,9 @@ tcp_receive(struct tcp_pcb *pcb) /* Clause 5 */ if (pcb->lastack == ackno) { found_dupack = 1; - if (pcb->dupacks + 1 > pcb->dupacks) + if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) { ++pcb->dupacks; + } if (pcb->dupacks > 3) { /* Inflate the congestion window, but not if it means that the value overflows. */ @@ -1018,6 +1060,13 @@ tcp_receive(struct tcp_pcb *pcb) pcb->rtime = 0; pcb->polltmr = 0; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ } else { /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ pcb->acked = 0; @@ -1038,6 +1087,11 @@ tcp_receive(struct tcp_pcb *pcb) next = pcb->unsent; pcb->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); /* Prevent ACK for FIN to generate a sent event */ @@ -1086,8 +1140,10 @@ tcp_receive(struct tcp_pcb *pcb) } /* If the incoming segment contains data, we must process it - further. */ - if (tcplen > 0) { + further unless the pcb already received a FIN. + (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { /* This code basically does three things: +) If the incoming segment contains data that is the next @@ -1165,9 +1221,6 @@ tcp_receive(struct tcp_pcb *pcb) LWIP_ASSERT("pbuf_header failed", 0); } } - /* KJM following line changed to use p->payload rather than inseg->p->payload - to fix bug #9076 */ - inseg.dataptr = p->payload; inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; } @@ -1228,8 +1281,7 @@ tcp_receive(struct tcp_pcb *pcb) pcb->ooseq = pcb->ooseq->next; tcp_seg_free(old_ooseq); } - } - else { + } else { next = pcb->ooseq; /* Remove all segments on ooseq that are covered by inseg already. * FIN is copied from ooseq to inseg if present. */ @@ -1338,6 +1390,13 @@ tcp_receive(struct tcp_pcb *pcb) /* Acknowledge the segment(s). */ tcp_ack(pcb); +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + } else { /* We get here if the incoming segment is out-of-sequence. */ tcp_send_empty_ack(pcb); @@ -1462,8 +1521,32 @@ tcp_receive(struct tcp_pcb *pcb) prev = next; } } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ #endif /* TCP_QUEUE_OOSEQ */ - } } else { /* The incoming segment is not withing the window. */ diff --git a/lib/liblwip/core/tcp_out.c b/lib/liblwip/core/tcp_out.c index bbe1d3c9a..8cfb3df9c 100644 --- a/lib/liblwip/core/tcp_out.c +++ b/lib/liblwip/core/tcp_out.c @@ -46,12 +46,17 @@ #include "lwip/def.h" #include "lwip/mem.h" #include "lwip/memp.h" -#include "lwip/sys.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" #include "lwip/inet_chksum.h" #include "lwip/stats.h" #include "lwip/snmp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif #include @@ -131,6 +136,7 @@ tcp_send_fin(struct tcp_pcb *pcb) if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + pcb->flags |= TF_FIN; return ERR_OK; } } @@ -166,7 +172,6 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, seg->flags = optflags; seg->next = NULL; seg->p = p; - seg->dataptr = p->payload; seg->len = p->tot_len - optlen; #if TCP_OVERSIZE_DBGCHECK seg->oversize_left = 0; @@ -227,7 +232,7 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, LWIP_UNUSED_ARG(apiflags); LWIP_UNUSED_ARG(first_seg); /* always create MSS-sized pbufs */ - alloc = TCP_MSS; + alloc = max_length; #else /* LWIP_NETIF_TX_SINGLE_PBUF */ if (length < max_length) { /* Should we allocate an oversized pbuf, or just the minimum @@ -367,6 +372,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) u16_t concat_chksummed = 0; #endif /* TCP_CHECKSUM_ON_COPY */ err_t err; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); #if LWIP_NETIF_TX_SINGLE_PBUF /* Always copy to try to create single pbufs for TX */ @@ -425,7 +432,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) /* Usable space at the end of the last unsent segment */ unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); - space = pcb->mss - (last_unsent->len + unsent_optlen); + space = mss_local - (last_unsent->len + unsent_optlen); /* * Phase 1: Copy data directly into an oversized pbuf. @@ -476,7 +483,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) goto memerr; } #if TCP_OVERSIZE_DBGCHECK - last_unsent->oversize_left = oversize; + last_unsent->oversize_left += oversize; #endif /* TCP_OVERSIZE_DBGCHECK */ TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); #if TCP_CHECKSUM_ON_COPY @@ -518,7 +525,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) while (pos < len) { struct pbuf *p; u16_t left = len - pos; - u16_t max_len = pcb->mss - optlen; + u16_t max_len = mss_local - optlen; u16_t seglen = left > max_len ? max_len : left; #if TCP_CHECKSUM_ON_COPY u16_t chksum = 0; @@ -528,7 +535,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) if (apiflags & TCP_WRITE_FLAG_COPY) { /* If copy is set, memory should be allocated and data copied * into pbuf */ - if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); goto memerr; } @@ -590,10 +597,6 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) seg->chksum_swapped = chksum_swapped; seg->flags |= TF_SEG_DATA_CHECKSUMMED; #endif /* TCP_CHECKSUM_ON_COPY */ - /* Fix dataptr for the nocopy case */ - if ((apiflags & TCP_WRITE_FLAG_COPY) == 0) { - seg->dataptr = (u8_t*)arg + pos; - } /* first segment of to-be-queued data? */ if (queue == NULL) { @@ -635,6 +638,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) } last_unsent->len += oversize_used; #if TCP_OVERSIZE_DBGCHECK + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); last_unsent->oversize_left -= oversize_used; #endif /* TCP_OVERSIZE_DBGCHECK */ } @@ -773,6 +778,7 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) TCP_STATS_INC(tcp.memerr); return ERR_MEM; } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, @@ -814,7 +820,6 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) return ERR_OK; } - #if LWIP_TCP_TIMESTAMPS /* Build a timestamp option (12 bytes long) at the specified options pointer) @@ -870,14 +875,14 @@ tcp_send_empty_ack(struct tcp_pcb *pcb) #endif #if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), - IP_PROTO_TCP, p->tot_len); + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); #endif #if LWIP_NETIF_HWADDRHINT - ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, - IP_PROTO_TCP, &(pcb->addr_hint)); + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP, &pcb->addr_hint); #else /* LWIP_NETIF_HWADDRHINT*/ - ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ pbuf_free(p); @@ -901,6 +906,10 @@ tcp_output(struct tcp_pcb *pcb) s16_t i = 0; #endif /* TCP_CWND_DEBUG */ + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + /* First, check if we are invoked by the TCP input processing code. If so, we do not output anything. Instead, we rely on the input processing code to call us when input processing is done @@ -984,6 +993,9 @@ tcp_output(struct tcp_pcb *pcb) pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ tcp_output_segment(seg, pcb); snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { @@ -1029,13 +1041,6 @@ tcp_output(struct tcp_pcb *pcb) } #endif /* TCP_OVERSIZE */ - if (seg != NULL && pcb->persist_backoff == 0 && - ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { - /* prepare for persist timer */ - pcb->persist_cnt = 0; - pcb->persist_backoff = 1; - } - pcb->flags &= ~TF_NAGLEMEMERR; return ERR_OK; } @@ -1050,7 +1055,6 @@ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) { u16_t len; - struct netif *netif; u32_t *opts; /** @bug Exclude retransmitted segments from this count. */ @@ -1067,10 +1071,15 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) /* Add any requested options. NB MSS option is only set on SYN packets, so ignore it here */ - LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)(seg->tcphdr + 1) % 4) == 0); opts = (u32_t *)(void *)(seg->tcphdr + 1); if (seg->flags & TF_SEG_OPTS_MSS) { - TCP_BUILD_MSS_OPTION(*opts); + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); opts += 1; } #if LWIP_TCP_TIMESTAMPS @@ -1082,19 +1091,22 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) } #endif + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. */ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + /* If we don't have a local IP address, we get one by calling ip_route(). */ - if (ip_addr_isany(&(pcb->local_ip))) { - netif = ip_route(&(pcb->remote_ip)); - if (netif == NULL) { + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { return; } - ip_addr_copy(pcb->local_ip, netif->ip_addr); - } - - /* Set retransmission timer running if it is not currently enabled */ - if(pcb->rtime == -1) { - pcb->rtime = 0; + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); } if (pcb->rttest == 0) { @@ -1115,14 +1127,12 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) seg->p->payload = seg->tcphdr; seg->tcphdr->chksum = 0; -#if CHECKSUM_GEN_TCP #if TCP_CHECKSUM_ON_COPY { u32_t acc; #if TCP_CHECKSUM_ON_COPY_SANITY_CHECK - u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip), - &(pcb->remote_ip), - IP_PROTO_TCP, seg->p->tot_len); + u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); #endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { LWIP_ASSERT("data included but not checksummed", @@ -1130,9 +1140,8 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) } /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ - acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip), - &(pcb->remote_ip), - IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4); + acc = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); /* add payload checksum */ if (seg->chksum_swapped) { seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); @@ -1150,19 +1159,19 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) #endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ } #else /* TCP_CHECKSUM_ON_COPY */ - seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip), - &(pcb->remote_ip), - IP_PROTO_TCP, seg->p->tot_len); -#endif /* TCP_CHECKSUM_ON_COPY */ +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); #endif /* CHECKSUM_GEN_TCP */ +#endif /* TCP_CHECKSUM_ON_COPY */ TCP_STATS_INC(tcp.xmit); #if LWIP_NETIF_HWADDRHINT - ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, - IP_PROTO_TCP, &(pcb->addr_hint)); + ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); #else /* LWIP_NETIF_HWADDRHINT*/ - ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, - IP_PROTO_TCP); + ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + pcb->tos, IP_PROTO_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ } @@ -1187,9 +1196,13 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) * @param remote_port the remote TCP port to send the segment to */ void -tcp_rst(u32_t seqno, u32_t ackno, - ip_addr_t *local_ip, ip_addr_t *remote_ip, - u16_t local_port, u16_t remote_port) +tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) { struct pbuf *p; struct tcp_hdr *tcphdr; @@ -1211,14 +1224,15 @@ tcp_rst(u32_t seqno, u32_t ackno, tcphdr->chksum = 0; tcphdr->urgp = 0; -#if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, - IP_PROTO_TCP, p->tot_len); -#endif TCP_STATS_INC(tcp.xmit); snmp_inc_tcpoutrsts(); - /* Send output with hardcoded TTL since we have no access to the pcb */ - ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len, + local_ip, remote_ip); +#endif + /* Send output with hardcoded TTL/HL since we have no access to the pcb */ + ipX_output(isipv6, p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); pbuf_free(p); LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); } @@ -1247,6 +1261,7 @@ tcp_rexmit_rto(struct tcp_pcb *pcb) pcb->unsent = pcb->unacked; /* unacked queue is now empty */ pcb->unacked = NULL; + /* last unsent hasn't changed, no need to reset unsent_oversize */ /* increment number of retransmissions */ ++pcb->nrtx; @@ -1287,6 +1302,12 @@ tcp_rexmit(struct tcp_pcb *pcb) } seg->next = *cur_seg; *cur_seg = seg; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ ++pcb->nrtx; @@ -1354,9 +1375,9 @@ tcp_keepalive(struct tcp_pcb *pcb) struct pbuf *p; struct tcp_hdr *tcphdr; - LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), - ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); @@ -1369,18 +1390,17 @@ tcp_keepalive(struct tcp_pcb *pcb) } tcphdr = (struct tcp_hdr *)p->payload; -#if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, - IP_PROTO_TCP, p->tot_len); -#endif + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); TCP_STATS_INC(tcp.xmit); /* Send output to IP */ #if LWIP_NETIF_HWADDRHINT - ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, - &(pcb->addr_hint)); + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); #else /* LWIP_NETIF_HWADDRHINT*/ - ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ pbuf_free(p); @@ -1407,11 +1427,9 @@ tcp_zero_window_probe(struct tcp_pcb *pcb) u16_t len; u8_t is_fin; - LWIP_DEBUGF(TCP_DEBUG, - ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" - U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), - ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: tcp_ticks %"U32_F @@ -1443,21 +1461,25 @@ tcp_zero_window_probe(struct tcp_pcb *pcb) TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); } else { /* Data segment, copy in one byte from the head of the unacked queue */ - *((char *)p->payload + TCP_HLEN) = *(char *)seg->dataptr; + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); } #if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, - IP_PROTO_TCP, p->tot_len); + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); #endif TCP_STATS_INC(tcp.xmit); /* Send output to IP */ #if LWIP_NETIF_HWADDRHINT - ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, - &(pcb->addr_hint)); + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP, &pcb->addr_hint); #else /* LWIP_NETIF_HWADDRHINT*/ - ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ pbuf_free(p); diff --git a/lib/liblwip/core/timers.c b/lib/liblwip/core/timers.c index d28cb0843..c8ead4e7e 100644 --- a/lib/liblwip/core/timers.c +++ b/lib/liblwip/core/timers.c @@ -42,6 +42,7 @@ #include "lwip/opt.h" #include "lwip/timers.h" +#include "lwip/tcp_impl.h" #if LWIP_TIMERS @@ -49,14 +50,17 @@ #include "lwip/memp.h" #include "lwip/tcpip.h" -#include "lwip/tcp_impl.h" #include "lwip/ip_frag.h" #include "netif/etharp.h" #include "lwip/dhcp.h" #include "lwip/autoip.h" #include "lwip/igmp.h" #include "lwip/dns.h" - +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" /** The one and only timeout list */ static struct sys_timeo *next_timeout; @@ -217,6 +221,54 @@ dns_timer(void *arg) } #endif /* LWIP_DNS */ +#if LWIP_IPV6 +/** + * Timer callback function that calls nd6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nd6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); + nd6_tmr(); + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +} + +#if LWIP_IPV6_REASS +/** + * Timer callback function that calls ip6_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip6_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); + ip6_reass_tmr(); + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +} +#endif /* LWIP_IPV6_REASS */ + +#if LWIP_IPV6_MLD +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +mld6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); + mld6_tmr(); + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +} +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + /** Initialize this module */ void sys_timeouts_init(void) { @@ -239,6 +291,15 @@ void sys_timeouts_init(void) #if LWIP_DNS sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); #endif /* LWIP_DNS */ +#if LWIP_IPV6 + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +#if LWIP_IPV6_REASS + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ #if NO_SYS /* Initialise timestamp for sys_check_timeouts */ @@ -355,22 +416,25 @@ sys_untimeout(sys_timeout_handler handler, void *arg) void sys_check_timeouts(void) { - struct sys_timeo *tmptimeout; - u32_t diff; - sys_timeout_handler handler; - void *arg; - int had_one; - u32_t now; - - now = sys_now(); if (next_timeout) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); /* this cares for wraparounds */ - diff = LWIP_U32_DIFF(now, timeouts_last_time); + diff = now - timeouts_last_time; do { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ had_one = 0; tmptimeout = next_timeout; - if (tmptimeout->time <= diff) { + if (tmptimeout && (tmptimeout->time <= diff)) { /* timeout has expired */ had_one = 1; timeouts_last_time = now; diff --git a/lib/liblwip/core/udp.c b/lib/liblwip/core/udp.c index 5d5b4e296..bba4a1ce2 100644 --- a/lib/liblwip/core/udp.c +++ b/lib/liblwip/core/udp.c @@ -55,8 +55,12 @@ #include "lwip/memp.h" #include "lwip/inet_chksum.h" #include "lwip/ip_addr.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" #include "lwip/netif.h" #include "lwip/icmp.h" +#include "lwip/icmp6.h" #include "lwip/stats.h" #include "lwip/snmp.h" #include "arch/perf.h" @@ -64,10 +68,77 @@ #include +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + /* The list of UDP PCBs */ /* exported in udp.h (was static) */ struct udp_pcb *udp_pcbs; +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto 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 +} + /** * Process an incoming UDP datagram. * @@ -76,7 +147,7 @@ struct udp_pcb *udp_pcbs; * recv function. If no pcb is found or the datagram is incorrect, the * pbuf is freed. * - * @param p pbuf to be demultiplexed to a UDP PCB. + * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) * @param inp network interface on which the datagram was received. * */ @@ -86,20 +157,17 @@ udp_input(struct pbuf *p, struct netif *inp) struct udp_hdr *udphdr; struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; - struct ip_hdr *iphdr; u16_t src, dest; u8_t local_match; u8_t broadcast; + u8_t for_us; PERF_START; UDP_STATS_INC(udp.recv); - iphdr = (struct ip_hdr *)p->payload; - - /* Check minimum length (IP header + UDP header) - * and move payload pointer to UDP header */ - if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { + /* Check minimum length (UDP header) */ + if (p->len < UDP_HLEN) { /* drop short packets */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); @@ -113,7 +181,11 @@ udp_input(struct pbuf *p, struct netif *inp) udphdr = (struct udp_hdr *)p->payload; /* is broadcast packet ? */ - broadcast = ip_addr_isbroadcast(¤t_iphdr_dest, inp); +#if LWIP_IPV6 + broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#else /* LWIP_IPV6 */ + broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#endif /* LWIP_IPV6 */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); @@ -124,13 +196,11 @@ udp_input(struct pbuf *p, struct netif *inp) udp_debug_print(udphdr); /* print the UDP source and destination */ - LWIP_DEBUGF(UDP_DEBUG, - ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " - "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", - ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest), - ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest), - ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src), - ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src))); + LWIP_DEBUGF(UDP_DEBUG, ("udp (")); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src))); #if LWIP_DHCP pcb = NULL; @@ -142,9 +212,10 @@ udp_input(struct pbuf *p, struct netif *inp) if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { /* accept the packe if (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! - - inp->dhcp->pcb->remote == ANY or iphdr->src */ - if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || - ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), ¤t_iphdr_src))) { + - inp->dhcp->pcb->remote == ANY or iphdr->src + (no need to check for IPv6 since the dhcp struct always uses IPv4) */ + if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) { pcb = inp->dhcp->pcb; } } @@ -162,38 +233,54 @@ udp_input(struct pbuf *p, struct netif *inp) for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { local_match = 0; /* print the PCB local and remote address */ - LWIP_DEBUGF(UDP_DEBUG, - ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " - "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", - ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), - ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port, - ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), - ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port)); + LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); /* compare PCB local addr+port to UDP destination addr+port */ - if ((pcb->local_port == dest) && - ((!broadcast && ip_addr_isany(&pcb->local_ip)) || - ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest) || + if (pcb->local_port == dest) { + if ( +#if LWIP_IPV6 + ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) && + (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) || +#if LWIP_IPV6_MLD + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6_MLD */ + ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(pcb) && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) || #if LWIP_IGMP - ip_addr_ismulticast(¤t_iphdr_dest) || + ip_addr_ismulticast(ip_current_dest_addr()) || #endif /* LWIP_IGMP */ #if IP_SOF_BROADCAST_RECV - (broadcast && (pcb->so_options & SOF_BROADCAST)))) { -#else /* IP_SOF_BROADCAST_RECV */ - (broadcast))) { -#endif /* IP_SOF_BROADCAST_RECV */ - local_match = 1; - if ((uncon_pcb == NULL) && - ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { - /* the first unconnected matching PCB */ - uncon_pcb = pcb; + (broadcast && ip_get_option(pcb, SOF_BROADCAST) && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } } } /* compare PCB remote addr+port to UDP source addr+port */ if ((local_match != 0) && - (pcb->remote_port == src) && - (ip_addr_isany(&pcb->remote_ip) || - ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src))) { + (pcb->remote_port == src) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is @@ -215,12 +302,24 @@ udp_input(struct pbuf *p, struct netif *inp) } /* Check checksum if this is a match or if it was directed at us. */ - if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, ¤t_iphdr_dest)) { + if (pcb != NULL) { + for_us = 1; + } else { +#if LWIP_IPV6 + if (ip_current_is_v6()) { + for_us = netif_matches_ip6_addr(inp, ip6_current_dest_addr()); + } else +#endif /* LWIP_IPV6 */ + { + for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()); + } + } + if (for_us) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if CHECKSUM_CHECK_UDP #if LWIP_UDPLITE - if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { + if (ip_current_header_proto() == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ -#if CHECKSUM_CHECK_UDP u16_t chklen = ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { @@ -230,42 +329,26 @@ udp_input(struct pbuf *p, struct netif *inp) } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ - UDP_STATS_INC(udp.chkerr); - UDP_STATS_INC(udp.drop); - snmp_inc_udpinerrors(); - pbuf_free(p); - goto end; + goto chkerr; } } - if (inet_chksum_pseudo_partial(p, ¤t_iphdr_src, ¤t_iphdr_dest, - IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); - UDP_STATS_INC(udp.chkerr); - UDP_STATS_INC(udp.drop); - snmp_inc_udpinerrors(); - pbuf_free(p); - goto end; + if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE, + p->tot_len, chklen, + ipX_current_src_addr(), ipX_current_dest_addr()) != 0) { + goto chkerr; } -#endif /* CHECKSUM_CHECK_UDP */ } else #endif /* LWIP_UDPLITE */ { -#if CHECKSUM_CHECK_UDP if (udphdr->chksum != 0) { - if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), - IP_PROTO_UDP, p->tot_len) != 0) { - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("udp_input: UDP datagram discarded due to failing checksum\n")); - UDP_STATS_INC(udp.chkerr); - UDP_STATS_INC(udp.drop); - snmp_inc_udpinerrors(); - pbuf_free(p); - goto end; + if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len, + ipX_current_src_addr(), + ipX_current_dest_addr()) != 0) { + goto chkerr; } } -#endif /* CHECKSUM_CHECK_UDP */ } +#endif /* CHECKSUM_CHECK_UDP */ if(pbuf_header(p, -UDP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed\n", 0); @@ -277,32 +360,45 @@ udp_input(struct pbuf *p, struct netif *inp) if (pcb != NULL) { snmp_inc_udpindatagrams(); #if SO_REUSE && SO_REUSE_RXTOALL - if ((broadcast || ip_addr_ismulticast(¤t_iphdr_dest)) && - ((pcb->so_options & SOF_REUSEADDR) != 0)) { + if ((broadcast || +#if LWIP_IPV6 + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(ip_current_dest_addr())) && + ip_get_option(pcb, SOF_REUSEADDR)) { /* pass broadcast- or multicast packets to all multicast pcbs if SOF_REUSEADDR is set on the first match */ struct udp_pcb *mpcb; u8_t p_header_changed = 0; + s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { if (mpcb != pcb) { /* compare PCB local addr+port to UDP destination addr+port */ if ((mpcb->local_port == dest) && - ((!broadcast && ip_addr_isany(&mpcb->local_ip)) || - ip_addr_cmp(&(mpcb->local_ip), ¤t_iphdr_dest) || +#if LWIP_IPV6 + ((PCB_ISIPV6(mpcb) && + (ip6_addr_ismulticast(ip6_current_dest_addr()) || + ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(mpcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) || #if LWIP_IGMP - ip_addr_ismulticast(¤t_iphdr_dest) || + ip_addr_ismulticast(ip_current_dest_addr()) || #endif /* LWIP_IGMP */ #if IP_SOF_BROADCAST_RECV - (broadcast && (mpcb->so_options & SOF_BROADCAST)))) { + (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))))) { #else /* IP_SOF_BROADCAST_RECV */ - (broadcast))) { + (broadcast))))) { #endif /* IP_SOF_BROADCAST_RECV */ /* pass a copy of the packet to all local matches */ - if (mpcb->recv != NULL) { + if (mpcb->recv.ip4 != NULL) { struct pbuf *q; /* for that, move payload to IP header again */ if (p_header_changed == 0) { - pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + pbuf_header(p, hdrs_len); p_header_changed = 1; } q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); @@ -310,8 +406,16 @@ udp_input(struct pbuf *p, struct netif *inp) err_t err = pbuf_copy(q, p); if (err == ERR_OK) { /* move payload to UDP data */ - pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); - mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + pbuf_header(q, -hdrs_len); +#if LWIP_IPV6 + if (PCB_ISIPV6(mpcb)) { + mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } } } } @@ -320,14 +424,22 @@ udp_input(struct pbuf *p, struct netif *inp) } if (p_header_changed) { /* and move payload to UDP data again */ - pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + pbuf_header(p, -hdrs_len); } } #endif /* SO_REUSE && SO_REUSE_RXTOALL */ /* callback */ - if (pcb->recv != NULL) { + if (pcb->recv.ip4 != NULL) { /* now the recv function is responsible for freeing p */ - pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); @@ -336,17 +448,19 @@ udp_input(struct pbuf *p, struct netif *inp) } else { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); -#if LWIP_ICMP +#if LWIP_ICMP || LWIP_ICMP6 /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if (!broadcast && - !ip_addr_ismulticast(¤t_iphdr_dest)) { +#if LWIP_IPV6 + !ip6_addr_ismulticast(ip6_current_dest_addr()) && +#endif /* LWIP_IPV6 */ + !ip_addr_ismulticast(ip_current_dest_addr())) { /* move payload pointer back to ip header */ - pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); - LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); - icmp_dest_unreach(p, ICMP_DUR_PORT); + pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); + icmp_port_unreach(ip_current_is_v6(), p); } -#endif /* LWIP_ICMP */ +#endif /* LWIP_ICMP || LWIP_ICMP6 */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); snmp_inc_udpnoports(); @@ -357,6 +471,17 @@ udp_input(struct pbuf *p, struct netif *inp) } end: PERF_STOP("udp_input"); + return; +#if CHECKSUM_CHECK_UDP +chkerr: + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + PERF_STOP("udp_input"); +#endif /* CHECKSUM_CHECK_UDP */ } /** @@ -381,7 +506,7 @@ err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) { /* send to the packet using remote ip and port stored in the pcb */ - return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); + return udp_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port); } #if LWIP_CHECKSUM_ON_COPY @@ -392,7 +517,7 @@ udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, u8_t have_chksum, u16_t chksum) { /* send to the packet using remote ip and port stored in the pcb */ - return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port, + return udp_sendto_chksum(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port, have_chksum, chksum); } #endif /* LWIP_CHECKSUM_ON_COPY */ @@ -429,20 +554,34 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, { #endif /* LWIP_CHECKSUM_ON_COPY */ struct netif *netif; + ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip); LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); - /* find the outgoing network interface for this packet */ +#if LWIP_IPV6 || LWIP_IGMP + if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) { + /* For multicast, find a netif based on source address. */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + dst_ip_route = &pcb->local_ip; + } else +#endif /* LWIP_IPV6 */ + { #if LWIP_IGMP - netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); -#else - netif = ip_route(dst_ip); + dst_ip_route = ip_2_ipX(&pcb->multicast_ip); #endif /* LWIP_IGMP */ + } + } +#endif /* LWIP_IPV6 || LWIP_IGMP */ + + /* find the outgoing network interface for this packet */ + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route); /* no outgoing network interface could be found? */ if (netif == NULL) { - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip))); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip)); + LWIP_DEBUGF(UDP_DEBUG, ("\n")); UDP_STATS_INC(udp.rterr); return ERR_RTE; } @@ -491,10 +630,15 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, ip_addr_t *src_ip; err_t err; struct pbuf *q; /* q will be sent down the stack */ + u8_t ip_proto; #if IP_SOF_BROADCAST /* broadcast filter? */ - if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { + if (!ip_get_option(pcb, SOF_BROADCAST) && +#if LWIP_IPV6 + !PCB_ISIPV6(pcb) && +#endif /* LWIP_IPV6 */ + ip_addr_isbroadcast(dst_ip, netif) ) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); return ERR_VAL; @@ -504,7 +648,7 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, /* if the PCB is not yet bound to a port, bind it here */ if (pcb->local_port == 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); - err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); if (err != ERR_OK) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); return err; @@ -520,8 +664,10 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); return ERR_MEM; } - /* chain header q in front of given pbuf p */ - pbuf_chain(q, p); + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } /* first pbuf q points to header pbuf */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); @@ -542,20 +688,60 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, /* Multicast Loop? */ #if LWIP_IGMP - if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) { + if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && +#if LWIP_IPV6 + ( +#if LWIP_IPV6_MLD + (PCB_ISIPV6(pcb) && + ip6_addr_ismulticast(ip_2_ip6(dst_ip))) || +#endif /* LWIP_IPV6_MLD */ + (!PCB_ISIPV6(pcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(dst_ip)))) { q->flags |= PBUF_FLAG_MCASTLOOP; } #endif /* LWIP_IGMP */ /* PCB local address is IP_ANY_ADDR? */ - if (ip_addr_isany(&pcb->local_ip)) { +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) { + src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip))); + if (src_ip == NULL) { + /* No suitable source address was found. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + } else { + /* use UDP PCB local IPv6 address as source address, if still valid. */ + if (netif_matches_ip6_addr(netif, ipX_2_ip6(&pcb->local_ip)) < 0) { + /* Address isn't valid anymore. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + src_ip = ipX_2_ip(&pcb->local_ip); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip))) { /* use outgoing network interface IP address as source address */ src_ip = &(netif->ip_addr); } else { /* check if UDP PCB local IP address is correct * this could be an old address if netif->ip_addr has changed */ - if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + if (!ip_addr_cmp(ipX_2_ip(&(pcb->local_ip)), &(netif->ip_addr))) { /* local_ip doesn't match, drop the packet */ if (q != p) { /* free the header pbuf */ @@ -566,7 +752,7 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, return ERR_VAL; } /* use UDP PCB local IP address as source address */ - src_ip = &(pcb->local_ip); + src_ip = ipX_2_ip(&(pcb->local_ip)); } LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); @@ -594,33 +780,28 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, udphdr->len = htons(chklen_hdr); /* calculate checksum */ #if CHECKSUM_GEN_UDP - udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, - IP_PROTO_UDPLITE, q->tot_len, -#if !LWIP_CHECKSUM_ON_COPY - chklen); -#else /* !LWIP_CHECKSUM_ON_COPY */ - (have_chksum ? UDP_HLEN : chklen)); +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + chklen = UDP_HLEN; + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE, + q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); +#if LWIP_CHECKSUM_ON_COPY if (have_chksum) { u32_t acc; acc = udphdr->chksum + (u16_t)~(chksum); udphdr->chksum = FOLD_U32T(acc); } -#endif /* !LWIP_CHECKSUM_ON_COPY */ +#endif /* LWIP_CHECKSUM_ON_COPY */ /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udphdr->chksum == 0x0000) { udphdr->chksum = 0xffff; } #endif /* CHECKSUM_GEN_UDP */ - /* output to IP */ - LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = &(pcb->addr_hint); -#endif /* LWIP_NETIF_HWADDRHINT*/ - err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = NULL; -#endif /* LWIP_NETIF_HWADDRHINT*/ + + ip_proto = IP_PROTO_UDPLITE; } else #endif /* LWIP_UDPLITE */ { /* UDP */ @@ -628,19 +809,21 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, udphdr->len = htons(q->tot_len); /* calculate checksum */ #if CHECKSUM_GEN_UDP - if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + /* Checksum is mandatory over IPv6. */ + if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { u16_t udpchksum; #if LWIP_CHECKSUM_ON_COPY if (have_chksum) { u32_t acc; - udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP, - q->tot_len, UDP_HLEN); + udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, + q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); acc = udpchksum + (u16_t)~(chksum); udpchksum = FOLD_U32T(acc); } else #endif /* LWIP_CHECKSUM_ON_COPY */ { - udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); + udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len, + ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); } /* chksum zero must become 0xffff, as zero means 'no checksum' */ @@ -650,17 +833,16 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, udphdr->chksum = udpchksum; } #endif /* CHECKSUM_GEN_UDP */ - LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); - LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); - /* output to IP */ -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = &(pcb->addr_hint); -#endif /* LWIP_NETIF_HWADDRHINT*/ - err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); -#if LWIP_NETIF_HWADDRHINT - netif->addr_hint = NULL; -#endif /* LWIP_NETIF_HWADDRHINT*/ + ip_proto = IP_PROTO_UDP; } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); + /* output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + /* TODO: must this be increased even if error occured? */ snmp_inc_udpoutdatagrams(); @@ -702,7 +884,7 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) u8_t rebind; LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); - ip_addr_debug_print(UDP_DEBUG, ipaddr); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(ipaddr)); LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); rebind = 0; @@ -720,17 +902,17 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) PCB is alread bound to, unless *all* PCBs with that port have tha REUSEADDR flag set. */ #if SO_REUSE - else if (((pcb->so_options & SOF_REUSEADDR) == 0) && - ((ipcb->so_options & SOF_REUSEADDR) == 0)) { + else if (!ip_get_option(pcb, SOF_REUSEADDR) && + !ip_get_option(ipcb, SOF_REUSEADDR)) { #else /* SO_REUSE */ /* port matches that of PCB in list and REUSEADDR not set -> reject */ else { #endif /* SO_REUSE */ - if ((ipcb->local_port == port) && + if ((ipcb->local_port == port) && IP_PCB_IPVER_EQ(pcb, ipcb) && /* IP address matches, or one is IP_ADDR_ANY? */ - (ip_addr_isany(&(ipcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { + (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) || + ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(ipaddr)))) { /* other PCB already binds to this local IP and port */ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); @@ -739,28 +921,12 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) } } - ip_addr_set(&pcb->local_ip, ipaddr); + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); /* no port specified? */ if (port == 0) { -#ifndef UDP_LOCAL_PORT_RANGE_START -#define UDP_LOCAL_PORT_RANGE_START 4096 -#define UDP_LOCAL_PORT_RANGE_END 0x7fff -#endif - port = UDP_LOCAL_PORT_RANGE_START; - ipcb = udp_pcbs; - while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { - if (ipcb->local_port == port) { - /* port is already used by another udp_pcb */ - port++; - /* restart scanning all udp pcbs */ - ipcb = udp_pcbs; - } else { - /* go on with next udp pcb */ - ipcb = ipcb->next; - } - } - if (ipcb != NULL) { + port = udp_new_port(); + if (port == 0) { /* no more ports available in local range */ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); return ERR_USE; @@ -774,13 +940,12 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) pcb->next = udp_pcbs; udp_pcbs = pcb; } - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, - ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", - ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), - ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), - pcb->local_port)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port)); return ERR_OK; } + /** * Connect an UDP PCB. * @@ -804,39 +969,44 @@ udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) struct udp_pcb *ipcb; if (pcb->local_port == 0) { - err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + err_t err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); if (err != ERR_OK) { return err; } } - ip_addr_set(&pcb->remote_ip, ipaddr); + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); pcb->remote_port = port; pcb->flags |= UDP_FLAGS_CONNECTED; /** TODO: this functionality belongs in upper layers */ #ifdef LWIP_UDP_TODO - /* Nail down local IP for netconn_addr()/getsockname() */ - if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { - struct netif *netif; - - if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { - LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); - UDP_STATS_INC(udp.rterr); - return ERR_RTE; +#if LWIP_IPV6 + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + struct netif *netif; + + if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", + ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip)))); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + ipX_addr_copy(0, pcb->local_ip, netif->ip_addr); + } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + ipX_addr_set_any(0, &pcb->local_ip); } - /** TODO: this will bind the udp pcb locally, to the interface which - is used to route output packets to the remote address. However, we - might want to accept incoming packets on any interface! */ - pcb->local_ip = netif->ip_addr; - } else if (ip_addr_isany(&pcb->remote_ip)) { - pcb->local_ip.addr = 0; } #endif - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, - ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", - ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), - ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), - pcb->remote_port)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port)); /* Insert UDP PCB into the list of active UDP PCBs. */ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { @@ -860,7 +1030,7 @@ void udp_disconnect(struct udp_pcb *pcb) { /* reset remote address association */ - ip_addr_set_any(&pcb->remote_ip); + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->remote_ip); pcb->remote_port = 0; /* mark PCB as unconnected */ pcb->flags &= ~UDP_FLAGS_CONNECTED; @@ -879,7 +1049,7 @@ void udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) { /* remember recv() callback and user data */ - pcb->recv = recv; + pcb->recv.ip4 = recv; pcb->recv_arg = recv_arg; } @@ -939,6 +1109,25 @@ udp_new(void) return pcb; } +#if LWIP_IPV6 +/** + * Create a UDP PCB for IPv6. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new_ip6(void) +{ + struct udp_pcb *pcb; + pcb = udp_new(); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + #if UDP_DEBUG /** * Print UDP header information for debug purposes. diff --git a/lib/liblwip/include/ipv4/lwip/autoip.h b/lib/liblwip/include/ipv4/lwip/autoip.h index 23c264a1e..e62b72e8c 100644 --- a/lib/liblwip/include/ipv4/lwip/autoip.h +++ b/lib/liblwip/include/ipv4/lwip/autoip.h @@ -89,8 +89,7 @@ struct autoip }; -/** Init srand, has to be called before entering mainloop */ -void autoip_init(void); +#define autoip_init() /* Compatibility define, no init needed. */ /** Set a struct autoip allocated by the application to work with */ void autoip_set_struct(struct netif *netif, struct autoip *autoip); diff --git a/lib/liblwip/include/ipv4/lwip/icmp.h b/lib/liblwip/include/ipv4/lwip/icmp.h index c73961c91..fa893b6b4 100644 --- a/lib/liblwip/include/ipv4/lwip/icmp.h +++ b/lib/liblwip/include/ipv4/lwip/icmp.h @@ -37,33 +37,37 @@ #include "lwip/ip_addr.h" #include "lwip/netif.h" +#if LWIP_IPV6 && LWIP_ICMP6 +#include "lwip/icmp6.h" +#endif + #ifdef __cplusplus extern "C" { #endif -#define ICMP_ER 0 /* echo reply */ -#define ICMP_DUR 3 /* destination unreachable */ -#define ICMP_SQ 4 /* source quench */ -#define ICMP_RD 5 /* redirect */ +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ #define ICMP_ECHO 8 /* echo */ -#define ICMP_TE 11 /* time exceeded */ -#define ICMP_PP 12 /* parameter problem */ -#define ICMP_TS 13 /* timestamp */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ #define ICMP_TSR 14 /* timestamp reply */ #define ICMP_IRQ 15 /* information request */ -#define ICMP_IR 16 /* information reply */ +#define ICMP_IR 16 /* information reply */ enum icmp_dur_type { - ICMP_DUR_NET = 0, /* net unreachable */ - ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ ICMP_DUR_PROTO = 2, /* protocol unreachable */ - ICMP_DUR_PORT = 3, /* port unreachable */ - ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ - ICMP_DUR_SR = 5 /* source route failed */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ }; enum icmp_te_type { - ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ }; @@ -104,6 +108,16 @@ void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); #endif /* LWIP_ICMP */ +#if (LWIP_IPV6 && LWIP_ICMP6) +#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \ + icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \ + icmp_dest_unreach(pbuf, ICMP_DUR_PORT)) +#elif LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT) +#else /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ +#define icmp_port_unreach(isipv6, pbuf) +#endif /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ + #ifdef __cplusplus } #endif diff --git a/lib/liblwip/include/ipv4/lwip/inet_chksum.h b/lib/liblwip/include/ipv4/lwip/inet_chksum.h deleted file mode 100644 index 79a2d90f2..000000000 --- a/lib/liblwip/include/ipv4/lwip/inet_chksum.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2001-2004 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 - * - */ -#ifndef __LWIP_INET_CHKSUM_H__ -#define __LWIP_INET_CHKSUM_H__ - -#include "lwip/opt.h" - -#include "lwip/pbuf.h" -#include "lwip/ip_addr.h" - -/** Swap the bytes in an u16_t: much like htons() for little-endian */ -#ifndef SWAP_BYTES_IN_WORD -#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) -/* little endian and PLATFORM_BYTESWAP defined */ -#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) -#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */ -/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ -#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) -#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/ -#endif /* SWAP_BYTES_IN_WORD */ - -/** Split an u32_t in two u16_ts and add them up */ -#ifndef FOLD_U32T -#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) -#endif - -#if LWIP_CHECKSUM_ON_COPY -/** Function-like macro: same as MEMCPY but returns the checksum of copied data - as u16_t */ -#ifndef LWIP_CHKSUM_COPY -#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) -#ifndef LWIP_CHKSUM_COPY_ALGORITHM -#define LWIP_CHKSUM_COPY_ALGORITHM 1 -#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ -#endif /* LWIP_CHKSUM_COPY */ -#else /* LWIP_CHECKSUM_ON_COPY */ -#define LWIP_CHKSUM_COPY_ALGORITHM 0 -#endif /* LWIP_CHECKSUM_ON_COPY */ - -#ifdef __cplusplus -extern "C" { -#endif - -u16_t inet_chksum(void *dataptr, u16_t len); -u16_t inet_chksum_pbuf(struct pbuf *p); -u16_t inet_chksum_pseudo(struct pbuf *p, - ip_addr_t *src, ip_addr_t *dest, - u8_t proto, u16_t proto_len); -u16_t inet_chksum_pseudo_partial(struct pbuf *p, - ip_addr_t *src, ip_addr_t *dest, - u8_t proto, u16_t proto_len, u16_t chksum_len); -#if LWIP_CHKSUM_COPY_ALGORITHM -u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); -#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ - -#ifdef __cplusplus -} -#endif - -#endif /* __LWIP_INET_H__ */ - diff --git a/lib/liblwip/include/ipv4/lwip/ip.h b/lib/liblwip/include/ipv4/lwip/ip4.h similarity index 51% rename from lib/liblwip/include/ipv4/lwip/ip.h rename to lib/liblwip/include/ipv4/lwip/ip4.h index a3de6a6e6..04b5b8a64 100644 --- a/lib/liblwip/include/ipv4/lwip/ip.h +++ b/lib/liblwip/include/ipv4/lwip/ip4.h @@ -29,14 +29,15 @@ * Author: Adam Dunkels * */ -#ifndef __LWIP_IP_H__ -#define __LWIP_IP_H__ +#ifndef __LWIP_IP4_H__ +#define __LWIP_IP4_H__ #include "lwip/opt.h" #include "lwip/def.h" #include "lwip/pbuf.h" #include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" #include "lwip/err.h" #include "lwip/netif.h" @@ -54,60 +55,6 @@ extern "C" { #define IP_PROTO_UDP 17 #define IP_PROTO_UDPLITE 136 #define IP_PROTO_TCP 6 -#define IP_PROTO_ANY 255 /* using a IANA reserved value */ - -/* This is passed as the destination address to ip_output_if (not - to ip_output), meaning that an IP header already is constructed - in the pbuf. This is used when TCP retransmits. */ -#ifdef IP_HDRINCL -#undef IP_HDRINCL -#endif /* IP_HDRINCL */ -#define IP_HDRINCL NULL - -#if LWIP_NETIF_HWADDRHINT -#define IP_PCB_ADDRHINT ;u8_t addr_hint -#else -#define IP_PCB_ADDRHINT -#endif /* LWIP_NETIF_HWADDRHINT */ - -/* This is the common part of all PCB types. It needs to be at the - beginning of a PCB type definition. It is located here so that - changes to this common part are made in one location instead of - having to change all PCB structs. */ -#define IP_PCB \ - /* ip addresses in network byte order */ \ - ip_addr_t local_ip; \ - ip_addr_t remote_ip; \ - /* Socket options */ \ - u8_t so_options; \ - /* Type Of Service */ \ - u8_t tos; \ - /* Time To Live */ \ - u8_t ttl \ - /* link layer address resolution hint */ \ - IP_PCB_ADDRHINT - -struct ip_pcb { -/* Common members of all PCB types */ - IP_PCB; -}; - -/* - * Option flags per-socket. These are the same like SO_XXX. - */ -/*#define SOF_DEBUG (u8_t)0x01U Unimplemented: turn on debugging info recording */ -#define SOF_ACCEPTCONN (u8_t)0x02U /* socket has had listen() */ -#define SOF_REUSEADDR (u8_t)0x04U /* allow local address reuse */ -#define SOF_KEEPALIVE (u8_t)0x08U /* keep connections alive */ -/*#define SOF_DONTROUTE (u8_t)0x10U Unimplemented: just use interface addresses */ -#define SOF_BROADCAST (u8_t)0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ -/*#define SOF_USELOOPBACK (u8_t)0x40U Unimplemented: bypass hardware when possible */ -#define SOF_LINGER (u8_t)0x80U /* linger on close if data present */ -/*#define SOF_OOBINLINE (u16_t)0x0100U Unimplemented: leave received OOB data in line */ -/*#define SOF_REUSEPORT (u16_t)0x0200U Unimplemented: allow local address & port reuse */ - -/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ -#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/) #ifdef PACK_STRUCT_USE_INCLUDES @@ -115,18 +62,20 @@ struct ip_pcb { #endif PACK_STRUCT_BEGIN struct ip_hdr { - /* version / header length / type of service */ - PACK_STRUCT_FIELD(u16_t _v_hl_tos); + /* version / header length */ + PACK_STRUCT_FIELD(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FIELD(u8_t _tos); /* total length */ PACK_STRUCT_FIELD(u16_t _len); /* identification */ PACK_STRUCT_FIELD(u16_t _id); /* fragment offset field */ PACK_STRUCT_FIELD(u16_t _offset); -#define IP_RF 0x8000 /* reserved fragment flag */ -#define IP_DF 0x4000 /* dont fragment flag */ -#define IP_MF 0x2000 /* more fragments flag */ -#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ +#define IP_RF 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* dont fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ /* time to live */ PACK_STRUCT_FIELD(u8_t _ttl); /* protocol*/ @@ -142,9 +91,9 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif -#define IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12) -#define IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f) -#define IPH_TOS(hdr) (ntohs((hdr)->_v_hl_tos) & 0xff) +#define IPH_V(hdr) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) #define IPH_LEN(hdr) ((hdr)->_len) #define IPH_ID(hdr) ((hdr)->_id) #define IPH_OFFSET(hdr) ((hdr)->_offset) @@ -152,7 +101,8 @@ PACK_STRUCT_END #define IPH_PROTO(hdr) ((hdr)->_proto) #define IPH_CHKSUM(hdr) ((hdr)->_chksum) -#define IPH_VHLTOS_SET(hdr, v, hl, tos) (hdr)->_v_hl_tos = (htons(((v) << 12) | ((hl) << 8) | (tos))) +#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl)) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos) #define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) #define IPH_ID_SET(hdr, id) (hdr)->_id = (id) #define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) @@ -160,16 +110,8 @@ PACK_STRUCT_END #define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) #define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) -/** The interface that provided the packet for the current callback invocation. */ -extern struct netif *current_netif; -/** Header of the input packet currently being processed. */ -extern const struct ip_hdr *current_header; -/** Source IP address of current_header */ -extern ip_addr_t current_iphdr_src; -/** Destination IP address of current_header */ -extern ip_addr_t current_iphdr_dest; -#define ip_init() /* Compatibility define, not init needed. */ +#define ip_init() /* Compatibility define, no init needed. */ struct netif *ip_route(ip_addr_t *dest); err_t ip_input(struct pbuf *p, struct netif *inp); err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, @@ -186,18 +128,8 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, u16_t optlen); #endif /* IP_OPTIONS_SEND */ -/** Get the interface that received the current packet. - * This function must only be called from a receive callback (udp_recv, - * raw_recv, tcp_accept). It will return NULL otherwise. */ -#define ip_current_netif() (current_netif) -/** Get the IP header of the current packet. - * This function must only be called from a receive callback (udp_recv, - * raw_recv, tcp_accept). It will return NULL otherwise. */ -#define ip_current_header() (current_header) -/** Source IP address of current_header */ -#define ip_current_src_addr() (¤t_iphdr_src) -/** Destination IP address of current_header */ -#define ip_current_dest_addr() (¤t_iphdr_dest) + +#define ip_netif_get_local_ipX(netif) (((netif) != NULL) ? ip_2_ipX(&((netif)->ip_addr)) : NULL) #if IP_DEBUG void ip_debug_print(struct pbuf *p); diff --git a/lib/liblwip/include/ipv4/lwip/ip_addr.h b/lib/liblwip/include/ipv4/lwip/ip4_addr.h similarity index 99% rename from lib/liblwip/include/ipv4/lwip/ip_addr.h rename to lib/liblwip/include/ipv4/lwip/ip4_addr.h index 77f84e02c..b05ae5370 100644 --- a/lib/liblwip/include/ipv4/lwip/ip_addr.h +++ b/lib/liblwip/include/ipv4/lwip/ip4_addr.h @@ -29,8 +29,8 @@ * Author: Adam Dunkels * */ -#ifndef __LWIP_IP_ADDR_H__ -#define __LWIP_IP_ADDR_H__ +#ifndef __LWIP_IP4_ADDR_H__ +#define __LWIP_IP4_ADDR_H__ #include "lwip/opt.h" #include "lwip/def.h" diff --git a/lib/liblwip/include/ipv4/lwip/ip_frag.h b/lib/liblwip/include/ipv4/lwip/ip_frag.h index 77b5eb1ee..47eca9f42 100644 --- a/lib/liblwip/include/ipv4/lwip/ip_frag.h +++ b/lib/liblwip/include/ipv4/lwip/ip_frag.h @@ -70,12 +70,15 @@ struct pbuf * ip_reass(struct pbuf *p); /** A custom pbuf that holds a reference to another pbuf, which is freed * when this custom pbuf is freed. This is used to create a custom PBUF_REF * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ struct pbuf_custom_ref { /** 'base class' */ struct pbuf_custom pc; /** pointer to the original pbuf that is referenced */ struct pbuf *original; }; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ #endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest); diff --git a/lib/liblwip/include/ipv6/lwip/dhcp6.h b/lib/liblwip/include/ipv6/lwip/dhcp6.h new file mode 100644 index 000000000..4b905c541 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/dhcp6.h @@ -0,0 +1,58 @@ +/** + * @file + * + * IPv6 address autoconfiguration as per RFC 4862. + */ + +/* + * 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 + * + * IPv6 address autoconfiguration as per RFC 4862. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_IP6_DHCP6_H__ +#define __LWIP_IP6_DHCP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + + +struct dhcp6 +{ + /*TODO: implement DHCP6*/ +}; + +#endif /* LWIP_IPV6_DHCP6 */ + +#endif /* __LWIP_IP6_DHCP6_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/ethip6.h b/lib/liblwip/include/ipv6/lwip/ethip6.h new file mode 100644 index 000000000..e7f412b49 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/ethip6.h @@ -0,0 +1,68 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * 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_ETHIP6_H__ +#define __LWIP_ETHIP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET /* 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" + + +#ifdef __cplusplus +extern "C" { +#endif + + +err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ + +#endif /* __LWIP_ETHIP6_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/icmp6.h b/lib/liblwip/include/ipv6/lwip/icmp6.h new file mode 100644 index 000000000..74bfdbe09 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/icmp6.h @@ -0,0 +1,152 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * 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_ICMP6_H__ +#define __LWIP_ICMP6_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +enum icmp6_type { + ICMP6_TYPE_DUR = 1, /* Destination unreachable */ + ICMP6_TYPE_PTB = 2, /* Packet too big */ + ICMP6_TYPE_TE = 3, /* Time exceeded */ + ICMP6_TYPE_PP = 4, /* Parameter problem */ + ICMP6_TYPE_PE1 = 100, /* Private experimentation */ + ICMP6_TYPE_PE2 = 101, /* Private experimentation */ + ICMP6_TYPE_RSV_ERR = 127, /* Reserved for expansion of error messages */ + + ICMP6_TYPE_EREQ = 128, /* Echo request */ + ICMP6_TYPE_EREP = 129, /* Echo reply */ + ICMP6_TYPE_MLQ = 130, /* Multicast listener query */ + ICMP6_TYPE_MLR = 131, /* Multicast listener report */ + ICMP6_TYPE_MLD = 132, /* Multicast listener done */ + ICMP6_TYPE_RS = 133, /* Router solicitation */ + ICMP6_TYPE_RA = 134, /* Router advertisement */ + ICMP6_TYPE_NS = 135, /* Neighbor solicitation */ + ICMP6_TYPE_NA = 136, /* Neighbor advertisement */ + ICMP6_TYPE_RD = 137, /* Redirect */ + ICMP6_TYPE_MRA = 151, /* Multicast router advertisement */ + ICMP6_TYPE_MRS = 152, /* Multicast router solicitation */ + ICMP6_TYPE_MRT = 153, /* Multicast router termination */ + ICMP6_TYPE_PE3 = 200, /* Private experimentation */ + ICMP6_TYPE_PE4 = 201, /* Private experimentation */ + ICMP6_TYPE_RSV_INF = 255 /* Reserved for expansion of informational messages */ +}; + +enum icmp6_dur_code { + ICMP6_DUR_NO_ROUTE = 0, /* No route to destination */ + ICMP6_DUR_PROHIBITED = 1, /* Communication with destination administratively prohibited */ + ICMP6_DUR_SCOPE = 2, /* Beyond scope of source address */ + ICMP6_DUR_ADDRESS = 3, /* Address unreachable */ + ICMP6_DUR_PORT = 4, /* Port unreachable */ + ICMP6_DUR_POLICY = 5, /* Source address failed ingress/egress policy */ + ICMP6_DUR_REJECT_ROUTE = 6 /* Reject route to destination */ +}; + +enum icmp6_te_code { + ICMP6_TE_HL = 0, /* Hop limit exceeded in transit */ + ICMP6_TE_FRAG = 1 /* Fragment reassembly time exceeded */ +}; + +enum icmp6_pp_code { + ICMP6_PP_FIELD = 0, /* Erroneous header field encountered */ + ICMP6_PP_HEADER = 1, /* Unrecognized next header type encountered */ + ICMP6_PP_OPTION = 2 /* Unrecognized IPv6 option encountered */ +}; + +/** This is the standard ICMP6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t data); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** This is the ICMP6 header adapted for echo req/resp. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#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); +void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); +void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); +void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* __LWIP_ICMP6_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/inet6.h b/lib/liblwip/include/ipv6/lwip/inet6.h new file mode 100644 index 000000000..dbf98df01 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/inet6.h @@ -0,0 +1,92 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * 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_INET6_H__ +#define __LWIP_INET6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in6_addr { + union { + u8_t u8_addr[16]; + u32_t u32_addr[4]; + } un; +#define s6_addr un.u32_addr +}; + +#define IN6ADDR_ANY_INIT {0,0,0,0} +#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)} + + +#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \ + (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \ + (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \ + (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];} +#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ + (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ + (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} +/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ +#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + +/* directly map this to the lwip internal functions */ +#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) +#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr)) +#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen) + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_INET6_H__ */ + diff --git a/lib/liblwip/include/ipv6/lwip/ip6.h b/lib/liblwip/include/ipv6/lwip/ip6.h new file mode 100644 index 000000000..b199c95a9 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/ip6.h @@ -0,0 +1,197 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * 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_IP6_H__ +#define __LWIP_IP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP6_HLEN 40 + +#define IP6_NEXTH_HOPBYHOP 0 +#define IP6_NEXTH_TCP 6 +#define IP6_NEXTH_UDP 17 +#define IP6_NEXTH_ENCAPS 41 +#define IP6_NEXTH_ROUTING 43 +#define IP6_NEXTH_FRAGMENT 44 +#define IP6_NEXTH_ICMP6 58 +#define IP6_NEXTH_NONE 59 +#define IP6_NEXTH_DESTOPTS 60 +#define IP6_NEXTH_UDPLITE 136 + + +/* The IPv6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hdr { + /* version / traffic class / flow label */ + PACK_STRUCT_FIELD(u32_t _v_tc_fl); + /* payload length */ + PACK_STRUCT_FIELD(u16_t _plen); + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* hop limit */ + PACK_STRUCT_FIELD(u8_t _hoplim); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip6_addr_p_t src); + PACK_STRUCT_FIELD(ip6_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Hop-by-hop router alert option. */ +#define IP6_HBH_HLEN 8 +#define IP6_PAD1_OPTION 0 +#define IP6_PADN_ALERT_OPTION 1 +#define IP6_ROUTER_ALERT_OPTION 5 +#define IP6_ROUTER_ALERT_VALUE_MLD 0 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hbh_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* header length */ + PACK_STRUCT_FIELD(u8_t _hlen); + /* router alert option type */ + PACK_STRUCT_FIELD(u8_t _ra_opt_type); + /* router alert option data len */ + PACK_STRUCT_FIELD(u8_t _ra_opt_dlen); + /* router alert option data */ + PACK_STRUCT_FIELD(u16_t _ra_opt_data); + /* PadN option type */ + PACK_STRUCT_FIELD(u8_t _padn_opt_type); + /* PadN option data len */ + PACK_STRUCT_FIELD(u8_t _padn_opt_dlen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Fragment header. */ +#define IP6_FRAG_HLEN 8 +#define IP6_FRAG_OFFSET_MASK 0xfff8 +#define IP6_FRAG_MORE_FLAG 0x0001 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_frag_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* reserved */ + PACK_STRUCT_FIELD(u8_t reserved); + /* fragment offset */ + PACK_STRUCT_FIELD(u16_t _fragment_offset); + /* fragmented packet identification */ + PACK_STRUCT_FIELD(u32_t _identification); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6H_V(hdr) ((ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f) +#define IP6H_TC(hdr) ((ntohl((hdr)->_v_tc_fl) >> 20) & 0xff) +#define IP6H_FL(hdr) (ntohl((hdr)->_v_tc_fl) & 0x000fffff) +#define IP6H_PLEN(hdr) (ntohs((hdr)->_plen)) +#define IP6H_NEXTH(hdr) ((hdr)->_nexth) +#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6) +#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim) + +#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (htonl(((v) << 28) | ((tc) << 20) | (fl))) +#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = htons(plen) +#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth) +#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl) + + +#define ip6_init() /* TODO should we init current addresses and header pointer? */ +struct netif *ip6_route(struct ip6_addr *src, struct ip6_addr *dest); +ip6_addr_t *ip6_select_source_address(struct netif *netif, ip6_addr_t * dest); +err_t ip6_input(struct pbuf *p, struct netif *inp); +err_t ip6_output(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth); +err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6_MLD +err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value); +#endif /* LWIP_IPV6_MLD */ + +#define ip6_netif_get_local_ipX(netif, dest) (((netif) != NULL) ? \ + ip6_2_ipX(ip6_select_source_address(netif, dest)) : NULL) + +#if IP6_DEBUG +void ip6_debug_print(struct pbuf *p); +#else +#define ip6_debug_print(p) +#endif /* IP6_DEBUG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/ip6_addr.h b/lib/liblwip/include/ipv6/lwip/ip6_addr.h new file mode 100644 index 000000000..89b5b8113 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/ip6_addr.h @@ -0,0 +1,286 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * 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 + * + * Structs and macros for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_ADDR_H__ +#define __LWIP_IP6_ADDR_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +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 + +/** ip6_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip6_addr_t as well as on ip6_addr_p_t. */ +typedef struct ip6_addr ip6_addr_t; +typedef struct ip6_addr_packed ip6_addr_p_t; + + +/** IP6_ADDR_ANY can be used as a fixed IPv6 address + * for the wildcard + */ +extern const ip6_addr_t ip6_addr_any; +#define IP6_ADDR_ANY ((ip6_addr_t *)&ip6_addr_any) + + + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IPv6 partial address given by byte-parts. */ +#define IP6_ADDR(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 htonl). */ +#define IP6_ADDR(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 + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0])) & 0xffff) +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1])) & 0xffff) +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2])) & 0xffff) +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3])) & 0xffff) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : htonl((src)->addr[3]);}while(0) + + + +/** + * Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || \ + (((ip6addr)->addr[0] == 0) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == 0) && \ + ((ip6addr)->addr[3] == 0))) + + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff0e0000UL)) + +/* TODO define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = htonl(0xff000000UL | (htonl(if_id) & 0x00ffffffUL));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == htonl(0xff000000UL | (htonl((sn_addr)->addr[3]) & 0x00ffffffUL)))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x50 + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F, \ + ipaddr != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_ADDR_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/ip6_frag.h b/lib/liblwip/include/ipv6/lwip/ip6_frag.h new file mode 100644 index 000000000..75898b8fc --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/ip6_frag.h @@ -0,0 +1,102 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * 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_IP6_FRAG_H__ +#define __LWIP_IP6_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + +/* The IPv6 reassembly timer interval in milliseconds. */ +#define IP6_REASS_TMR_INTERVAL 1000 + +/* IPv6 reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip6_reassdata { + struct ip6_reassdata *next; + struct pbuf *p; + struct ip6_hdr * iphdr; + u32_t identification; + u16_t datagram_len; + u8_t nexth; + u8_t timer; +}; + +#define ip6_reass_init() /* Compatibility define */ +void ip6_reass_tmr(void); +struct pbuf * ip6_reass(struct pbuf *p); + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */ + +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ + +err_t ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest); + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP6_FRAG_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/mld6.h b/lib/liblwip/include/ipv6/lwip/mld6.h new file mode 100644 index 000000000..abd86e553 --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/mld6.h @@ -0,0 +1,118 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * 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_MLD6_H__ +#define __LWIP_MLD6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +struct mld_group { + /** next link */ + struct mld_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip6_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/** Multicast listener report/query/done message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mld_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t max_resp_delay); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t multicast_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define MLD6_TMR_INTERVAL 100 /* Milliseconds */ + +/* MAC Filter Actions, these are passed to a netif's + * mld_mac_filter callback function. */ +#define MLD6_DEL_MAC_FILTER 0 +#define MLD6_ADD_MAC_FILTER 1 + + +#define mld6_init() /* TODO should we init tables? */ +err_t mld6_stop(struct netif *netif); +void mld6_report_groups(struct netif *netif); +void mld6_tmr(void); +struct mld_group *mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr); +void mld6_input(struct pbuf *p, struct netif *inp); +err_t mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); +err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */ + +#endif /* __LWIP_MLD6_H__ */ diff --git a/lib/liblwip/include/ipv6/lwip/nd6.h b/lib/liblwip/include/ipv6/lwip/nd6.h new file mode 100644 index 000000000..28636e89d --- /dev/null +++ b/lib/liblwip/include/ipv6/lwip/nd6.h @@ -0,0 +1,369 @@ +/** + * @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_ND6_H__ +#define __LWIP_ND6_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.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.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; + u32_t delay_time; + u32_t probes_sent; + u32_t stale_time; + } counter; +}; + +struct nd6_destination_cache_entry { + ip6_addr_t destination_addr; + ip6_addr_t next_hop_addr; + u32_t pmtu; + u32_t age; +}; + +struct nd6_prefix_list_entry { + ip6_addr_t prefix; + struct netif * netif; + u32_t invalidation_timer; +#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; + 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 */ + +/** Neighbor solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ns_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Neighbor advertisement message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct na_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u8_t reserved[3]); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define ND6_FLAG_ROUTER (0x80) +#define ND6_FLAG_SOLICITED (0x40) +#define ND6_FLAG_OVERRIDE (0x20) + +/** Router solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rs_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Router advertisement message header. */ +#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80) +#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40) +#define ND6_RA_FLAG_HOME_AGENT (0x20) +#define ND6_RA_PREFERENCE_MASK (0x18) +#define ND6_RA_PREFERENCE_HIGH (0x08) +#define ND6_RA_PREFERENCE_MEDIUM (0x00) +#define ND6_RA_PREFERENCE_LOW (0x18) +#define ND6_RA_PREFERENCE_DISABLED (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ra_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t current_hop_limit); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u16_t router_lifetime); + PACK_STRUCT_FIELD(u32_t reachable_time); + PACK_STRUCT_FIELD(u32_t retrans_timer); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirect message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirect_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + PACK_STRUCT_FIELD(ip6_addr_p_t destination_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Link-layer address option. */ +#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01) +#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct lladdr_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t addr[NETIF_MAX_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Prefix information option. */ +#define ND6_OPTION_TYPE_PREFIX_INFO (0x03) +#define ND6_PREFIX_FLAG_ON_LINK (0x80) +#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40) +#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20) +#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct prefix_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u32_t valid_lifetime); + PACK_STRUCT_FIELD(u32_t preferred_lifetime); + PACK_STRUCT_FIELD(u8_t reserved2[3]); + PACK_STRUCT_FIELD(u8_t site_prefix_length); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirected header option. */ +#define ND6_OPTION_TYPE_REDIR_HDR (0x04) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirected_header_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t reserved[6]); + /* Portion of redirected packet follows. */ + /* PACK_STRUCT_FIELD(u8_t redirected[8]); */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** MTU option. */ +#define ND6_OPTION_TYPE_MTU (0x05) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mtu_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t mtu); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Route information option. */ +#define ND6_OPTION_TYPE_ROUTE_INFO (24) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct route_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* the possible states of an IP address */ +#define IP6_ADDRESS_STATE_INVALID (0) +#define IP6_ADDRESS_STATE_VALID (0x4) +#define IP6_ADDRESS_STATE_PREFERRED (0x5) /* includes valid */ +#define IP6_ADDRESS_STATE_DEPRECATED (0x6) /* includes valid */ +#define IP6_ADDRESS_STATE_TENTATIV (0x8) + +/** 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; + +#define nd6_init() /* TODO should we init tables? */ +void nd6_tmr(void); +void nd6_input(struct pbuf *p, struct netif *inp); +s8_t nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif); +s8_t nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif); +u16_t nd6_get_destination_mtu(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(ip6_addr_t * ip6addr); +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_ND6_H__ */ diff --git a/lib/liblwip/include/lwip/api.h b/lib/liblwip/include/lwip/api.h index 9add7e89e..ac58eebbb 100644 --- a/lib/liblwip/include/lwip/api.h +++ b/lib/liblwip/include/lwip/api.h @@ -73,23 +73,50 @@ extern "C" { /** If a nonblocking write has been rejected before, poll_tcp needs to check if the netconn is writable again */ #define NETCONN_FLAG_CHECK_WRITESPACE 0x10 +#if LWIP_IPV6 +/** If this flag is set then only IPv6 communication is allowed on the + netconn. As per RFC#3493 this features defaults to OFF allowing + dual-stack usage by default. */ +#define NETCONN_FLAG_IPV6_V6ONLY 0x20 +#endif /* LWIP_IPV6 */ /* Helpers to process several netconn_types by the same code */ -#define NETCONNTYPE_GROUP(t) (t&0xF0) -#define NETCONNTYPE_DATAGRAM(t) (t&0xE0) +#define NETCONNTYPE_GROUP(t) ((t)&0xF0) +#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0) +#if LWIP_IPV6 +#define NETCONN_TYPE_IPV6 0x08 +#define NETCONNTYPE_ISIPV6(t) ((t)&0x08) +#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM) +#else /* LWIP_IPV6 */ +#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM) +#endif /* LWIP_IPV6 */ /** Protocol family and type of the netconn */ enum netconn_type { - NETCONN_INVALID = 0, + NETCONN_INVALID = 0, /* NETCONN_TCP Group */ - NETCONN_TCP = 0x10, + NETCONN_TCP = 0x10, +#if LWIP_IPV6 + NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */, +#endif /* LWIP_IPV6 */ /* NETCONN_UDP Group */ - NETCONN_UDP = 0x20, - NETCONN_UDPLITE = 0x21, - NETCONN_UDPNOCHKSUM= 0x22, + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM = 0x22, +#if LWIP_IPV6 + NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */, + NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */, + NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */, +#endif /* LWIP_IPV6 */ /* NETCONN_RAW Group */ - NETCONN_RAW = 0x40 + NETCONN_RAW = 0x40 +#if LWIP_IPV6 + , + NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */ +#endif /* LWIP_IPV6 */ }; /** Current state of the netconn. Non-TCP netconns are always @@ -111,13 +138,13 @@ enum netconn_evt { NETCONN_EVT_ERROR }; -#if LWIP_IGMP +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) /** Used for netconn_join_leave_group() */ enum netconn_igmp { NETCONN_JOIN, NETCONN_LEAVE }; -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ /* forward-declare some structs to avoid to include their headers */ struct ip_pcb; @@ -159,6 +186,11 @@ struct netconn { #if LWIP_SOCKET int socket; #endif /* LWIP_SOCKET */ +#if LWIP_SO_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVTIMEO /** timeout to wait for new data to be received (or connections to arrive for listening netconns) */ @@ -168,12 +200,11 @@ struct netconn { /** maximum amount of bytes queued in recvmbox not used for TCP: adjust TCP_WND instead! */ int recv_bufsize; -#endif /* LWIP_SO_RCVBUF */ /** number of bytes currently in recvmbox to be received, tested against recv_bufsize to limit bytes on recvmbox - for UDP and RAW - @todo: should only be necessary with LWIP_SO_RCVBUF==1 */ + for UDP and RAW, used for FIONREAD */ s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ u8_t flags; #if LWIP_TCP @@ -231,18 +262,34 @@ void netconn_recved(struct netconn *conn, u32_t length); err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port); err_t netconn_send(struct netconn *conn, struct netbuf *buf); -err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size, - u8_t apiflags); +err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) err_t netconn_close(struct netconn *conn); err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); -#if LWIP_IGMP +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr, ip_addr_t *netif_addr, enum netconn_igmp join_or_leave); -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); #endif /* LWIP_DNS */ +#if LWIP_IPV6 + +#define netconn_bind_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_bind(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_connect_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_connect(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_sendto_ip6(conn, buf, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_sendto(conn, buf, ip6_2_ip(ip6addr), port) : ERR_VAL) +#if LWIP_IPV6_MLD +#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_join_leave_group(conn, ip6_2_ip(multiaddr), ip6_2_ip(srcaddr), join_or_leave) :\ + ERR_VAL) +#endif /* LWIP_IPV6_MLD*/ +#endif /* LWIP_IPV6 */ #define netconn_err(conn) ((conn)->last_err) #define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) @@ -263,6 +310,12 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); /** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ #define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0) +#if LWIP_SO_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO /** Set the receive timeout in milliseconds */ #define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) diff --git a/lib/liblwip/include/lwip/api_msg.h b/lib/liblwip/include/lwip/api_msg.h index ccb1015f2..8268036af 100644 --- a/lib/liblwip/include/lwip/api_msg.h +++ b/lib/liblwip/include/lwip/api_msg.h @@ -48,9 +48,10 @@ extern "C" { #endif +/* For the netconn API, these values are use as a bitmask! */ #define NETCONN_SHUT_RD 1 #define NETCONN_SHUT_WR 2 -#define NETCONN_SHUT_RDWR 3 +#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR) /* IP addresses and port numbers are expected to be in * the same byte order as in the corresponding pcb. @@ -66,45 +67,48 @@ struct api_msg_msg { err_t err; /** Depending on the executed function, one of these union members is used */ union { - /** used for do_send */ + /** used for lwip_netconn_do_send */ struct netbuf *b; - /** used for do_newconn */ + /** used for lwip_netconn_do_newconn */ struct { u8_t proto; } n; - /** used for do_bind and do_connect */ + /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */ struct { ip_addr_t *ipaddr; u16_t port; } bc; - /** used for do_getaddr */ + /** used for lwip_netconn_do_getaddr */ struct { - ip_addr_t *ipaddr; + ipX_addr_t *ipaddr; u16_t *port; u8_t local; } ad; - /** used for do_write */ + /** used for lwip_netconn_do_write */ struct { const void *dataptr; size_t len; u8_t apiflags; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ } w; - /** used for do_recv */ + /** used for lwip_netconn_do_recv */ struct { u32_t len; } r; - /** used for do_close (/shutdown) */ + /** used for lwip_netconn_do_close (/shutdown) */ struct { u8_t shut; } sd; -#if LWIP_IGMP - /** used for do_join_leave_group */ +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** used for lwip_netconn_do_join_leave_group */ struct { - ip_addr_t *multiaddr; - ip_addr_t *netif_addr; + ipX_addr_t *multiaddr; + ipX_addr_t *netif_addr; enum netconn_igmp join_or_leave; } jl; -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if TCP_LISTEN_BACKLOG struct { u8_t backlog; @@ -124,9 +128,9 @@ struct api_msg { }; #if LWIP_DNS -/** As do_gethostbyname requires more arguments but doesn't require a netconn, +/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn, it has its own struct (to avoid struct api_msg getting bigger than necessary). - do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg (see netconn_gethostbyname). */ struct dns_api_msg { /** Hostname to query or dotted IP address string */ @@ -141,24 +145,24 @@ struct dns_api_msg { }; #endif /* LWIP_DNS */ -void do_newconn ( struct api_msg_msg *msg); -void do_delconn ( struct api_msg_msg *msg); -void do_bind ( struct api_msg_msg *msg); -void do_connect ( struct api_msg_msg *msg); -void do_disconnect ( struct api_msg_msg *msg); -void do_listen ( struct api_msg_msg *msg); -void do_send ( struct api_msg_msg *msg); -void do_recv ( struct api_msg_msg *msg); -void do_write ( struct api_msg_msg *msg); -void do_getaddr ( struct api_msg_msg *msg); -void do_close ( struct api_msg_msg *msg); -void do_shutdown ( struct api_msg_msg *msg); -#if LWIP_IGMP -void do_join_leave_group( struct api_msg_msg *msg); -#endif /* LWIP_IGMP */ +void lwip_netconn_do_newconn ( struct api_msg_msg *msg); +void lwip_netconn_do_delconn ( struct api_msg_msg *msg); +void lwip_netconn_do_bind ( struct api_msg_msg *msg); +void lwip_netconn_do_connect ( struct api_msg_msg *msg); +void lwip_netconn_do_disconnect ( struct api_msg_msg *msg); +void lwip_netconn_do_listen ( struct api_msg_msg *msg); +void lwip_netconn_do_send ( struct api_msg_msg *msg); +void lwip_netconn_do_recv ( struct api_msg_msg *msg); +void lwip_netconn_do_write ( struct api_msg_msg *msg); +void lwip_netconn_do_getaddr ( struct api_msg_msg *msg); +void lwip_netconn_do_close ( struct api_msg_msg *msg); +void lwip_netconn_do_shutdown ( struct api_msg_msg *msg); +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS -void do_gethostbyname(void *arg); +void lwip_netconn_do_gethostbyname(void *arg); #endif /* LWIP_DNS */ struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); diff --git a/lib/liblwip/include/lwip/arch.h b/lib/liblwip/include/lwip/arch.h index 524af6be0..4d6df773f 100644 --- a/lib/liblwip/include/lwip/arch.h +++ b/lib/liblwip/include/lwip/arch.h @@ -204,27 +204,6 @@ extern "C" { #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ - -#define ENSROK 0 /* DNS server returned answer with no data */ -#define ENSRNODATA 160 /* DNS server returned answer with no data */ -#define ENSRFORMERR 161 /* DNS server claims query was misformatted */ -#define ENSRSERVFAIL 162 /* DNS server returned general failure */ -#define ENSRNOTFOUND 163 /* Domain name not found */ -#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */ -#define ENSRREFUSED 165 /* DNS server refused query */ -#define ENSRBADQUERY 166 /* Misformatted DNS query */ -#define ENSRBADNAME 167 /* Misformatted domain name */ -#define ENSRBADFAMILY 168 /* Unsupported address family */ -#define ENSRBADRESP 169 /* Misformatted DNS reply */ -#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */ -#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */ -#define ENSROF 172 /* End of file */ -#define ENSRFILE 173 /* Error reading file */ -#define ENSRNOMEM 174 /* Out of memory */ -#define ENSRDESTRUCTION 175 /* Application terminated lookup */ -#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */ -#define ENSRCNAMELOOP 177 /* Domain name is too long */ - #ifndef errno extern int errno; #endif diff --git a/lib/liblwip/include/lwip/autoip.h b/lib/liblwip/include/lwip/autoip.h deleted file mode 100644 index 4b58eaa95..000000000 --- a/lib/liblwip/include/lwip/autoip.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MINIX_AUTOIP_H__ -#define __MINIX_AUTOIP_H__ - -#include "ipv4/lwip/autoip.h" - -#endif /* __MINIX_AUTOIP_H__ */ diff --git a/lib/liblwip/include/lwip/debug.h b/lib/liblwip/include/lwip/debug.h index d8359ea3a..0fe041396 100644 --- a/lib/liblwip/include/lwip/debug.h +++ b/lib/liblwip/include/lwip/debug.h @@ -33,6 +33,7 @@ #define __LWIP_DEBUG_H__ #include "lwip/arch.h" +#include "lwip/opt.h" /** lower two bits indicate debug level * - 0 all diff --git a/lib/liblwip/include/lwip/def.h b/lib/liblwip/include/lwip/def.h index 9b6de6a8b..73a1b560b 100644 --- a/lib/liblwip/include/lwip/def.h +++ b/lib/liblwip/include/lwip/def.h @@ -47,10 +47,6 @@ extern "C" { #define NULL ((void *)0) #endif -/** Get the absolute difference between 2 u32_t values (correcting overflows) - * 'a' is expected to be 'higher' (without overflow) than 'b'. */ -#define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1))) - /* 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) diff --git a/lib/liblwip/include/lwip/dhcp.h b/lib/liblwip/include/lwip/dhcp.h index 8767605c9..32d93381d 100644 --- a/lib/liblwip/include/lwip/dhcp.h +++ b/lib/liblwip/include/lwip/dhcp.h @@ -106,6 +106,9 @@ PACK_STRUCT_END #endif void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp); +/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */ +#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0) +void dhcp_cleanup(struct netif *netif); /** start DHCP configuration */ err_t dhcp_start(struct netif *netif); /** enforce early lease renewal (not needed normally)*/ diff --git a/lib/liblwip/include/lwip/dns.h b/lib/liblwip/include/lwip/dns.h index 6042bf75b..6c7d9b073 100644 --- a/lib/liblwip/include/lwip/dns.h +++ b/lib/liblwip/include/lwip/dns.h @@ -38,6 +38,10 @@ #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ +#ifdef __cplusplus +extern "C" { +#endif + /** DNS timer period */ #define DNS_TMR_INTERVAL 1000 @@ -111,6 +115,10 @@ int dns_local_removehost(const char *hostname, const ip_addr_t *addr) err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr); #endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#ifdef __cplusplus +} +#endif + #endif /* LWIP_DNS */ #endif /* __LWIP_DNS_H__ */ diff --git a/lib/liblwip/include/lwip/err.h b/lib/liblwip/include/lwip/err.h index b35ae75f8..ac907729f 100644 --- a/lib/liblwip/include/lwip/err.h +++ b/lib/liblwip/include/lwip/err.h @@ -57,20 +57,19 @@ typedef s8_t err_t; #define ERR_INPROGRESS -5 /* Operation in progress */ #define ERR_VAL -6 /* Illegal value. */ #define ERR_WOULDBLOCK -7 /* Operation would block. */ +#define ERR_USE -8 /* Address in use. */ +#define ERR_ISCONN -9 /* Already connected. */ -#define ERR_IS_FATAL(e) ((e) < ERR_VAL) +#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) -#define ERR_ABRT -8 /* Connection aborted. */ -#define ERR_RST -9 /* Connection reset. */ -#define ERR_CLSD -10 /* Connection closed. */ -#define ERR_CONN -11 /* Not connected. */ +#define ERR_ABRT -10 /* Connection aborted. */ +#define ERR_RST -11 /* Connection reset. */ +#define ERR_CLSD -12 /* Connection closed. */ +#define ERR_CONN -13 /* Not connected. */ -#define ERR_ARG -12 /* Illegal argument. */ +#define ERR_ARG -14 /* Illegal argument. */ -#define ERR_USE -13 /* Address in use. */ - -#define ERR_IF -14 /* Low-level netif error */ -#define ERR_ISCONN -15 /* Already connected. */ +#define ERR_IF -15 /* Low-level netif error */ #ifdef LWIP_DEBUG diff --git a/lib/liblwip/include/lwip/icmp.h b/lib/liblwip/include/lwip/icmp.h deleted file mode 100644 index 998ebe53b..000000000 --- a/lib/liblwip/include/lwip/icmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MINIX_ICMP_H__ -#define __MINIX_ICMP_H__ - -#include "ipv4/lwip/icmp.h" - -#endif /* __MINIX_ICMP_H__ */ diff --git a/lib/liblwip/include/lwip/igmp.h b/lib/liblwip/include/lwip/igmp.h deleted file mode 100644 index 221327747..000000000 --- a/lib/liblwip/include/lwip/igmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MINIX_IGMP_H__ -#define __MINIX_IGMP_H__ - -#include "ipv4/lwip/igmp.h" - -#endif /* __MINIX_IGMP_H__ */ diff --git a/lib/liblwip/include/lwip/inet.h b/lib/liblwip/include/lwip/inet.h deleted file mode 100644 index d183af91e..000000000 --- a/lib/liblwip/include/lwip/inet.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MINIX_INET_H__ -#define __MINIX_INET_H__ - -#include "ipv4/lwip/inet.h" - -#endif /* __MINIX_INET_H__ */ diff --git a/lib/liblwip/include/lwip/inet_chksum.h b/lib/liblwip/include/lwip/inet_chksum.h index f7cfe049a..e1687888f 100644 --- a/lib/liblwip/include/lwip/inet_chksum.h +++ b/lib/liblwip/include/lwip/inet_chksum.h @@ -1,6 +1,112 @@ -#ifndef __MINIX_INET_CHKSUM_H__ -#define __MINIX_INET_CHKSUM_H__ +/* + * Copyright (c) 2001-2004 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 + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ -#include "ipv4/lwip/inet_chksum.h" +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +/** Swap the bytes in an u16_t: much like htons() for little-endian */ +#ifndef SWAP_BYTES_IN_WORD +#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) +/* little endian and PLATFORM_BYTESWAP defined */ +#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) +#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */ +/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ +#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) +#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/ +#endif /* SWAP_BYTES_IN_WORD */ + +/** Split an u32_t in two u16_ts and add them up */ +#ifndef FOLD_U32T +#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) +#endif + +#if LWIP_CHECKSUM_ON_COPY +/** Function-like macro: same as MEMCPY but returns the checksum of copied data + as u16_t */ +#ifndef LWIP_CHKSUM_COPY +#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) +#ifndef LWIP_CHKSUM_COPY_ALGORITHM +#define LWIP_CHKSUM_COPY_ALGORITHM 1 +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ +#endif /* LWIP_CHKSUM_COPY */ +#else /* LWIP_CHECKSUM_ON_COPY */ +#define LWIP_CHKSUM_COPY_ALGORITHM 0 +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, + u16_t proto_len, u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#if LWIP_IPV6 +u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest); +u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest); + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo(p, proto, proto_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo(p, proto, proto_len, ipX_2_ip(src), ipX_2_ip(dest))) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip(src), ipX_2_ip(dest))) + +#else /* LWIP_IPV6 */ + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + inet_chksum_pseudo(p, proto, proto_len, src, dest) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, src, dest) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ -#endif /* __MINIX_INET_CHKSUM_H__ */ diff --git a/lib/liblwip/include/lwip/init.h b/lib/liblwip/include/lwip/init.h index 1c54d3e51..4e2e28565 100644 --- a/lib/liblwip/include/lwip/init.h +++ b/lib/liblwip/include/lwip/init.h @@ -43,11 +43,11 @@ extern "C" { /** x.X.x: Minor version of the stack */ #define LWIP_VERSION_MINOR 4U /** x.x.X: Revision of the stack */ -#define LWIP_VERSION_REVISION 0U +#define LWIP_VERSION_REVISION 1U /** For release candidates, this is set to 1..254 * For official releases, this is set to 255 (LWIP_RC_RELEASE) * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */ -#define LWIP_VERSION_RC 1U +#define LWIP_VERSION_RC 0U /** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ #define LWIP_RC_RELEASE 255U diff --git a/lib/liblwip/include/lwip/ip.h b/lib/liblwip/include/lwip/ip.h index fe49b5c60..a0cd1d4d4 100644 --- a/lib/liblwip/include/lwip/ip.h +++ b/lib/liblwip/include/lwip/ip.h @@ -1,6 +1,254 @@ -#ifndef __MINIX_IP_H__ -#define __MINIX_IP_H__ +/* + * Copyright (c) 2001-2004 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 + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/ip4.h" +#include "lwip/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +#if LWIP_IPV6 +#define IP_PCB_ISIPV6_MEMBER u8_t isipv6; +#define IP_PCB_IPVER_EQ(pcb1, pcb2) ((pcb1)->isipv6 == (pcb2)->isipv6) +#define IP_PCB_IPVER_INPUT_MATCH(pcb) (ip_current_is_v6() ? \ + ((pcb)->isipv6 != 0) : \ + ((pcb)->isipv6 == 0)) +#define PCB_ISIPV6(pcb) ((pcb)->isipv6) +#else +#define IP_PCB_ISIPV6_MEMBER +#define IP_PCB_IPVER_EQ(pcb1, pcb2) 1 +#define IP_PCB_IPVER_INPUT_MATCH(pcb) 1 +#define PCB_ISIPV6(pcb) 0 +#endif /* LWIP_IPV6 */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + IP_PCB_ISIPV6_MEMBER \ + /* ip addresses in network byte order */ \ + ipX_addr_t local_ip; \ + ipX_addr_t remote_ip; \ + /* Socket options */ \ + u8_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +/*#define SOF_DEBUG 0x01U Unimplemented: turn on debugging info recording */ +#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */ +#define SOF_LINGER 0x80U /* linger on close if data present */ +/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */ +/*#define SOF_REUSEPORT 0x0200U Unimplemented: allow local address & port reuse */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/) + +/* Global variables of this module, kept in a struct for efficient access using base+index. */ +struct ip_globals +{ + /** The interface that provided the packet for the current callback invocation. */ + struct netif *current_netif; + /** Header of the input packet currently being processed. */ + const struct ip_hdr *current_ip4_header; +#if LWIP_IPV6 + /** Header of the input IPv6 packet currently being processed. */ + const struct ip6_hdr *current_ip6_header; +#endif /* LWIP_IPV6 */ + /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */ + u16_t current_ip_header_tot_len; + /** Source IP address of current_header */ + ipX_addr_t current_iphdr_src; + /** Destination IP address of current_header */ + ipX_addr_t current_iphdr_dest; +}; +extern struct ip_globals ip_data; + + +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (ip_data.current_netif) +/** Get the IP header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_header() (ip_data.current_ip4_header) +/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */ +#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len) +/** Source IP address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +#if LWIP_IPV6 +/** Get the IPv6 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip6_current_header() (ip_data.current_ip6_header) +/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */ +#define ip_current_is_v6() (ip6_current_header() != NULL) +/** Source IPv6 address of current_header */ +#define ip6_current_src_addr() (ipX_2_ip6(&ip_data.current_iphdr_src)) +/** Destination IPv6 address of current_header */ +#define ip6_current_dest_addr() (ipX_2_ip6(&ip_data.current_iphdr_dest)) +/** Get the transport layer protocol */ +#define ip_current_header_proto() (ip_current_is_v6() ? \ + IP6H_NEXTH(ip6_current_header()) :\ + IPH_PROTO(ip_current_header())) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((ip_current_is_v6() ? \ + (u8_t*)ip6_current_header() : (u8_t*)ip_current_header()) + ip_current_header_tot_len())) + +/** Set an IP_PCB to IPv6 (IPv4 is the default) */ +#define ip_set_v6(pcb, val) do{if(pcb != NULL) { pcb->isipv6 = val; }}while(0) + +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src)) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest)) + +#else /* LWIP_IPV6 */ + +/** Always returns FALSE when only supporting IPv4 */ +#define ip_current_is_v6() 0 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IPH_PROTO(ip_current_header()) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len())) +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +#endif /* LWIP_IPV6 */ + +/** Union source address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Union destination address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) + +#if LWIP_IPV6 +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ((isipv6) ? \ + ip6_output(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto) : \ + ip_output(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto)) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ((isipv6) ? \ + ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip_output_if(p, (src), (dest), ttl, tos, proto, netif)) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ((isipv6) ? \ + ip6_output_hinted(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto, addr_hint) : \ + ip_output_hinted(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto, addr_hint)) +#define ipX_route(isipv6, src, dest) \ + ((isipv6) ? \ + ip6_route(ipX_2_ip6(src), ipX_2_ip6(dest)) : \ + ip_route(ipX_2_ip(dest))) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ((isipv6) ? \ + ip6_netif_get_local_ipX(netif, ipX_2_ip6(dest)) : \ + ip_netif_get_local_ipX(netif)) +#define ipX_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip_debug_print(p)) +#else /* LWIP_IPV6 */ +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ip_output(p, src, dest, ttl, tos, proto) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ip_output_if(p, src, dest, ttl, tos, proto, netif) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ipX_route(isipv6, src, dest) \ + ip_route(ipX_2_ip(dest)) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ip_netif_get_local_ipX(netif) +#define ipX_debug_print(is_ipv6, p) ip_debug_print(p) +#endif /* LWIP_IPV6 */ + +#define ipX_route_get_local_ipX(isipv6, src, dest, netif, ipXaddr) do { \ + (netif) = ipX_route(isipv6, src, dest); \ + (ipXaddr) = ipX_netif_get_local_ipX(isipv6, netif, dest); \ +}while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ -#include "ipv4/lwip/ip.h" -#endif /* __MINIX_IP_H__ */ diff --git a/lib/liblwip/include/lwip/ip_addr.h b/lib/liblwip/include/lwip/ip_addr.h index 4963cbe25..7bd03cbd0 100644 --- a/lib/liblwip/include/lwip/ip_addr.h +++ b/lib/liblwip/include/lwip/ip_addr.h @@ -1,6 +1,130 @@ -#ifndef __MINIX_IPADDR_H__ -#define __MINIX_IPADDR_H__ +/* + * Copyright (c) 2001-2004 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 + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ -#include "ipv4/lwip/ip_addr.h" +#include "lwip/opt.h" +#include "lwip/def.h" -#endif /* __MINIX_IPADDR_H__ */ +#include "lwip/ip4_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_IPV6 +/* A union struct for both IP version's addresses. */ +typedef union { + ip_addr_t ip4; + ip6_addr_t ip6; +} ipX_addr_t; + +/** These functions only exist for type-safe conversion from ip_addr_t to + ip6_addr_t and back */ +#ifdef LWIP_ALLOW_STATIC_FN_IN_HEADER +static ip6_addr_t* ip_2_ip6(ip_addr_t *ipaddr) +{ return (ip6_addr_t*)ipaddr;} +static ip_addr_t* ip6_2_ip(ip6_addr_t *ip6addr) +{ return (ip_addr_t*)ip6addr; } +static ipX_addr_t* ip_2_ipX(ip_addr_t *ipaddr) +{ return (ipX_addr_t*)ipaddr; } +static ipX_addr_t* ip6_2_ipX(ip6_addr_t *ip6addr) +{ return (ipX_addr_t*)ip6addr; } +#else /* LWIP_ALLOW_STATIC_FN_IN_HEADER */ +#define ip_2_ip6(ipaddr) ((ip6_addr_t*)(ipaddr)) +#define ip6_2_ip(ip6addr) ((ip_addr_t*)(ip6addr)) +#define ip_2_ipX(ipaddr) ((ipX_addr_t*)ipaddr) +#define ip6_2_ipX(ip6addr) ((ipX_addr_t*)ip6addr) +#endif /* LWIP_ALLOW_STATIC_FN_IN_HEADER*/ +#define ipX_2_ip6(ip6addr) (&((ip6addr)->ip6)) +#define ipX_2_ip(ipaddr) (&((ipaddr)->ip4)) + +#define ipX_addr_copy(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_copy((dest).ip6, (src).ip6); }else{ \ + ip_addr_copy((dest).ip4, (src).ip4); }}while(0) +#define ipX_addr_set(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ipX_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), ipX_2_ip(src)); }}while(0) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), src); }}while(0) +#define ipX_addr_set_zero(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_zero(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_zero(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_any(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_loopback(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_hton(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set_hton(ipX_2_ip6(ipaddr), (src)) ;}else{ \ + ip_addr_set_hton(ipX_2_ip(ipaddr), (src));}}while(0) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ((is_ipv6) ? \ + ip6_addr_cmp(ipX_2_ip6(addr1), ipX_2_ip6(addr2)) : \ + ip_addr_cmp(ipX_2_ip(addr1), ipX_2_ip(addr2))) +#define ipX_addr_isany(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_isany(ipX_2_ip6(ipaddr)) : \ + ip_addr_isany(ipX_2_ip(ipaddr))) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_ismulticast(ipX_2_ip6(ipaddr)) : \ + ip_addr_ismulticast(ipX_2_ip(ipaddr))) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) do { if(is_ipv6) { \ + ip6_addr_debug_print(debug, ipX_2_ip6(ipaddr)); } else { \ + ip_addr_debug_print(debug, ipX_2_ip(ipaddr)); }}while(0) + +#else /* LWIP_IPV6 */ + +typedef ip_addr_t ipX_addr_t; +#define ipX_2_ip(ipaddr) (ipaddr) +#define ip_2_ipX(ipaddr) (ipaddr) + +#define ipX_addr_copy(is_ipv6, dest, src) ip_addr_copy(dest, src) +#define ipX_addr_set(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_zero(is_ipv6, ipaddr) ip_addr_set_zero(ipaddr) +#define ipX_addr_set_any(is_ipv6, ipaddr) ip_addr_set_any(ipaddr) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) ip_addr_set_loopback(ipaddr) +#define ipX_addr_set_hton(is_ipv6, dest, src) ip_addr_set_hton(dest, src) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ip_addr_cmp(addr1, addr2) +#define ipX_addr_isany(is_ipv6, ipaddr) ip_addr_isany(ipaddr) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ip_addr_ismulticast(ipaddr) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) ip_addr_debug_print(debug, ipaddr) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/lib/liblwip/include/lwip/ip_frag.h b/lib/liblwip/include/lwip/ip_frag.h deleted file mode 100644 index d30cbec66..000000000 --- a/lib/liblwip/include/lwip/ip_frag.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MINIX_FRAG_IP_FRAG_H__ -#define __MINIX_FRAG_IP_FRAG_H__ - -#include "ipv4/lwip/ip_frag.h" - -#endif /* __MINIX_FRAG_IP_FRAG_H__ */ diff --git a/lib/liblwip/include/lwip/mem.h b/lib/liblwip/include/lwip/mem.h index 67cf00528..190863e83 100644 --- a/lib/liblwip/include/lwip/mem.h +++ b/lib/liblwip/include/lwip/mem.h @@ -44,6 +44,7 @@ extern "C" { #include typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F /* aliases for C library malloc() */ #define mem_init() @@ -69,7 +70,7 @@ typedef size_t mem_size_t; /* MEM_SIZE would have to be aligned, but using 64000 here instead of * 65535 leaves some room for alignment... */ -#if MEM_SIZE > 64000l +#if MEM_SIZE > 64000L typedef u32_t mem_size_t; #define MEM_SIZE_F U32_F #else diff --git a/lib/liblwip/include/lwip/memp_std.h b/lib/liblwip/include/lwip/memp_std.h index 6ce408fb0..592a2824e 100644 --- a/lib/liblwip/include/lwip/memp_std.h +++ b/lib/liblwip/include/lwip/memp_std.h @@ -47,7 +47,7 @@ LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), #if IP_REASSEMBLY LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") #endif /* IP_REASSEMBLY */ -#if IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || LWIP_IPV6_FRAG LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF") #endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ @@ -63,9 +63,9 @@ LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), #endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ #endif /* NO_SYS==0 */ -#if ARP_QUEUEING +#if LWIP_ARP && ARP_QUEUEING LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") -#endif /* ARP_QUEUEING */ +#endif /* LWIP_ARP && ARP_QUEUEING */ #if LWIP_IGMP LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") @@ -91,6 +91,19 @@ LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") #endif /* PPP_SUPPORT && PPPOE_SUPPORT */ +#if LWIP_IPV6 && LWIP_ND6_QUEUEING +LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE") +#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */ + +#if LWIP_IPV6 && LWIP_IPV6_REASS +LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA") +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP") +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + /* * A list of pools of pbuf's used by LWIP. * diff --git a/lib/liblwip/include/lwip/netbuf.h b/lib/liblwip/include/lwip/netbuf.h index 7d247d71b..d12fe2709 100644 --- a/lib/liblwip/include/lwip/netbuf.h +++ b/lib/liblwip/include/lwip/netbuf.h @@ -35,6 +35,7 @@ #include "lwip/opt.h" #include "lwip/pbuf.h" #include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" #ifdef __cplusplus extern "C" { @@ -47,7 +48,7 @@ extern "C" { struct netbuf { struct pbuf *p, *ptr; - ip_addr_t addr; + ipX_addr_t addr; u16_t port; #if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY #if LWIP_CHECKSUM_ON_COPY @@ -55,7 +56,7 @@ struct netbuf { #endif /* LWIP_CHECKSUM_ON_COPY */ u16_t toport_chksum; #if LWIP_NETBUF_RECVINFO - ip_addr_t toaddr; + ipX_addr_t toaddr; #endif /* LWIP_NETBUF_RECVINFO */ #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ }; @@ -81,12 +82,12 @@ void netbuf_first (struct netbuf *buf); #define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) #define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) #define netbuf_len(buf) ((buf)->p->tot_len) -#define netbuf_fromaddr(buf) (&((buf)->addr)) -#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set((&(buf)->addr), fromaddr) +#define netbuf_fromaddr(buf) (ipX_2_ip(&((buf)->addr))) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((buf)->addr)), fromaddr) #define netbuf_fromport(buf) ((buf)->port) #if LWIP_NETBUF_RECVINFO -#define netbuf_destaddr(buf) (&((buf)->toaddr)) -#define netbuf_set_destaddr(buf, destaddr) ip_addr_set((&(buf)->addr), destaddr) +#define netbuf_destaddr(buf) (ipX_2_ip(&((buf)->toaddr))) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(ipX_2_ip(&((buf)->toaddr)), destaddr) #define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0) #endif /* LWIP_NETBUF_RECVINFO */ #if LWIP_CHECKSUM_ON_COPY @@ -94,6 +95,16 @@ void netbuf_first (struct netbuf *buf); (buf)->toport_chksum = chksum; } while(0) #endif /* LWIP_CHECKSUM_ON_COPY */ +#if LWIP_IPV6 +#define netbuf_fromaddr_ip6(buf) (ipX_2_ip6(&((buf)->addr))) +#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set(ipX_2_ip6(&((buf)->addr)), fromaddr) +#define netbuf_destaddr_ip6(buf) (ipX_2_ip6(&((buf)->toaddr))) +#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set(ipX_2_ip6(&((buf)->toaddr)), destaddr) +#endif /* LWIP_IPV6 */ + +#define netbuf_fromaddr_ipX(buf) (&((buf)->addr)) +#define netbuf_destaddr_ipX(buf) (&((buf)->toaddr)) + #ifdef __cplusplus } #endif diff --git a/lib/liblwip/include/lwip/netdb.h b/lib/liblwip/include/lwip/netdb.h index 54a1eb878..7587e2f2d 100644 --- a/lib/liblwip/include/lwip/netdb.h +++ b/lib/liblwip/include/lwip/netdb.h @@ -38,6 +38,10 @@ #include "lwip/inet.h" #include "lwip/sockets.h" +#ifdef __cplusplus +extern "C" { +#endif + /* some rarely used options */ #ifndef LWIP_DNS_API_DECLARE_H_ERRNO #define LWIP_DNS_API_DECLARE_H_ERRNO 1 @@ -111,6 +115,10 @@ int lwip_getaddrinfo(const char *nodename, lwip_getaddrinfo(nodname, servname, hints, res) #endif /* LWIP_COMPAT_SOCKETS */ +#ifdef __cplusplus +} +#endif + #endif /* LWIP_DNS && LWIP_SOCKET */ #endif /* __LWIP_NETDB_H__ */ diff --git a/lib/liblwip/include/lwip/netif.h b/lib/liblwip/include/lwip/netif.h index a8790b5f6..322d2d0ed 100644 --- a/lib/liblwip/include/lwip/netif.h +++ b/lib/liblwip/include/lwip/netif.h @@ -39,6 +39,7 @@ #include "lwip/err.h" #include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" #include "lwip/def.h" #include "lwip/pbuf.h" @@ -48,6 +49,9 @@ struct dhcp; #if LWIP_AUTOIP struct autoip; #endif +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ #ifdef __cplusplus extern "C" { @@ -117,6 +121,18 @@ typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp); */ typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr); +#if LWIP_IPV6 +/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'nd_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IPv6 address to which the packet shall be sent + */ +typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p, + ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ /** Function prototype for netif->linkoutput functions. Only used for ethernet * netifs. This function is called by ARP when a packet shall be sent. * @@ -129,6 +145,11 @@ typedef void (*netif_status_callback_fn)(struct netif *netif); /** Function prototype for netif igmp_mac_filter functions */ typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, ip_addr_t *group, u8_t action); +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** Function prototype for netif mld_mac_filter functions */ +typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, + ip6_addr_t *group, u8_t action); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ /** Generic data structure used for all lwIP network interfaces. * The following fields should be filled in by the initialization @@ -142,6 +163,13 @@ struct netif { ip_addr_t netmask; ip_addr_t gw; +#if LWIP_IPV6 + /** Array of IPv6 addresses for this netif. */ + ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + /** The state of each IPv6 address (Tentative, Preferred, etc). + * @see ip6_addr.h */ + u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#endif /* LWIP_IPV6 */ /** This function is called by the network device driver * to pass a packet up the TCP/IP stack. */ netif_input_fn input; @@ -153,6 +181,12 @@ struct netif { * to send a packet on the interface. This function outputs * the pbuf as-is on the link medium. */ netif_linkoutput_fn linkoutput; +#if LWIP_IPV6 + /** This function is called by the IPv6 module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_ip6_fn output_ip6; +#endif /* LWIP_IPV6 */ #if LWIP_NETIF_STATUS_CALLBACK /** This function is called when the netif state is set to up or down */ @@ -163,6 +197,10 @@ struct netif { */ netif_status_callback_fn link_callback; #endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ /** This field can be set by the device driver and could point * to state information for the device. */ void *state; @@ -174,6 +212,18 @@ struct netif { /** the AutoIP client state information for this netif */ struct autoip *autoip; #endif +#if LWIP_IPV6_AUTOCONFIG + /** is this netif enabled for IPv6 autoconfiguration */ + u8_t ip6_autoconfig_enabled; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /** Number of Router Solicitation messages that remain to be sent. */ + u8_t rs_count; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /** the DHCPv6 client state information for this netif */ + struct dhcp6 *dhcp6; +#endif /* LWIP_IPV6_DHCP6 */ #if LWIP_NETIF_HOSTNAME /* the hostname for this netif, NULL is a valid value */ char* hostname; @@ -208,10 +258,15 @@ struct netif { u32_t ifoutdiscards; #endif /* LWIP_SNMP */ #if LWIP_IGMP - /** This function could be called to add or delete a entry in the multicast + /** This function could be called to add or delete an entry in the multicast filter table of the ethernet MAC.*/ netif_igmp_mac_filter_fn igmp_mac_filter; #endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /** This function could be called to add or delete an entry in the IPv6 multicast + filter table of the ethernet MAC. */ + netif_mld_mac_filter_fn mld_mac_filter; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ #if LWIP_NETIF_HWADDRHINT u8_t *addr_hint; #endif /* LWIP_NETIF_HWADDRHINT */ @@ -280,6 +335,9 @@ void netif_set_down(struct netif *netif); #if LWIP_NETIF_STATUS_CALLBACK void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback); #endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ void netif_set_link_up(struct netif *netif); void netif_set_link_down(struct netif *netif); @@ -308,6 +366,20 @@ void netif_poll_all(void); #endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ #endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6 +#define netif_ip6_addr(netif, i) (&((netif)->ip6_addr[(i)])) +#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[(i)]) +#define netif_ip6_addr_set_state(netif, i, state) ((netif)->ip6_addr_state[(i)] = (state)) +s8_t netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr); +void netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit); +#endif /* LWIP_IPV6 */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + #ifdef __cplusplus } #endif diff --git a/lib/liblwip/include/lwip/opt.h b/lib/liblwip/include/lwip/opt.h index c5e3967e8..e51f8e556 100644 --- a/lib/liblwip/include/lwip/opt.h +++ b/lib/liblwip/include/lwip/opt.h @@ -309,9 +309,11 @@ /** * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. * (requires NO_SYS==0) + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. */ #ifndef MEMP_NUM_SYS_TIMEOUT -#define MEMP_NUM_SYS_TIMEOUT 3 +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) #endif /** @@ -432,11 +434,14 @@ #endif /** - * ARP_QUEUEING==1: Outgoing packets are queued during hardware address - * resolution. + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. */ #ifndef ARP_QUEUEING -#define ARP_QUEUEING 1 +#define ARP_QUEUEING 0 #endif /** @@ -458,6 +463,8 @@ * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. */ #ifndef ETHARP_SUPPORT_VLAN #define ETHARP_SUPPORT_VLAN 0 @@ -590,6 +597,26 @@ #define IP_SOF_BROADCAST_RECV 0 #endif +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0 +#endif + /* ---------------------------------- ---------- ICMP options ---------- @@ -943,10 +970,11 @@ /** - * TCP_SND_BUF: TCP sender buffer space (bytes). + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. */ #ifndef TCP_SND_BUF -#define TCP_SND_BUF 256 +#define TCP_SND_BUF (2 * TCP_MSS) #endif /** @@ -954,7 +982,7 @@ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ #ifndef TCP_SND_QUEUELEN -#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF)/(TCP_MSS)) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) #endif /** @@ -963,16 +991,32 @@ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). */ #ifndef TCP_SNDLOWAT -#define TCP_SNDLOWAT ((TCP_SND_BUF)/2) +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) #endif /** - * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below * this number, select returns writable (combined with TCP_SNDLOWAT). */ #ifndef TCP_SNDQUEUELOWAT -#define TCP_SNDQUEUELOWAT ((TCP_SND_QUEUELEN)/2) +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#endif + +/** + * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 0 #endif /** @@ -1029,14 +1073,11 @@ * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all * events (accept, sent, etc) that happen in the system. * LWIP_CALLBACK_API==1: The PCB callback function is called directly - * for the event. + * for the event. This is the default. */ -#ifndef LWIP_EVENT_API +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) #define LWIP_EVENT_API 0 #define LWIP_CALLBACK_API 1 -#else -#define LWIP_EVENT_API 1 -#define LWIP_CALLBACK_API 0 #endif @@ -1099,6 +1140,14 @@ #define LWIP_NETIF_LINK_CALLBACK 0 #endif +/** + * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#ifndef LWIP_NETIF_REMOVE_CALLBACK +#define LWIP_NETIF_REMOVE_CALLBACK 0 +#endif + /** * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table * indices) in struct netif. TCP and UDP can make use of this to prevent @@ -1404,7 +1453,16 @@ #endif /** - * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing. + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. */ #ifndef LWIP_SO_RCVTIMEO #define LWIP_SO_RCVTIMEO 0 @@ -1440,6 +1498,18 @@ #define SO_REUSE_RXTOALL 0 #endif +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#ifndef LWIP_FIONREAD_LINUXMODE +#define LWIP_FIONREAD_LINUXMODE 0 +#endif + /* ---------------------------------------- ---------- Statistics options ---------- @@ -1541,6 +1611,41 @@ #define SYS_STATS (NO_SYS == 0) #endif +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#ifndef IP6_STATS +#define IP6_STATS (LWIP_IPV6) +#endif + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#ifndef ICMP6_STATS +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) +#endif + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#ifndef IP6_FRAG_STATS +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) +#endif + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#ifndef MLD6_STATS +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) +#endif + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#ifndef ND6_STATS +#define ND6_STATS (LWIP_IPV6) +#endif + #else #define LINK_STATS 0 @@ -1554,6 +1659,11 @@ #define MEMP_STATS 0 #define SYS_STATS 0 #define LWIP_STATS_DISPLAY 0 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 0 #endif /* LWIP_STATS */ @@ -1746,6 +1856,13 @@ #ifndef CHECKSUM_GEN_TCP #define CHECKSUM_GEN_TCP 1 #endif + +/** + * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#ifndef CHECKSUM_GEN_ICMP +#define CHECKSUM_GEN_ICMP 1 +#endif /** * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. @@ -1776,6 +1893,259 @@ #define LWIP_CHECKSUM_ON_COPY 0 #endif +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#ifndef LWIP_IPV6 +#define LWIP_IPV6 0 +#endif + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#ifndef LWIP_IPV6_NUM_ADDRESSES +#define LWIP_IPV6_NUM_ADDRESSES 3 +#endif + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#ifndef LWIP_IPV6_FORWARD +#define LWIP_IPV6_FORWARD 0 +#endif + +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#ifndef LWIP_ICMP6 +#define LWIP_ICMP6 (LWIP_IPV6) +#endif + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#ifndef LWIP_ICMP6_HL +#define LWIP_ICMP6_HL 255 +#endif + +/** + * LWIP_ICMP6_CHECKSUM_CHECK==1: verify checksum on ICMPv6 packets + */ +#ifndef LWIP_ICMP6_CHECKSUM_CHECK +#define LWIP_ICMP6_CHECKSUM_CHECK 1 +#endif + +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + */ +#ifndef LWIP_IPV6_MLD +#define LWIP_IPV6_MLD (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined. + */ +#ifndef MEMP_NUM_MLD6_GROUP +#define MEMP_NUM_MLD6_GROUP 4 +#endif + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#ifndef LWIP_IPV6_FRAG +#define LWIP_IPV6_FRAG 0 +#endif + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#ifndef LWIP_IPV6_REASS +#define LWIP_IPV6_REASS (LWIP_IPV6) +#endif + +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#ifndef LWIP_ND6_QUEUEING +#define LWIP_ND6_QUEUEING (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#ifndef MEMP_NUM_ND6_QUEUE +#define MEMP_NUM_ND6_QUEUE 20 +#endif + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#ifndef LWIP_ND6_NUM_NEIGHBORS +#define LWIP_ND6_NUM_NEIGHBORS 10 +#endif + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#ifndef LWIP_ND6_NUM_DESTINATIONS +#define LWIP_ND6_NUM_DESTINATIONS 10 +#endif + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#ifndef LWIP_ND6_NUM_PREFIXES +#define LWIP_ND6_NUM_PREFIXES 5 +#endif + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#ifndef LWIP_ND6_NUM_ROUTERS +#define LWIP_ND6_NUM_ROUTERS 3 +#endif + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 +#endif + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 +#endif + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 +#endif + +/** + * Unused: See ND RFC + */ +#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 +#endif + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#ifndef LWIP_ND6_REACHABLE_TIME +#define LWIP_ND6_REACHABLE_TIME 30000 +#endif + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#ifndef LWIP_ND6_RETRANS_TIMER +#define LWIP_ND6_RETRANS_TIMER 1000 +#endif + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 +#endif + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#ifndef LWIP_ND6_ALLOW_RA_UPDATES +#define LWIP_ND6_ALLOW_RA_UPDATES 1 +#endif + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#ifndef LWIP_IPV6_SEND_ROUTER_SOLICIT +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#endif + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#ifndef LWIP_ND6_TCP_REACHABILITY_HINTS +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 +#endif + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#ifndef LWIP_IPV6_AUTOCONFIG +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS: Number of duplicate address detection attempts. + */ +#ifndef LWIP_IPV6_DUP_DETECT_ATTEMPTS +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +#endif + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#ifndef LWIP_IPV6_DHCP6 +#define LWIP_IPV6_DHCP6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* Hooks are undefined by default, define them to a function if you need them. */ + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ + /* --------------------------------------- ---------- Debugging options ---------- @@ -2037,4 +2407,11 @@ #define DNS_DEBUG LWIP_DBG_OFF #endif +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#ifndef IP6_DEBUG +#define IP6_DEBUG LWIP_DBG_OFF +#endif + #endif /* __LWIP_OPT_H__ */ diff --git a/lib/liblwip/include/lwip/pbuf.h b/lib/liblwip/include/lwip/pbuf.h index 3b1d608b9..786b288b4 100644 --- a/lib/liblwip/include/lwip/pbuf.h +++ b/lib/liblwip/include/lwip/pbuf.h @@ -44,8 +44,15 @@ extern "C" { * of IP_FRAG */ #define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) +/* @todo: We need a mechanism to prevent wasting memory in every pbuf + (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ + #define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else #define PBUF_IP_HLEN 20 +#endif typedef enum { PBUF_TRANSPORT, @@ -69,6 +76,12 @@ typedef enum { #define PBUF_FLAG_IS_CUSTOM 0x02U /** indicates this pbuf is UDP multicast to be looped back */ #define PBUF_FLAG_MCASTLOOP 0x04U +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U struct pbuf { /** next pbuf in singly linked pbuf chain */ @@ -116,6 +129,24 @@ struct pbuf_custom { }; #endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +#if LWIP_TCP && TCP_QUEUE_OOSEQ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ +#if NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/ +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */ + /* Initializes the pbuf module. This call is empty for now, but may not be in future. */ #define pbuf_init() diff --git a/lib/liblwip/include/lwip/raw.h b/lib/liblwip/include/lwip/raw.h index 17d0a1c54..f0c8ed475 100644 --- a/lib/liblwip/include/lwip/raw.h +++ b/lib/liblwip/include/lwip/raw.h @@ -40,6 +40,7 @@ #include "lwip/def.h" #include "lwip/ip.h" #include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" #ifdef __cplusplus extern "C" { @@ -60,6 +61,27 @@ struct raw_pcb; typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr); +#if LWIP_IPV6 +/** Function prototype for raw pcb IPv6 receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_ip6_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define RAW_PCB_RECV_IP6 raw_recv_ip6_fn ip6; +#else +#define RAW_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + struct raw_pcb { /* Common members of all PCB types */ IP_PCB; @@ -69,7 +91,10 @@ struct raw_pcb { u8_t protocol; /** receive callback function */ - raw_recv_fn recv; + union { + raw_recv_fn ip4; + RAW_PCB_RECV_IP6 + } recv; /* user-supplied argument for the recv callback */ void *recv_arg; }; @@ -85,6 +110,14 @@ void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *re err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr); err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); +#if LWIP_IPV6 +struct raw_pcb * raw_new_ip6 (u8_t proto); +#define raw_bind_ip6(pcb, ip6addr) raw_bind(pcb, ip6_2_ip(ip6addr)) +#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, ip6_2_ip(ip6addr)) +#define raw_recv_ip6(pcb, recv_ip6_fn, recv_arg) raw_recv(pcb, (raw_recv_fn)recv_ip6_fn, recv_arg) +#define raw_sendto_ip6(pcb, pbuf, ip6addr) raw_sendto(pcb, pbuf, ip6_2_ip(ip6addr)) +#endif /* LWIP_IPV6 */ + /* The following functions are the lower layer interface to RAW. */ u8_t raw_input (struct pbuf *p, struct netif *inp); #define raw_init() /* Compatibility define, not init needed. */ diff --git a/lib/liblwip/include/lwip/sockets.h b/lib/liblwip/include/lwip/sockets.h index ccc012dbd..73461374b 100644 --- a/lib/liblwip/include/lwip/sockets.h +++ b/lib/liblwip/include/lwip/sockets.h @@ -42,6 +42,7 @@ #include "lwip/ip_addr.h" #include "lwip/inet.h" +#include "lwip/inet6.h" #ifdef __cplusplus extern "C" { @@ -53,17 +54,34 @@ struct sockaddr_in { u8_t sin_family; u16_t sin_port; struct in_addr sin_addr; - char sin_zero[8]; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; }; +#if LWIP_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + u8_t sin6_family; /* AF_INET6 */ + u16_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ +}; +#endif /* LWIP_IPV6 */ + struct sockaddr { u8_t sa_len; u8_t sa_family; - u16_t sa_data[14]; +#if LWIP_IPV6 + u8_t sa_data[22]; +#else /* LWIP_IPV6 */ + u8_t sa_data[14]; +#endif /* LWIP_IPV6 */ }; -#ifndef socklen_t -# define socklen_t u32_t +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; #endif /* Socket protocol types (TCP/UDP/RAW) */ @@ -118,12 +136,21 @@ struct linger { #define AF_UNSPEC 0 #define AF_INET 2 +#if LWIP_IPV6 +#define AF_INET6 10 +#else /* LWIP_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* LWIP_IPV6 */ #define PF_INET AF_INET +#define PF_INET6 AF_INET6 #define PF_UNSPEC AF_UNSPEC #define IPPROTO_IP 0 #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 +#if LWIP_IPV6 +#define IPPROTO_IPV6 41 +#endif /* LWIP_IPV6 */ #define IPPROTO_UDPLITE 136 /* Flags we can use with send and recv. */ @@ -151,6 +178,13 @@ struct linger { #define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ #endif /* LWIP_TCP */ +#if LWIP_IPV6 +/* + * Options for level IPPROTO_IPV6 + */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* LWIP_IPV6 */ + #if LWIP_UDP && LWIP_UDPLITE /* * Options for level IPPROTO_UDPLITE @@ -280,9 +314,9 @@ typedef struct ip_mreq { #endif #ifndef SHUT_RD - #define SHUT_RD 1 - #define SHUT_WR 2 - #define SHUT_RDWR 3 + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 #endif /* FD_SET used for lwip_select */ @@ -363,6 +397,7 @@ int lwip_fcntl(int s, int cmd, int val); #define read(a,b,c) lwip_read(a,b,c) #define write(a,b,c) lwip_write(a,b,c) #define close(s) lwip_close(s) +#define fcntl(a,b,c) lwip_fcntl(a,b,c) #endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ #endif /* LWIP_COMPAT_SOCKETS */ diff --git a/lib/liblwip/include/lwip/stats.h b/lib/liblwip/include/lwip/stats.h index 015b7ce79..d91121608 100644 --- a/lib/liblwip/include/lwip/stats.h +++ b/lib/liblwip/include/lwip/stats.h @@ -144,6 +144,21 @@ struct stats_ { #if SYS_STATS struct stats_sys sys; #endif +#if IP6_STATS + struct stats_proto ip6; +#endif +#if ICMP6_STATS + struct stats_proto icmp6; +#endif +#if IP6_FRAG_STATS + struct stats_proto ip6_frag; +#endif +#if MLD6_STATS + struct stats_igmp mld6; +#endif +#if ND6_STATS + struct stats_proto nd6; +#endif }; extern struct stats_ lwip_stats; @@ -190,7 +205,7 @@ void stats_init(void); #if IGMP_STATS #define IGMP_STATS_INC(x) STATS_INC(x) -#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP") #else #define IGMP_STATS_INC(x) #define IGMP_STATS_DISPLAY() @@ -268,18 +283,58 @@ void stats_init(void); #define SYS_STATS_DISPLAY() #endif +#if IP6_STATS +#define IP6_STATS_INC(x) STATS_INC(x) +#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6") +#else +#define IP6_STATS_INC(x) +#define IP6_STATS_DISPLAY() +#endif + +#if ICMP6_STATS +#define ICMP6_STATS_INC(x) STATS_INC(x) +#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6") +#else +#define ICMP6_STATS_INC(x) +#define ICMP6_STATS_DISPLAY() +#endif + +#if IP6_FRAG_STATS +#define IP6_FRAG_STATS_INC(x) STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG") +#else +#define IP6_FRAG_STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() +#endif + +#if MLD6_STATS +#define MLD6_STATS_INC(x) STATS_INC(x) +#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1") +#else +#define MLD6_STATS_INC(x) +#define MLD6_STATS_DISPLAY() +#endif + +#if ND6_STATS +#define ND6_STATS_INC(x) STATS_INC(x) +#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND") +#else +#define ND6_STATS_INC(x) +#define ND6_STATS_DISPLAY() +#endif + /* Display of statistics */ #if LWIP_STATS_DISPLAY void stats_display(void); -void stats_display_proto(struct stats_proto *proto, char *name); -void stats_display_igmp(struct stats_igmp *igmp); -void stats_display_mem(struct stats_mem *mem, char *name); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp, const char *name); +void stats_display_mem(struct stats_mem *mem, const char *name); void stats_display_memp(struct stats_mem *mem, int index); void stats_display_sys(struct stats_sys *sys); #else /* LWIP_STATS_DISPLAY */ #define stats_display() #define stats_display_proto(proto, name) -#define stats_display_igmp(igmp) +#define stats_display_igmp(igmp, name) #define stats_display_mem(mem, name) #define stats_display_memp(mem, index) #define stats_display_sys(sys) diff --git a/lib/liblwip/include/lwip/sys.h b/lib/liblwip/include/lwip/sys.h index 9f62c7540..dc9351335 100644 --- a/lib/liblwip/include/lwip/sys.h +++ b/lib/liblwip/include/lwip/sys.h @@ -51,16 +51,22 @@ typedef u8_t sys_mbox_t; #define sys_sem_wait(s) #define sys_arch_sem_wait(s,t) #define sys_sem_free(s) +#define sys_sem_valid(s) 0 +#define sys_sem_set_invalid(s) #define sys_mutex_new(mu) ERR_OK #define sys_mutex_lock(mu) #define sys_mutex_unlock(mu) #define sys_mutex_free(mu) +#define sys_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(mu) #define sys_mbox_new(m, s) ERR_OK #define sys_mbox_fetch(m,d) #define sys_mbox_tryfetch(m,d) #define sys_mbox_post(m,d) #define sys_mbox_trypost(m,d) #define sys_mbox_free(m) +#define sys_mbox_valid(m) +#define sys_mbox_set_invalid(m) #define sys_thread_new(n,t,a,s,p) diff --git a/lib/liblwip/include/lwip/tcp.h b/lib/liblwip/include/lwip/tcp.h index a09c5ef7d..0ab301f0e 100644 --- a/lib/liblwip/include/lwip/tcp.h +++ b/lib/liblwip/include/lwip/tcp.h @@ -36,12 +36,13 @@ #if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ -#include "lwip/sys.h" #include "lwip/mem.h" #include "lwip/pbuf.h" #include "lwip/ip.h" #include "lwip/icmp.h" #include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" #ifdef __cplusplus extern "C" { @@ -156,11 +157,11 @@ enum tcp_state { */ #define TCP_PCB_COMMON(type) \ type *next; /* for the linked list */ \ - enum tcp_state state; /* TCP state */ \ - u8_t prio; \ void *callback_arg; \ /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ DEF_ACCEPT_CALLBACK \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ /* ports are in host byte order */ \ u16_t local_port @@ -187,21 +188,23 @@ struct tcp_pcb { /* the rest of the fields are in host byte order as we have to do some math with them */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + /* receiver variables */ u32_t rcv_nxt; /* next seqno expected */ u16_t rcv_wnd; /* receiver window available */ u16_t rcv_ann_wnd; /* receiver window to announce */ u32_t rcv_ann_right_edge; /* announced right edge of window */ - /* Timers */ - u32_t tmr; - u8_t polltmr, pollinterval; - /* Retransmission timer. */ s16_t rtime; - + u16_t mss; /* maximum segment size */ - + /* RTT (round trip time) estimation variables */ u32_t rttest; /* RTT estimate in 500ms ticks */ u32_t rtseq; /* sequence number being timed */ @@ -211,24 +214,25 @@ struct tcp_pcb { u8_t nrtx; /* number of retransmissions */ /* fast retransmit/recovery */ - u32_t lastack; /* Highest acknowledged seqno. */ u8_t dupacks; - + u32_t lastack; /* Highest acknowledged seqno. */ + /* congestion avoidance/control variables */ - u16_t cwnd; + u16_t cwnd; u16_t ssthresh; /* sender variables */ u32_t snd_nxt; /* next new seqno to be sent */ - u16_t snd_wnd; /* sender window */ u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last window update. */ u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ + u16_t snd_wnd; /* sender window */ + u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */ u16_t acked; - + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ -#define TCP_SNDQUEUELEN_OVERFLOW (0xffff-3) +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ #if TCP_OVERSIZE @@ -271,7 +275,7 @@ struct tcp_pcb { #endif /* LWIP_TCP_KEEPALIVE */ /* Persist timer counter */ - u32_t persist_cnt; + u8_t persist_cnt; /* Persist timer back-off */ u8_t persist_backoff; @@ -279,7 +283,7 @@ struct tcp_pcb { u8_t keep_cnt_sent; }; -struct tcp_pcb_listen { +struct tcp_pcb_listen { /* Common members of all PCB types */ IP_PCB; /* Protocol specific PCB members */ @@ -289,6 +293,9 @@ struct tcp_pcb_listen { u8_t backlog; u8_t accepts_pending; #endif /* TCP_LISTEN_BACKLOG */ +#if LWIP_IPV6 + u8_t accept_any_ip_version; +#endif /* LWIP_IPV6 */ }; #if LWIP_EVENT_API @@ -333,7 +340,7 @@ void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0) #else /* TCP_LISTEN_BACKLOG */ #define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \ - pcb->state == LISTEN) + (pcb)->state == LISTEN) #endif /* TCP_LISTEN_BACKLOG */ void tcp_recved (struct tcp_pcb *pcb, u16_t len); @@ -367,6 +374,19 @@ err_t tcp_output (struct tcp_pcb *pcb); const char* tcp_debug_state_str(enum tcp_state s); +#if LWIP_IPV6 +struct tcp_pcb * tcp_new_ip6 (void); +#define tcp_bind_ip6(pcb, ip6addr, port) \ + tcp_bind(pcb, ip6_2_ip(ip6addr), port) +#define tcp_connect_ip6(pcb, ip6addr, port, connected) \ + tcp_connect(pcb, ip6_2_ip(ip6addr), port, connected) +struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen_dual(pcb) tcp_listen_dual_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) +#else /* LWIP_IPV6 */ +#define tcp_listen_dual_with_backlog(pcb, backlog) tcp_listen_with_backlog(pcb, backlog) +#define tcp_listen_dual(pcb) tcp_listen(pcb) +#endif /* LWIP_IPV6 */ + #ifdef __cplusplus } diff --git a/lib/liblwip/include/lwip/tcp_impl.h b/lib/liblwip/include/lwip/tcp_impl.h index 320b88ef2..2afc20d37 100644 --- a/lib/liblwip/include/lwip/tcp_impl.h +++ b/lib/liblwip/include/lwip/tcp_impl.h @@ -37,12 +37,13 @@ #if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ #include "lwip/tcp.h" -#include "lwip/sys.h" #include "lwip/mem.h" #include "lwip/pbuf.h" #include "lwip/ip.h" #include "lwip/icmp.h" #include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" #ifdef __cplusplus extern "C" { @@ -51,7 +52,7 @@ extern "C" { /* Functions for interfacing with TCP: */ /* Lower layer interface to TCP: */ -#define tcp_init() /* Compatibility define, no init needed. */ +void tcp_init (void); /* Initialize this module. */ void tcp_tmr (void); /* Must be called every TCP_TMR_INTERVAL ms. (Typically 250 ms). */ @@ -71,6 +72,7 @@ void tcp_rexmit (struct tcp_pcb *pcb); void tcp_rexmit_rto (struct tcp_pcb *pcb); void tcp_rexmit_fast (struct tcp_pcb *pcb); u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); /** * This is the Nagle algorithm: try to combine user data to send as few TCP @@ -84,15 +86,16 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); #define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ - ((tpcb)->unsent->len >= (tpcb)->mss))) \ + ((tpcb)->unsent->len >= (tpcb)->mss))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ ) ? 1 : 0) #define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) -#define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0) -#define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0) -#define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0) -#define TCP_SEQ_GEQ(a,b) ((s32_t)((a)-(b)) >= 0) +#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0) /* is b<=a<=c? */ #if 0 /* see bug #10548 */ #define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) @@ -170,11 +173,9 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif -#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8) #define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) #define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) -#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr)) #define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) #define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags)) #define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags)) @@ -278,7 +279,6 @@ PACK_STRUCT_END struct tcp_seg { struct tcp_seg *next; /* used when putting segements on a queue */ struct pbuf *p; /* buffer containing data + TCP header */ - void *dataptr; /* pointer to the TCP data in the pbuf */ u16_t len; /* the TCP length of this segment */ #if TCP_OVERSIZE_DBGCHECK u16_t oversize_left; /* Extra bytes available at the end of the last @@ -302,14 +302,12 @@ struct tcp_seg { (flags & TF_SEG_OPTS_TS ? 12 : 0) /** This returns a TCP header option for MSS in an u32_t */ -#define TCP_BUILD_MSS_OPTION(x) (x) = PP_HTONL(((u32_t)2 << 24) | \ - ((u32_t)4 << 16) | \ - (((u32_t)TCP_MSS / 256) << 8) | \ - (TCP_MSS & 255)) +#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) /* Global variables: */ extern struct tcp_pcb *tcp_input_pcb; extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; /* The TCP PCB lists. */ union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ @@ -396,6 +394,24 @@ extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */ #endif /* LWIP_DEBUG */ +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + /* Internal functions: */ struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); @@ -427,9 +443,20 @@ err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); -void tcp_rst(u32_t seqno, u32_t ackno, - ip_addr_t *local_ip, ip_addr_t *remote_ip, - u16_t local_port, u16_t remote_port); +void tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port) +#endif /* LWIP_IPV6 */ u32_t tcp_next_iss(void); @@ -437,7 +464,16 @@ void tcp_keepalive(struct tcp_pcb *pcb); void tcp_zero_window_probe(struct tcp_pcb *pcb); #if TCP_CALCULATE_EFF_SEND_MSS -u16_t tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr); +u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest, src, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest) +#endif /* LWIP_IPV6 */ #endif /* TCP_CALCULATE_EFF_SEND_MSS */ #if LWIP_CALLBACK_API diff --git a/lib/liblwip/include/lwip/tcpip.h b/lib/liblwip/include/lwip/tcpip.h index 995ba8ad0..04567f2c0 100644 --- a/lib/liblwip/include/lwip/tcpip.h +++ b/lib/liblwip/include/lwip/tcpip.h @@ -59,14 +59,29 @@ extern "C" { extern sys_mutex_t lock_tcpip_core; #define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) #define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) -#define TCPIP_APIMSG(m) tcpip_apimsg_lock(m) +#ifdef LWIP_DEBUG +#define TCIP_APIMSG_SET_ERR(m, e) (m)->msg.err = e /* catch functions that don't set err */ +#else +#define TCIP_APIMSG_SET_ERR(m, e) +#endif +#define TCPIP_APIMSG_NOERR(m,f) do { \ + TCIP_APIMSG_SET_ERR(m, ERR_VAL); \ + LOCK_TCPIP_CORE(); \ + f(&((m)->msg)); \ + UNLOCK_TCPIP_CORE(); \ +} while(0) +#define TCPIP_APIMSG(m,f,e) do { \ + TCPIP_APIMSG_NOERR(m,f); \ + (e) = (m)->msg.err; \ +} while(0) #define TCPIP_APIMSG_ACK(m) #define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) #define TCPIP_NETIFAPI_ACK(m) #else /* LWIP_TCPIP_CORE_LOCKING */ #define LOCK_TCPIP_CORE() #define UNLOCK_TCPIP_CORE() -#define TCPIP_APIMSG(m) tcpip_apimsg(m) +#define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) #define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed) #define TCPIP_NETIFAPI(m) tcpip_netifapi(m) #define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) @@ -77,13 +92,13 @@ typedef void (*tcpip_init_done_fn)(void *arg); /** Function prototype for functions passed to tcpip_callback() */ typedef void (*tcpip_callback_fn)(void *ctx); +/* Forward declarations */ +struct tcpip_callback_msg; + void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); #if LWIP_NETCONN err_t tcpip_apimsg(struct api_msg *apimsg); -#if LWIP_TCPIP_CORE_LOCKING -err_t tcpip_apimsg_lock(struct api_msg *apimsg); -#endif /* LWIP_TCPIP_CORE_LOCKING */ #endif /* LWIP_NETCONN */ err_t tcpip_input(struct pbuf *p, struct netif *inp); @@ -98,6 +113,10 @@ err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block); #define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + /* free pbufs or heap memory from another context without blocking */ err_t pbuf_free_callback(struct pbuf *p); err_t mem_free_callback(void *m); @@ -119,7 +138,8 @@ enum tcpip_msg_type { TCPIP_MSG_TIMEOUT, TCPIP_MSG_UNTIMEOUT, #endif /* LWIP_TCPIP_TIMEOUT */ - TCPIP_MSG_CALLBACK + TCPIP_MSG_CALLBACK, + TCPIP_MSG_CALLBACK_STATIC }; struct tcpip_msg { diff --git a/lib/liblwip/include/lwip/timers.h b/lib/liblwip/include/lwip/timers.h index fb92b4b47..04e78e0fe 100644 --- a/lib/liblwip/include/lwip/timers.h +++ b/lib/liblwip/include/lwip/timers.h @@ -41,7 +41,9 @@ #if LWIP_TIMERS #include "lwip/err.h" +#if !NO_SYS #include "lwip/sys.h" +#endif #ifdef __cplusplus extern "C" { diff --git a/lib/liblwip/include/lwip/udp.h b/lib/liblwip/include/lwip/udp.h index c98c1b3ec..aa1ad3fba 100644 --- a/lib/liblwip/include/lwip/udp.h +++ b/lib/liblwip/include/lwip/udp.h @@ -40,6 +40,7 @@ #include "lwip/netif.h" #include "lwip/ip_addr.h" #include "lwip/ip.h" +#include "lwip/ip6_addr.h" #ifdef __cplusplus extern "C" { @@ -87,6 +88,26 @@ struct udp_pcb; typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +#if LWIP_IPV6 +/** Function prototype for udp pcb IPv6 receive callback functions + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_ip6_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr, u16_t port); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define UDP_PCB_RECV_IP6 udp_recv_ip6_fn ip6; +#else +#define UDP_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ struct udp_pcb { /* Common members of all PCB types */ @@ -111,7 +132,10 @@ struct udp_pcb { #endif /* LWIP_UDPLITE */ /** receive callback function */ - udp_recv_fn recv; + union { + udp_recv_fn ip4; + UDP_PCB_RECV_IP6 + }recv; /** user-supplied argument for the recv callback */ void *recv_arg; }; @@ -154,7 +178,27 @@ err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, /* The following functions are the lower layer interface to UDP. */ void udp_input (struct pbuf *p, struct netif *inp); -#define udp_init() /* Compatibility define, not init needed. */ +void udp_init (void); + +#if LWIP_IPV6 +struct udp_pcb * udp_new_ip6(void); +#define udp_bind_ip6(pcb, ip6addr, port) \ + udp_bind(pcb, ip6_2_ip(ip6addr), port) +#define udp_connect_ip6(pcb, ip6addr, port) \ + udp_connect(pcb, ip6_2_ip(ip6addr), port) +#define udp_recv_ip6(pcb, recv_ip6_fn, recv_arg) \ + udp_recv(pcb, (udp_recv_fn)recv_ip6_fn, recv_arg) +#define udp_sendto_ip6(pcb, pbuf, ip6addr, port) \ + udp_sendto(pcb, pbuf, ip6_2_ip(ip6addr), port) +#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \ + udp_sendto_if(pcb, pbuf, ip6_2_ip(ip6addr), port, netif) +#if LWIP_CHECKSUM_ON_COPY +#define udp_sendto_chksum_ip6(pcb, pbuf, ip6addr, port, have_chk, chksum) \ + udp_sendto_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, have_chk, chksum) +#define udp_sendto_if_chksum_ip6(pcb, pbuf, ip6addr, port, netif, have_chk, chksum) \ + udp_sendto_if_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, netif, have_chk, chksum) +#endif /*LWIP_CHECKSUM_ON_COPY */ +#endif /* LWIP_IPV6 */ #if UDP_DEBUG void udp_debug_print(struct udp_hdr *udphdr); diff --git a/lib/liblwip/include/lwipopts.h b/lib/liblwip/include/lwipopts.h index d3d6ff0bb..2e816368b 100644 --- a/lib/liblwip/include/lwipopts.h +++ b/lib/liblwip/include/lwipopts.h @@ -235,7 +235,7 @@ * interfaces. If you are going to run lwIP on a device with only one network * interface, define this to 0. */ -#define IP_FORWARD 0 +#define IP_FORWARD 1 /** * IP_OPTIONS: Defines the behavior for IP options. diff --git a/lib/liblwip/include/netif/etharp.h b/lib/liblwip/include/netif/etharp.h index a4803ec4d..92d57ae47 100644 --- a/lib/liblwip/include/netif/etharp.h +++ b/lib/liblwip/include/netif/etharp.h @@ -94,8 +94,8 @@ PACK_STRUCT_BEGIN * if 'type' in ethernet header is ETHTYPE_VLAN. * See IEEE802.Q */ struct eth_vlan_hdr { - PACK_STRUCT_FIELD(u16_t tpid); PACK_STRUCT_FIELD(u16_t prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); } PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES @@ -134,11 +134,12 @@ PACK_STRUCT_END /** 5 seconds period */ #define ARP_TMR_INTERVAL 5000 -#define ETHTYPE_ARP 0x0806 -#define ETHTYPE_IP 0x0800 -#define ETHTYPE_VLAN 0x8100 -#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ -#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ +#define ETHTYPE_ARP 0x0806U +#define ETHTYPE_IP 0x0800U +#define ETHTYPE_VLAN 0x8100U +#define ETHTYPE_IPV6 0x86DDU +#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */ /** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables * or known to be 32-bit aligned within the protocol header. */ @@ -190,6 +191,7 @@ err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); * nodes to update an entry in their ARP cache. * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ #define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) +void etharp_cleanup_netif(struct netif *netif); #if ETHARP_SUPPORT_STATIC_ENTRIES err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr); diff --git a/lib/liblwip/include/netif/slipif.h b/lib/liblwip/include/netif/slipif.h index ccd03c8a1..7b6ce5e28 100644 --- a/lib/liblwip/include/netif/slipif.h +++ b/lib/liblwip/include/netif/slipif.h @@ -34,14 +34,44 @@ #ifndef __NETIF_SLIPIF_H__ #define __NETIF_SLIPIF_H__ +#include "lwip/opt.h" #include "lwip/netif.h" +/** Set this to 1 to start a thread that blocks reading on the serial line + * (using sio_read()). + */ +#ifndef SLIP_USE_RX_THREAD +#define SLIP_USE_RX_THREAD !NO_SYS +#endif + +/** Set this to 1 to enable functions to pass in RX bytes from ISR context. + * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled + * packets on a queue, which is fed into lwIP from slipif_poll(). + * If disabled, slipif_poll() polls the serila line (using sio_tryread()). + */ +#ifndef SLIP_RX_FROM_ISR +#define SLIP_RX_FROM_ISR 0 +#endif + +/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets + * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available. + * If disabled, packets will be dropped if more than one packet is received. + */ +#ifndef SLIP_RX_QUEUE +#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR +#endif + #ifdef __cplusplus extern "C" { #endif err_t slipif_init(struct netif * netif); void slipif_poll(struct netif *netif); +#if SLIP_RX_FROM_ISR +void slipif_process_rxqueue(struct netif *netif); +void slipif_received_byte(struct netif *netif, u8_t data); +void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len); +#endif /* SLIP_RX_FROM_ISR */ #ifdef __cplusplus } diff --git a/lib/liblwip/include/posix/netdb.h b/lib/liblwip/include/posix/netdb.h new file mode 100644 index 000000000..7134032d2 --- /dev/null +++ b/lib/liblwip/include/posix/netdb.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/netdb.h. + */ + +/* + * 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. + * + */ + +#include "lwip/netdb.h" diff --git a/lib/liblwip/include/posix/sys/socket.h b/lib/liblwip/include/posix/sys/socket.h new file mode 100644 index 000000000..f7c7066e6 --- /dev/null +++ b/lib/liblwip/include/posix/sys/socket.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/sockets.h. + */ + +/* + * 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. + * + */ + +#include "lwip/sockets.h" diff --git a/lib/liblwip/netif/etharp.c b/lib/liblwip/netif/etharp.c index edd9958ff..1b7eb9883 100644 --- a/lib/liblwip/netif/etharp.c +++ b/lib/liblwip/netif/etharp.c @@ -55,6 +55,7 @@ #include "lwip/dhcp.h" #include "lwip/autoip.h" #include "netif/etharp.h" +#include "lwip/ip6.h" #if PPPOE_SUPPORT #include "netif/ppp_oe.h" @@ -65,13 +66,22 @@ const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; const struct eth_addr ethzero = {{0,0,0,0,0,0}}; +/** The 24-bit IANA multicast OUI is 01-00-5e: */ +#define LL_MULTICAST_ADDR_0 0x01 +#define LL_MULTICAST_ADDR_1 0x00 +#define LL_MULTICAST_ADDR_2 0x5e + #if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ /** the time an ARP entry stays valid after its last update, * for ARP_TMR_INTERVAL = 5000, this is * (240 * 5) seconds = 20 minutes. */ -#define ARP_MAXAGE 240 +#define ARP_MAXAGE 240 +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12) + /** the time an ARP entry stays pending after first request, * for ARP_TMR_INTERVAL = 5000, this is * (2 * 5) seconds = 10 seconds. @@ -86,24 +96,26 @@ const struct eth_addr ethzero = {{0,0,0,0,0,0}}; enum etharp_state { ETHARP_STATE_EMPTY = 0, ETHARP_STATE_PENDING, - ETHARP_STATE_STABLE + ETHARP_STATE_STABLE, + ETHARP_STATE_STABLE_REREQUESTING +#if ETHARP_SUPPORT_STATIC_ENTRIES + ,ETHARP_STATE_STATIC +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ }; struct etharp_entry { #if ARP_QUEUEING /** Pointer to queue of pending outgoing packets on this ARP entry. */ struct etharp_q_entry *q; +#else /* ARP_QUEUEING */ + /** Pointer to a single pending outgoing packet on this ARP entry. */ + struct pbuf *q; #endif /* ARP_QUEUEING */ ip_addr_t ipaddr; - struct eth_addr ethaddr; -#if LWIP_SNMP struct netif *netif; -#endif /* LWIP_SNMP */ + struct eth_addr ethaddr; u8_t state; u8_t ctime; -#if ETHARP_SUPPORT_STATIC_ENTRIES - u8_t static_entry; -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ }; static struct etharp_entry arp_table[ARP_TABLE_SIZE]; @@ -116,7 +128,9 @@ static u8_t etharp_cached_entry; the cache (even if this means removing an active entry or so). */ #define ETHARP_FLAG_TRY_HARD 1 #define ETHARP_FLAG_FIND_ONLY 2 +#if ETHARP_SUPPORT_STATIC_ENTRIES #define ETHARP_FLAG_STATIC_ENTRY 4 +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ #if LWIP_NETIF_HWADDRHINT #define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ @@ -125,8 +139,6 @@ static u8_t etharp_cached_entry; #define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) #endif /* LWIP_NETIF_HWADDRHINT */ -static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags); - /* Some checks, instead of etharp_init(): */ #if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) @@ -154,34 +166,32 @@ free_etharp_q(struct etharp_q_entry *q) memp_free(MEMP_ARP_QUEUE, r); } } +#else /* ARP_QUEUEING */ + +/** Compatibility define: free the queued pbuf */ +#define free_etharp_q(q) pbuf_free(q) + #endif /* ARP_QUEUEING */ /** Clean up ARP table entries */ static void -free_entry(int i) +etharp_free_entry(int i) { /* remove from SNMP ARP index tree */ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); -#if ARP_QUEUEING /* and empty packet queue */ if (arp_table[i].q != NULL) { /* remove all queued packets */ - LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); free_etharp_q(arp_table[i].q); arp_table[i].q = NULL; } -#endif /* ARP_QUEUEING */ - /* recycle entry for re-use */ + /* recycle entry for re-use */ arp_table[i].state = ETHARP_STATE_EMPTY; -#if ETHARP_SUPPORT_STATIC_ENTRIES - arp_table[i].static_entry = 0; -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ #ifdef LWIP_DEBUG /* for debugging, clean out the complete entry */ arp_table[i].ctime = 0; -#if LWIP_SNMP arp_table[i].netif = NULL; -#endif /* LWIP_SNMP */ ip_addr_set_zero(&arp_table[i].ipaddr); arp_table[i].ethaddr = ethzero; #endif /* LWIP_DEBUG */ @@ -204,7 +214,7 @@ etharp_tmr(void) u8_t state = arp_table[i].state; if (state != ETHARP_STATE_EMPTY #if ETHARP_SUPPORT_STATIC_ENTRIES - && (arp_table[i].static_entry == 0) + && (state != ETHARP_STATE_STATIC) #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ ) { arp_table[i].ctime++; @@ -213,9 +223,14 @@ etharp_tmr(void) (arp_table[i].ctime >= ARP_MAXPENDING))) { /* pending or stable entry has become old! */ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", - arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); /* clean up entries that have just been expired */ - free_entry(i); + etharp_free_entry(i); + } + else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; } #if ARP_QUEUEING /* still pending entry? (not expired) */ @@ -249,17 +264,15 @@ etharp_tmr(void) * entry is found or could be recycled. */ static s8_t -find_entry(ip_addr_t *ipaddr, u8_t flags) +etharp_find_entry(ip_addr_t *ipaddr, u8_t flags) { s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; s8_t empty = ARP_TABLE_SIZE; u8_t i = 0, age_pending = 0, age_stable = 0; -#if ARP_QUEUEING /* oldest entry with packets on queue */ s8_t old_queue = ARP_TABLE_SIZE; /* its age */ u8_t age_queue = 0; -#endif /* ARP_QUEUEING */ /** * a) do a search through the cache, remember candidates @@ -280,29 +293,27 @@ find_entry(ip_addr_t *ipaddr, u8_t flags) u8_t state = arp_table[i].state; /* no empty entry found yet and now we do find one? */ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { - LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i)); /* remember first empty entry */ empty = i; } else if (state != ETHARP_STATE_EMPTY) { - LWIP_ASSERT("state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE", - state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE); + LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE", + state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE); /* if given, does IP address match IP address in ARP entry? */ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i)); /* found exact IP address match, simply bail out */ return i; } /* pending entry? */ if (state == ETHARP_STATE_PENDING) { /* pending with queued packets? */ -#if ARP_QUEUEING if (arp_table[i].q != NULL) { if (arp_table[i].ctime >= age_queue) { old_queue = i; age_queue = arp_table[i].ctime; } } else -#endif /* ARP_QUEUEING */ /* pending without queued packets? */ { if (arp_table[i].ctime >= age_pending) { @@ -311,10 +322,10 @@ find_entry(ip_addr_t *ipaddr, u8_t flags) } } /* stable entry? */ - } else if (state == ETHARP_STATE_STABLE) { + } else if (state >= ETHARP_STATE_STABLE) { #if ETHARP_SUPPORT_STATIC_ENTRIES /* don't record old_stable for static entries since they never expire */ - if (arp_table[i].static_entry == 0) + if (state < ETHARP_STATE_STATIC) #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ { /* remember entry with oldest stable entry in oldest, its age in maxtime */ @@ -332,7 +343,7 @@ find_entry(ip_addr_t *ipaddr, u8_t flags) if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || /* or no empty entry found and not allowed to recycle? */ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n")); return (s8_t)ERR_MEM; } @@ -348,38 +359,34 @@ find_entry(ip_addr_t *ipaddr, u8_t flags) /* 1) empty entry available? */ if (empty < ARP_TABLE_SIZE) { i = empty; - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); } else { /* 2) found recyclable stable entry? */ if (old_stable < ARP_TABLE_SIZE) { /* recycle oldest stable*/ i = old_stable; - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); -#if ARP_QUEUEING + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); /* no queued packets should exist on stable entries */ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); -#endif /* ARP_QUEUEING */ /* 3) found recyclable pending entry without queued packets? */ } else if (old_pending < ARP_TABLE_SIZE) { /* recycle oldest pending */ i = old_pending; - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); -#if ARP_QUEUEING + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); /* 4) found recyclable pending entry with queued packets? */ } else if (old_queue < ARP_TABLE_SIZE) { - /* recycle oldest pending (queued packets are free in free_entry) */ + /* recycle oldest pending (queued packets are free in etharp_free_entry) */ i = old_queue; - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); -#endif /* ARP_QUEUEING */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); /* no empty or recyclable entries found */ } else { - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n")); return (s8_t)ERR_MEM; } /* { empty or recyclable entry found } */ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); - free_entry(i); + etharp_free_entry(i); } LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); @@ -392,9 +399,6 @@ find_entry(ip_addr_t *ipaddr, u8_t flags) ip_addr_copy(arp_table[i].ipaddr, *ipaddr); } arp_table[i].ctime = 0; -#if ETHARP_SUPPORT_STATIC_ENTRIES - arp_table[i].static_entry = 0; -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ return (err_t)i; } @@ -442,11 +446,11 @@ etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct * @see pbuf_free() */ static err_t -update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); @@ -454,11 +458,11 @@ update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethadd if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) { - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find or create ARP entry */ - i = find_entry(ipaddr, flags); + i = etharp_find_entry(ipaddr, flags); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; @@ -467,27 +471,26 @@ update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethadd #if ETHARP_SUPPORT_STATIC_ENTRIES if (flags & ETHARP_FLAG_STATIC_ENTRY) { /* record static type */ - arp_table[i].static_entry = 1; - } + arp_table[i].state = ETHARP_STATE_STATIC; + } else #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + } - /* mark it stable */ - arp_table[i].state = ETHARP_STATE_STABLE; - -#if LWIP_SNMP /* record network interface */ arp_table[i].netif = netif; -#endif /* LWIP_SNMP */ /* insert in SNMP ARP index tree */ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); /* update address */ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); /* reset time stamp */ arp_table[i].ctime = 0; -#if ARP_QUEUEING /* this is where we will send out queued packets! */ +#if ARP_QUEUEING while (arp_table[i].q != NULL) { struct pbuf *p; /* remember remainder of queue */ @@ -498,12 +501,16 @@ update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethadd p = q->p; /* now queue entry can be freed */ memp_free(MEMP_ARP_QUEUE, q); +#else /* ARP_QUEUEING */ + if (arp_table[i].q != NULL) { + struct pbuf *p = arp_table[i].q; + arp_table[i].q = NULL; +#endif /* ARP_QUEUEING */ /* send the queued IP packet */ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); /* free the queued IP packet */ pbuf_free(p); } -#endif /* ARP_QUEUEING */ return ERR_OK; } @@ -530,7 +537,7 @@ etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) return ERR_RTE; } - return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); + return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); } /** Remove a static entry from the ARP table previously added with a call to @@ -549,23 +556,39 @@ etharp_remove_static_entry(ip_addr_t *ipaddr) ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); /* find or create ARP entry */ - i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; } - if ((arp_table[i].state != ETHARP_STATE_STABLE) || - (arp_table[i].static_entry == 0)) { + if (arp_table[i].state != ETHARP_STATE_STATIC) { /* entry wasn't a static entry, cannot remove it */ return ERR_ARG; } /* entry found, free it */ - free_entry(i); + etharp_free_entry(i); return ERR_OK; } #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +/** + * Remove all ARP table entries of the specified netif. + * + * @param netif points to a network interface + */ +void etharp_cleanup_netif(struct netif *netif) +{ + u8_t i; + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { + etharp_free_entry(i); + } + } +} + /** * Finds (stable) ethernet/IP address pair from ARP table * using interface and IP address index. @@ -588,8 +611,8 @@ etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, LWIP_UNUSED_ARG(netif); - i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); - if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) { + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { *eth_ret = &arp_table[i].ethaddr; *ip_ret = &arp_table[i].ipaddr; return i; @@ -626,7 +649,7 @@ etharp_ip_input(struct netif *netif, struct pbuf *p) ethhdr = (struct eth_hdr *)p->payload; iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); #if ETHARP_SUPPORT_VLAN - if (ethhdr->type == ETHTYPE_VLAN) { + if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) { iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); } #endif /* ETHARP_SUPPORT_VLAN */ @@ -643,7 +666,7 @@ etharp_ip_input(struct netif *netif, struct pbuf *p) /* update the source IP address in the cache, if present */ /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk * back soon (for example, if the destination IP address is ours. */ - update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY); + etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY); } #endif /* ETHARP_TRUST_IP_MAC */ @@ -691,7 +714,7 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) ethhdr = (struct eth_hdr *)p->payload; hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); #if ETHARP_SUPPORT_VLAN - if (ethhdr->type == ETHTYPE_VLAN) { + if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) { hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); } #endif /* ETHARP_SUPPORT_VLAN */ @@ -700,11 +723,10 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || (hdr->hwlen != ETHARP_HWADDR_LEN) || (hdr->protolen != sizeof(ip_addr_t)) || - (hdr->proto != PP_HTONS(ETHTYPE_IP)) || - (ethhdr->type != PP_HTONS(ETHTYPE_ARP))) { + (hdr->proto != PP_HTONS(ETHTYPE_IP))) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, - ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", - hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen, ethhdr->type)); + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen)); ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); pbuf_free(p); @@ -737,7 +759,7 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) can result in directly sending the queued packets for this host. ARP message not directed to us? -> update the source IP address in the cache, if present */ - update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), + etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); /* now act on the message itself */ @@ -814,6 +836,28 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) pbuf_free(p); } +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && + (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; + } + } + + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[arp_idx].ethaddr); +} + /** * Resolve and fill-in Ethernet address header for outgoing IP packet. * @@ -835,7 +879,13 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) { - struct eth_addr *dest, mcastaddr; + struct eth_addr *dest; + struct eth_addr mcastaddr; + ip_addr_t *dst_addr = ipaddr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); /* make room for Ethernet header - should not fail */ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { @@ -846,8 +896,6 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) return ERR_BUF; } - /* assume unresolved Ethernet address */ - dest = NULL; /* Determine on destination hardware address. Broadcasts and multicasts * are special, other IP addresses are looked up in the ARP table. */ @@ -858,9 +906,9 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) /* multicast destination IP address? */ } else if (ip_addr_ismulticast(ipaddr)) { /* Hash IP multicast address to MAC address.*/ - mcastaddr.addr[0] = 0x01; - mcastaddr.addr[1] = 0x00; - mcastaddr.addr[2] = 0x5e; + mcastaddr.addr[0] = LL_MULTICAST_ADDR_0; + mcastaddr.addr[1] = LL_MULTICAST_ADDR_1; + mcastaddr.addr[2] = LL_MULTICAST_ADDR_2; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); @@ -868,17 +916,30 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) dest = &mcastaddr; /* unicast destination IP address? */ } else { - /* outside local network? */ + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) && !ip_addr_islinklocal(ipaddr)) { - /* interface has default gateway? */ - if (!ip_addr_isany(&netif->gw)) { - /* send to hardware address of default gateway IP address */ - ipaddr = &(netif->gw); - /* no default gateway available */ - } else { - /* no route to destination error (default gateway missing) */ - return ERR_RTE; +#if LWIP_AUTOIP + struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload + + sizeof(struct eth_hdr)); + /* 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 + router for forwarding". */ + if (!ip_addr_islinklocal(&iphdr->src)) +#endif /* LWIP_AUTOIP */ + { + /* interface has default gateway? */ + if (!ip_addr_isany(&netif->gw)) { + /* send to hardware address of default gateway IP address */ + dst_addr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } } } #if LWIP_NETIF_HWADDRHINT @@ -887,19 +948,30 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) u8_t etharp_cached_entry = *(netif->addr_hint); if (etharp_cached_entry < ARP_TABLE_SIZE) { #endif /* LWIP_NETIF_HWADDRHINT */ - if ((arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) && - (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr))) { + if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { /* the per-pcb-cached entry is stable and the right one! */ ETHARP_STATS_INC(etharp.cachehit); - return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), - &arp_table[etharp_cached_entry].ethaddr); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); } #if LWIP_NETIF_HWADDRHINT } } #endif /* LWIP_NETIF_HWADDRHINT */ - /* queue on destination Ethernet address belonging to ipaddr */ - return etharp_query(netif, ipaddr, q); + + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* no stable entry found, use the (slower) query function: + queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, dst_addr, q); } /* continuation for multicast/broadcast destinations */ @@ -957,7 +1029,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) } /* find entry in ARP cache, ask to create entry if queueing packet */ - i = find_entry(ipaddr, ETHARP_FLAG_TRY_HARD); + i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD); /* could not find or create entry? */ if (i < 0) { @@ -977,7 +1049,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) /* { i is either a STABLE or (new or existing) PENDING entry } */ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", ((arp_table[i].state == ETHARP_STATE_PENDING) || - (arp_table[i].state == ETHARP_STATE_STABLE))); + (arp_table[i].state >= ETHARP_STATE_STABLE))); /* do we have a pending entry? or an implicit query request? */ if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { @@ -997,14 +1069,14 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) /* packet given? */ LWIP_ASSERT("q != NULL", q != NULL); /* stable entry? */ - if (arp_table[i].state == ETHARP_STATE_STABLE) { + if (arp_table[i].state >= ETHARP_STATE_STABLE) { /* we have a valid IP->Ethernet address mapping */ ETHARP_SET_HINT(netif, i); /* send the packet */ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); /* pending entry? (either just created or already pending */ } else if (arp_table[i].state == ETHARP_STATE_PENDING) { -#if ARP_QUEUEING /* queue the given q packet */ + /* entry is still pending, queue the given packet 'q' */ struct pbuf *p; int copy_needed = 0; /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but @@ -1036,6 +1108,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) /* packet could be taken over? */ if (p != NULL) { /* queue packet ... */ +#if ARP_QUEUEING struct etharp_q_entry *new_entry; /* allocate a new arp queue entry */ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); @@ -1060,18 +1133,23 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) /* the pool MEMP_ARP_QUEUE is empty */ pbuf_free(p); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); - /* { result == ERR_MEM } through initialization */ + result = ERR_MEM; + } +#else /* ARP_QUEUEING */ + /* always queue one packet per ARP request only, freeing a previously queued packet */ + if (arp_table[i].q != NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + pbuf_free(arp_table[i].q); } + arp_table[i].q = p; + result = ERR_OK; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); +#endif /* ARP_QUEUEING */ } else { ETHARP_STATS_INC(etharp.memerr); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); - /* { result == ERR_MEM } through initialization */ + result = ERR_MEM; } -#else /* ARP_QUEUEING */ - /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */ - /* { result == ERR_MEM } through initialization */ - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q)); -#endif /* ARP_QUEUEING */ } return result; } @@ -1109,6 +1187,8 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, const u8_t * ethdst_hwaddr; #endif /* LWIP_AUTOIP */ + LWIP_ASSERT("netif != NULL", netif != NULL); + /* allocate a pbuf for the outgoing ARP request packet */ p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); /* could allocate a pbuf for an ARP request? */ @@ -1199,6 +1279,16 @@ ethernet_input(struct pbuf *p, struct netif *netif) { struct eth_hdr* ethhdr; u16_t type; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; +#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } /* points to packet payload, which starts with an Ethernet header */ ethhdr = (struct eth_hdr *)p->payload; @@ -1214,14 +1304,25 @@ ethernet_input(struct pbuf *p, struct netif *netif) #if ETHARP_SUPPORT_VLAN if (type == PP_HTONS(ETHTYPE_VLAN)) { struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); -#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */ + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + /* a packet with only an ethernet/vlan header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ +#ifdef ETHARP_VLAN_CHECK_FN + if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK) if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { +#endif /* silently ignore this packet: not for our VLAN */ pbuf_free(p); return ERR_OK; } -#endif /* ETHARP_VLAN_CHECK */ +#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ type = vlan->tpid; + ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; } #endif /* ETHARP_SUPPORT_VLAN */ @@ -1229,6 +1330,20 @@ ethernet_input(struct pbuf *p, struct netif *netif) netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type)); #endif /* LWIP_ARP_FILTER_NETIF*/ + if (ethhdr->dest.addr[0] & 1) { + /* this might be a multicast or broadcast packet */ + if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) { + if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } + } else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + /* mark the pbuf as link-layer broadcast */ + p->flags |= PBUF_FLAG_LLBCAST; + } + } + switch (type) { #if LWIP_ARP /* IP packet? */ @@ -1241,7 +1356,7 @@ ethernet_input(struct pbuf *p, struct netif *netif) etharp_ip_input(netif, p); #endif /* ETHARP_TRUST_IP_MAC */ /* skip Ethernet header */ - if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { + if(pbuf_header(p, -ip_hdr_offset)) { LWIP_ASSERT("Can't move over header in packet", 0); goto free_and_return; } else { @@ -1268,6 +1383,19 @@ ethernet_input(struct pbuf *p, struct netif *netif) break; #endif /* PPPOE_SUPPORT */ +#if LWIP_IPV6 + case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ + /* skip Ethernet header */ + if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IPv6 layer */ + ip6_input(p, netif); + } + break; +#endif /* LWIP_IPV6 */ + default: ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); diff --git a/lib/liblwip/netif/ethernetif.c b/lib/liblwip/netif/ethernetif.c index a5b7d990b..46900bdbf 100644 --- a/lib/liblwip/netif/ethernetif.c +++ b/lib/liblwip/netif/ethernetif.c @@ -50,9 +50,9 @@ #include "lwip/def.h" #include "lwip/mem.h" #include "lwip/pbuf.h" -#include "lwip/sys.h" -#include -#include +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" #include "netif/etharp.h" #include "netif/ppp_oe.h" @@ -239,6 +239,7 @@ ethernetif_input(struct netif *netif) switch (htons(ethhdr->type)) { /* IP or ARP packet? */ case ETHTYPE_IP: + case ETHTYPE_IPV6: case ETHTYPE_ARP: #if PPPOE_SUPPORT /* PPPoE packet? */ @@ -305,6 +306,9 @@ ethernetif_init(struct netif *netif) * from it if you have to do some checks before sending (e.g. if link * is available...) */ netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ netif->linkoutput = low_level_output; ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); diff --git a/lib/liblwip/netif/ppp/auth.c b/lib/liblwip/netif/ppp/auth.c index c3c49d22a..0fd87a379 100644 --- a/lib/liblwip/netif/ppp/auth.c +++ b/lib/liblwip/netif/ppp/auth.c @@ -68,7 +68,7 @@ #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "fsm.h" diff --git a/lib/liblwip/netif/ppp/chap.c b/lib/liblwip/netif/ppp/chap.c index 3a49ff8a5..f10e27d2e 100644 --- a/lib/liblwip/netif/ppp/chap.c +++ b/lib/liblwip/netif/ppp/chap.c @@ -72,7 +72,7 @@ #if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "magic.h" diff --git a/lib/liblwip/netif/ppp/chpms.c b/lib/liblwip/netif/ppp/chpms.c index 83acefcec..81a887b83 100644 --- a/lib/liblwip/netif/ppp/chpms.c +++ b/lib/liblwip/netif/ppp/chpms.c @@ -75,7 +75,7 @@ #if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "md4.h" diff --git a/lib/liblwip/netif/ppp/fsm.c b/lib/liblwip/netif/ppp/fsm.c index 2e73c5af8..e8a254ede 100644 --- a/lib/liblwip/netif/ppp/fsm.c +++ b/lib/liblwip/netif/ppp/fsm.c @@ -59,7 +59,7 @@ #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "fsm.h" diff --git a/lib/liblwip/netif/ppp/ipcp.c b/lib/liblwip/netif/ppp/ipcp.c index 461a600f8..f0ab2e0e1 100644 --- a/lib/liblwip/netif/ppp/ipcp.c +++ b/lib/liblwip/netif/ppp/ipcp.c @@ -55,7 +55,7 @@ #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "auth.h" diff --git a/lib/liblwip/netif/ppp/lcp.c b/lib/liblwip/netif/ppp/lcp.c index 21c83ac44..54f758aa6 100644 --- a/lib/liblwip/netif/ppp/lcp.c +++ b/lib/liblwip/netif/ppp/lcp.c @@ -55,7 +55,7 @@ #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "fsm.h" diff --git a/lib/liblwip/netif/ppp/magic.c b/lib/liblwip/netif/ppp/magic.c index 390133089..3732a4241 100644 --- a/lib/liblwip/netif/ppp/magic.c +++ b/lib/liblwip/netif/ppp/magic.c @@ -53,7 +53,7 @@ #if PPP_SUPPORT -#include "ppp.h" +#include "ppp_impl.h" #include "randm.h" #include "magic.h" diff --git a/lib/liblwip/netif/ppp/md5.c b/lib/liblwip/netif/ppp/md5.c index 3cb69e2b0..dc3cc751b 100644 --- a/lib/liblwip/netif/ppp/md5.c +++ b/lib/liblwip/netif/ppp/md5.c @@ -37,7 +37,7 @@ #if CHAP_SUPPORT || MD5_SUPPORT -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "md5.h" diff --git a/lib/liblwip/netif/ppp/pap.c b/lib/liblwip/netif/ppp/pap.c index ac44e6464..5fb9f886c 100644 --- a/lib/liblwip/netif/ppp/pap.c +++ b/lib/liblwip/netif/ppp/pap.c @@ -55,7 +55,7 @@ #if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "auth.h" @@ -218,7 +218,7 @@ upap_timeout(void *arg) u->us_unit, u->us_timeouttime, u->us_clientstate)); if (u->us_clientstate != UPAPCS_AUTHREQ) { - UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); + UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); return; } diff --git a/lib/liblwip/netif/ppp/ppp.c b/lib/liblwip/netif/ppp/ppp.c index 2a280ff0c..2a3465756 100644 --- a/lib/liblwip/netif/ppp/ppp.c +++ b/lib/liblwip/netif/ppp/ppp.c @@ -83,9 +83,9 @@ #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ +#include "ppp_impl.h" #include "lwip/ip.h" /* for ip_input() */ -#include "ppp.h" #include "pppdebug.h" #include "randm.h" @@ -172,7 +172,9 @@ typedef struct PPPControlRx_s { /** the rx file descriptor */ sio_fd_t fd; /** receive buffer - encoded data is stored here */ +#if PPP_INPROC_OWNTHREAD u_char rxbuf[PPPOS_RX_BUFSIZE]; +#endif /* PPP_INPROC_OWNTHREAD */ /* The input packet. */ struct pbuf *inHead, *inTail; @@ -241,14 +243,13 @@ static void pppInputThread(void *arg); #endif /* PPP_INPROC_OWNTHREAD */ static void pppDrop(PPPControlRx *pcrx); static void pppInProc(PPPControlRx *pcrx, u_char *s, int l); +static void pppFreeCurrentInputPacket(PPPControlRx *pcrx); #endif /* PPPOS_SUPPORT */ /******************************/ /*** PUBLIC DATA STRUCTURES ***/ /******************************/ -u_long subnetMask; - static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ /* @@ -339,13 +340,17 @@ static u_char pppACCMMask[] = { 0x80 }; +#if PPP_INPROC_OWNTHREAD /** Wake up the task blocked in reading from serial line (if any) */ static void pppRecvWakeup(int pd) { PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd)); - sio_read_abort(pppControl[pd].fd); + if (pppControl[pd].openFlag != 0) { + sio_read_abort(pppControl[pd].fd); + } } +#endif /* PPP_INPROC_OWNTHREAD */ #endif /* PPPOS_SUPPORT */ void @@ -361,9 +366,10 @@ pppLinkTerminated(int pd) { #if PPPOS_SUPPORT PPPControl* pc; +#if PPP_INPROC_OWNTHREAD pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ pc = &pppControl[pd]; - pppDrop(&pc->rx); /* bug fix #17726 */ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); if (pc->linkStatusCB) { @@ -387,9 +393,9 @@ pppLinkDown(int pd) } else #endif /* PPPOE_SUPPORT */ { -#if PPPOS_SUPPORT +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD pppRecvWakeup(pd); -#endif /* PPPOS_SUPPORT */ +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/ } } @@ -439,8 +445,6 @@ pppInit(void) magicInit(); - subnetMask = PP_HTONL(0xffffff00); - for (i = 0; i < NUM_PPP; i++) { /* Initialize each protocol to the standard option set. */ for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { @@ -526,7 +530,7 @@ pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) * pppOpen() is directly defined to this function. */ int -pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) { PPPControl *pc; int pd; @@ -544,6 +548,8 @@ pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void pd = PPPERR_OPEN; } else { pc = &pppControl[pd]; + /* input pbuf left over from last session? */ + pppFreeCurrentInputPacket(&pc->rx); /* @todo: is this correct or do I overwrite something? */ memset(pc, 0, sizeof(PPPControl)); pc->rx.pd = pd; @@ -573,7 +579,7 @@ pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void pppStart(pd); #if PPP_INPROC_OWNTHREAD sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); -#endif +#endif /* PPP_INPROC_OWNTHREAD */ } return pd; @@ -594,7 +600,8 @@ pppOverEthernetClose(int pd) pppoe_destroy(&pc->netif); } -int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) { PPPControl *pc; int pd; @@ -670,7 +677,9 @@ pppClose(int pd) pc->errCode = PPPERR_USER; /* This will leave us at PHASE_DEAD. */ pppStop(pd); +#if PPP_INPROC_OWNTHREAD pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ #endif /* PPPOS_SUPPORT */ } @@ -681,20 +690,8 @@ pppClose(int pd) void pppSigHUP(int pd) { -#if PPPOE_SUPPORT - PPPControl *pc = &pppControl[pd]; - if(pc->ethif) { - PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); - pppHup(pd); - } else -#endif /* PPPOE_SUPPORT */ - { -#if PPPOS_SUPPORT - PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); - pppHup(pd); - pppRecvWakeup(pd); -#endif /* PPPOS_SUPPORT */ - } + PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + pppHup(pd); } #if PPPOS_SUPPORT @@ -1271,7 +1268,7 @@ GetMask(u32_t addr) { u32_t mask, nmask; - htonl(addr); + addr = htonl(addr); if (IP_CLASSA(addr)) { /* determine network mask for address class */ nmask = IP_CLASSA_NET; } else if (IP_CLASSB(addr)) { @@ -1281,7 +1278,7 @@ GetMask(u32_t addr) } /* class D nets are disallowed by bad_ip_adrs */ - mask = subnetMask | htonl(nmask); + mask = PP_HTONL(0xffffff00UL) | htonl(nmask); /* XXX * Scan through the system's network interfaces. @@ -1589,10 +1586,21 @@ pppSingleBuf(struct pbuf *p) return q; } +/** Input helper struct, must be packed since it is stored to pbuf->payload, + * which might be unaligned. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN struct pppInputHeader { - int unit; - u16_t proto; -}; + PACK_STRUCT_FIELD(int unit); + PACK_STRUCT_FIELD(u16_t proto); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif /* * Pass the processed input packet to the appropriate handler. @@ -1704,8 +1712,8 @@ pppInput(void *arg) } #if BYTE_ORDER == LITTLE_ENDIAN protocol = htons(protocol); - SMEMCPY(nb->payload, &protocol, sizeof(protocol)); #endif /* BYTE_ORDER == LITTLE_ENDIAN */ + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); lcp_sprotrej(pd, nb->payload, nb->len); } break; @@ -1725,20 +1733,31 @@ out: * Drop the input packet. */ static void -pppDrop(PPPControlRx *pcrx) +pppFreeCurrentInputPacket(PPPControlRx *pcrx) { if (pcrx->inHead != NULL) { -#if 0 - PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload)); -#endif - PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead)); if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) { pbuf_free(pcrx->inTail); } pbuf_free(pcrx->inHead); pcrx->inHead = NULL; - pcrx->inTail = NULL; } + pcrx->inTail = NULL; +} + +/* + * Drop the input packet and increase error counters. + */ +static void +pppDrop(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { +#if 0 + PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload)); +#endif + PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead)); + } + pppFreeCurrentInputPacket(pcrx); #if VJ_SUPPORT vj_uncompress_err(&pppControl[pcrx->pd].vjComp); #endif /* VJ_SUPPORT */ @@ -1747,6 +1766,7 @@ pppDrop(PPPControlRx *pcrx) snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); } +#if !PPP_INPROC_OWNTHREAD /** Pass received raw characters to PPPoS to be decoded. This function is * thread-safe and can be called from a dedicated RX-thread or from a main-loop. * @@ -1759,6 +1779,7 @@ pppos_input(int pd, u_char* data, int len) { pppInProc(&pppControl[pd].rx, data, len); } +#endif /** * Process a received octet string. @@ -1809,8 +1830,9 @@ pppInProc(PPPControlRx *pcrx, u_char *s, int l) pppDrop(pcrx); /* Otherwise it's a good packet so pass it on. */ } else { + struct pbuf *inp; /* Trim off the checksum. */ - if(pcrx->inTail->len >= 2) { + if(pcrx->inTail->len > 2) { pcrx->inTail->len -= 2; pcrx->inTail->tot_len = pcrx->inTail->len; @@ -1827,18 +1849,20 @@ pppInProc(PPPControlRx *pcrx, u_char *s, int l) } /* Dispatch the packet thereby consuming it. */ + inp = pcrx->inHead; + /* Packet consumed, release our references. */ + pcrx->inHead = NULL; + pcrx->inTail = NULL; #if PPP_INPROC_MULTITHREADED - if(tcpip_callback_with_block(pppInput, pcrx->inHead, 0) != ERR_OK) { + if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) { PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd)); - pbuf_free(pcrx->inHead); + pbuf_free(inp); LINK_STATS_INC(link.drop); snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); } #else /* PPP_INPROC_MULTITHREADED */ - pppInput(pcrx->inHead); + pppInput(inp); #endif /* PPP_INPROC_MULTITHREADED */ - pcrx->inHead = NULL; - pcrx->inTail = NULL; } /* Prepare for a new packet. */ @@ -1912,10 +1936,12 @@ pppInProc(PPPControlRx *pcrx, u_char *s, int l) case PDDATA: /* Process data byte. */ /* Make space to receive processed data. */ if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) { - if(pcrx->inTail) { + if (pcrx->inTail != NULL) { pcrx->inTail->tot_len = pcrx->inTail->len; if (pcrx->inTail != pcrx->inHead) { pbuf_cat(pcrx->inHead, pcrx->inTail); + /* give up the inTail reference now */ + pcrx->inTail = NULL; } } /* If we haven't started a packet, we need a packet header. */ diff --git a/lib/liblwip/netif/ppp/ppp.h b/lib/liblwip/netif/ppp/ppp.h index a72ac9576..08d6e62d8 100644 --- a/lib/liblwip/netif/ppp/ppp.h +++ b/lib/liblwip/netif/ppp/ppp.h @@ -46,67 +46,6 @@ #include "lwip/sys.h" #include "lwip/timers.h" -/** Some defines for code we skip compared to the original pppd. - * These are just here to minimise the use of the ugly "#if 0". */ -#define PPP_ADDITIONAL_CALLBACKS 0 - -/** Some error checks to test for unsupported code */ -#if CBCP_SUPPORT -#error "CBCP is not supported in lwIP PPP" -#endif -#if CCP_SUPPORT -#error "CCP is not supported in lwIP PPP" -#endif - -/* - * pppd.h - PPP daemon global declarations. - * - * Copyright (c) 1989 Carnegie Mellon University. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by Carnegie Mellon University. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - */ -/* - * ppp_defs.h - PPP definitions. - * - * Copyright (c) 1994 The Australian National University. - * All rights reserved. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation is hereby granted, provided that the above copyright - * notice appears in all copies. This software is provided without any - * warranty, express or implied. The Australian National University - * makes no representations about the suitability of this software for - * any purpose. - * - * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF - * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - * - * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, - * OR MODIFICATIONS. - */ - -#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) -#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) - #ifndef __u_char_defined @@ -118,123 +57,6 @@ typedef unsigned char u_char; #endif -/* - * Constants and structures defined by the internet system, - * Per RFC 790, September 1981, and numerous additions. - */ - -/* - * The basic PPP frame. - */ -#define PPP_HDRLEN 4 /* octets for standard ppp header */ -#define PPP_FCSLEN 2 /* octets for FCS */ - - -/* - * Significant octet values. - */ -#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ -#define PPP_UI 0x03 /* Unnumbered Information */ -#define PPP_FLAG 0x7e /* Flag Sequence */ -#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ -#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ - -/* - * Protocol field values. - */ -#define PPP_IP 0x21 /* Internet Protocol */ -#define PPP_AT 0x29 /* AppleTalk Protocol */ -#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ -#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ -#define PPP_COMP 0xfd /* compressed packet */ -#define PPP_IPCP 0x8021 /* IP Control Protocol */ -#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ -#define PPP_CCP 0x80fd /* Compression Control Protocol */ -#define PPP_LCP 0xc021 /* Link Control Protocol */ -#define PPP_PAP 0xc023 /* Password Authentication Protocol */ -#define PPP_LQR 0xc025 /* Link Quality Report protocol */ -#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ -#define PPP_CBCP 0xc029 /* Callback Control Protocol */ - -/* - * Values for FCS calculations. - */ -#define PPP_INITFCS 0xffff /* Initial FCS value */ -#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ -#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) - -/* - * Extended asyncmap - allows any character to be escaped. - */ -typedef u_char ext_accm[32]; - -/* - * What to do with network protocol (NP) packets. - */ -enum NPmode { - NPMODE_PASS, /* pass the packet through */ - NPMODE_DROP, /* silently drop the packet */ - NPMODE_ERROR, /* return an error */ - NPMODE_QUEUE /* save it up for later. */ -}; - -/* - * Inline versions of get/put char/short/long. - * Pointer is advanced; we assume that both arguments - * are lvalues and will already be in registers. - * cp MUST be u_char *. - */ -#define GETCHAR(c, cp) { \ - (c) = *(cp)++; \ -} -#define PUTCHAR(c, cp) { \ - *(cp)++ = (u_char) (c); \ -} - - -#define GETSHORT(s, cp) { \ - (s) = *(cp); (cp)++; (s) <<= 8; \ - (s) |= *(cp); (cp)++; \ -} -#define PUTSHORT(s, cp) { \ - *(cp)++ = (u_char) ((s) >> 8); \ - *(cp)++ = (u_char) (s & 0xff); \ -} - -#define GETLONG(l, cp) { \ - (l) = *(cp); (cp)++; (l) <<= 8; \ - (l) |= *(cp); (cp)++; (l) <<= 8; \ - (l) |= *(cp); (cp)++; (l) <<= 8; \ - (l) |= *(cp); (cp)++; \ -} -#define PUTLONG(l, cp) { \ - *(cp)++ = (u_char) ((l) >> 24); \ - *(cp)++ = (u_char) ((l) >> 16); \ - *(cp)++ = (u_char) ((l) >> 8); \ - *(cp)++ = (u_char) (l); \ -} - - -#define INCPTR(n, cp) ((cp) += (n)) -#define DECPTR(n, cp) ((cp) -= (n)) - -#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) -#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) -#define BZERO(s, n) memset(s, 0, n) - -#if PPP_DEBUG -#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); } -#else /* PPP_DEBUG */ -#define PRINTMSG(m, l) -#endif /* PPP_DEBUG */ - -/* - * MAKEHEADER - Add PPP Header fields to a packet. - */ -#define MAKEHEADER(p, t) { \ - PUTCHAR(PPP_ALLSTATIONS, p); \ - PUTCHAR(PPP_UI, p); \ - PUTSHORT(t, p); } /************************* *** PUBLIC DEFINITIONS *** @@ -267,89 +89,10 @@ enum NPmode { *** PUBLIC DATA TYPES *** ************************/ -/* - * The following struct gives the addresses of procedures to call - * for a particular protocol. - */ -struct protent { - u_short protocol; /* PPP protocol number */ - /* Initialization procedure */ - void (*init) (int unit); - /* Process a received packet */ - void (*input) (int unit, u_char *pkt, int len); - /* Process a received protocol-reject */ - void (*protrej) (int unit); - /* Lower layer has come up */ - void (*lowerup) (int unit); - /* Lower layer has gone down */ - void (*lowerdown) (int unit); - /* Open the protocol */ - void (*open) (int unit); - /* Close the protocol */ - void (*close) (int unit, char *reason); -#if PPP_ADDITIONAL_CALLBACKS - /* Print a packet in readable form */ - int (*printpkt) (u_char *pkt, int len, - void (*printer) (void *, char *, ...), - void *arg); - /* Process a received data packet */ - void (*datainput) (int unit, u_char *pkt, int len); -#endif /* PPP_ADDITIONAL_CALLBACKS */ - int enabled_flag; /* 0 if protocol is disabled */ - char *name; /* Text name of protocol */ -#if PPP_ADDITIONAL_CALLBACKS - /* Check requested options, assign defaults */ - void (*check_options) (u_long); - /* Configure interface for demand-dial */ - int (*demand_conf) (int unit); - /* Say whether to bring up link for this pkt */ - int (*active_pkt) (u_char *pkt, int len); -#endif /* PPP_ADDITIONAL_CALLBACKS */ -}; - -/* - * The following structure records the time in seconds since - * the last NP packet was sent or received. - */ -struct ppp_idle { - u_short xmit_idle; /* seconds since last NP packet sent */ - u_short recv_idle; /* seconds since last NP packet received */ -}; - -struct ppp_settings { - - u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ - u_int auth_required : 1; /* Peer is required to authenticate */ - u_int explicit_remote : 1; /* remote_name specified with remotename opt */ - u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ - u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ - u_int usehostname : 1; /* Use hostname for our_name */ - u_int usepeerdns : 1; /* Ask peer for DNS adds */ - - u_short idle_time_limit; /* Shut down link if idle for this long */ - int maxconnect; /* Maximum connect time (seconds) */ - - char user [MAXNAMELEN + 1]; /* Username for PAP */ - char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ - char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ - char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ -}; - struct ppp_addrs { ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2; }; -/***************************** -*** PUBLIC DATA STRUCTURES *** -*****************************/ - -/* Buffers for outgoing packets. */ -extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; - -extern struct ppp_settings ppp_settings; - -extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ - /*********************** *** PUBLIC FUNCTIONS *** @@ -386,6 +129,10 @@ enum pppAuthType { void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); +/* Link status callback function prototype */ +typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg); + +#if PPPOS_SUPPORT /* * Open a new PPP connection using the given serial I/O device. * This initializes the PPP control block but does not @@ -393,12 +140,16 @@ void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) * Return a new PPP connection descriptor on success or * an error code (negative) on failure. */ -int pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); +int pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT /* * Open a new PPP Over Ethernet (PPPOE) connection. */ -int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOE_SUPPORT */ /* for source code compatibility */ #define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls) @@ -426,55 +177,22 @@ int pppIOCtl(int pd, int cmd, void *arg); */ u_short pppMTU(int pd); +#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD /* - * Write n characters to a ppp link. - * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + * PPP over Serial: this is the input function to be called for received data. + * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking + * sio_read() is used, so this is deactivated. */ -int pppWrite(int pd, const u_char *s, int n); - -void pppInProcOverEthernet(int pd, struct pbuf *pb); - -struct pbuf *pppSingleBuf(struct pbuf *p); - -void pppLinkTerminated(int pd); - -void pppLinkDown(int pd); - void pppos_input(int pd, u_char* data, int len); +#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */ -/* Configure i/f transmit parameters */ -void ppp_send_config (int, u16_t, u32_t, int, int); -/* Set extended transmit ACCM */ -void ppp_set_xaccm (int, ext_accm *); -/* Configure i/f receive parameters */ -void ppp_recv_config (int, int, u32_t, int, int); -/* Find out how long link has been idle */ -int get_idle_time (int, struct ppp_idle *); - -/* Configure VJ TCP header compression */ -int sifvjcomp (int, int, u8_t, u8_t); -/* Configure i/f down (for IP) */ -int sifup (int); -/* Set mode for handling packets for proto */ -int sifnpmode (int u, int proto, enum NPmode mode); -/* Configure i/f down (for IP) */ -int sifdown (int); -/* Configure IP addresses for i/f */ -int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); -/* Reset i/f IP addresses */ -int cifaddr (int, u32_t, u32_t); -/* Create default route through i/f */ -int sifdefaultroute (int, u32_t, u32_t); -/* Delete default route through i/f */ -int cifdefaultroute (int, u32_t, u32_t); - -/* Get appropriate netmask for address */ -u32_t GetMask (u32_t); #if LWIP_NETIF_STATUS_CALLBACK +/* Set an lwIP-style status-callback for the selected PPP device */ void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback); #endif /* LWIP_NETIF_STATUS_CALLBACK */ #if LWIP_NETIF_LINK_CALLBACK +/* Set an lwIP-style link-callback for the selected PPP device */ void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback); #endif /* LWIP_NETIF_LINK_CALLBACK */ diff --git a/lib/liblwip/netif/ppp/ppp_impl.h b/lib/liblwip/netif/ppp/ppp_impl.h new file mode 100644 index 000000000..89aea60be --- /dev/null +++ b/lib/liblwip/netif/ppp/ppp_impl.h @@ -0,0 +1,363 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *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 CONTRIBUTORS 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. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_IMPL_H +#define PPP_IMPL_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + +/** Some defines for code we skip compared to the original pppd. + * These are just here to minimise the use of the ugly "#if 0". */ +#define PPP_ADDITIONAL_CALLBACKS 0 + +/** Some error checks to test for unsupported code */ +#if CBCP_SUPPORT +#error "CBCP is not supported in lwIP PPP" +#endif +#if CCP_SUPPORT +#error "CCP is not supported in lwIP PPP" +#endif + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if PPP_ADDITIONAL_CALLBACKS + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + int enabled_flag; /* 0 if protocol is disabled */ + char *name; /* Text name of protocol */ +#if PPP_ADDITIONAL_CALLBACKS + /* Check requested options, assign defaults */ + void (*check_options) (u_long); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u_short xmit_idle; /* seconds since last NP packet sent */ + u_short recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + + u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u_int auth_required : 1; /* Peer is required to authenticate */ + u_int explicit_remote : 1; /* remote_name specified with remotename opt */ + u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u_int usehostname : 1; /* Use hostname for our_name */ + u_int usepeerdns : 1; /* Ask peer for DNS adds */ + + u_short idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int pppWrite(int pd, const u_char *s, int n); + +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, u16_t, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, u8_t, u8_t); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum NPmode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_IMPL_H */ diff --git a/lib/liblwip/netif/ppp/ppp_oe.c b/lib/liblwip/netif/ppp/ppp_oe.c index 040a0bc93..fdf52ae23 100644 --- a/lib/liblwip/netif/ppp/ppp_oe.c +++ b/lib/liblwip/netif/ppp/ppp_oe.c @@ -74,7 +74,7 @@ #include "netif/ppp_oe.h" -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "lwip/timers.h" diff --git a/lib/liblwip/netif/ppp/randm.c b/lib/liblwip/netif/ppp/randm.c index 2f35caf6a..b736091fc 100644 --- a/lib/liblwip/netif/ppp/randm.c +++ b/lib/liblwip/netif/ppp/randm.c @@ -38,7 +38,7 @@ #include "md5.h" #include "randm.h" -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include diff --git a/lib/liblwip/netif/ppp/readme.txt b/lib/liblwip/netif/ppp/readme.txt new file mode 100644 index 000000000..5be41b90f --- /dev/null +++ b/lib/liblwip/netif/ppp/readme.txt @@ -0,0 +1,21 @@ +About the PPP code: + +The PPP code is not our "own" code - we just copied it from pppd (http://ppp.samba.org/) and adapted it to lwIP. +Unfortunately, not many here know their way around it too well. Back in 2009, we took the effort to see which +version of pppd our code relates to and we're pretty much on 2.3.11 with some bugs from 2.4.x backported. + +Aside from simple code adaptions, there are some files that are different, however: +- chpms.c/.h are named chap_ms.c/.h in the original pppd 2.3.11 sources +- pap.c/.h are named upap.c/.h in the original pppd 2.3.11 sources +- randm.c is a random generator not included in the original pppd +- magic.c does not use the C library's random functions, but uses randm.c instead +- vj.c/.h is an implementation of the Van Jacobson header compression algorithm adapted to lwIP pbufs, + probably copied from one of the vjcompress.c files from pppd. +- ppp.c, ppp.h and ppp_impl.h contain the adaption from pppd to lwIP. This is the "OS"-dependent part like there + is an implementation for linux, xBSD etc. in the pppd sources. +- ppp_oe.c is Marc Boucher's implementation based on NetBSD's if_pppoe.c + +There is of course potential for bugs in it, but when analyzing of reporting bugs, it is strongly encouraged to +compare the code in question to pppd 2.3.11 (our basis) and newer versions (perhaps it's already fixed?) and to +share this knowledge with us when reporting a bug. + diff --git a/lib/liblwip/netif/ppp/vj.c b/lib/liblwip/netif/ppp/vj.c index b7f2d54c6..40fdad13d 100644 --- a/lib/liblwip/netif/ppp/vj.c +++ b/lib/liblwip/netif/ppp/vj.c @@ -32,7 +32,7 @@ #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ -#include "ppp.h" +#include "ppp_impl.h" #include "pppdebug.h" #include "vj.h" @@ -213,7 +213,7 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) */ INCR(vjs_misses); comp->last_cs = lcs; - hlen += TCPH_OFFSET(th); + hlen += TCPH_HDRLEN(th); hlen <<= 2; /* Check that the IP/TCP headers are contained in the first buffer. */ if (hlen > pb->len) { @@ -236,7 +236,7 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen]; deltaS = hlen; - hlen += TCPH_OFFSET(th); + hlen += TCPH_HDRLEN(th); hlen <<= 2; /* Check that the IP/TCP headers are contained in the first buffer. */ if (hlen > pb->len) { @@ -258,9 +258,9 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] - || TCPH_OFFSET(th) != TCPH_OFFSET(oth) + || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) - || (TCPH_OFFSET(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_OFFSET(th) - 5) << 2))) { + || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { goto uncompressed; } @@ -434,7 +434,7 @@ vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) hlen = IPH_HL(ip) << 2; if (IPH_PROTO(ip) >= MAX_SLOTS || hlen + sizeof(struct tcp_hdr) > nb->len - || (hlen += TCPH_OFFSET(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) + || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) > nb->len || hlen > MAX_HDR) { PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", diff --git a/lib/liblwip/netif/slipif.c b/lib/liblwip/netif/slipif.c index c19333dd7..137ba89d8 100644 --- a/lib/liblwip/netif/slipif.c +++ b/lib/liblwip/netif/slipif.c @@ -35,6 +35,19 @@ * This file is built upon the file: src/arch/rtxc/netif/sioslip.c * * Author: Magnus Ivarsson + * Simon Goldschmidt + * + * Usage: This netif can be used in three ways: + * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() + * until data is received. + * 2) In your main loop, call slipif_poll() to check for new RX bytes, + * completed packets are fed into netif->input(). + * 3) Call slipif_received_byte[s]() from your serial RX ISR and + * slipif_process_rxqueue() from your main loop. ISR level decodes + * packets and puts completed packets on a queue which is fed into + * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for + * pbuf_alloc to work on ISR level!). + * */ /* @@ -49,20 +62,28 @@ #include "lwip/def.h" #include "lwip/pbuf.h" -#include "lwip/sys.h" #include "lwip/stats.h" #include "lwip/snmp.h" #include "lwip/sio.h" +#include "lwip/sys.h" -#define SLIP_BLOCK 1 -#define SLIP_DONTBLOCK 0 - -#define SLIP_END 0300 /* 0xC0 */ -#define SLIP_ESC 0333 /* 0xDB */ -#define SLIP_ESC_END 0334 /* 0xDC */ -#define SLIP_ESC_ESC 0335 /* 0xDD */ +#define SLIP_END 0xC0 /* 0300: start and end of every packet */ +#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ +#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ +#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ +/** Maximum packet size that is received by this netif */ +#ifndef SLIP_MAX_SIZE #define SLIP_MAX_SIZE 1500 +#endif + +/** Define this to the interface speed for SNMP + * (sio_fd is the sio_fd_t returned by sio_open). + * The default value of zero means 'unknown'. + */ +#ifndef SLIP_SIO_SPEED +#define SLIP_SIO_SPEED(sio_fd) 0 +#endif enum slipif_recv_state { SLIP_RECV_NORMAL, @@ -73,8 +94,11 @@ struct slipif_priv { sio_fd_t sd; /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ struct pbuf *p, *q; - enum slipif_recv_state state; + u8_t state; u16_t i, recved; +#if SLIP_RX_FROM_ISR + struct pbuf *rxpackets; +#endif }; /** @@ -84,11 +108,10 @@ struct slipif_priv { * * @param netif the lwip network interface structure for this slipif * @param p the pbuf chaing packet to send - * @param ipaddr the ip address to send the packet to (not used for slipif) * @return always returns ERR_OK since the serial layer does not provide return values */ -err_t -slipif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +static err_t +slipif_output(struct netif *netif, struct pbuf *p) { struct slipif_priv *priv; struct pbuf *q; @@ -99,11 +122,11 @@ slipif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); LWIP_ASSERT("p != NULL", (p != NULL)); - LWIP_UNUSED_ARG(ipaddr); - + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); priv = netif->state; /* Send pbuf out on the serial I/O device. */ + /* Start with packet delimiter. */ sio_send(SLIP_END, priv->sd); for (q = p; q != NULL; q = q->next) { @@ -111,59 +134,75 @@ slipif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) c = ((u8_t *)q->payload)[i]; switch (c) { case SLIP_END: + /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ sio_send(SLIP_ESC, priv->sd); sio_send(SLIP_ESC_END, priv->sd); break; case SLIP_ESC: + /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ sio_send(SLIP_ESC, priv->sd); sio_send(SLIP_ESC_ESC, priv->sd); break; default: + /* normal byte - no need for escaping */ sio_send(c, priv->sd); break; } } } + /* End with packet delimiter. */ sio_send(SLIP_END, priv->sd); return ERR_OK; } /** - * Static function for easy use of blockig or non-blocking - * sio_read + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() * - * @param fd serial device handle - * @param data pointer to data buffer for receiving - * @param len maximum length (in bytes) of data to receive - * @param block if 1, call sio_read; if 0, call sio_tryread - * @return return value of sio_read of sio_tryread + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values */ -static u32_t -slip_sio_read(sio_fd_t fd, u8_t* data, u32_t len, u8_t block) +static err_t +slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) { - if (block) { - return sio_read(fd, data, len); - } else { - return sio_tryread(fd, data, len); - } + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); } +#if LWIP_IPV6 /** - * Handle the incoming SLIP stream character by character + * Send a pbuf doing the necessary SLIP encapsulation * - * Poll the serial layer by calling sio_read() or sio_tryread(). + * Uses the serial layer's sio_send() * * @param netif the lwip network interface structure for this slipif - * @param block if 1, block until data is received; if 0, return when all data - * from the buffer is received (multiple calls to this function will + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} +#endif /* LWIP_IPV6 */ + +/** + * Handle the incoming SLIP stream character by character + * + * @param netif the lwip network interface structure for this slipif + * @param c received character (multiple calls to this function will * return a complete packet, NULL is returned before - used for polling) * @return The IP packet when SLIP_END is received */ -static struct pbuf * -slipif_input(struct netif *netif, u8_t block) +static struct pbuf* +slipif_rxbyte(struct netif *netif, u8_t c) { struct slipif_priv *priv; - u8_t c; struct pbuf *t; LWIP_ASSERT("netif != NULL", (netif != NULL)); @@ -171,89 +210,105 @@ slipif_input(struct netif *netif, u8_t block) priv = netif->state; - while (slip_sio_read(priv->sd, &c, 1, block) > 0) { - switch (priv->state) { - case SLIP_RECV_NORMAL: - switch (c) { - case SLIP_END: - if (priv->recved > 0) { - /* Received whole packet. */ - /* Trim the pbuf to the size of the received packet. */ - pbuf_realloc(priv->q, priv->recved); - - LINK_STATS_INC(link.recv); - - LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n")); - t = priv->q; - priv->p = priv->q = NULL; - priv->i = priv->recved = 0; - return t; - } - continue; - case SLIP_ESC: - priv->state = SLIP_RECV_ESCAPE; - continue; + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved)); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; } + return NULL; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + return NULL; + } /* end switch (c) */ + break; + case SLIP_RECV_ESCAPE: + /* un-escape END or ESC bytes, leave other bytes + (although that would be a protocol error) */ + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; break; - case SLIP_RECV_ESCAPE: - switch (c) { - case SLIP_ESC_END: - c = SLIP_END; - break; - case SLIP_ESC_ESC: - c = SLIP_ESC; - break; - } - priv->state = SLIP_RECV_NORMAL; - /* FALLTHROUGH */ } + priv->state = SLIP_RECV_NORMAL; + break; + } /* end switch (priv->state) */ + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); - /* byte received, packet not yet completely received */ if (priv->p == NULL) { - /* allocate a new pbuf */ - LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); - priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); - - if (priv->p == NULL) { - LINK_STATS_INC(link.drop); - LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); - /* don't process any further since we got no pbuf to receive to */ - break; - } + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + return NULL; + } - if (priv->q != NULL) { - /* 'chain' the pbuf to the existing chain */ - pbuf_cat(priv->q, priv->p); - } else { - /* p is the first pbuf in the chain */ - priv->q = priv->p; - } + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; } + } - /* this automatically drops bytes if > SLIP_MAX_SIZE */ - if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { - ((u8_t *)priv->p->payload)[priv->i] = c; - priv->recved++; - priv->i++; - if (priv->i >= priv->p->len) { - /* on to the next pbuf */ - priv->i = 0; - if (priv->p->next != NULL && priv->p->next->len > 0) { - /* p is a chain, on to the next in the chain */ - priv->p = priv->p->next; - } else { - /* p is a single pbuf, set it to NULL so next time a new - * pbuf is allocated */ - priv->p = NULL; - } + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; } } } - return NULL; } -#if !NO_SYS +/** Like slipif_rxbyte, but passes completed packets to netif->input + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +static void +slipif_rxbyte_input(struct netif *netif, u8_t c) +{ + struct pbuf *p; + p = slipif_rxbyte(netif, c); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#if SLIP_USE_RX_THREAD /** * The SLIP input thread. * @@ -264,20 +319,17 @@ slipif_input(struct netif *netif, u8_t block) static void slipif_loop_thread(void *nf) { - struct pbuf *p; + u8_t c; struct netif *netif = (struct netif *)nf; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; while (1) { - p = slipif_input(netif, SLIP_BLOCK); - if (p != NULL) { - if (netif->input(p, netif) != ERR_OK) { - pbuf_free(p); - p = NULL; - } + if (sio_read(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); } } } -#endif /* !NO_SYS */ +#endif /* SLIP_USE_RX_THREAD */ /** * SLIP netif initialization @@ -291,29 +343,41 @@ slipif_loop_thread(void *nf) * ERR_IF is serial line couldn't be opened * * @note netif->num must contain the number of the serial port to open - * (0 by default) + * (0 by default). If netif->state is != NULL, it is interpreted as an + * u8_t pointer pointing to the serial port number instead of netif->num. + * */ err_t slipif_init(struct netif *netif) { struct slipif_priv *priv; + u8_t sio_num; LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); /* Allocate private data */ - priv = mem_malloc(sizeof(struct slipif_priv)); + priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); if (!priv) { return ERR_MEM; } netif->name[0] = 's'; netif->name[1] = 'l'; - netif->output = slipif_output; + netif->output = slipif_output_v4; +#if LWIP_IPV6 + netif->output_ip6 = slipif_output_v6; +#endif /* LWIP_IPV6 */ netif->mtu = SLIP_MAX_SIZE; netif->flags |= NETIF_FLAG_POINTTOPOINT; - /* Try to open the serial port (netif->num contains the port number). */ - priv->sd = sio_open(netif->num); + /* netif->state or netif->num contain the port number */ + if (netif->state != NULL) { + sio_num = *(u8_t*)netif->state; + } else { + sio_num = netif->num; + } + /* Try to open the serial port. */ + priv->sd = sio_open(sio_num); if (!priv->sd) { /* Opening the serial port failed. */ mem_free(priv); @@ -326,18 +390,20 @@ slipif_init(struct netif *netif) priv->state = SLIP_RECV_NORMAL; priv->i = 0; priv->recved = 0; +#if SLIP_RX_FROM_ISR + priv->rxpackets = NULL; +#endif netif->state = priv; - /* initialize the snmp variables and counters inside the struct netif - * ifSpeed: no assumption can be made without knowing more about the - * serial line! - */ - NETIF_INIT_SNMP(netif, snmp_ifType_slip, 0); + /* initialize the snmp variables and counters inside the struct netif */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); +#if SLIP_USE_RX_THREAD /* Create a thread to poll the serial line. */ sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); +#endif /* SLIP_USE_RX_THREAD */ return ERR_OK; } @@ -349,19 +415,132 @@ slipif_init(struct netif *netif) void slipif_poll(struct netif *netif) { - struct pbuf *p; + u8_t c; struct slipif_priv *priv; LWIP_ASSERT("netif != NULL", (netif != NULL)); LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); - priv = netif->state; + priv = (struct slipif_priv *)netif->state; + + while (sio_tryread(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } +} + +#if SLIP_RX_FROM_ISR +/** + * Feeds the IP layer with incoming packets that were receive + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_process_rxqueue(struct netif *netif) +{ + struct slipif_priv *priv; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; - while ((p = slipif_input(netif, SLIP_DONTBLOCK)) != NULL) { + SYS_ARCH_PROTECT(old_level); + while (priv->rxpackets != NULL) { + struct pbuf *p = priv->rxpackets; +#if SLIP_RX_QUEUE + /* dequeue packet */ + struct pbuf *q = p; + while ((q->len != q->tot_len) && (q->next != NULL)) { + q = q->next; + } + priv->rxpackets = q->next; + q->next = NULL; +#else /* SLIP_RX_QUEUE */ + priv->rxpackets = NULL; +#endif /* SLIP_RX_QUEUE */ + SYS_ARCH_UNPROTECT(old_level); if (netif->input(p, netif) != ERR_OK) { pbuf_free(p); } + SYS_ARCH_PROTECT(old_level); + } +} + +/** Like slipif_rxbyte, but queues completed packets. + * + * @param netif The lwip network interface structure for this slipif + * @param data Received serial byte + */ +static void +slipif_rxbyte_enqueue(struct netif *netif, u8_t data) +{ + struct pbuf *p; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + SYS_ARCH_DECL_PROTECT(old_level); + + p = slipif_rxbyte(netif, data); + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + if (priv->rxpackets != NULL) { +#if SLIP_RX_QUEUE + /* queue multiple pbufs */ + struct pbuf *q = p; + while(q->next != NULL) { + q = q->next; + } + q->next = p; + } else { +#else /* SLIP_RX_QUEUE */ + pbuf_free(priv->rxpackets); + } + { +#endif /* SLIP_RX_QUEUE */ + priv->rxpackets = p; + } + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Process a received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +void +slipif_received_byte(struct netif *netif, u8_t data) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + slipif_rxbyte_enqueue(netif, data); +} + +/** + * Process multiple received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + * @param len Number of received characters + */ +void +slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) +{ + u8_t i; + u8_t *rxdata = data; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + for (i = 0; i < len; i++, rxdata++) { + slipif_rxbyte_enqueue(netif, *rxdata); } } +#endif /* SLIP_RX_FROM_ISR */ #endif /* LWIP_HAVE_SLIPIF */