]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/commitdiff
... and finally, we got a first working version of a dual-stack lwIP runnin IPv4...
authorgoldsimon <goldsimon>
Tue, 17 May 2011 19:35:14 +0000 (19:35 +0000)
committergoldsimon <goldsimon>
Tue, 17 May 2011 19:35:14 +0000 (19:35 +0000)
40 files changed:
CHANGELOG
src/api/api_lib.c
src/api/api_msg.c
src/api/netbuf.c
src/api/sockets.c
src/core/init.c
src/core/ipv4/icmp.c
src/core/ipv4/igmp.c
src/core/ipv4/inet_chksum.c
src/core/ipv4/ip.c
src/core/ipv6/icmp6.c
src/core/ipv6/inet6.c
src/core/ipv6/ip6.c
src/core/ipv6/ip6_addr.c
src/core/memp.c
src/core/netif.c
src/core/raw.c
src/core/tcp.c
src/core/tcp_in.c
src/core/tcp_out.c
src/core/timers.c
src/core/udp.c
src/include/ipv4/lwip/ip.h
src/include/ipv4/lwip/ip_frag.h
src/include/lwip/api.h
src/include/lwip/api_msg.h
src/include/lwip/memp_std.h
src/include/lwip/netbuf.h
src/include/lwip/netif.h
src/include/lwip/opt.h
src/include/lwip/pbuf.h
src/include/lwip/raw.h
src/include/lwip/sockets.h
src/include/lwip/stats.h
src/include/lwip/tcp.h
src/include/lwip/tcp_impl.h
src/include/lwip/udp.h
src/include/netif/etharp.h
src/netif/etharp.c
src/netif/ethernetif.c

index 2951830332a376cd56aae275e510c430a11e832b..f23128ede9d62449bf8064e4e04ae92b1b49988d 100644 (file)
--- 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
index b1a9e5253d89952342d70d30998cc1b9882f35a6..bc7507b7e0212dcd516619807db11bf6a908f015 100644 (file)
@@ -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
 /**
index 448f96dda0a9fd057105256195d73d2ee07ad342..6f6ffc4dd81877e538a67569c9cdb49f7920e635 100644 (file)
@@ -51,6 +51,7 @@
 #include "lwip/tcpip.h"
 #include "lwip/igmp.h"
 #include "lwip/dns.h"
+#include "lwip/mld6.h"
 
 #include <string.h>
 
@@ -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
 /**
index 9390c9ee9831dc4be1443ea5c12be705ae661a96..0aded3af0ed47f701454685ac2921edda3f933af 100644 (file)
@@ -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;
index e36012ce43b6624db66b2b85227f4030990cc956..ba035de558d4a2e562c94b78d0a5515d41a57452 100644 (file)
 
 #include <string.h>
 
+/* 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);
-
-    if (*addrlen > sizeof(sin))
-      *addrlen = sizeof(sin);
+    memset(&tempaddr, 0, sizeof(tempaddr));
+
+#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);
+
+      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, &sin, *addrlen);
+    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);
+
+          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, ("lwip_recvfrom(%d): addr=", s));
-        ip_addr_debug_print(SOCKETS_DEBUG, addr);
         LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
-#endif /*  SOCKETS_DEBUG */
       }
     }
 
@@ -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;
+
+    memset(&sin6, 0, sizeof(sin6));
+    sin6.sin6_len = sizeof(sin6);
+    sin6.sin6_family = AF_INET6;
 
-  /* get the IP address and port */
-  netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local);
+    /* get the IP address and port */
+    netconn_getaddr(sock->conn, (ip_addr_t *)&naddr6, &sin6.sin6_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));
+    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));
 
-  sin.sin_port = htons(sin.sin_port);
-  inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
+    sin6.sin6_port = htons(sin6.sin6_port);
+    inet6_addr_from_ip6addr(&sin6.sin6_addr, &naddr6);
 
-  if (*namelen > sizeof(sin)) {
-    *namelen = sizeof(sin);
+    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;
index bf5c79da0d6e7c9a2c1bea6a3f9402b88eb1cc25..30f09845cf54fd0f13c1c26026bcaa1cc7e9c3b5 100644 (file)
@@ -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();
index 32902a522ca2e5538b468cf1a644551bba8f0969..ca59acfc093ec9c963e9dae77a1b3f830d730781 100644 (file)
@@ -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(&current_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(&current_iphdr_dest, inp)) {
+      if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) {
         accepted = 0;
       }
 #endif /* LWIP_BROADCAST_PING */
index 4e4405e16f9da0bc0b4ae646b224a812ff8882e6..cf2e8ce1ee80887a812a547ae85f998dc667f2ff 100644 (file)
@@ -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 */
index 960252f64f1a6d8ccf033f41d819f8d1d214344d..b95f7f28de6ca22473a37a04d65dd1c52cf15606 100644 (file)
@@ -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;
index 6f248716b63def78813f28271104ac298219ff94..5e9a387dac6a1005c733424c90891cf784474819 100644 (file)
@@ -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, &current_iphdr_dest);
       break;
 #endif /* LWIP_IGMP */
