diff --git a/CHANGELOG b/CHANGELOG index 29518303..f23128ed 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,10 @@ HISTORY ++ New features: + 2011-05-17: Patch by Ivan Delamer (only checked in by Simon Goldschmidt) + * nearly the whole stack: Finally, we got decent IPv6 support, big thanks to + Ivan! (this is work in progress: we're just post release anyway :-) + 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage) * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static memory message diff --git a/src/api/api_lib.c b/src/api/api_lib.c index b1a9e525..bc7507b7 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -372,7 +372,7 @@ netconn_recv_data(struct netconn *conn, void **new_buf) #endif /* LWIP_SO_RCVTIMEO*/ #if LWIP_TCP - if (conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) { 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 @@ -434,7 +434,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); } @@ -461,7 +461,7 @@ 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 (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) { struct pbuf *p = NULL; /* This is not a listening netconn, since recvmbox is set */ @@ -481,7 +481,11 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf) buf->p = p; buf->ptr = p; buf->port = 0; - ip_addr_set_any(&buf->addr); +#if LWIP_IPV6 + ip6_addr_set_any(&buf->addr.ip6); +#else /* LWIP_IPV6 */ + ip_addr_set_any(&buf->addr.ip4); +#endif /* LWIP_IPV6 */ *new_buf = buf; /* don't set conn->last_err: it's only ERR_OK, anyway */ return ERR_OK; @@ -508,7 +512,7 @@ void netconn_recved(struct netconn *conn, u32_t length) { #if LWIP_TCP - if ((conn != NULL) && (conn->type == NETCONN_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. */ @@ -540,7 +544,15 @@ 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); +#if LWIP_IPV6 + if (conn->pcb.ip->isipv6) { + ip6_addr_set(&buf->addr.ip6, (ip6_addr_t *)addr); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set(&buf->addr.ip4, addr); + } buf->port = port; return netconn_send(conn, buf); } @@ -591,7 +603,7 @@ netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apifl err_t err; 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; } @@ -664,7 +676,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. * @@ -696,7 +708,7 @@ netconn_join_leave_group(struct netconn *conn, NETCONN_SET_SAFE_ERR(conn, err); return err; } -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS /** diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 448f96dd..6f6ffc4d 100644 --- a/src/api/api_msg.c +++ b/src/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 @@ -112,7 +113,15 @@ 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()); +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_copy(buf->addr.ip6, *ip6_current_src_addr()); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_copy(buf->addr.ip4, *ip_current_src_addr()); + } buf->port = pcb->protocol; len = q->tot_len; @@ -175,9 +184,30 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, } else { buf->p = p; buf->ptr = p; - ip_addr_set(&buf->addr, addr); +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + ip6_addr_set(&buf->addr.ip6, (ip6_addr_t *)addr); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set(&buf->addr.ip4, addr); + } buf->port = port; #if LWIP_NETBUF_RECVINFO +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = (void*)(((char*)ip6_current_header()) + + ip6_current_header_tot_len()); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ip6_addr_set(&buf->toaddr.ip6, ip6_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } + else +#endif /* LWIP_IPV6 */ { const struct ip_hdr* iphdr = ip_current_header(); /* get the UDP header - always in the first pbuf, ensured by udp_input */ @@ -185,7 +215,7 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, #if LWIP_CHECKSUM_ON_COPY buf->flags = NETBUF_FLAG_DESTADDR; #endif /* LWIP_CHECKSUM_ON_COPY */ - ip_addr_set(&buf->toaddr, ip_current_dest_addr()); + ip_addr_set(&buf->toaddr.ip4, ip_current_dest_addr()); buf->toport_chksum = udphdr->dest; } #endif /* LWIP_NETBUF_RECVINFO */ @@ -487,7 +517,15 @@ pcb_new(struct api_msg_msg *msg) switch(NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: - msg->conn->pcb.raw = raw_new(msg->msg.n.proto); +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + msg->conn->pcb.raw = raw_new_ip6(msg->msg.n.proto); + } + else +#endif /* LWIP_IPV6 */ + { + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + } if(msg->conn->pcb.raw == NULL) { msg->err = ERR_MEM; break; @@ -497,17 +535,25 @@ pcb_new(struct api_msg_msg *msg) #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: - msg->conn->pcb.udp = udp_new(); +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + msg->conn->pcb.udp = udp_new_ip6(); + } + else +#endif /* LWIP_IPV6 */ + { + msg->conn->pcb.udp = udp_new(); + } if(msg->conn->pcb.udp == NULL) { msg->err = ERR_MEM; break; } #if LWIP_UDPLITE - if (msg->conn->type==NETCONN_UDPLITE) { + if (NETCONNTYPE_ISUDPLITE((msg->conn->type)) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); } #endif /* LWIP_UDPLITE */ - if (msg->conn->type==NETCONN_UDPNOCHKSUM) { + 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); @@ -515,7 +561,15 @@ pcb_new(struct api_msg_msg *msg) #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: - msg->conn->pcb.tcp = tcp_new(); +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + msg->conn->pcb.tcp = tcp_new_ip6(); + } + else +#endif /* LWIP_IPV6 */ + { + msg->conn->pcb.tcp = tcp_new(); + } if(msg->conn->pcb.tcp == NULL) { msg->err = ERR_MEM; break; @@ -680,7 +734,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() */ @@ -738,7 +792,7 @@ do_close_internal(struct netconn *conn) 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); @@ -824,7 +878,7 @@ do_delconn(struct api_msg_msg *msg) (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("msg->conn->type == NETCONN_TCP", NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); msg->err = ERR_INPROGRESS; } else { LWIP_ASSERT("blocking connect in progress", @@ -942,7 +996,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); @@ -1056,7 +1110,7 @@ do_listen(struct api_msg_msg *msg) } 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) { #if TCP_LISTEN_BACKLOG struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); @@ -1113,29 +1167,62 @@ 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 LWIP_IPV6 + if (msg->conn->pcb.ip->isipv6) { + if (ip6_addr_isany(&msg->msg.b->addr.ip6)) { + msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->err = raw_sendto_ip6(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr.ip6); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&msg->msg.b->addr.ip4)) { 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, &msg->msg.b->addr.ip4); } break; #endif #if LWIP_UDP case NETCONN_UDP: #if LWIP_CHECKSUM_ON_COPY - if (ip_addr_isany(&msg->msg.b->addr)) { +#if LWIP_IPV6 + if (msg->conn->pcb.ip->isipv6) { + if (ip6_addr_isany(&msg->msg.b->addr.ip6)) { + 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_ip6(msg->conn->pcb.udp, msg->msg.b->p, + &msg->msg.b->addr.ip6, msg->msg.b->port, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&msg->msg.b->addr.ip4)) { 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, + &msg->msg.b->addr.ip4, 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 LWIP_IPV6 + if (msg->conn->pcb.ip->isipv6) { + if (ip6_addr_isany(&msg->msg.b->addr.ip6)) { + msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->err = udp_sendto_ip6(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr.ip6, msg->msg.b->port); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&msg->msg.b->addr.ip4)) { 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, &msg->msg.b->addr.ip4, msg->msg.b->port); } #endif /* LWIP_CHECKSUM_ON_COPY */ break; @@ -1160,7 +1247,7 @@ do_recv(struct api_msg_msg *msg) { 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); @@ -1315,7 +1402,7 @@ 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 */ @@ -1368,8 +1455,20 @@ void 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 LWIP_IPV6 + if (msg->conn->pcb.ip->isipv6) { + if (msg->msg.ad.local) { + ip6_addr_set((ip6_addr_t *)msg->msg.ad.ipaddr, &(msg->conn->pcb.ip->local_ip.ip6)); + } else { + ip6_addr_set((ip6_addr_t *)msg->msg.ad.ipaddr, &(msg->conn->pcb.ip->remote_ip.ip6)); + } + } + else +#endif /* LWIP_IPV6 */ + { + *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip.ip4 : + msg->conn->pcb.ip->remote_ip.ip4); + } msg->err = ERR_OK; switch (NETCONNTYPE_GROUP(msg->conn->type)) { @@ -1424,9 +1523,9 @@ do_close(struct api_msg_msg *msg) /* @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("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; @@ -1451,7 +1550,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 @@ -1467,10 +1566,24 @@ 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 (msg->conn->pcb.udp->isipv6) { + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mld6_joingroup((ip6_addr_t *)msg->msg.jl.netif_addr, (ip6_addr_t *)msg->msg.jl.multiaddr); + } else { + msg->err = mld6_leavegroup((ip6_addr_t *)msg->msg.jl.netif_addr, (ip6_addr_t *)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(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); + } else { + msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); + } +#endif /* LWIP_IGMP */ } #endif /* LWIP_UDP */ #if (LWIP_TCP || LWIP_RAW) @@ -1484,7 +1597,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 /** diff --git a/src/api/netbuf.c b/src/api/netbuf.c index 9390c9ee..0aded3af 100644 --- a/src/api/netbuf.c +++ b/src/api/netbuf.c @@ -61,7 +61,11 @@ netbuf *netbuf_new(void) if (buf != NULL) { buf->p = NULL; buf->ptr = NULL; - ip_addr_set_any(&buf->addr); +#if LWIP_IPV6 + ip6_addr_set_any(&buf->addr.ip6); +#else /* LWIP_IPV6 */ + ip_addr_set_any(&buf->addr.ip4); +#endif /* LWIP_IPV6 */ buf->port = 0; #if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY #if LWIP_CHECKSUM_ON_COPY @@ -69,7 +73,11 @@ netbuf *netbuf_new(void) #endif /* LWIP_CHECKSUM_ON_COPY */ buf->toport_chksum = 0; #if LWIP_NETBUF_RECVINFO - ip_addr_set_any(&buf->toaddr); +#if LWIP_IPV6 + ip6_addr_set_any(&buf->toaddr.ip6); +#else /* LWIP_IPV6 */ + ip_addr_set_any(&buf->toaddr.ip4); +#endif /* LWIP_IPV6 */ #endif /* LWIP_NETBUF_RECVINFO */ #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ return buf; diff --git a/src/api/sockets.c b/src/api/sockets.c index e36012ce..ba035de5 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -58,6 +58,16 @@ #include +/* Check that the family member of a struct sockaddr matches the socket's IP version */ +#if LWIP_IPV6 +#define SOCK_ADDR_MATCH(name, sock) \ + ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ + (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) +#else /* LWIP_IPV6 */ +#define SOCK_ADDR_MATCH(name, sock) (((name)->sa_family) == AF_INET) +#endif /* LWIP_IPV6 */ + + #define NUM_SOCKETS MEMP_NUM_NETCONN /** Contains all internal pointers and states used for a socket */ @@ -259,7 +269,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; @@ -313,10 +323,19 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { struct lwip_sock *sock, *nsock; struct netconn *newconn; - ip_addr_t naddr; + union { + ip_addr_t ip4; +#if LWIP_IPV6 + ip6_addr_t ip6; +#endif /* LWIP_IPV6 */ + } naddr; u16_t port; int newsock; - struct sockaddr_in sin; + struct sockaddr tempaddr; + struct sockaddr_in * sin; +#if LWIP_IPV6 + struct sockaddr_in6 * sin6; +#endif /* LWIP_IPV6 */ err_t err; SYS_ARCH_DECL_PROTECT(lev); @@ -344,7 +363,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) netconn_set_noautorecved(newconn, 1); /* get the IP address and port of the remote host */ - err = netconn_peer(newconn, &naddr, &port); + err = netconn_peer(newconn, &naddr.ip4, &port); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); netconn_delete(newconn); @@ -357,16 +376,34 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) */ if (NULL != addr) { 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); + memset(&tempaddr, 0, sizeof(tempaddr)); - if (*addrlen > sizeof(sin)) - *addrlen = sizeof(sin); +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(newconn->type)) { + sin6 = (struct sockaddr_in6 *)&tempaddr; + 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, &naddr.ip6); - MEMCPY(addr, &sin, *addrlen); + if (*addrlen > sin6->sin6_len) + *addrlen = sin6->sin6_len; + } + else +#endif /* LWIP_IPV6 */ + { + sin = (struct sockaddr_in *)&tempaddr; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + inet_addr_from_ipaddr(&sin->sin_addr, &naddr.ip4); + + if (*addrlen > sin->sin_len) + *addrlen = sin->sin_len; + } + + MEMCPY(addr, &tempaddr, *addrlen); } newsock = alloc_socket(newconn, 1); @@ -390,7 +427,15 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) 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); +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(newconn->type)) { + ip6_addr_debug_print(SOCKETS_DEBUG, &naddr.ip6); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_debug_print(SOCKETS_DEBUG, &naddr.ip4); + } LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); sock_set_errno(sock, 0); @@ -401,10 +446,18 @@ int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) { struct lwip_sock *sock; - ip_addr_t local_addr; + union { + ip_addr_t ip4; +#if LWIP_IPV6 + ip6_addr_t ip6; +#endif /* LWIP_IPV6 */ + } local_addr; u16_t local_port; err_t err; const struct sockaddr_in *name_in; +#if LWIP_IPV6 + const struct sockaddr_in6 *name_in6; +#endif /* LWIP_IPV6 */ sock = get_socket(s); if (!sock) { @@ -413,18 +466,33 @@ lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) /* 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)), + SOCK_ADDR_MATCH(name, sock) && + ((((mem_ptr_t)name) % 4) == 0)), 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_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); +#if LWIP_IPV6 + if ((name->sa_family) == AF_INET6) { + name_in6 = (const struct sockaddr_in6 *)(void*)name; + + inet6_addr_to_ip6addr(&local_addr.ip6, &name_in6->sin6_addr); + ip6_addr_debug_print(SOCKETS_DEBUG, &local_addr.ip6); + + local_port = name_in6->sin6_port; + } + else +#endif /* LWIP_IPV6 */ + { + name_in = (const struct sockaddr_in *)(void*)name; + + inet_addr_to_ipaddr(&local_addr.ip4, &name_in->sin_addr); + ip_addr_debug_print(SOCKETS_DEBUG, &local_addr.ip4); + + local_port = name_in->sin_port; + } LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port))); - err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); + err = netconn_bind(sock->conn, &local_addr.ip4, ntohs(local_port)); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); @@ -451,7 +519,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); } @@ -468,7 +536,6 @@ 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) { @@ -477,17 +544,38 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) /* 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)), + SOCK_ADDR_MATCH(name, sock) && + ((((mem_ptr_t)name) % 4) == 0)), 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) { + if (name->sa_family == AF_UNSPEC) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); err = netconn_disconnect(sock->conn); - } else { + } +#if LWIP_IPV6 + else if (name->sa_family == AF_INET6) { + const struct sockaddr_in6 *name_in6; + ip6_addr_t remote_addr; + u16_t remote_port; + + name_in6 = (const struct sockaddr_in6 *)(void*)name; + + inet6_addr_to_ip6addr(&remote_addr, &name_in6->sin6_addr); + remote_port = name_in6->sin6_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ip6_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port))); + + err = netconn_connect(sock->conn, (ip_addr_t *)&remote_addr, ntohs(remote_port)); + } +#endif /* LWIP_IPV6 */ + else { + const struct sockaddr_in *name_in; ip_addr_t remote_addr; u16_t remote_port; + name_in = (const struct sockaddr_in *)(void*)name; + inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr); remote_port = name_in->sin_port; @@ -554,7 +642,6 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, struct pbuf *p; u16_t buflen, copylen; int off = 0; - ip_addr_t *addr; u16_t port; u8_t done = 0; err_t err; @@ -588,7 +675,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); @@ -618,7 +705,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; @@ -641,7 +728,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) || @@ -656,47 +743,69 @@ 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); - } else { - addr = netbuf_fromaddr((struct netbuf *)buf); - port = netbuf_fromport((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); - +#if !SOCKETS_DEBUG + if (from && fromlen) +#endif /* !SOCKETS_DEBUG */ + { 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)); - } 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 LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn))) { + ip6_addr_t *fromaddr6; + ip6_addr_t tmpaddr6; + struct sockaddr_in6 sin6; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + /* @todo: implement netconn_getaddr() for IPv6 addresses */ + ip6_addr_set_any(&tmpaddr6); + fromaddr6 = &tmpaddr6; + port = 0; + } else { + fromaddr6 = netbuf_fromaddr_ip6((struct netbuf *)buf); + port = netbuf_fromport((struct netbuf *)buf); + } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(port); + inet6_addr_from_ip6addr(&sin6.sin6_addr, fromaddr6); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); - ip_addr_debug_print(SOCKETS_DEBUG, addr); + if (from && fromlen) { + if (*fromlen > sizeof(sin6)) { + *fromlen = sizeof(sin6); + } + MEMCPY(from, &sin6, *fromlen); + } + + ip6_addr_debug_print(SOCKETS_DEBUG, fromaddr6); + } else +#endif /* LWIP_IPV6 */ + { + ip_addr_t *fromaddr4; + ip_addr_t tmpaddr4; + struct sockaddr_in sin; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + fromaddr4 = &tmpaddr4; + netconn_getaddr(sock->conn, fromaddr4, &port, 0); + } else { + fromaddr4 = netbuf_fromaddr((struct netbuf *)buf); + port = netbuf_fromport((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, fromaddr4); + + if (from && fromlen) { + if (*fromlen > sizeof(sin)) { + *fromlen = sizeof(sin); + } + MEMCPY(from, &sin, *fromlen); + } + + ip_addr_debug_print(SOCKETS_DEBUG, &fromaddr4); + } LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); -#endif /* SOCKETS_DEBUG */ } } @@ -705,7 +814,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)); @@ -713,7 +822,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); @@ -757,7 +866,7 @@ lwip_send(int s, const void *data, size_t size, int flags) return -1; } - if (sock->conn->type != NETCONN_TCP) { + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) { #if (LWIP_UDP || LWIP_RAW) return lwip_sendto(s, data, size, flags, NULL, 0); #else /* (LWIP_UDP || LWIP_RAW) */ @@ -791,7 +900,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; @@ -802,7 +910,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, return -1; } - if (sock->conn->type == NETCONN_TCP) { + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_TCP) { #if LWIP_TCP return lwip_send(s, data, size, flags); #else /* LWIP_TCP */ @@ -817,22 +925,25 @@ lwip_sendto(int s, const void *data, size_t size, int flags, 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))), + SOCK_ADDR_MATCH(to, sock) && + ((((mem_ptr_t)to) % 4) == 0))), sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - to_in = (const struct sockaddr_in *)(void*)to; #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...) */ { struct pbuf* p; ip_addr_t *remote_addr; +#if LWIP_IPV6 + ip6_addr_t *remote_addr6; +#endif /* LWIP_IPV6 */ #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(sock->conn->type) != NETCONN_RAW) { chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); } else #endif /* LWIP_CHECKSUM_ON_COPY */ @@ -843,20 +954,39 @@ 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) { +#if LWIP_IPV6 + if (to->sa_family == AF_INET6) { + const struct sockaddr_in6 *to_in6; + to_in6 = (const struct sockaddr_in6 *)(void*)to; + inet6_addr_to_ip6addr_p(remote_addr6, &to_in6->sin6_addr); + remote_addr = (ip_addr_t *)remote_addr6; + remote_port = ntohs(to_in6->sin6_port); + } + else +#endif /* LWIP_IPV6 */ + { + const struct sockaddr_in *to_in; + to_in = (const struct sockaddr_in *)(void*)to; + inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr); + remote_port = ntohs(to_in->sin_port); + } } else { - remote_addr = &sock->conn->pcb.raw->remote_ip; - if (sock->conn->type == NETCONN_RAW) { - remote_port = 0; - } else { - remote_port = sock->conn->pcb.udp->remote_port; + remote_addr = IP_ADDR_ANY; +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(sock->conn->type)) { + remote_addr6 = IP6_ADDR_ANY; + remote_addr = (ip_addr_t *)remote_addr6; + } + else +#endif /* LWIP_IPV6 */ + { + remote_addr = IP_ADDR_ANY; } } LOCK_TCPIP_CORE(); - if (sock->conn->type == NETCONN_RAW) { + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_RAW) { err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr); } else { #if LWIP_UDP @@ -885,18 +1015,48 @@ 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); +#if LWIP_IPV6 + if ((to->sa_family) == AF_INET6) { + const struct sockaddr_in6 *to_in6; + to_in6 = (const struct sockaddr_in6 *)(void*)to; + inet6_addr_to_ip6addr(&buf.addr.ip6, &to_in6->sin6_addr); + remote_port = ntohs(to_in6->sin6_port); + } + else +#endif /* LWIP_IPV6 */ + { + const struct sockaddr_in *to_in; + to_in = (const struct sockaddr_in *)(void*)to; + inet_addr_to_ipaddr(&buf.addr.ip4, &to_in->sin_addr); + remote_port = ntohs(to_in->sin_port); + } netbuf_fromport(&buf) = remote_port; } else { - remote_port = 0; - ip_addr_set_any(&buf.addr); + remote_port = 0; +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(sock->conn->type)) { + ip6_addr_set_any(&buf.addr.ip6); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set_any(&buf.addr.ip4); + } netbuf_fromport(&buf) = 0; } + 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); +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(sock->conn->type)) { + ip6_addr_debug_print(SOCKETS_DEBUG, &buf.addr.ip6); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr.ip4); + } LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); /* make the buffer point to the data that should be sent */ @@ -906,7 +1066,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(sock->conn->type) != NETCONN_RAW) { u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); netbuf_set_chksum(&buf, chksum); err = ERR_OK; @@ -937,23 +1097,41 @@ lwip_socket(int domain, int type, int protocol) struct netconn *conn; int i; +#if !LWIP_IPV6 LWIP_UNUSED_ARG(domain); +#endif /* LWIP_IPV6 */ /* create a netconn */ switch (type) { case SOCK_RAW: +#if LWIP_IPV6 + conn = netconn_new_with_proto_and_callback((domain == AF_INET) ? NETCONN_RAW : NETCONN_RAW_IPV6, + (u8_t)protocol, event_callback); +#else /* LWIP_IPV6 */ conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); +#endif /* LWIP_IPV6 */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; case SOCK_DGRAM: +#if LWIP_IPV6 + conn = netconn_new_with_callback((domain == AF_INET) ? + ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP) : + ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE_IPV6 : NETCONN_UDP_IPV6) , + event_callback); +#else /* LWIP_IPV6 */ conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP, event_callback); +#endif /* LWIP_IPV6 */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; case SOCK_STREAM: +#if LWIP_IPV6 + conn = netconn_new_with_callback((domain == AF_INET) ? NETCONN_TCP : NETCONN_TCP_IPV6, event_callback); +#else /* LWIP_IPV6 */ conn = netconn_new_with_callback(NETCONN_TCP, event_callback); +#endif /* LWIP_IPV6 */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); if (conn != NULL) { @@ -1365,7 +1543,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; } @@ -1395,33 +1573,63 @@ 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; sock = get_socket(s); if (!sock) { return -1; } - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; +#if LWIP_IPV6 + if (NETCONNTYPE_ISIPV6(sock->conn->type)) { + struct sockaddr_in6 sin6; + ip6_addr_t naddr6; - /* get the IP address and port */ - netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; - 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)); + /* get the IP address and port */ + netconn_getaddr(sock->conn, (ip_addr_t *)&naddr6, &sin6.sin6_port, local); - sin.sin_port = htons(sin.sin_port); - inet_addr_from_ipaddr(&sin.sin_addr, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ip6_addr_debug_print(SOCKETS_DEBUG, &naddr6); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin6.sin6_port)); - if (*namelen > sizeof(sin)) { - *namelen = sizeof(sin); + sin6.sin6_port = htons(sin6.sin6_port); + inet6_addr_from_ip6addr(&sin6.sin6_addr, &naddr6); + + if (*namelen > sizeof(sin6)) { + *namelen = sizeof(sin6); + } + + MEMCPY(name, &sin6, *namelen); } + else +#endif /* LWIP_IPV6 */ + { + struct sockaddr_in sin; + ip_addr_t naddr; - MEMCPY(name, &sin, *namelen); + 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); + + 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); + + if (*namelen > sizeof(sin)) { + *namelen = sizeof(sin); + } + + MEMCPY(name, &sin, *namelen); + } sock_set_errno(sock, 0); return 0; } @@ -1495,7 +1703,12 @@ 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 ( +#if LWIP_IPV6 + ((sock->conn->type != NETCONN_UDP) && (sock->conn->type != NETCONN_UDP_IPV6)) || +#else /* LWIP_IPV6 */ + (sock->conn->type != NETCONN_UDP) || +#endif /* LWIP_IPV6 */ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { /* this flag is only available for UDP, not for UDP lite */ err = EAFNOSUPPORT; @@ -1559,7 +1772,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(sock->conn->type) != NETCONN_TCP) return 0; switch (optname) { @@ -1588,7 +1801,11 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) } /* If this is no UDP lite socket, ignore any options. */ +#if LWIP_IPV6 + if ((sock->conn->type != NETCONN_UDPLITE) && (sock->conn->type != NETCONN_UDPLITE_IPV6)) { +#else /* LWIP_IPV6 */ if (sock->conn->type != NETCONN_UDPLITE) { +#endif /* LWIP_IPV6 */ return 0; } @@ -1892,7 +2109,12 @@ 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 ( +#if LWIP_IPV6 + ((sock->conn->type != NETCONN_UDP) && (sock->conn->type != NETCONN_UDP_IPV6)) || +#else /* LWIP_IPV6 */ + (sock->conn->type != NETCONN_UDP) || +#endif /* LWIP_IPV6 */ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { /* this flag is only available for UDP, not for UDP lite */ err = EAFNOSUPPORT; @@ -1969,7 +2191,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(sock->conn->type) != NETCONN_TCP) return 0; switch (optname) { @@ -1998,7 +2220,11 @@ 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 LWIP_IPV6 + if ((sock->conn->type != NETCONN_UDPLITE) && (sock->conn->type != NETCONN_UDPLITE_IPV6)) +#else /* LWIP_IPV6 */ if (sock->conn->type != NETCONN_UDPLITE) +#endif /* LWIP_IPV6 */ return 0; switch (optname) { @@ -2281,7 +2507,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; diff --git a/src/core/init.c b/src/core/init.c index bf5c79da..30f09845 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -56,6 +56,9 @@ #include "lwip/dns.h" #include "lwip/timers.h" #include "netif/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" /* Compile-time sanity checks for configuration errors. * These can be done independently of LWIP_DEBUG, without penalty. @@ -299,6 +302,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/src/core/ipv4/icmp.c b/src/core/ipv4/icmp.c index 32902a52..ca59acfc 100644 --- a/src/core/ipv4/icmp.c +++ b/src/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 */ diff --git a/src/core/ipv4/igmp.c b/src/core/ipv4/igmp.c index 4e4405e1..cf2e8ce1 100644 --- a/src/core/ipv4/igmp.c +++ b/src/core/ipv4/igmp.c @@ -382,14 +382,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 +396,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 +404,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 */ diff --git a/src/core/ipv4/inet_chksum.c b/src/core/ipv4/inet_chksum.c index 960252f6..b95f7f28 100644 --- a/src/core/ipv4/inet_chksum.c +++ b/src/core/ipv4/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; diff --git a/src/core/ipv4/ip.c b/src/core/ipv4/ip.c index 6f248716..5e9a387d 100644 --- a/src/core/ipv4/ip.c +++ b/src/core/ipv4/ip.c @@ -496,23 +496,27 @@ ip_input(struct pbuf *p, struct netif *inp) case IP_PROTO_UDPLITE: #endif /* LWIP_UDPLITE */ snmp_inc_ipindelivers(); + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ udp_input(p, inp); break; #endif /* LWIP_UDP */ #if LWIP_TCP case IP_PROTO_TCP: snmp_inc_ipindelivers(); + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ tcp_input(p, inp); break; #endif /* LWIP_TCP */ #if LWIP_ICMP case IP_PROTO_ICMP: snmp_inc_ipindelivers(); + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ icmp_input(p, inp); break; #endif /* LWIP_ICMP */ #if LWIP_IGMP case IP_PROTO_IGMP: + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ igmp_input(p, inp, ¤t_iphdr_dest); break; #endif /* LWIP_IGMP */ diff --git a/src/core/ipv6/icmp6.c b/src/core/ipv6/icmp6.c index 4fcc8955..c92235e1 100644 --- a/src/core/ipv6/icmp6.c +++ b/src/core/ipv6/icmp6.c @@ -1,5 +1,11 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + /* - * 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, @@ -26,154 +32,272 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Adam Dunkels + * Author: Ivan Delamer * + * + * Please coordinate changes and requests with Ivan Delamer + * */ -/* Some ICMP messages should be passed to the transport protocols. This - is not implemented. */ - #include "lwip/opt.h" -#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ -#include "lwip/icmp.h" -#include "lwip/inet.h" -#include "lwip/ip.h" -#include "lwip/def.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_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 type, u8_t code, u32_t data); + + +/** + * 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 -icmp_input(struct pbuf *p, struct netif *inp) +icmp6_input(struct pbuf *p, struct netif *inp) { - u8_t type; - struct icmp_echo_hdr *iecho; - struct ip_hdr *iphdr; - struct ip_addr tmpaddr; + struct icmp6_hdr *icmp6hdr; + struct pbuf * r; + ip6_addr_t * reply_src; - ICMP_STATS_INC(icmp.recv); + ICMP6_STATS_INC(icmp6.recv); - /* TODO: check length before accessing payload! */ + /* 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; + } - type = ((u8_t *)p->payload)[0]; + icmp6hdr = (struct icmp6_hdr *)p->payload; - switch (type) { - case ICMP6_ECHO: - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); - - if (p->tot_len < sizeof(struct icmp_echo_hdr)) { - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); +#if LWIP_ICMP6_CHECKSUM_CHECK + if (ip6_chksum_pseudo(p, ip6_current_src_addr(), ip6_current_dest_addr(), + IP6_NEXTH_ICMP6, p->tot_len) != 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); - ICMP_STATS_INC(icmp.lenerr); + ICMP6_STATS_INC(icmp6.drop); return; } - iecho = p->payload; - iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN); - if (inet_chksum_pbuf(p) != 0) { - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); - ICMP_STATS_INC(icmp.chkerr); - /* return;*/ - } - LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len)); - ip_addr_set(&tmpaddr, &(iphdr->src)); - ip_addr_set(&(iphdr->src), &(iphdr->dest)); - ip_addr_set(&(iphdr->dest), &tmpaddr); - iecho->type = ICMP6_ER; - /* adjust the checksum */ - if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) { - iecho->chksum += htons(ICMP6_ECHO << 8) + 1; - } else { - iecho->chksum += htons(ICMP6_ECHO << 8); - } - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); - ICMP_STATS_INC(icmp.xmit); +#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. */ + 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; + } + + /* 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, + reply_src, ip6_current_src_addr(), + IP6_NEXTH_ICMP6, r->tot_len); + + /* 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); - /* LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ - ip_output_if (p, &(iphdr->src), IP_HDRINCL, - iphdr->hoplim, IP_PROTO_ICMP, inp); break; default: - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type)); - ICMP_STATS_INC(icmp.proterr); - ICMP_STATS_INC(icmp.drop); + 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 -icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) { - struct pbuf *q; - struct ip_hdr *iphdr; - struct icmp_dur_hdr *idur; - - /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ - q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); - /* ICMP header + IP header + 8 bytes of data */ - if (q == NULL) { - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); - pbuf_free(p); - return; - } - LWIP_ASSERT("check that first pbuf can hold icmp message", - (q->len >= (8 + IP_HLEN + 8))); - - iphdr = p->payload; - - idur = q->payload; - idur->type = (u8_t)ICMP6_DUR; - idur->icode = (u8_t)t; - - SMEMCPY((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8); - - /* calculate checksum */ - idur->chksum = 0; - idur->chksum = inet_chksum(idur, q->len); - ICMP_STATS_INC(icmp.xmit); - - ip_output(q, NULL, - (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); - pbuf_free(q); + icmp6_send_response(p, ICMP6_TYPE_DUR, c, 0); } +/** + * 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 -icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, ICMP6_TYPE_PTB, 0, mtu); +} + +/** + * 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, ICMP6_TYPE_TE, c, 0); +} + +/** + * 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, ICMP6_TYPE_PP, c, pointer); +} + +/** + * 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 type Type of the ICMPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t type, u8_t code, u32_t data) { struct pbuf *q; - struct ip_hdr *iphdr; - struct icmp_te_hdr *tehdr; + struct icmp6_hdr *icmp6hdr; + ip6_addr_t * reply_src; - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n")); - - /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ - q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); - /* ICMP header + IP header + 8 bytes of data */ + /* 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_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); - pbuf_free(p); + 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 message", - (q->len >= (8 + IP_HLEN + 8))); + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); - iphdr = p->payload; - - tehdr = q->payload; - tehdr->type = (u8_t)ICMP6_TE; - tehdr->icode = (u8_t)t; + 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 + 8, (u8_t *)p->payload, IP_HLEN + 8); + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Select an address to use as source. */ + reply_src = ip6_select_source_address(current_netif, ip6_current_src_addr()); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } /* calculate checksum */ - tehdr->chksum = 0; - tehdr->chksum = inet_chksum(tehdr, q->len); - ICMP_STATS_INC(icmp.xmit); - ip_output(q, NULL, - (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + icmp6hdr->chksum = 0; + icmp6hdr->chksum = ip6_chksum_pseudo(q, reply_src, ip6_current_src_addr(), + IP6_NEXTH_ICMP6, q->tot_len); + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output(q, reply_src, ip6_current_src_addr(), LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6); pbuf_free(q); } -#endif /* LWIP_ICMP */ + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/src/core/ipv6/inet6.c b/src/core/ipv6/inet6.c index c3de85c0..bdf4ff4f 100644 --- a/src/core/ipv6/inet6.c +++ b/src/core/ipv6/inet6.c @@ -1,12 +1,11 @@ /** * @file - * Functions common to all TCP/IPv6 modules, such as the Internet checksum and the - * byte order functions. * + * INET v6 addresses. */ /* - * 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, @@ -33,131 +32,20 @@ * * 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" +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + #include "lwip/def.h" -#include "lwip/inet.h" +#include "lwip/inet6.h" -/* chksum: - * - * Sums up all 16 bit words in a memory portion. Also includes any odd byte. - * This function is used by the other checksum functions. - * - * For now, this is not optimized. Must be optimized for the particular processor - * arcitecture on which it is to run. Preferebly coded in assembler. - */ +/** @see ip6_addr.c for implementation of functions. */ -static u32_t -chksum(void *dataptr, u16_t len) -{ - u16_t *sdataptr = dataptr; - u32_t acc; - - - for(acc = 0; len > 1; len -= 2) { - acc += *sdataptr++; - } - - /* add up any odd byte */ - if (len == 1) { - acc += htons((u16_t)(*(u8_t *)dataptr) << 8); - } - - return acc; - -} - -/* inet_chksum_pseudo: - * - * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. - */ - -u16_t -inet_chksum_pseudo(struct pbuf *p, - struct ip_addr *src, struct ip_addr *dest, - u8_t proto, u32_t proto_len) -{ - u32_t acc; - struct pbuf *q; - u8_t swapped, i; - - acc = 0; - swapped = 0; - for(q = p; q != NULL; q = q->next) { - acc += chksum(q->payload, q->len); - while (acc >> 16) { - acc = (acc & 0xffff) + (acc >> 16); - } - if (q->len % 2 != 0) { - swapped = 1 - swapped; - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - } - } - - if (swapped) { - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - } - - for(i = 0; i < 8; i++) { - acc += ((u16_t *)src->addr)[i] & 0xffff; - acc += ((u16_t *)dest->addr)[i] & 0xffff; - while (acc >> 16) { - acc = (acc & 0xffff) + (acc >> 16); - } - } - acc += (u16_t)htons((u16_t)proto); - acc += ((u16_t *)&proto_len)[0] & 0xffff; - acc += ((u16_t *)&proto_len)[1] & 0xffff; - - while (acc >> 16) { - acc = (acc & 0xffff) + (acc >> 16); - } - return ~(acc & 0xffff); -} - -/* inet_chksum: - * - * Calculates the Internet checksum over a portion of memory. Used primarely for IP - * and ICMP. - */ - -u16_t -inet_chksum(void *dataptr, u16_t len) -{ - u32_t acc, sum; - - acc = chksum(dataptr, len); - sum = (acc & 0xffff) + (acc >> 16); - sum += (sum >> 16); - return ~(sum & 0xffff); -} - -u16_t -inet_chksum_pbuf(struct pbuf *p) -{ - u32_t acc; - struct pbuf *q; - u8_t swapped; - - acc = 0; - swapped = 0; - for(q = p; q != NULL; q = q->next) { - acc += chksum(q->payload, q->len); - while (acc >> 16) { - acc = (acc & 0xffff) + (acc >> 16); - } - if (q->len % 2 != 0) { - swapped = 1 - swapped; - acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); - } - } - - if (swapped) { - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - } - return ~(acc & 0xffff); -} +#endif /* LWIP_IPV6 */ diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c index b945fc5d..5c58f05c 100644 --- a/src/core/ipv6/ip6.c +++ b/src/core/ipv6/ip6.c @@ -1,5 +1,11 @@ +/** + * @file + * + * IPv6 layer. + */ + /* - * 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, @@ -26,372 +32,947 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Adam Dunkels + * Author: Ivan Delamer * - */ - - - -/* ip.c - * - * This is the code for the IP layer for IPv6. * + * 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/ip.h" -#include "lwip/inet.h" #include "lwip/netif.h" -#include "lwip/icmp.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" -#include "arch/perf.h" -/* ip_init: +/** Header of the input IPv6 packet currently being processed. */ +const struct ip6_hdr *current_ip6_header; +/** Total header length of current_ip6_header (i.e. after this, the UDP/TCP header starts) */ +u16_t current_ip6_header_tot_len; +/** Source IPv6 address of current_header */ +ip6_addr_t current_ip6hdr_src; +/** Destination IPv6 address of current_header */ +ip6_addr_t current_ip6hdr_dest; + + + +/** + * 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 * - * Initializes the IP layer. + * @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 */ - -void -ip_init(void) -{ -} - -/* ip_route: - * - * Finds the appropriate network interface for a given IP address. It searches the - * list of network interfaces linearly. A match is found if the masked IP address of - * the network interface equals the masked IP address given to the function. - */ - struct netif * -ip_route(struct ip_addr *dest) +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) { - if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { - return netif; + 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; } -/* ip_forward: +/** + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. * - * Forwards an IP packet. It finds an appropriate route for the packet, decrements - * the TTL value of the packet, adjusts the checksum and outputs the packet on the - * appropriate interface. + * @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; + } + } + + 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 -ip_forward(struct pbuf *p, struct ip_hdr *iphdr) +ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) { struct netif *netif; - PERF_START; - - if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { - - LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for ")); -#if IP_DEBUG - ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); -#endif /* IP_DEBUG */ - LWIP_DEBUGF(IP_DEBUG, ("\n")); - pbuf_free(p); + /* 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; } - /* Decrement TTL and send ICMP if ttl == 0. */ - if (--iphdr->hoplim == 0) { -#if LWIP_ICMP + + /* 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 (iphdr->nexthdr != IP_PROTO_ICMP) { - icmp_time_exceeded(p, ICMP_TE_TTL); + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); } -#endif /* LWIP_ICMP */ - pbuf_free(p); +#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; } - /* Incremental update of the IP checksum. */ - /* if (iphdr->chksum >= htons(0xffff - 0x100)) { - iphdr->chksum += htons(0x100) + 1; - } else { - iphdr->chksum += htons(0x100); - }*/ + /* 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; + } + 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()))); - LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to ")); -#if IP_DEBUG - ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); -#endif /* IP_DEBUG */ - LWIP_DEBUGF(IP_DEBUG, ("\n")); - - IP_STATS_INC(ip.fw); - IP_STATS_INC(ip.xmit); - - PERF_STOP("ip_forward"); - - netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); + /* 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 */ -/* ip_input: - * - * This function is called by the network interface device driver when an IP 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 ip_forward). The IP checksum is always checked. + +/** + * 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) */ - -void -ip_input(struct pbuf *p, struct netif *inp) { - struct ip_hdr *iphdr; +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 IP_ACCEPT_LINK_LAYER_ADDRESSING + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ - - PERF_START; - -#if IP_DEBUG - ip_debug_print(p); -#endif /* IP_DEBUG */ - - - IP_STATS_INC(ip.recv); + IP6_STATS_INC(ip6.recv); /* identify the IP header */ - iphdr = p->payload; - - - if (iphdr->v != 6) { - LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n")); -#if IP_DEBUG - ip_debug_print(p); -#endif /* IP_DEBUG */ + 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); - IP_STATS_INC(ip.err); - IP_STATS_INC(ip.drop); - return; + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; } - /* is this packet for us? */ - for(netif = netif_list; netif != NULL; netif = netif->next) { -#if IP_DEBUG - LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest ")); - ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); - LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr ")); - ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); - LWIP_DEBUGF(IP_DEBUG, ("\n")); -#endif /* IP_DEBUG */ - if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) { + /* 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(current_ip6hdr_dest, ip6hdr->dest); + ip6_addr_copy(current_ip6hdr_src, ip6hdr->src); + + /* current header pointer. */ + current_ip6_header = ip6hdr; + + /* 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())) { + /* Accept all solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = inp; + } +#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 (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->name[0], netif->name[1])); + } + + /* "::" 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. */ + current_netif = inp; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = current_ip6_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); + current_ip6_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); + current_ip6_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); + current_ip6_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; + current_ip6_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 = current_ip6_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, current_ip6_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, -current_ip6_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -current_ip6_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -current_ip6_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, current_ip6_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: + current_netif = NULL; + current_ip6_header = NULL; + current_ip6_header_tot_len = 0; + ip6_addr_set_any(¤t_ip6hdr_src); + ip6_addr_set_any(¤t_ip6hdr_dest); - if (netif == NULL) { - /* packet not for us, route or discard */ -#if IP_FORWARD - ip_forward(p, iphdr); -#endif - pbuf_free(p); - return; - } - - pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len)); - - /* send to upper layers */ -#if IP_DEBUG - /* LWIP_DEBUGF("ip_input: \n"); - ip_debug_print(p); - LWIP_DEBUGF("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ -#endif /* IP_DEBUG */ - - if(pbuf_header(p, -IP_HLEN)) { - LWIP_ASSERT("Can't move over header in packet", 0); - return; - } - - switch (iphdr->nexthdr) { - case IP_PROTO_UDP: - udp_input(p, inp); - break; - case IP_PROTO_TCP: - tcp_input(p, inp); - break; -#if LWIP_ICMP - case IP_PROTO_ICMP: - icmp_input(p, inp); - break; -#endif /* LWIP_ICMP */ - default: -#if LWIP_ICMP - /* send ICMP destination protocol unreachable */ - icmp_dest_unreach(p, ICMP_DUR_PROTO); -#endif /* LWIP_ICMP */ - pbuf_free(p); - LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n", - iphdr->nexthdr)); - - IP_STATS_INC(ip.proterr); - IP_STATS_INC(ip.drop); - } - PERF_STOP("ip_input"); + return ERR_OK; } -/* ip_output_if: +/** + * 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. * - * Sends an IP packet on a network interface. This function constructs the IP header - * and calculates the IP header checksum. If the source IP address is NULL, - * the IP address of the outgoing network interface is filled in as source address. + * @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 -ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, - u8_t ttl, - u8_t proto, struct netif *netif) +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 ip_hdr *iphdr; + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; - PERF_START; + /* 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); - LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); - if (pbuf_header(p, IP_HLEN)) { - LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n")); - IP_STATS_INC(ip.err); - - return ERR_BUF; - } - LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); - - iphdr = p->payload; - - - if (dest != IP_HDRINCL) { - LWIP_DEBUGF(IP_DEBUG, ("!IP_HDRLINCL\n")); - iphdr->hoplim = ttl; - iphdr->nexthdr = proto; - iphdr->len = htons(p->tot_len - IP_HLEN); - ip_addr_set(&(iphdr->dest), dest); - - iphdr->v = 6; - - if (ip_addr_isany(src)) { - ip_addr_set(&(iphdr->src), &(netif->ip_addr)); - } else { - ip_addr_set(&(iphdr->src), src); + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != IP6_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 { - dest = &(iphdr->dest); + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; } - IP_STATS_INC(ip.xmit); + IP6_STATS_INC(ip6.xmit); - LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len)); -#if IP_DEBUG - ip_debug_print(p); -#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip6_debug_print(p); - PERF_STOP("ip_output_if"); - return netif->output(netif, p, dest); +#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); } -/* ip_output: +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_output_if to do the actual work. * - * Simple interface to ip_output_if. It finds the outgoing network interface and - * calls upon ip_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 -ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, - u8_t ttl, u8_t proto) +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; - if ((netif = ip_route(dest)) == NULL) { - LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); - IP_STATS_INC(ip.rterr); + + /* 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 ((netif = ip6_route(src, dest)) == 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 ip_output_if (p, src, dest, ttl, proto, netif); + 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 -ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, - u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +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; err_t err; - if ((netif = ip_route(dest)) == NULL) { - LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); - IP_STATS_INC(ip.rterr); + /* 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 ((netif = ip6_route(src, dest)) == 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->addr_hint = addr_hint; - err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); netif->addr_hint = NULL; return err; } #endif /* LWIP_NETIF_HWADDRHINT*/ -#if IP_DEBUG -void -ip_debug_print(struct pbuf *p) +#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 ip_hdr *iphdr = p->payload; + struct ip6_hbh_hdr * hbh_hdr; - LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); - LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" | %"X16_F"%"X16_F" | %"X16_F"%"X16_F" | (v, traffic class, flow label)\n", - iphdr->v, - iphdr->tclass1, iphdr->tclass2, - iphdr->flow1, iphdr->flow2)); - LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" | %2"U16_F" | %2"U16_F" | (len, nexthdr, hoplim)\n", - ntohs(iphdr->len), - iphdr->nexthdr, - iphdr->hoplim)); - LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", - (ntohl(iphdr->src.addr[0]) >> 16) & 0xffff, - ntohl(iphdr->src.addr[0]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", - (ntohl(iphdr->src.addr[1]) >> 16) & 0xffff, - ntohl(iphdr->src.addr[1]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", - (ntohl(iphdr->src.addr[2]) >> 16) & 0xffff, - ntohl(iphdr->src.addr[2]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", - (ntohl(iphdr->src.addr[3]) >> 16) & 0xffff, - ntohl(iphdr->src.addr[3]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", - (ntohl(iphdr->dest.addr[0]) >> 16) & 0xffff, - ntohl(iphdr->dest.addr[0]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", - (ntohl(iphdr->dest.addr[1]) >> 16) & 0xffff, - ntohl(iphdr->dest.addr[1]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", - (ntohl(iphdr->dest.addr[2]) >> 16) & 0xffff, - ntohl(iphdr->dest.addr[2]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", - (ntohl(iphdr->dest.addr[3]) >> 16) & 0xffff, - ntohl(iphdr->dest.addr[3]) & 0xffff)); - LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + /* 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 /* IP_DEBUG */ +#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/src/core/ipv6/ip6_addr.c b/src/core/ipv6/ip6_addr.c index 2da6cea4..3b682051 100644 --- a/src/core/ipv6/ip6_addr.c +++ b/src/core/ipv6/ip6_addr.c @@ -1,5 +1,11 @@ +/** + * @file + * + * IPv6 addresses. + */ + /* - * 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, @@ -26,47 +32,210 @@ * * This file is part of the lwIP TCP/IP stack. * - * Author: Adam Dunkels + * Author: Ivan Delamer * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * */ #include "lwip/opt.h" -#include "lwip/ip_addr.h" -#include "lwip/inet.h" -u8_t -ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, - struct ip_addr *mask) +#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) { - return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) && - (addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) && - (addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) && - (addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3])); - + 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 (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 { + 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 (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. */ + 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; + } -u8_t -ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2) +/** + * 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) { - return(addr1->addr[0] == addr2->addr[0] && - addr1->addr[1] == addr2->addr[1] && - addr1->addr[2] == addr2->addr[2] && - addr1->addr[3] == addr2->addr[3]); + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); } -void -ip_addr_set(struct ip_addr *dest, struct ip_addr *src) +/** + * 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) { - SMEMCPY(dest, src, sizeof(struct ip_addr)); - /* dest->addr[0] = src->addr[0]; - dest->addr[1] = src->addr[1]; - dest->addr[2] = src->addr[2]; - dest->addr[3] = src->addr[3];*/ -} + u32_t current_block_index, current_block_value; + s32_t zero_flag, i; -u8_t -ip_addr_isany(struct ip_addr *addr) -{ - if (addr == NULL) return 1; - return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0); + 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)); + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + 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/src/core/memp.c b/src/core/memp.c index 4da879a5..bfe6ee60 100644 --- a/src/core/memp.c +++ b/src/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 diff --git a/src/core/netif.c b/src/core/netif.c index f190b1f2..e93762af 100644 --- a/src/core/netif.c +++ b/src/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) @@ -138,6 +145,9 @@ 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 +155,12 @@ 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); + } +#endif /* LWIP_IPV6 */ netif->flags = 0; #if LWIP_DHCP /* netif not under DHCP control by default */ @@ -154,6 +170,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 +190,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; @@ -245,6 +275,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); @@ -331,10 +365,10 @@ netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) 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(&(pcb->local_ip.ip4), &(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(&(pcb->local_ip.ip4)) #endif /* LWIP_AUTOIP */ ) { /* this connection must be aborted */ @@ -348,11 +382,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(&(lpcb->local_ip.ip4)))) && + (ip_addr_cmp(&(lpcb->local_ip.ip4), &(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(&(lpcb->local_ip.ip4), ipaddr); } } } @@ -471,6 +505,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 */ + } } } @@ -541,6 +585,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 +798,62 @@ 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 */ +} +#endif /* LWIP_IPV6 */ diff --git a/src/core/raw.c b/src/core/raw.c index 9fcb1003..8a0ce762 100644 --- a/src/core/raw.c +++ b/src/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,11 +81,24 @@ 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; @@ -91,17 +106,38 @@ raw_input(struct pbuf *p, struct netif *inp) /* this allows multiple pcbs to match against the packet by design */ while ((eaten == 0) && (pcb != NULL)) { if ((pcb->protocol == proto) && - (ip_addr_isany(&pcb->local_ip) || - ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) { +#if LWIP_IPV6 + ((pcb->isipv6 && + (ip6_addr_isany(&pcb->local_ip.ip6) || + ip6_addr_cmp(&pcb->local_ip.ip6, ip6_current_dest_addr()))) || + (!pcb->isipv6 && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + (ip_addr_isany(&pcb->local_ip.ip4) || + ip_addr_cmp(&(pcb->local_ip.ip4), ip_current_dest_addr()))))) { #if IP_SOF_BROADCAST_RECV /* broadcast filter? */ - if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp)) + if (((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) +#if LWIP_IPV6 + && !pcb->isipv6 +#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) { +#if LWIP_IPV6 + if (pcb->isipv6) { + eaten = pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr()); + } + else +#endif /* LWIP_IPV6 */ + { + 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 +177,15 @@ 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_set(&pcb->local_ip.ip6, (ip6_addr_t *)ipaddr); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set(&pcb->local_ip.ip4, ipaddr); + } return ERR_OK; } @@ -161,7 +205,15 @@ 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_set(&pcb->remote_ip.ip6, (ip6_addr_t *)ipaddr); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set(&pcb->remote_ip.ip4, ipaddr); + } return ERR_OK; } @@ -183,7 +235,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; } @@ -209,6 +261,33 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); +#if LWIP_IPV6 + /* TODO lots of v4 and v6 code duplication, optimize! Or will compiler optimize? */ + if (pcb->isipv6) { + /* not enough space to add an IPv6 header to first pbuf in given p chain? */ + if (pbuf_header(p, IP6_HLEN)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + 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); + /* { 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, -IP6_HLEN)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + } + else +#endif /* LWIP_IPV6 */ /* not enough space to add an IP header to first pbuf in given p chain? */ if (pbuf_header(p, IP_HLEN)) { /* allocate header in new pbuf */ @@ -233,6 +312,19 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) } } +#if LWIP_IPV6 + if (pcb->isipv6) { + if ((netif = ip6_route(&pcb->local_ip.ip6, (ip6_addr_t *)ipaddr)) == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to IPv6 destionation\n")); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + } + else +#endif /* LWIP_IPV6 */ 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))); @@ -244,29 +336,62 @@ 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 + if (!netif->isipv6) { +#endif /* LWIP_IPV6 */ + /* 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); + } + return ERR_VAL; } - return ERR_VAL; +#if LWIP_IPV6 } +#endif /* LWIP_IPV6 */ #endif /* IP_SOF_BROADCAST */ - if (ip_addr_isany(&pcb->local_ip)) { +#if LWIP_IPV6 + if (pcb->isipv6) { + if (ip6_addr_isany(&pcb->local_ip.ip6)) { + /* select an IPv6 address from the netif as source address */ + src_ip = (ip_addr_t *)ip6_select_source_address(netif, (ip6_addr_t *)ipaddr); + if (src_ip == NULL) { + /* No suitable source address was found. */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + } else { + /* use RAW PCB local IPv6 address as source address */ + src_ip = (ip_addr_t *)&(pcb->local_ip.ip6); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&pcb->local_ip.ip4)) { /* use outgoing network interface IP address as source address */ src_ip = &(netif->ip_addr); } else { /* use RAW PCB local IP address as source address */ - src_ip = &(pcb->local_ip); + src_ip = &(pcb->local_ip.ip4); } #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_IPV6 + if (pcb->isipv6) { + err = ip6_output_if(q, (ip6_addr_t *)src_ip, (ip6_addr_t *)ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); + } + else +#endif /* LWIP_IPV6 */ + { + 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*/ @@ -289,7 +414,13 @@ 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + /* TODO is this necessary, or ar ip4 and ip6 pointers the same (think union)? */ + return raw_sendto(pcb, p, (ip_addr_t *)&pcb->remote_ip.ip6); + } +#endif /* LWIP_IPV6 */ + return raw_sendto(pcb, p, &pcb->remote_ip.ip4); } /** @@ -351,4 +482,28 @@ 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); + if (pcb != NULL) { + pcb->isipv6 = 1; + } + return pcb; +} +#endif /* LWIP_IPV6 */ + #endif /* LWIP_RAW */ diff --git a/src/core/tcp.c b/src/core/tcp.c index c629bc4e..1d62def6 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -52,6 +52,9 @@ #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 @@ -147,8 +150,17 @@ 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + tcp_rst_ip6(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, + pcb->local_port, pcb->remote_port); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, + pcb->local_port, pcb->remote_port); + } tcp_pcb_purge(pcb); @@ -318,8 +330,8 @@ 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; + /*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 */ @@ -337,10 +349,10 @@ tcp_abandon(struct tcp_pcb *pcb, int reset) } else { seqno = pcb->snd_nxt; ackno = pcb->rcv_nxt; - ip_addr_copy(local_ip, pcb->local_ip); - ip_addr_copy(remote_ip, pcb->remote_ip); + /*ip_addr_copy(local_ip, pcb->local_ip.ip4); + ip_addr_copy(remote_ip, pcb->remote_ip.ip4); local_port = pcb->local_port; - remote_port = pcb->remote_port; + remote_port = pcb->remote_port;*/ #if LWIP_CALLBACK_API errf = pcb->errf; #endif /* LWIP_CALLBACK_API */ @@ -357,12 +369,20 @@ 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) { LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); - tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); +#if LWIP_IPV6 + if (pcb->isipv6) { + tcp_rst_ip6(seqno, ackno, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, pcb->local_port, pcb->remote_port); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(seqno, ackno, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, pcb->local_port, pcb->remote_port); + } } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); } } @@ -432,18 +452,40 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) ((cpcb->so_options & SOF_REUSEADDR) == 0)) #endif /* SO_REUSE */ { - if (ip_addr_isany(&(cpcb->local_ip)) || + if ( +#if LWIP_IPV6 + !pcb->isipv6 && + !cpcb->isipv6 && +#endif /* LWIP_IPV6 */ + (ip_addr_isany(&(cpcb->local_ip.ip4)) || ip_addr_isany(ipaddr) || - ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + ip_addr_cmp(&(cpcb->local_ip.ip4), ipaddr))) { return ERR_USE; } +#if LWIP_IPV6 + if (pcb->isipv6 && + cpcb->isipv6 && + (ip6_addr_isany(&(cpcb->local_ip.ip6)) || + ip6_addr_isany((ip6_addr_t *)ipaddr) || + ip6_addr_cmp(&(cpcb->local_ip.ip6), (ip6_addr_t *)ipaddr))) { + return ERR_USE; + } +#endif /* LWIP_IPV6 */ } } } } +#if LWIP_IPV6 + if (pcb->isipv6) { + if (!ip6_addr_isany((ip6_addr_t *)ipaddr)) { + ip6_addr_set(&pcb->local_ip.ip6, (ip6_addr_t *)ipaddr); + } + } + else +#endif /* LWIP_IPV6 */ if (!ip_addr_isany(ipaddr)) { - pcb->local_ip = *ipaddr; + pcb->local_ip.ip4 = *ipaddr; } pcb->local_port = port; TCP_REG(&tcp_bound_pcbs, pcb); @@ -498,7 +540,15 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) 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 (( +#if LWIP_IPV6 + pcb->isipv6 && + lpcb->isipv6 && + ip6_addr_cmp(&lpcb->local_ip.ip6, &pcb->local_ip.ip6)) || + (!pcb->isipv6 && + !lpcb->isipv6 && +#endif /* LWIP_IPV6 */ + ip_addr_cmp(&lpcb->local_ip.ip4, &pcb->local_ip.ip4))) { /* this address/port is already used */ return NULL; } @@ -518,7 +568,16 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) lpcb->so_options |= SOF_ACCEPTCONN; lpcb->ttl = pcb->ttl; lpcb->tos = pcb->tos; - ip_addr_copy(lpcb->local_ip, pcb->local_ip); +#if LWIP_IPV6 + lpcb->isipv6 = pcb->isipv6; + if (lpcb->isipv6) { + ip6_addr_copy(lpcb->local_ip.ip6, pcb->local_ip.ip6); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_copy(lpcb->local_ip.ip4, pcb->local_ip.ip4); + } if (pcb->local_port != 0) { TCP_RMV(&tcp_bound_pcbs, pcb); } @@ -657,23 +716,55 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); if (ipaddr != NULL) { - pcb->remote_ip = *ipaddr; +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_set(&pcb->remote_ip.ip6, (ip6_addr_t *)ipaddr); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->remote_ip.ip4 = *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 LWIP_IPV6 + if (pcb->isipv6) { + if (ip6_addr_isany(&(pcb->local_ip.ip6))) { + /* no local IPv6 address set, yet. */ + ip6_addr_t * local_addr6; + struct netif *netif = ip6_route(&(pcb->remote_ip.ip6), &(pcb->remote_ip.ip6)); + if (netif == NULL) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Select and IPv6 address from the netif. */ + local_addr6 = ip6_select_source_address(netif, &(pcb->remote_ip.ip6)); + if (local_addr6 == NULL) { + /* Don't even try to send a SYN packet if we have no suitable + source address. */ + return ERR_RTE; + } + + ip6_addr_set(&pcb->local_ip.ip6, local_addr6); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&(pcb->local_ip.ip4))) { /* no local IP address set, yet. */ - struct netif *netif = ip_route(&(pcb->remote_ip)); + struct netif *netif = ip_route(&(pcb->remote_ip.ip4)); if (netif == 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); + ip_addr_copy(pcb->local_ip.ip4, netif->ip_addr); } old_local_port = pcb->local_port; @@ -691,8 +782,18 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, 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)) { +#if LWIP_IPV6 + ((cpcb->isipv6 && + pcb->isipv6 && + ip6_addr_cmp(&cpcb->local_ip.ip6, &pcb->local_ip.ip6) && + ip6_addr_cmp(&cpcb->remote_ip.ip6, (ip6_addr_t *)ipaddr)) || + (!cpcb->isipv6 && + !pcb->isipv6 && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ip_addr_cmp(&cpcb->local_ip.ip4, &pcb->local_ip.ip4) && + ip_addr_cmp(&cpcb->remote_ip.ip4, ipaddr)))) { /* linux returns EISCONN here, but ERR_USE should be OK for us */ return ERR_USE; } @@ -713,7 +814,15 @@ 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + pcb->mss = tcp_eff_send_mss_ip6(pcb->mss, &pcb->local_ip.ip6, &pcb->remote_ip.ip6); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->remote_ip.ip4); + } #endif /* TCP_CALCULATE_EFF_SEND_MSS */ pcb->cwnd = 1; pcb->ssthresh = pcb->mss * 10; @@ -852,9 +961,13 @@ tcp_slowtmr(void) (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) #endif /* LWIP_TCP_KEEPALIVE */ { - 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 ")); + if (!pcb->isipv6) { + ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip.ip4); + } else { + ip6_addr_debug_print(TCP_DEBUG, &pcb->remote_ip.ip6); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); ++pcb_remove; ++pcb_reset; @@ -919,8 +1032,17 @@ tcp_slowtmr(void) 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + tcp_rst_ip6(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, + pcb->local_port, pcb->remote_port); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, + pcb->local_port, pcb->remote_port); + } } pcb2 = pcb; @@ -1266,6 +1388,27 @@ 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); + /* could allocate TCP PCB? */ + if (pcb != NULL) { + pcb->isipv6 = 1; + } + return pcb; +} +#endif /* LWIP_IPV6 */ + /** * Used to specify the argument that should be passed callback * functions. @@ -1376,8 +1519,18 @@ 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))) { +#if LWIP_IPV6 + ((lpcb->isipv6 && + pcb->isipv6 && + (ip6_addr_isany(&lpcb->local_ip.ip6) || + ip6_addr_cmp(&pcb->local_ip.ip6, &lpcb->local_ip.ip6))) || + (!lpcb->isipv6 && + !pcb->isipv6 && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + (ip_addr_isany(&lpcb->local_ip.ip4) || + ip_addr_cmp(&pcb->local_ip.ip4, &lpcb->local_ip.ip4))))) { /* 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); @@ -1492,6 +1645,35 @@ tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr) } return sendmss; } + +#if LWIP_IPV6 +/** + * Calculates the effective send mss that can be used for a specific IPv6 + * address by using ip6_route to determine the netif used to send to the + * address and calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss_ip6(u16_t sendmss, ip6_addr_t *src, ip6_addr_t *dest) +{ + u16_t mss_s; + struct netif *outif; + s16_t mtu; + + /* First look in destination cache, to see if there is a PAth MTU. */ + outif = ip6_route(src, dest); + mtu = nd6_get_destination_mtu(dest, outif); + + if (mtu != 0) { + mss_s = mtu - IP6_HLEN - TCP_HLEN; + /* 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(). + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* LWIP_IPV6 */ #endif /* TCP_CALCULATE_EFF_SEND_MSS */ const char* diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c index 90952648..86bb93e7 100644 --- a/src/core/tcp_in.c +++ b/src/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/ip6_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 @@ -105,15 +110,14 @@ tcp_input(struct pbuf *p, struct netif *inp) 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); @@ -124,8 +128,14 @@ tcp_input(struct pbuf *p, struct netif *inp) } /* Don't even process incoming broadcasts/multicasts. */ - if (ip_addr_isbroadcast(¤t_iphdr_dest, inp) || - ip_addr_ismulticast(¤t_iphdr_dest)) { + if (( +#if LWIP_IPV6 + (ip6_current_header() != NULL) && + ip6_addr_ismulticast(ip6_current_dest_addr())) || + ((ip_current_header() != NULL) && +#endif /* LWIP_IPV6 */ + (ip_addr_isbroadcast(ip_current_dest_addr(), inp) || + ip_addr_ismulticast(ip_current_dest_addr())))) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); @@ -134,6 +144,25 @@ tcp_input(struct pbuf *p, struct netif *inp) } #if CHECKSUM_CHECK_TCP +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + if (ip6_chksum_pseudo(p, ip6_current_src_addr(), ip6_current_dest_addr(), + IP6_NEXTH_TCP, p->tot_len) != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + ip6_chksum_pseudo(p, ip6_current_src_addr(), ip6_current_dest_addr(), + IP6_NEXTH_TCP, p->tot_len))); + #if TCP_DEBUG + tcp_debug_print(tcphdr); + #endif /* TCP_DEBUG */ + TCP_STATS_INC(tcp.chkerr); + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + } + else +#endif /* LWIP_IPV6 */ /* Verify TCP checksum. */ if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), IP_PROTO_TCP, p->tot_len) != 0) { @@ -149,7 +178,7 @@ tcp_input(struct pbuf *p, struct netif *inp) pbuf_free(p); return; } -#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. */ @@ -184,9 +213,18 @@ 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 && + (( +#if LWIP_IPV6 + pcb->isipv6 && + (ip6_current_header() != NULL) && + ip6_addr_cmp(&(pcb->remote_ip.ip6), ip6_current_src_addr()) && + ip6_addr_cmp(&(pcb->local_ip.ip6), ip6_current_dest_addr())) || + (!pcb->isipv6 && + (ip_current_header() != NULL) && +#endif /* LWIP_IPV6 */ + ip_addr_cmp(&(pcb->remote_ip.ip4), ip_current_src_addr()) && + ip_addr_cmp(&(pcb->local_ip.ip4), ip_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 @@ -209,9 +247,18 @@ 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 && + (( +#if LWIP_IPV6 + pcb->isipv6 && + (ip6_current_header() != NULL) && + ip6_addr_cmp(&(pcb->remote_ip.ip6), ip6_current_src_addr()) && + ip6_addr_cmp(&(pcb->local_ip.ip6), ip6_current_dest_addr())) || + (!pcb->isipv6 && + (ip_current_header() != NULL) && +#endif /* LWIP_IPV6 */ + ip_addr_cmp(&(pcb->remote_ip.ip4), ip_current_src_addr()) && + ip_addr_cmp(&(pcb->local_ip.ip4), ip_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. */ @@ -228,19 +275,50 @@ tcp_input(struct pbuf *p, struct netif *inp) 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))) { - /* found an ANY-match */ - lpcb_any = lpcb; - lpcb_prev = prev; +#if LWIP_IPV6 + if (lpcb->isipv6 && + (ip6_current_header() != NULL)) { + if (ip6_addr_cmp(&(lpcb->local_ip.ip6), ip6_current_dest_addr())) { + /* found an exact match */ + break; + } else if(ip6_addr_isany(&(lpcb->local_ip.ip6))) { + /* found an ANY-match */ + lpcb_any = lpcb; + lpcb_prev = prev; + } + } + else if (!lpcb->isipv6 && + (ip_current_header() != NULL)) +#endif /* LWIP_IPV6 */ + { + if (ip_addr_cmp(&(lpcb->local_ip.ip4), ip_current_dest_addr())) { + /* found an exact match */ + break; + } else if(ip_addr_isany(&(lpcb->local_ip.ip4))) { + /* found an ANY-match */ + 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; +#if LWIP_IPV6 + if (lpcb->isipv6 && + (ip6_current_header() != NULL)) { + if (ip6_addr_cmp(&(lpcb->local_ip.ip6), ip6_current_dest_addr()) || + ip6_addr_isany(&(lpcb->local_ip.ip6))) { + /* found an exact match */ + break; + } + } + else if (!lpcb->isipv6 && + (ip_current_header() != NULL)) +#endif /* LWIP_IPV6 */ + { + if (ip_addr_cmp(&(lpcb->local_ip.ip4), ip_current_dest_addr()) || + ip_addr_isany(&(lpcb->local_ip.ip4))) { + /* found a match */ + break; + } } #endif /* SO_REUSE */ } @@ -415,9 +493,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); +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + tcp_rst_ip6(ackno, seqno + tcplen, + ip6_current_dest_addr(), ip6_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(ackno, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } } pbuf_free(p); } @@ -450,9 +538,19 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) /* 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); +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + tcp_rst_ip6(ackno + 1, seqno + tcplen, + ip6_current_dest_addr(), ip6_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(ackno + 1, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } } 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 @@ -474,9 +572,19 @@ 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 + npcb->isipv6 = pcb->isipv6; + if (npcb->isipv6) { + ip6_addr_copy(npcb->local_ip.ip6, *ip6_current_dest_addr()); + ip6_addr_copy(npcb->remote_ip.ip6, *ip6_current_src_addr()); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_copy(npcb->local_ip.ip4, *ip_current_dest_addr()); + ip_addr_copy(npcb->remote_ip.ip4, *ip_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; @@ -497,7 +605,15 @@ tcp_listen_input(struct tcp_pcb_listen *pcb) /* 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)); +#if LWIP_IPV6 + if (npcb->isipv6) { + npcb->mss = tcp_eff_send_mss_ip6(npcb->mss, &(npcb->local_ip.ip6), &(npcb->remote_ip.ip6)); + } + else +#endif /* LWIP_IPV6 */ + { + npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip.ip4)); + } #endif /* TCP_CALCULATE_EFF_SEND_MSS */ snmp_inc_tcppassiveopens(); @@ -539,8 +655,17 @@ 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); +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + tcp_rst_ip6(ackno, seqno + tcplen, ip6_current_dest_addr(), ip6_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } return ERR_OK; } } else if (flags & TCP_FIN) { @@ -637,7 +762,15 @@ tcp_process(struct tcp_pcb *pcb) pcb->state = ESTABLISHED; #if TCP_CALCULATE_EFF_SEND_MSS - pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); +#if LWIP_IPV6 + if (pcb->isipv6) { + pcb->mss = tcp_eff_send_mss_ip6(pcb->mss, &(pcb->local_ip.ip6), &(pcb->remote_ip.ip6)); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip.ip4)); + } #endif /* TCP_CALCULATE_EFF_SEND_MSS */ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect @@ -673,8 +806,17 @@ 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); +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + tcp_rst_ip6(ackno, seqno + tcplen, ip6_current_dest_addr(), ip6_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } } break; case SYN_RCVD: @@ -716,8 +858,17 @@ 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); +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + tcp_rst_ip6(ackno, seqno + tcplen, ip6_current_dest_addr(), ip6_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + else +#endif /* LWIP_IPV6 */ + { + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } } } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { /* Looks like another copy of the SYN - retransmit our SYN-ACK */ @@ -1019,6 +1170,13 @@ tcp_receive(struct tcp_pcb *pcb) pcb->rtime = 0; pcb->polltmr = 0; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (pcb->isipv6) { + /* 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; @@ -1336,6 +1494,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) { + /* 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); diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index 86e09195..308b8273 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -52,6 +52,9 @@ #include "lwip/inet_chksum.h" #include "lwip/stats.h" #include "lwip/snmp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_chksum.h" #include @@ -865,17 +868,34 @@ tcp_send_empty_ack(struct tcp_pcb *pcb) } #endif +#if LWIP_IPV6 + if (pcb->isipv6) { + /* Chksum is mandatory over IPv6 */ + tcphdr->chksum = ip6_chksum_pseudo(p, &(pcb->local_ip.ip6), &(pcb->remote_ip.ip6), + IP6_NEXTH_TCP, p->tot_len); +#if LWIP_NETIF_HWADDRHINT + ip6_output_hinted(p, &(pcb->local_ip.ip6), &(pcb->remote_ip.ip6), pcb->ttl, pcb->tos, + IP6_NEXTH_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip6_output(p, &(pcb->local_ip.ip6), &(pcb->remote_ip.ip6), pcb->ttl, pcb->tos, + IP6_NEXTH_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + } + else +#endif /* LWIP_IPV6 */ + { #if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), - IP_PROTO_TCP, p->tot_len); + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip.ip4), &(pcb->remote_ip.ip4), + IP_PROTO_TCP, p->tot_len); #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)); + ip_output_hinted(p, &(pcb->local_ip.ip4), &(pcb->remote_ip.ip4), 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, - IP_PROTO_TCP); + ip_output(p, &(pcb->local_ip.ip4), &(pcb->remote_ip.ip4), pcb->ttl, pcb->tos, + IP_PROTO_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ + } pbuf_free(p); return ERR_OK; @@ -1086,12 +1106,30 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) /* 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 LWIP_IPV6 + if (pcb->isipv6) { + if (ip6_addr_isany(&(pcb->local_ip.ip6))) { + ip6_addr_t * local_addr6; + netif = ip6_route(&(pcb->local_ip.ip6), &(pcb->remote_ip.ip6)); + if (netif == NULL) { + return; + } + /* Select and IPv6 address from the netif. */ + local_addr6 = ip6_select_source_address(netif, &(pcb->remote_ip.ip6)); + if (local_addr6 == NULL) { + return; + } + ip6_addr_set(&pcb->local_ip.ip6, local_addr6); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&(pcb->local_ip.ip4))) { + netif = ip_route(&(pcb->remote_ip.ip4)); if (netif == NULL) { return; } - ip_addr_copy(pcb->local_ip, netif->ip_addr); + ip_addr_copy(pcb->local_ip.ip4, netif->ip_addr); } if (pcb->rttest == 0) { @@ -1112,14 +1150,24 @@ 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; +#if LWIP_IPV6 + if (pcb->isipv6) { + chksum_slow = ip6_chksum_pseudo(seg->p, &(pcb->local_ip.ip6), + &(pcb->remote_ip.ip6), + IP6_NEXTH_TCP, seg->p->tot_len); + } + else +#endif /* LWIP_IPV6 */ + { + chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip.ip4), + &(pcb->remote_ip.ip4), + IP_PROTO_TCP, seg->p->tot_len); + } #endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { LWIP_ASSERT("data included but not checksummed", @@ -1127,9 +1175,19 @@ 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); +#if LWIP_IPV6 + if (pcb->isipv6) { + acc = ip6_chksum_pseudo_partial(seg->p, &(pcb->local_ip.ip6), + &(pcb->remote_ip.ip6), + IP6_NEXTH_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4); + } + else +#endif /* LWIP_IPV6 */ + { + acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip.ip4), + &(pcb->remote_ip.ip4), + IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4); + } /* add payload checksum */ if (seg->chksum_swapped) { seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); @@ -1147,20 +1205,46 @@ 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 LWIP_IPV6 + if (pcb->isipv6) { + /* Chksum is mandatory in IPv6 */ + seg->tcphdr->chksum = ip6_chksum_pseudo(seg->p, &(pcb->local_ip.ip6), + &(pcb->remote_ip.ip6), + IP6_NEXTH_TCP, seg->p->tot_len); + } + else +#endif /* LWIP_IPV6 */ + { +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip.ip4), + &(pcb->remote_ip.ip4), + IP_PROTO_TCP, seg->p->tot_len); #endif /* CHECKSUM_GEN_TCP */ + } +#endif /* TCP_CHECKSUM_ON_COPY */ TCP_STATS_INC(tcp.xmit); +#if LWIP_IPV6 + if (pcb->isipv6) { #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)); + ip6_output_hinted(seg->p, &(pcb->local_ip.ip6), &(pcb->remote_ip.ip6), pcb->ttl, pcb->tos, + IP6_NEXTH_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); + ip6_output(seg->p, &(pcb->local_ip.ip6), &(pcb->remote_ip.ip6), pcb->ttl, pcb->tos, + IP6_NEXTH_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ + } + else +#endif /* LWIP_IPV6 */ + { +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(seg->p, &(pcb->local_ip.ip4), &(pcb->remote_ip.ip4), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(seg->p, &(pcb->local_ip.ip4), &(pcb->remote_ip.ip4), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + } } /** @@ -1220,6 +1304,65 @@ tcp_rst(u32_t seqno, u32_t ackno, LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); } +#if LWIP_IPV6 +/** + * Send a TCP RESET packet (empty segment with RST flag set) over IPv6, + * either to abort a connection or to show that there is no matching local + * connection for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip6 the local IPv6 address to send the segment from + * @param remote_ip6 the remote IPv6 address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst_ip6(u32_t seqno, u32_t ackno, + ip6_addr_t *local_ip6, ip6_addr_t *remote_ip6, + u16_t local_port, u16_t remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); + tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* chksum us mandatory over IPv6. */ + tcphdr->chksum = ip6_chksum_pseudo(p, local_ip6, remote_ip6, + IP6_NEXTH_TCP, p->tot_len); + + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + /* Send output with hardcoded HL since we have no access to the pcb */ + ip6_output(p, local_ip6, remote_ip6, TCP_TTL, 0, IP6_NEXTH_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} +#endif /* LWIP_IPV6 */ + /** * Requeue all unacked segments for retransmission * @@ -1351,9 +1494,13 @@ 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 ")); + if (!pcb->isipv6) { + ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip.ip4); + } else { + ip6_addr_debug_print(TCP_DEBUG, &pcb->remote_ip.ip6); + } + 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)); @@ -1366,19 +1513,41 @@ tcp_keepalive(struct tcp_pcb *pcb) } tcphdr = (struct tcp_hdr *)p->payload; +#if LWIP_IPV6 + if (pcb->isipv6) { + tcphdr->chksum = ip6_chksum_pseudo(p, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, + IP6_NEXTH_TCP, p->tot_len); + } + else +#endif /* LWIP_IPV6 */ + { #if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, - IP_PROTO_TCP, p->tot_len); + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, + IP_PROTO_TCP, p->tot_len); #endif + } TCP_STATS_INC(tcp.xmit); /* Send output to IP */ +#if LWIP_IPV6 + if (pcb->isipv6) { #if LWIP_NETIF_HWADDRHINT - ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, - &(pcb->addr_hint)); + ip6_output_hinted(p, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, pcb->ttl, 0, IP6_NEXTH_TCP, + &(pcb->addr_hint)); #else /* LWIP_NETIF_HWADDRHINT*/ - ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); + ip6_output(p, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, pcb->ttl, 0, IP6_NEXTH_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ + } + else +#endif /* LWIP_IPV6 */ + { +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + } pbuf_free(p); @@ -1404,11 +1573,13 @@ 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 ")); + if (!pcb->isipv6) { + ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip.ip4); + } else { + ip6_addr_debug_print(TCP_DEBUG, &pcb->remote_ip.ip6); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: tcp_ticks %"U32_F @@ -1445,19 +1616,41 @@ tcp_zero_window_probe(struct tcp_pcb *pcb) pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4); } +#if LWIP_IPV6 + if (pcb->isipv6) { + tcphdr->chksum = ip6_chksum_pseudo(p, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, + IP6_NEXTH_TCP, p->tot_len); + } + else +#endif /* LWIP_IPV6 */ + { #if CHECKSUM_GEN_TCP - tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, - IP_PROTO_TCP, p->tot_len); + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, + IP_PROTO_TCP, p->tot_len); #endif + } TCP_STATS_INC(tcp.xmit); /* Send output to IP */ +#if LWIP_IPV6 + if (pcb->isipv6) { #if LWIP_NETIF_HWADDRHINT - ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, - &(pcb->addr_hint)); + ip6_output_hinted(p, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, pcb->ttl, 0, IP6_NEXTH_TCP, + &(pcb->addr_hint)); #else /* LWIP_NETIF_HWADDRHINT*/ - ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); + ip6_output(p, &pcb->local_ip.ip6, &pcb->remote_ip.ip6, pcb->ttl, 0, IP6_NEXTH_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ + } + else +#endif /* LWIP_IPV6 */ + { +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip.ip4, &pcb->remote_ip.ip4, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + } pbuf_free(p); diff --git a/src/core/timers.c b/src/core/timers.c index f0e92cce..da6278f0 100644 --- a/src/core/timers.c +++ b/src/core/timers.c @@ -56,7 +56,9 @@ #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" /** The one and only timeout list */ static struct sys_timeo *next_timeout; @@ -217,6 +219,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 +289,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 */ diff --git a/src/core/udp.c b/src/core/udp.c index 4596ba2b..2594657c 100644 --- a/src/core/udp.c +++ b/src/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/ip6_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" @@ -76,7 +80,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,7 +90,6 @@ 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; @@ -95,11 +98,8 @@ udp_input(struct pbuf *p, struct netif *inp) 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 +113,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_header() != NULL) && 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 +128,19 @@ 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 (")); + if (ip_current_header() != NULL) { + ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr()); + } else { + ip6_addr_debug_print(UDP_DEBUG, ip6_current_dest_addr()); + } + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); + if (ip_current_header() != NULL) { + ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr()); + } else { + ip6_addr_debug_print(UDP_DEBUG, ip6_current_src_addr()); + } + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src))); #if LWIP_DHCP pcb = NULL; @@ -143,8 +153,12 @@ udp_input(struct pbuf *p, struct netif *inp) /* 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))) { + if ( +#if LWIP_IPV6 + !pcb->isipv6 && +#endif /* LWIP_IPV6 */ + ((ip_addr_isany(&inp->dhcp->pcb->remote_ip.ip4) || + ip_addr_cmp(&(inp->dhcp->pcb->remote_ip.ip4), ip_current_src_addr())))) { pcb = inp->dhcp->pcb; } } @@ -162,25 +176,44 @@ 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 (")); + if (!pcb->isipv6) { + ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip.ip4); + } else { + ip6_addr_debug_print(UDP_DEBUG, &pcb->local_ip.ip6); + } + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + if (!pcb->isipv6) { + ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip.ip4); + } else { + ip6_addr_debug_print(UDP_DEBUG, &pcb->remote_ip.ip6); + } + 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 LWIP_IPV6 + ((pcb->isipv6 && + (ip6_current_header() != NULL) && + (ip6_addr_isany(&pcb->local_ip.ip6) || +#if LWIP_IPV6_MLD + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6_MLD */ + ip6_addr_cmp(&pcb->local_ip.ip6, ip6_current_dest_addr()))) || + (!pcb->isipv6 && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ip_addr_isany(&pcb->local_ip.ip4)) || + ip_addr_cmp(&(pcb->local_ip.ip4), 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)))) { + (broadcast && (pcb->so_options & SOF_BROADCAST)))))) { #else /* IP_SOF_BROADCAST_RECV */ - (broadcast))) { + (broadcast))))) { #endif /* IP_SOF_BROADCAST_RECV */ local_match = 1; if ((uncon_pcb == NULL) && @@ -192,8 +225,18 @@ udp_input(struct pbuf *p, struct netif *inp) /* 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))) { +#if LWIP_IPV6 + ((pcb->isipv6 && + (ip6_current_header() != NULL) && + (ip6_addr_isany(&pcb->remote_ip.ip6) || + ip6_addr_cmp(&pcb->remote_ip.ip6, ip6_current_src_addr()))) || + (!pcb->isipv6 && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((ip_addr_isany(&pcb->remote_ip.ip4) || + ip_addr_cmp(&(pcb->remote_ip.ip4), ip_current_src_addr())))))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is @@ -215,10 +258,26 @@ 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) || +#if LWIP_IPV6 + ((ip6_current_header() != NULL) && + netif_matches_ip6_addr(inp, ip6_current_dest_addr())) || + ((ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + ( +#endif /* LWIP_IPV6 */ + ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()))) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); #if LWIP_UDPLITE - if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { + if ( +#if LWIP_IPV6 + ((ip6_current_header() != NULL) && + (IP6H_NEXTH(ip6_current_header()) == IP_PROTO_UDPLITE)) || + ((ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + ( +#endif /* LWIP_IPV6 */ + (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE))) { /* Do the UDP Lite checksum */ #if CHECKSUM_CHECK_UDP u16_t chklen = ntohs(udphdr->len); @@ -237,7 +296,22 @@ udp_input(struct pbuf *p, struct netif *inp) goto end; } } - if (inet_chksum_pseudo_partial(p, ¤t_iphdr_src, ¤t_iphdr_dest, +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + if (ip6_chksum_pseudo_partial(p, ip6_current_src_addr(), ip6_current_dest_addr(), + IP6_NEXTH_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; + } + } + else +#endif /* LWIP_IPV6 */ + if (inet_chksum_pseudo_partial(p, ip_current_src_addr(), ip_current_dest_addr(), 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")); @@ -253,6 +327,21 @@ udp_input(struct pbuf *p, struct netif *inp) { #if CHECKSUM_CHECK_UDP if (udphdr->chksum != 0) { +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + if (ip6_chksum_pseudo(p, ip6_current_src_addr(), ip6_current_dest_addr(), + IP6_NEXTH_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; + } + } + else +#endif /* LWIP_IPV6 */ 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, @@ -277,32 +366,53 @@ 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)) && + if ((broadcast || +#if LWIP_IPV6 + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(ip_current_dest_addr())) && ((pcb->so_options & SOF_REUSEADDR) != 0)) { /* 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; +#if LWIP_IPV6 + if (ip6_current_header() != NULL) { + hdrs_len = (s16_t)(ip6_current_header_tot_len() + UDP_HLEN); + } else +#endif /* LWIP_IPV6 */ + { + hdrs_len = (s16_t)((IPH_HL(ip_current_header()) * 4) + 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 + ((mpcb->isipv6 && + (ip6_addr_ismulticast(ip6_current_dest_addr()) || + ip6_addr_cmp(&mpcb->local_ip.ip6, ip6_current_dest_addr()))) || + (!mpcb->isipv6 && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ip_addr_isany(&mpcb->local_ip.ip4)) || + ip_addr_cmp(&(mpcb->local_ip.ip4), 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 && (mpcb->so_options & 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 +420,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 (mpcb->isipv6) { + 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 +438,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->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 +462,33 @@ 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)) { - /* 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); - } +#if LWIP_IPV6 + !ip6_addr_ismulticast(ip6_current_dest_addr()) && +#endif /* LWIP_IPV6 */ + !ip_addr_ismulticast(ip_current_dest_addr())) { +#if LWIP_IPV6 && LWIP_ICMP6 + if (ip6_current_header() != NULL) { + /* move payload pointer back to ip header */ + pbuf_header(p, ip6_current_header_tot_len() + UDP_HLEN); + LWIP_ASSERT("p->payload == ip6_current_header()", (p->payload == ip6_current_header())); + icmp6_dest_unreach(p, ICMP6_DUR_PORT); + } + else +#endif /* LWIP_IPV6 && LWIP_ICMP6 */ + { +#if LWIP_ICMP + /* move payload pointer back to ip header */ + pbuf_header(p, (IPH_HL(ip_current_header()) * 4) + UDP_HLEN); + LWIP_ASSERT("p->payload == ip_current_header()", (p->payload == ip_current_header())); + icmp_dest_unreach(p, ICMP_DUR_PORT); #endif /* LWIP_ICMP */ + } + } +#endif /* LWIP_ICMP || LWIP_ICMP6 */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); snmp_inc_udpnoports(); @@ -381,7 +523,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, &pcb->remote_ip.ip4, pcb->remote_port); } #if LWIP_CHECKSUM_ON_COPY @@ -392,7 +534,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, &pcb->remote_ip.ip4, pcb->remote_port, have_chksum, chksum); } #endif /* LWIP_CHECKSUM_ON_COPY */ @@ -433,16 +575,35 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); /* find the outgoing network interface for this packet */ +#if LWIP_IPV6 + if (pcb->isipv6) { + if (ip6_addr_ismulticast((ip6_addr_t *)dst_ip)) { + /* For multicast, find a netif based on source address. */ + netif = ip6_route(&(pcb->local_ip.ip6), &(pcb->local_ip.ip6)); + } + else { + netif = ip6_route(&(pcb->local_ip.ip6), (ip6_addr_t *)dst_ip); + } + } + else +#endif /* LWIP_IPV6 */ + { #if LWIP_IGMP - netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); + netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); #else - netif = ip_route(dst_ip); + netif = ip_route(dst_ip); #endif /* LWIP_IGMP */ + } /* 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 ")); + if (!pcb->isipv6) { + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, dst_ip); + } else { + ip6_addr_debug_print(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, (ip6_addr_t *)dst_ip); + } + LWIP_DEBUGF(UDP_DEBUG, ("\n")); UDP_STATS_INC(udp.rterr); return ERR_RTE; } @@ -494,7 +655,11 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, #if IP_SOF_BROADCAST /* broadcast filter? */ - if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { + if ( ((pcb->so_options & SOF_BROADCAST) == 0) && +#if LWIP_IPV6 + !pcb->isipv6 && +#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 +669,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, &pcb->local_ip.ip4, 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; @@ -544,20 +709,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 && + ip6_addr_ismulticast((ip6_addr_t*)dst_ip)) || +#endif /* LWIP_IPV6_MLD */ + (!pcb->isipv6 && +#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) { + if (ip6_addr_isany(&pcb->local_ip.ip6)) { + src_ip =(ip_addr_t *)ip6_select_source_address(netif, (ip6_addr_t *)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, &(pcb->local_ip.ip6)) < 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 = (ip_addr_t *)&(pcb->local_ip.ip6); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(&pcb->local_ip.ip4)) { /* 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(&(pcb->local_ip.ip4), &(netif->ip_addr))) { /* local_ip doesn't match, drop the packet */ if (q != p) { /* free the header pbuf */ @@ -568,7 +773,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 = &(pcb->local_ip.ip4); } LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); @@ -595,31 +800,64 @@ 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 (have_chksum) { - u32_t acc; - acc = udphdr->chksum + (u16_t)~(chksum); - udphdr->chksum = FOLD_U32T(acc); - } -#endif /* !LWIP_CHECKSUM_ON_COPY */ +#if LWIP_IPV6 + /* Checksum is mandatory for IPv6. */ + if (pcb->isipv6) { + udphdr->chksum = ip6_chksum_pseudo_partial(q, (ip6_addr_t *)src_ip, (ip6_addr_t *)dst_ip, + IP6_NEXTH_UDPLITE, q->tot_len, + #if !LWIP_CHECKSUM_ON_COPY + chklen); + #else /* !LWIP_CHECKSUM_ON_COPY */ + (have_chksum ? UDP_HLEN : chklen)); + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } + #endif /* !LWIP_CHECKSUM_ON_COPY */ - /* chksum zero must become 0xffff, as zero means 'no checksum' */ - if (udphdr->chksum == 0x0000) { - udphdr->chksum = 0xffff; + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } } + else +#endif /* LWIP_IPV6 */ + { +#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 (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } + #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_IPV6 + if (pcb->isipv6) { + err = ip6_output_if(q, (ip6_addr_t*)src_ip, (ip6_addr_t*)dst_ip, pcb->ttl, pcb->tos, IP6_NEXTH_UDPLITE, netif); + } + else +#endif /* LWIP_IPV6 */ + { + 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*/ @@ -629,20 +867,21 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); udphdr->len = htons(q->tot_len); /* calculate checksum */ -#if CHECKSUM_GEN_UDP - if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { +#if LWIP_IPV6 + /* Checksum is mandatory over IPv6. */ + if (pcb->isipv6) { 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, + udpchksum = ip6_chksum_pseudo_partial(q, (ip6_addr_t*)src_ip, (ip6_addr_t*)dst_ip, IP6_NEXTH_UDP, q->tot_len, UDP_HLEN); 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 = ip6_chksum_pseudo(q, (ip6_addr_t*)src_ip, (ip6_addr_t*)dst_ip, IP6_NEXTH_UDP, q->tot_len); } /* chksum zero must become 0xffff, as zero means 'no checksum' */ @@ -651,14 +890,48 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, } udphdr->chksum = udpchksum; } + else +#endif /* LWIP_IPV6 */ + { +#if CHECKSUM_GEN_UDP + if ((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); + 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); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + 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_IPV6 + if (pcb->isipv6) { + err = ip6_output_if(q, (ip6_addr_t*)src_ip, (ip6_addr_t*)dst_ip, pcb->ttl, pcb->tos, IP6_NEXTH_UDP, netif); + } + else +#endif /* LWIP_IPV6 */ + { + 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*/ @@ -704,7 +977,11 @@ 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); + if (!pcb->isipv6) { + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr); + } else { + ip6_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, (ip6_addr_t *)ipaddr); + } LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); rebind = 0; @@ -730,9 +1007,20 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) #endif /* SO_REUSE */ if ((ipcb->local_port == port) && /* 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))) { +#if LWIP_IPV6 + ((pcb->isipv6 && + ipcb->isipv6 && + (ip6_addr_isany(&(ipcb->local_ip.ip6)) || + ip6_addr_isany((ip6_addr_t *)ipaddr) || + ip6_addr_cmp(&(ipcb->local_ip.ip6), (ip6_addr_t *)ipaddr))) || + (!pcb->isipv6 && + !ipcb->isipv6 && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + (ip_addr_isany(&(ipcb->local_ip.ip4)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(ipcb->local_ip.ip4), 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)); @@ -741,7 +1029,15 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) } } - ip_addr_set(&pcb->local_ip, ipaddr); +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_set(&pcb->local_ip.ip6, (ip6_addr_t *)ipaddr); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set(&pcb->local_ip.ip4, ipaddr); + } /* no port specified? */ if (port == 0) { @@ -778,11 +1074,13 @@ 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 ")); + if (!pcb->isipv6) { + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip.ip4); + } else { + ip6_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip.ip6); + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port)); return ERR_OK; } /** @@ -808,39 +1106,54 @@ 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, &pcb->local_ip.ip4, pcb->local_port); if (err != ERR_OK) { return err; } } - ip_addr_set(&pcb->remote_ip, ipaddr); +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_set(&pcb->remote_ip.ip6, (ip6_addr_t *)ipaddr); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set(&pcb->remote_ip.ip4, 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 LWIP_IPV6 + if (!pcb->isipv6) +#endif /* LWIP_IPV6 */ + { + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(&pcb->local_ip.ip4) && !ip_addr_isany(&pcb->remote_ip.ip4)) { + 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 ((netif = ip_route(&(pcb->remote_ip.ip4))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.ip4.addr)); + 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! */ + pcb->local_ip.ip4 = netif->ip_addr; + } else if (ip_addr_isany(&pcb->remote_ip.ip4)) { + pcb->local_ip.ip4.addr = 0; } - /** 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->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_connect: connected to ")); + if (!pcb->isipv6) { + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->remote_ip.ip4); + } else { + ip6_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->remote_ip.ip6); + } + 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) { @@ -864,7 +1177,15 @@ void udp_disconnect(struct udp_pcb *pcb) { /* reset remote address association */ - ip_addr_set_any(&pcb->remote_ip); +#if LWIP_IPV6 + if (pcb->isipv6) { + ip6_addr_set_any(&pcb->remote_ip.ip6); + } + else +#endif /* LWIP_IPV6 */ + { + ip_addr_set_any(&pcb->remote_ip.ip4); + } pcb->remote_port = 0; /* mark PCB as unconnected */ pcb->flags &= ~UDP_FLAGS_CONNECTED; @@ -883,7 +1204,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; } @@ -943,6 +1264,28 @@ 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(); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + pcb->isipv6 = 1; + } + return pcb; +} +#endif /* LWIP_IPV6 */ + #if UDP_DEBUG /** * Print UDP header information for debug purposes. diff --git a/src/include/ipv4/lwip/ip.h b/src/include/ipv4/lwip/ip.h index 74f501d1..18a2abb8 100644 --- a/src/include/ipv4/lwip/ip.h +++ b/src/include/ipv4/lwip/ip.h @@ -37,6 +37,7 @@ #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" @@ -69,14 +70,29 @@ extern "C" { #define IP_PCB_ADDRHINT #endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6 +#define IP_PCB_ISIPV6 u8_t isipv6; +#define IP_PCB_IP6 ip6_addr_t ip6; +#else +#define IP_PCB_ISIPV6 +#define IP_PCB_IP6 +#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 \ /* ip addresses in network byte order */ \ - ip_addr_t local_ip; \ - ip_addr_t remote_ip; \ + union { \ + ip_addr_t ip4; \ + IP_PCB_IP6 \ + } local_ip; \ + union { \ + ip_addr_t ip4; \ + IP_PCB_IP6 \ + } remote_ip; \ /* Socket options */ \ u8_t so_options; \ /* Type Of Service */ \ diff --git a/src/include/ipv4/lwip/ip_frag.h b/src/include/ipv4/lwip/ip_frag.h index 77b5eb1e..47eca9f4 100644 --- a/src/include/ipv4/lwip/ip_frag.h +++ b/src/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/src/include/lwip/api.h b/src/include/lwip/api.h index 91b9e5d2..f85a2dc9 100644 --- a/src/include/lwip/api.h +++ b/src/include/lwip/api.h @@ -76,20 +76,35 @@ extern "C" { /* 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) +#define NETCONNTYPE_ISIPV6(t) ((t)&0x08) +#define NETCONNTYPE_ISUDPLITE(t)(((t)&0xF7) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t)(((t)&0xF7) == NETCONN_UDPNOCHKSUM) /** Protocol family and type of the netconn */ enum netconn_type { NETCONN_INVALID = 0, /* NETCONN_TCP Group */ NETCONN_TCP = 0x10, +#if LWIP_IPV6 + NETCONN_TCP_IPV6 = 0x18, +#endif /* LWIP_IPV6 */ /* NETCONN_UDP Group */ NETCONN_UDP = 0x20, NETCONN_UDPLITE = 0x21, NETCONN_UDPNOCHKSUM= 0x22, +#if LWIP_IPV6 + NETCONN_UDP_IPV6 = 0x28, + NETCONN_UDPLITE_IPV6 = 0x29, + NETCONN_UDPNOCHKSUM_IPV6= 0x2a, +#endif /* LWIP_IPV6 */ /* NETCONN_RAW Group */ NETCONN_RAW = 0x40 +#if LWIP_IPV6 + , + NETCONN_RAW_IPV6 = 0x48 +#endif /* LWIP_IPV6 */ }; /** Current state of the netconn. Non-TCP netconns are always @@ -111,13 +126,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; @@ -235,13 +250,25 @@ err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size, 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) \ + netconn_bind(conn, (ip_addr_t*) ip6addr, port) +#define netconn_connect_ip6(conn, ip6addr, port) \ + netconn_connect(conn, (ip_addr_t*) ip6addr, port) +#define netconn_sendto_ip6(conn, buf, ip6addr, port) \ + netconn_sendto(conn, buf, (ip_addr_t*) ip6addr, port) +#if LWIP_IPV6_MLD +#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) \ + netconn_join_leave_group(conn, (ip_addr_t*)multiaddr, (ip_addr_t*)srcaddr, join_or_leave) +#endif /* LWIP_IPV6_MLD*/ +#endif /* LWIP_IPV6 */ #define netconn_err(conn) ((conn)->last_err) #define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) diff --git a/src/include/lwip/api_msg.h b/src/include/lwip/api_msg.h index f99d8c3b..2541e641 100644 --- a/src/include/lwip/api_msg.h +++ b/src/include/lwip/api_msg.h @@ -98,14 +98,14 @@ struct api_msg_msg { struct { u8_t shut; } sd; -#if LWIP_IGMP +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) /** used for do_join_leave_group */ struct { ip_addr_t *multiaddr; ip_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; @@ -154,9 +154,9 @@ 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 +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) void do_join_leave_group( struct api_msg_msg *msg); -#endif /* LWIP_IGMP */ +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS void do_gethostbyname(void *arg); diff --git a/src/include/lwip/memp_std.h b/src/include/lwip/memp_std.h index 6ce408fb..e7b90f29 100644 --- a/src/include/lwip/memp_std.h +++ b/src/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 */ @@ -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/src/include/lwip/netbuf.h b/src/include/lwip/netbuf.h index 7d247d71..82d3d701 100644 --- a/src/include/lwip/netbuf.h +++ b/src/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" { @@ -45,9 +46,18 @@ extern "C" { /** This netbuf includes a checksum */ #define NETBUF_FLAG_CHKSUM 0x02 +#if LWIP_IPV6 +#define NETBUF_IP6 ip6_addr_t ip6; +#else +#define NETBUF_IP6 +#endif /* LWIP_IPV6 */ + struct netbuf { struct pbuf *p, *ptr; - ip_addr_t addr; + union { + ip_addr_t ip4; + NETBUF_IP6 + } addr; u16_t port; #if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY #if LWIP_CHECKSUM_ON_COPY @@ -55,7 +65,10 @@ struct netbuf { #endif /* LWIP_CHECKSUM_ON_COPY */ u16_t toport_chksum; #if LWIP_NETBUF_RECVINFO - ip_addr_t toaddr; + union { + ip_addr_t ip4; + NETBUF_IP6 + } toaddr; #endif /* LWIP_NETBUF_RECVINFO */ #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ }; @@ -81,12 +94,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) (&((buf)->addr.ip4)) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set((&(buf)->addr.ip4), 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) (&((buf)->toaddr.ip4)) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set((&(buf)->toaddr.ip4), 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 +107,13 @@ 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) (&((buf)->addr.ip6)) +#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set((&(buf)->addr.ip6), fromaddr) +#define netbuf_destaddr_ip6(buf) (&((buf)->toaddr.ip6)) +#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set((&(buf)->toaddr.ip6), destaddr) +#endif /* LWIP_IPV6 */ + #ifdef __cplusplus } #endif diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h index a8790b5f..a8527f88 100644 --- a/src/include/lwip/netif.h +++ b/src/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 */ @@ -174,6 +208,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 +254,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 */ @@ -308,6 +359,15 @@ 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 */ + + #ifdef __cplusplus } #endif diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index a1b87658..e5b5eed8 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -1544,6 +1544,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 @@ -1557,6 +1592,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 */ @@ -1779,6 +1819,231 @@ #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 + /* --------------------------------------- ---------- Debugging options ---------- @@ -2040,4 +2305,11 @@ #define DNS_DEBUG LWIP_DBG_OFF #endif +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#ifndef IP6_DEBUG +#define IP6_DEBUG LWIP_DBG_ON +#endif + #endif /* __LWIP_OPT_H__ */ diff --git a/src/include/lwip/pbuf.h b/src/include/lwip/pbuf.h index 3b1d608b..59eec8c5 100644 --- a/src/include/lwip/pbuf.h +++ b/src/include/lwip/pbuf.h @@ -45,7 +45,11 @@ extern "C" { #define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) #define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else #define PBUF_IP_HLEN 20 +#endif typedef enum { PBUF_TRANSPORT, diff --git a/src/include/lwip/raw.h b/src/include/lwip/raw.h index 17d0a1c5..a3751f81 100644 --- a/src/include/lwip/raw.h +++ b/src/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, (ip_addr_t *)ip6addr) +#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, (ip_addr_t *)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, (ip_addr_t *)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/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index 3c8fed24..09842b67 100644 --- a/src/include/lwip/sockets.h +++ b/src/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" { @@ -56,10 +57,24 @@ struct sockaddr_in { char sin_zero[8]; }; +#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; - char 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 @@ -118,7 +133,13 @@ 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 diff --git a/src/include/lwip/stats.h b/src/include/lwip/stats.h index 015b7ce7..3ca6ed22 100644 --- a/src/include/lwip/stats.h +++ b/src/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; @@ -268,6 +283,46 @@ 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_proto(&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); diff --git a/src/include/lwip/tcp.h b/src/include/lwip/tcp.h index 07dcd10e..53bc5320 100644 --- a/src/include/lwip/tcp.h +++ b/src/include/lwip/tcp.h @@ -42,6 +42,8 @@ #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" { @@ -367,6 +369,14 @@ 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, (ip_addr_t *)ip6addr, port) +#define tcp_connect_ip6(pcb, ip6addr, port, connected) \ + udp_connect(pcb, (ip_addr_t *)ip6addr, port, connected) +#endif /* LWIP_IPV6 */ + #ifdef __cplusplus } diff --git a/src/include/lwip/tcp_impl.h b/src/include/lwip/tcp_impl.h index b4feec0d..126e8eb0 100644 --- a/src/include/lwip/tcp_impl.h +++ b/src/include/lwip/tcp_impl.h @@ -43,6 +43,8 @@ #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" { @@ -429,6 +431,11 @@ 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); +#if LWIP_IPV6 +void tcp_rst_ip6(u32_t seqno, u32_t ackno, + ip6_addr_t *local_ip6, ip6_addr_t *remote_ip6, + u16_t local_port, u16_t remote_port); +#endif /* LWIP_IPV6 */ u32_t tcp_next_iss(void); @@ -437,6 +444,9 @@ 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); +#if LWIP_IPV6 +u16_t tcp_eff_send_mss_ip6(u16_t sendmss, ip6_addr_t *src, ip6_addr_t *dest); +#endif /* LWIP_IPV6 */ #endif /* TCP_CALCULATE_EFF_SEND_MSS */ #if LWIP_CALLBACK_API diff --git a/src/include/lwip/udp.h b/src/include/lwip/udp.h index c98c1b3e..68b7b4e1 100644 --- a/src/include/lwip/udp.h +++ b/src/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; }; @@ -156,6 +180,26 @@ void udp_input (struct pbuf *p, struct netif *inp); #define udp_init() /* Compatibility define, not init needed. */ +#if LWIP_IPV6 +struct udp_pcb * udp_new_ip6(void); +#define udp_bind_ip6(pcb, ip6addr, port) \ + udp_bind(pcb, (ip_addr_t *)ip6addr, port) +#define udp_connect_ip6(pcb, ip6addr, port) \ + udp_connect(pcb, (ip_addr_t *)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, (ip_addr_t *)ip6addr, port) +#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \ + udp_sendto_if(pcb, pbuf, (ip_addr_t *)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, (ip_addr_t *)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, (ip_addr_t *)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); #else diff --git a/src/include/netif/etharp.h b/src/include/netif/etharp.h index 691575fa..2eda1488 100644 --- a/src/include/netif/etharp.h +++ b/src/include/netif/etharp.h @@ -137,6 +137,7 @@ PACK_STRUCT_END #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 */ diff --git a/src/netif/etharp.c b/src/netif/etharp.c index b60dadd0..99ae853a 100644 --- a/src/netif/etharp.c +++ b/src/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" @@ -1301,6 +1302,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/src/netif/ethernetif.c b/src/netif/ethernetif.c index a5b7d990..c22731ec 100644 --- a/src/netif/ethernetif.c +++ b/src/netif/ethernetif.c @@ -51,8 +51,9 @@ #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 +240,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 +307,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]);