index 4fcc8955110981f1209d5b03a078c56f24b18413..c92235e1a50e83e8f111e1e5c6230131c5d8f640 100644 (file)
@@ -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,
  *
  * This file is part of the lwIP TCP/IP stack.
  *
- * Author: Adam Dunkels <adam@sics.se>
+ * Author: Ivan Delamer <delamer@inicotech.com>
  *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
  */
 
-/* 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 <string.h>
+
+#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 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 */
 
-    if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
-      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+  switch (icmp6hdr->type) {
+  case ICMP6_TYPE_NA: /* Neighbor advertisement */
+  case ICMP6_TYPE_NS: /* Neighbor solicitation */
+  case ICMP6_TYPE_RA: /* Router advertisement */
+  case ICMP6_TYPE_RD: /* Redirect */
+  case ICMP6_TYPE_PTB: /* Packet too big */
+    nd6_input(p, inp);
+    return;
+    break;
+  case ICMP6_TYPE_RS:
+#if LWIP_IPV6_FORWARD
+    /* TODO implement router functionality */
+#endif
+    break;
+#if LWIP_IPV6_MLD
+  case ICMP6_TYPE_MLQ:
+  case ICMP6_TYPE_MLR:
+  case ICMP6_TYPE_MLD:
+    mld6_input(p, inp);
+    return;
+    break;
+#endif
+  case ICMP6_TYPE_EREQ:
+#if !LWIP_MULTICAST_PING
+    /* multicast destination address? */
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      /* drop */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.drop);
+      return;
+    }
+#endif /* LWIP_MULTICAST_PING */
 
+    /* Allocate reply. */
+    r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
+    if (r == NULL) {
+      /* drop */
       pbuf_free(p);
-      ICMP_STATS_INC(icmp.lenerr);
+      ICMP6_STATS_INC(icmp6.memerr);
       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;*/
+
+    /* Copy echo request. */
+    if (pbuf_copy(r, p) != ERR_OK) {
+      /* drop */
+      pbuf_free(p);
+      pbuf_free(r);
+      ICMP6_STATS_INC(icmp6.err);
+      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);
+
+    /* 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;
     }
-    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);
 
-    /*    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);
+    /* 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);
+
     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);
+  icmp6_send_response(p, ICMP6_TYPE_DUR, c, 0);
+}
 
-  /* calculate checksum */
-  idur->chksum = 0;
-  idur->chksum = inet_chksum(idur, q->len);
-  ICMP_STATS_INC(icmp.xmit);
+/**
+ * Send an icmpv6 'packet too big' packet.
+ *
+ * @param p the input packet for which the 'packet too big' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param mtu the maximum mtu that we can accept
+ */
+void
+icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
+{
+  icmp6_send_response(p, ICMP6_TYPE_PTB, 0, mtu);
+}
 
-  ip_output(q, NULL,
-      (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
-  pbuf_free(q);
+/**
+ * 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
-icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
 {
-  struct pbuf *q;
-  struct ip_hdr *iphdr;
-  struct icmp_te_hdr *tehdr;
+  icmp6_send_response(p, ICMP6_TYPE_PP, c, pointer);
+}
 
-  LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n"));
+/**
+ * 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 icmp6_hdr *icmp6hdr;
+  ip6_addr_t * reply_src;
 
-  /* @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 */
index c3de85c0930211de5b970995b723008f18b5ae43..bdf4ff4ffe361158253cb75b0f3afd3891d0a769 100644 (file)
@@ -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, 
  *
  * This file is part of the lwIP TCP/IP stack.
  * 
- * Author: Adam Dunkels <adam@sics.se>
+ * Author: Ivan Delamer <delamer@inicotech.com>
  *
- */
-
-#include "lwip/opt.h"
-
-#include "lwip/def.h"
-#include "lwip/inet.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.
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
  */
 
-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;
+#include "lwip/opt.h"
 
-  while (acc >> 16) {
-    acc = (acc & 0xffff) + (acc >> 16);
-  }
-  return ~(acc & 0xffff);
-}
+#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
 
-/* 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;
+#include "lwip/def.h"
+#include "lwip/inet6.h"
 
-  acc = chksum(dataptr, len);
-  sum = (acc & 0xffff) + (acc >> 16);
-  sum += (sum >> 16);
-  return ~(sum & 0xffff);
-}
+/** @see ip6_addr.c for implementation of functions. */
 
-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 */
index b945fc5d35d814fbc94a9800af89235541ec416a..5c58f05c4b1cb6e95a106081c25ee8e050d3651f 100644 (file)
@@ -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,
  *
  * This file is part of the lwIP TCP/IP stack.
  *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-
-
-/* ip.c
+ * Author: Ivan Delamer <delamer@inicotech.com>
  *
- * This is the code for the IP layer for IPv6.
  *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
  */
 
 
 #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);
-    }*/
-
-
-  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");
+  /* 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;
+  }
 
-  netif->output(netif, p, (struct ip_addr *)&(iphdr->dest));
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+      IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+
+  /* transmit pbuf on chosen interface */
+  netif->output_ip6(netif, p, ip6_current_dest_addr());
+  IP6_STATS_INC(ip6.fw);
+  IP6_STATS_INC(ip6.xmit);
+  return;
 }
+#endif /* LWIP_IPV6_FORWARD */
 
-/* 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;
-
+  ip6hdr = (struct ip6_hdr *)p->payload;
+  if (IP6H_V(ip6hdr) != 6) {
+    LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U16_F"\n",
+        IP6H_V(ip6hdr)));
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.err);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
 
-  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 */
+  /* 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);
-    IP_STATS_INC(ip.err);
-    IP_STATS_INC(ip.drop);
-    return;
+    IP6_STATS_INC(ip6.lenerr);
+    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))) {
-      break;
+  /* 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 */
-#if IP_FORWARD
-    ip_forward(p, iphdr);
-#endif
+    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);
-    return;
+    goto ip6_input_cleanup;
   }
 
-  pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len));
+  /* 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;
 
-  /* 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;
+    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);
 
-  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;
+  /* 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_ICMP
-    /* send ICMP destination protocol unreachable */
-    icmp_dest_unreach(p, ICMP_DUR_PROTO);
+    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 */
-    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);
+      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;
+    }
   }
-  PERF_STOP("ip_input");
+
+ip6_input_cleanup:
+  current_netif = NULL;
+  current_ip6_header = NULL;
+  current_ip6_header_tot_len = 0;
+  ip6_addr_set_any(&current_ip6hdr_src);
+  ip6_addr_set_any(&current_ip6hdr_dest);
+
+  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;
-
-  PERF_START;
-
-  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));
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t dest_addr;
+
+  /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+     gets altered as the packet is passed down the stack */
+  LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+  /* Should the IPv6 header be generated or is it already included in p? */
+  if (dest != 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;
+    }
 
-  iphdr = p->payload;
+    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);
 
-  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);
+    /* dest cannot be NULL here */
+    ip6_addr_copy(ip6hdr->dest, *dest);
 
-    iphdr->v = 6;
+    IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
+    IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN);
 
-    if (ip_addr_isany(src)) {
-      ip_addr_set(&(iphdr->src), &(netif->ip_addr));
-    } else {
-      ip_addr_set(&(iphdr->src), src);
+    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);
-
-  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 */
+  IP6_STATS_INC(ip6.xmit);
+
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+  ip6_debug_print(p);
+
+#if ENABLE_LOOPBACK
+  /* TODO implement loopback for v6
+  if (ip6_addr_cmp(dest, netif_ip6_addr(0))) {
+    return netif_loop_output(netif, p, dest);
+  }*/
+#endif /* ENABLE_LOOPBACK */
+#if LWIP_IPV6_FRAG
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) {
+    return ip6_frag(p, netif, dest);
+  }
+#endif /* LWIP_IPV6_FRAG */
 
-  PERF_STOP("ip_output_if");
-  return netif->output(netif, p, dest);
+  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
+#if LWIP_IPV6_MLD
+/**
+ * Add a hop-by-hop options header with a router alert option and padding.
+ *
+ * Used by MLD when sending a Multicast listener report/done message.
+ *
+ * @param p the packet to which we will prepend the options header
+ * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6)
+ * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD)
+ * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise
+ */
+err_t
+ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value)
+{
+  struct ip6_hbh_hdr * hbh_hdr;
+
+  /* Move pointer to make room for hop-by-hop options header. */
+  if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
+    IP6_STATS_INC(ip6.err);
+    return ERR_BUF;
+  }
+
+  hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+
+  /* Set fields. */
+  hbh_hdr->_nexth = nexth;
+  hbh_hdr->_hlen = 0;
+  hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION;
+  hbh_hdr->_ra_opt_dlen = 2;
+  hbh_hdr->_ra_opt_data = value;
+  hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION;
+  hbh_hdr->_padn_opt_dlen = 0;
+
+  return ERR_OK;
+}
+#endif /* LWIP_IPV6_MLD */
+
+#if IP6_DEBUG
+/* Print an IPv6 header by using LWIP_DEBUGF
+ * @param p an IPv6 packet, p->payload pointing to the IPv6 header
+ */
 void
-ip_debug_print(struct pbuf *p)
+ip6_debug_print(struct pbuf *p)
 {
-  struct ip_hdr *iphdr = p->payload;
-
-  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"));
+  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 /* IP_DEBUG */
+#endif /* IP6_DEBUG */
+
+#endif /* LWIP_IPV6 */
index 2da6cea4276b7aca1094229a6d2aab48a00df1c6..3b6820515c6d92a38a9d52eafc5f7d06dc1fb652 100644 (file)
@@ -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, 
  *
  * This file is part of the lwIP TCP/IP stack.
  * 
- * Author: Adam Dunkels <adam@sics.se>
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Functions for handling IPv6 addresses.
  *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
  */
 
 #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)
-{
-  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]));
-        
-}
+#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 } };
 
-u8_t
-ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2)
+#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] == addr2->addr[0] &&
-         addr1->addr[1] == addr2->addr[1] &&
-         addr1->addr[2] == addr2->addr[2] &&
-         addr1->addr[3] == addr2->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;
+
 }
 
-void
-ip_addr_set(struct ip_addr *dest, struct ip_addr *src)
+/**
+ * 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)
 {
-  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];*/
+  static char str[40];
+  return ip6addr_ntoa_r(addr, str, 40);
 }
 
-u8_t
-ip_addr_isany(struct ip_addr *addr)
+/**
+ * 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)
 {
-  if (addr == NULL) return 1;
-  return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0);
+  u32_t current_block_index, current_block_value;
+  s32_t zero_flag, i;
+
+  i = 0;
+  zero_flag = 0; /* used to indicate a zero chain for "::' */
+
+  for (current_block_index = 0; current_block_index < 8; current_block_index++) {
+    /* get the current 16-bit block */
+    current_block_value = htonl(addr->addr[current_block_index >> 1]);
+    if ((current_block_index & 0x1) == 0) {
+      current_block_value = current_block_value >> 16;
+    }
+    current_block_value &= 0xffff;
+
+    if (current_block_value == 0) {
+      /* generate empty block "::" */
+      if (!zero_flag) {
+        if (current_block_index > 0) {
+          zero_flag = 1;
+          buf[i++] = ':';
+          if (i >= buflen) return NULL;
+        }
+      }
+    }
+    else {
+      if (current_block_index > 0) {
+        buf[i++] = ':';
+        if (i >= buflen) return NULL;
+      }
+
+      if ((current_block_value & 0xf000) == 0) {
+        zero_flag = 1;
+      }
+      else {
+        buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
+        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 */
index 4da879a57b46766b499ea70c1f8342302219a417..bfe6ee60568e1a67bef5fc1b3fbb39eeb45e11a7 100644 (file)
@@ -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 <string.h>
 
index f190b1f2e03cad48c09c30ef7f42738851413287..e93762af328648abc0fddbe64df6ed4be3dd83ff 100644 (file)
@@ -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"
 #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 */
index 9fcb100316d534a29237426cfc3725ffc509e3f3..8a0ce7620f42038f2462090f35d88d765785e32c 100644 (file)
@@ -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 <string.h>
 
@@ -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), &current_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(&current_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 */
index c629bc4e8ac1f796ad57ffa7d11b5bba8ae0e654..1d62def6fb3b24ffb30538157138e73cff7b92dc 100644 (file)
@@ -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 <string.h>
 
@@ -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*
index 90952648d7f71ef8f3849c0ba8902682a385ec0e..86bb93e7fa23fcbeb8cecbc991e5bac6733beb79 100644 (file)
 #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(&current_iphdr_dest, inp) ||
-      ip_addr_ismulticast(&current_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), &current_iphdr_src) &&
-       ip_addr_cmp(&(pcb->local_ip), &current_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), &current_iphdr_src) &&
-         ip_addr_cmp(&(pcb->local_ip), &current_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), &current_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), &current_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);
index 86e0919531fe638bc8ddb5633f257ebfadda7e3a..308b8273c837a10930e1c37e0bcfe9b79054e8f7 100644 (file)
@@ -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 <string.h>
 
@@ -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);
 
index f0e92ccebe79e3b018c2287b0cd1a479d4f5776f..da6278f02dc9740f7906f8c5570ee14dee70fa6b 100644 (file)
@@ -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 */
index 4596ba2b79e2fcec5f7c90f21c8fc635513c6ede..2594657c360736a0cf2b3cacd5cce54458b14a58 100644 (file)
 #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(&current_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), &current_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), &current_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(&current_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), &current_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, &current_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, &current_iphdr_src, &current_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(&current_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), &current_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(&current_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(&current_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 ((netif = ip_route(&(pcb->remote_ip))) == NULL) {
-      LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr));
-      UDP_STATS_INC(udp.rterr);
-      return ERR_RTE;
+#if LWIP_IPV6
+  if (!pcb->isipv6)
+#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.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.
index 74f501d1ab49f9649791deb66beba33d2401fcad..18a2abb8afc1e729721f54df42948f01124bd33a 100644 (file)
@@ -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 */ \
index 77b5eb1eef7ad9ffd6aa3055c73233465ee30ae3..47eca9f42ca3ce31b56aeb199df90ecd1b531954 100644 (file)
@@ -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);
index 91b9e5d243556ec51e4554bfa095dc43327d8a98..f85a2dc984ba417da5ffacfdfd312e243201e28a 100644 (file)
@@ -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)
index f99d8c3b7199ac49d362045978e965a1c0793b35..2541e6410ff27754dbd657f63500ebc1f5966434 100644 (file)
@@ -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);
index 6ce408fb02a9352f8afdc615143baf55ffdfe69b..e7b90f29a9b189f1480771094c300d6cf8daefb0 100644 (file)
@@ -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.
  *
index 7d247d71b60c71b6ec952f6d286b11663daff64d..82d3d7010caa7216873792e4aa981c14704b6763 100644 (file)
@@ -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
index a8790b5f6a58a1b0f2b2a962ecc14802fe93b712..a8527f8823e646d4cbecfe7efec33819ff239d2a 100644 (file)
@@ -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
index a1b876583f1a27ada93f4a2d50bf43aaaf0f47c9..e5b5eed850ccef8ba1bdadde3bb0d170718dd261 100644 (file)
 #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
 #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 */
 
 #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 ----------
 #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__ */
index 3b1d608b9cb1153c0b4eaa46151cdc722b3cb3a9..59eec8c5031ff1a9cc9896d346b332bab92bbf76 100644 (file)
@@ -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,
index 17d0a1c54068253778624a86c1c6189da6dca869..a3751f81d6d580e84b0ee5dfad8441308b3fc4a6 100644 (file)
@@ -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. */
index 3c8fed24e6e705570e8f16a67d638323dc18a1dd..09842b673f1629041e07b05f90c79f8d8334aa29 100644 (file)
@@ -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
index 015b7ce79d8e5ecec0ca05393f8542c482485aa6..3ca6ed22f9387da8a898caddbe434281172b17e0 100644 (file)
@@ -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);
index 07dcd10e7863a7c9c723299034feacce1f53000f..53bc5320ed5078f2ee9adf92d06f773d79301c4c 100644 (file)
@@ -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
 }
index b4feec0da1965e978134723eb2e188d516cd8f76..126e8eb07facf538c61500b28c7611a2b052fe04 100644 (file)
@@ -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
index c98c1b3ec14ccc71e627cc8b036fc97d1cc87ff7..68b7b4e15080aed4d25c5a32e7bbfa931de1d287 100644 (file)
@@ -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
index 691575fa1308d4d8b18bb8be1ec755c7a2d99928..2eda1488b17515ff8861acae46772a1b71f8beb5 100644 (file)
@@ -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 */
 
index b60dadd0244529275e3a1918b643ca5c2ef3e8c3..99ae853acd90d142be10117155abb080d07b0532 100644 (file)
@@ -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);
index a5b7d990b11eddddc5cdb81c5263f813aa2ca7e0..c22731ec1123b1b55fc920533afba3fbadfd4c94 100644 (file)
@@ -51,8 +51,9 @@
 #include "lwip/mem.h"
 #include "lwip/pbuf.h"
 #include "lwip/sys.h"
-#include <lwip/stats.h>
-#include <lwip/snmp.h>
+#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]);