From bd5283f4d24be4b982652b38a680264b4a3efdf4 Mon Sep 17 00:00:00 2001
From: wanfeng <wanfeng@kylinos.cn>
Date: Mon, 8 Apr 2024 10:31:20 +0800
Subject: [PATCH] support mldv2

---
 src/api/api_msg.c             |  60 +++--
 src/api/sockets.c             | 262 +++++++++++++++++---
 src/core/ipv6/icmp6.c         |   3 +
 src/core/ipv6/mld6.c          | 360 +++++++++++++++++++++++++++-
 src/core/mcast.c              | 438 +++++++++++++++++++++++++++++++++-
 src/core/udp.c                |  12 +-
 src/include/lwip/api.h        |   1 +
 src/include/lwip/mcast.h      |  35 +++
 src/include/lwip/mld6.h       |  30 +++
 src/include/lwip/netbuf.h     |   3 +
 src/include/lwip/opt.h        |   8 +
 src/include/lwip/prot/icmp6.h |   2 +
 src/include/lwip/prot/mld6.h  |  79 ++++++
 src/include/lwip/sockets.h    |  45 +++-
 src/include/lwipopts.h        |   5 +
 15 files changed, 1249 insertions(+), 94 deletions(-)

diff --git a/src/api/api_msg.c b/src/api/api_msg.c
index 7cae567..f0a3e0b 100644
--- a/src/api/api_msg.c
+++ b/src/api/api_msg.c
@@ -202,7 +202,23 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
         if (IP_IS_V4(addr)) {
           struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
           ip_addr_copy_from_ip4(buf->toaddr, iphdr->dest);
-        } else {}
+        } else
+#if LWIP_IPV6
+        {
+          struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+          ip_addr_copy_from_ip6_packed(buf->toaddr, ip6hdr->dest);
+        }
+#endif /* LWIP_IPV6 */
+      }
+#if LWIP_IPV6
+      if (conn->flags & NETCONN_FLAG_HOPLIM) { /* IPV6_HOPLIMIT */
+        if (IP_IS_V6(addr)) {
+          struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+          buf->flags |= NETBUF_FLAG_HOPLIM; /* IPV6_RECVHOPLIMIT (Hop limit) */
+          buf->hoplim = IP6H_HOPLIM(ip6hdr);
+        }
+      }
+#endif /* LWIP_IPV6 */
 #endif /* LWIP_NETBUF_RECVINFO */
 
       len = q->tot_len;
@@ -290,6 +306,15 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
       ip_addr_set(&buf->toaddr, ip_current_dest_addr());
       buf->toport_chksum = udphdr->dest;
     }
+#if LWIP_IPV6
+    if (conn->flags & NETCONN_FLAG_HOPLIM) { /* IPV6_HOPLIMIT */
+      if (IP_IS_V6(addr)) {
+        struct ip6_hdr *ip6hdr = (struct ip6_hdr *)ip6_current_header();
+        buf->flags |= NETBUF_FLAG_HOPLIM; /* IPV6_RECVHOPLIMIT (Hop limit) */
+        buf->hoplim = IP6H_HOPLIM(ip6hdr);
+      }
+    }
+#endif /* LWIP_IPV6 */
 #endif /* LWIP_NETBUF_RECVINFO */
   }
 
@@ -2084,25 +2109,11 @@ lwip_netconn_do_join_leave_group(void *m)
 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
 #if LWIP_UDP
-#if LWIP_IPV6 && LWIP_IPV6_MLD
-      if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
-                                    ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        } else {
-          msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
-                                     ip_2_ip6(API_EXPR_REF(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 = mcast_join_group(&msg->conn->pcb.udp->ipmc, API_EXPR_REF(msg->msg.jl.netif_addr), API_EXPR_REF(msg->msg.jl.multiaddr), NULL);
       } else {
         msg->err = mcast_leave_group(&msg->conn->pcb.udp->ipmc, API_EXPR_REF(msg->msg.jl.netif_addr), API_EXPR_REF(msg->msg.jl.multiaddr), NULL);
     }
-#endif /*LWIP_IGMP*/   
-      }
 #else
       msg->err = ERR_VAL;
 #endif /* LWIP_UDP */
@@ -2146,30 +2157,15 @@ lwip_netconn_do_join_leave_group_netif(void *m)
 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
 #if LWIP_UDP
-#if LWIP_IPV6 && LWIP_IPV6_MLD
-      if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = mld6_joingroup_netif(netif,
-                                          ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        } else {
-          msg->err = mld6_leavegroup_netif(netif,
-                                           ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        }
-      } else
-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
-      { 
-#if LWIP_IGMP   
-	if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+      if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
         msg->err = mcast_join_netif(&msg->conn->pcb.udp->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL);
       } else {
         msg->err = mcast_leave_netif(&msg->conn->pcb.udp->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL);
       }
-#endif /*LWIP_IGMP*/
 #else
       msg->err = ERR_VAL;
 #endif /* LWIP_UDP */
-      }
-      } else if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_RAW) {
+    } else if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_RAW) {
 #if LWIP_RAW
       if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
         msg->err = mcast_join_netif(&msg->conn->pcb.raw->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL);
diff --git a/src/api/sockets.c b/src/api/sockets.c
index 0caebdd..6b2f5ee 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3936,34 +3936,43 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
 #if LWIP_IPV6_MLD
         case IPV6_JOIN_GROUP:
         case IPV6_LEAVE_GROUP: {
-          /* If this is a TCP or a RAW socket, ignore these options. */
-          err_t mld6_err;
-          struct netif *netif;
-          ip6_addr_t multi_addr;
-          const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
-          inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
-          LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu);
-          netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
-          if (netif == NULL) {
-            err = EADDRNOTAVAIL;
-            break;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct ipv6_mreq);
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_UDP_IPV6) {
+            err = mcast_sock_ipv6_add_drop_membership(s, &sock->conn->pcb.udp->ipmc, optname, (const struct ipv6_mreq *)optval);
+          } else
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_RAW_IPV6) {
+            err = mcast_sock_ipv6_add_drop_membership(s, &sock->conn->pcb.raw->ipmc, optname, (const struct ipv6_mreq *)optval);
+          } else
+#endif /* LWIP_RAW */
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
           }
-
-          if (optname == IPV6_JOIN_GROUP) {
-            if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
-              /* cannot track membership (out of memory) */
-              err = ENOMEM;
-              mld6_err = ERR_OK;
-            } else {
-              mld6_err = mld6_joingroup_netif(netif, &multi_addr);
-            }
-          } else {
-            mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
-            lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
+        }
+        break;
+        case MCAST_JOIN_GROUP:
+        case MCAST_LEAVE_GROUP: {
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct group_req);
+          if (((const struct group_req *)optval)->gr_group.ss_family != AF_INET6) {
+            done_socket(sock);
+            return EINVAL;
           }
-          if (mld6_err != ERR_OK) {
-            err = EADDRNOTAVAIL;
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_UDP_IPV6) {
+            err = mcast_sock_join_leave_group(s, &sock->conn->pcb.udp->ipmc, optname, (const struct group_req *)optval);
+          } else
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_RAW_IPV6) {
+            err = mcast_sock_join_leave_group(s, &sock->conn->pcb.raw->ipmc, optname, (const struct group_req *)optval);
+          } else
+#endif /* LWIP_RAW */
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
           }
         }
         break;
@@ -4486,17 +4495,13 @@ lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_
   if (!sock) {
     return 0;
   }
-  
   SYS_ARCH_DECL_PROTECT(lev);
   SYS_ARCH_PROTECT(lev);
+#if GAZELLE_ENABLE
+  i = s;
+#else
   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
-	if ((socket_ipv6_multicast_memberships[i].sock   == sock) &&
-        (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
-        ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
-      done_socket(sock);
-      return 1;
-    }
-
+#endif
     if (socket_ipv6_multicast_memberships[i].sock == NULL) {
       socket_ipv6_multicast_memberships[i].sock   = sock;
       socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx;
@@ -4504,7 +4509,9 @@ lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_
       done_socket(sock);
       return 1;
     }
+#if !GAZELLE_ENABLE
   }
+#endif
   SYS_ARCH_UNPROTECT(lev);
   done_socket(sock);
   return 0;
@@ -4524,19 +4531,26 @@ lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_add
   if (!sock) {
     return;
   }
-
   SYS_ARCH_DECL_PROTECT(lev);
   SYS_ARCH_PROTECT(lev);
+#if GAZELLE_ENABLE
+    i = s;
+#else
   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+#endif
     if ((socket_ipv6_multicast_memberships[i].sock   == sock) &&
         (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
-        ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {	
+        ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
       socket_ipv6_multicast_memberships[i].sock   = NULL;
       socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
       ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+#if !GAZELLE_ENABLE
       break;
+#endif
     }
+#if !GAZELLE_ENABLE
   }
+#endif
   SYS_ARCH_UNPROTECT(lev);
   done_socket(sock);
 }
@@ -4556,7 +4570,11 @@ lwip_socket_drop_registered_mld6_memberships(int s)
   }
   SYS_ARCH_DECL_PROTECT(lev);
   SYS_ARCH_PROTECT(lev);
+#if GAZELLE_ENABLE
+    i = s;
+#else
   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+#endif
     if (socket_ipv6_multicast_memberships[i].sock == sock) {
       ip_addr_t multi_addr;
       u8_t if_idx;
@@ -4570,7 +4588,9 @@ lwip_socket_drop_registered_mld6_memberships(int s)
 
       netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE);
     }
+#if !GAZELLE_ENABLE
   }
+#endif
   SYS_ARCH_UNPROTECT(lev);
   done_socket(sock);
 }
@@ -4597,6 +4617,25 @@ mcast_pcb_remove(struct ip_mc *ipmc)
     }
   }
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+  {
+    struct ip6_mc *mc, *next;
+    struct netif *netif;
+
+    mc = ipmc->mc6;
+    while (mc) {
+      next = mc->next;
+      mcast_ip6_mc_src_remove(mc->src);
+      netif = netif_get_by_index(mc->if_idx);
+      if (netif) {
+        mld6_leavegroup_netif(netif, &mc->multi_addr);
+      }
+      mem_free(mc);
+      mc = next;
+    }
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
 }
 
 /** Common code to see if the current input multicast packet matches the pcb
@@ -4637,8 +4676,38 @@ mcast_input_local_match(struct ip_mc *ipmc, struct netif *inp)
         }
       }
     }
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct mld6_src *src;
+    struct ip6_mc *mc;
+    ip6_addr_t *multi_addr = ip6_current_dest_addr();
+    ip6_addr_t *src_addr = ip6_current_src_addr();
+
+    IP6_MC_FOREACH(ipmc, mc) {
+      if (((mc->if_idx == NETIF_NO_INDEX) || (mc->if_idx == netif_get_index(inp))) &&
+          ip6_addr_cmp_zoneless(&mc->multi_addr, multi_addr)) {
+        if (mc->fmode == MCAST_EXCLUDE) {
+          IP6_MC_SRC_FOREACH(mc, src) {
+            if (ip6_addr_cmp_zoneless(&src->src_addr, src_addr)) {
+              return 0;
+            }
+          }
+          return 1; /* MCAST_EXCLUDE src_addr must not in src list */
+          
+        } else { /* MCAST_INCLUDE */
+          IP6_MC_SRC_FOREACH(mc, src) {
+            if (ip6_addr_cmp_zoneless(&src->src_addr, src_addr)) {
+              return 1;
+            }
+          }
+          return 0; /* MCAST_INCLUDE src_addr must in src list */
+        }
+      }
+    }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return 0;
 }
 
@@ -4722,6 +4791,125 @@ mcast_sock_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const str
 out:
   return (u8_t)err_to_errno(err);
 }
+
+/**
+ * setsockopt() MCAST_JOIN_GROUP / MCAST_LEAVE_GROUP command
+ */
+int
+mcast_sock_join_leave_group(int s, struct ip_mc *ipmc, int optname, const struct group_req *gr)
+{
+  struct netif *netif;
+  ip_addr_t multi_addr;
+  err_t err;
+  u8_t if_idx;
+
+  if (gr->gr_group.ss_family == AF_INET) {
+    inet_addr_to_ip4addr(ip_2_ip4(&multi_addr), &(((struct sockaddr_in *)&(gr->gr_group))->sin_addr));
+    IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V4);
+    if (!ip4_addr_ismulticast(ip_2_ip4(&multi_addr))) {
+      return EADDRNOTAVAIL;
+    }
+
+  } else if (gr->gr_group.ss_family == AF_INET6) {
+    inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &(((struct sockaddr_in6 *)&(gr->gr_group))->sin6_addr));
+    IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V6);
+    if (!ip6_addr_ismulticast(ip_2_ip6(&multi_addr))) {
+      return EADDRNOTAVAIL;
+    }
+
+  } else {
+    return EADDRNOTAVAIL;
+  }
+
+  if (gr->gr_interface) {
+    netif = netif_get_by_index((u8_t)gr->gr_interface);
+  } else {
+    netif = netif_default; /* To default network interface */
+  }
+  if (netif == NULL) {
+    return ENXIO;
+  }
+
+  if_idx = netif_get_index(netif);
+  if (optname == MCAST_JOIN_GROUP) {
+#if LWIP_IPV4 && LWIP_IGMP
+    if (IP_IS_V4(&multi_addr)) {
+      if (!lwip_socket_register_membership(s, if_idx, ip_2_ip4(&multi_addr))) {
+        /* cannot track membership (out of memory) */
+        err = ENOMEM;
+        goto out;
+      }
+    } else 
+#endif
+    {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+      if (!lwip_socket_register_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr))) {
+        /* cannot track membership (out of memory) */
+        err = ENOMEM;
+        goto out;
+      }
+#endif
+    }
+
+    err = mcast_join_netif(ipmc, netif, &multi_addr, NULL);
+  } else {
+    err = mcast_leave_netif(ipmc, netif, &multi_addr, NULL);
+#if LWIP_IPV4 && LWIP_IGMP
+    if (IP_IS_V4(&multi_addr)) {
+      lwip_socket_unregister_membership(s, if_idx, ip_2_ip4(&multi_addr));
+    } else 
+#endif
+    {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+      lwip_socket_unregister_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr));
+#endif
+    }
+  }
+out:
+  return err_to_errno(err);
+}
+
+/**
+ * setsockopt() IPV6_JOIN_GROUP / IPV6_LEAVE_GROUP command
+ */
+int
+mcast_sock_ipv6_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const struct ipv6_mreq *imr)
+{
+  struct netif *netif;
+  ip_addr_t multi_addr;
+  err_t err;
+
+  inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &imr->ipv6mr_multiaddr);
+  IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V6);
+
+  if (!ip6_addr_ismulticast(ip_2_ip6(&multi_addr))) {
+    return EADDRNOTAVAIL;
+  }
+
+  if (imr->ipv6mr_interface) {
+    netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
+  } else {
+    netif = netif_default; /* To default network interface */
+  }
+  if (netif == NULL) {
+    return ENXIO;
+  }
+
+  u8_t if_idx = netif_get_index(netif);
+  if (optname == IPV6_JOIN_GROUP) {
+    if (!lwip_socket_register_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr))) {
+      /* cannot track membership (out of memory) */
+      err = ENOMEM;
+      goto out;
+    }
+    err = mcast_join_netif(ipmc, netif, &multi_addr, NULL);
+  } else {
+    err = mcast_leave_netif(ipmc, netif, &multi_addr, NULL);
+    lwip_socket_unregister_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr));
+  }
+out:
+  return err_to_errno(err);
+}
 #endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)*/
 
 #if GAZELLE_ENABLE
diff --git a/src/core/ipv6/icmp6.c b/src/core/ipv6/icmp6.c
index ed0bd7b..143cb20 100644
--- a/src/core/ipv6/icmp6.c
+++ b/src/core/ipv6/icmp6.c
@@ -129,6 +129,9 @@ icmp6_input(struct pbuf *p, struct netif *inp)
   case ICMP6_TYPE_MLQ:
   case ICMP6_TYPE_MLR:
   case ICMP6_TYPE_MLD:
+#if LWIP_IPV6_MLD_V2
+  case ICMP6_TYPE_MLR2:
+#endif
     mld6_input(p, inp);
     return;
 #endif
diff --git a/src/core/ipv6/mld6.c b/src/core/ipv6/mld6.c
index ac4fb01..29c45bc 100644
--- a/src/core/ipv6/mld6.c
+++ b/src/core/ipv6/mld6.c
@@ -66,6 +66,7 @@
 #include "lwip/netif.h"
 #include "lwip/memp.h"
 #include "lwip/stats.h"
+#include "lwip/sockets.h"
 
 #include <string.h>
 
@@ -80,12 +81,22 @@
 #define MLD6_GROUP_DELAYING_MEMBER        1
 #define MLD6_GROUP_IDLE_MEMBER            2
 
+#define MLD6_MINLEN                    12
+#define MLD6_V2_MINLEN                  16
+
 /* Forward declarations. */
 static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
 static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
 static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
 static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
+static void mld6_timeout(struct netif *netif, struct mld_group *group);
+
 
+#if LWIP_IPV6_MLD_V2
+static void mld6_v2_timeout(struct netif *netif, struct mld_group *group);
+static void mld6_v2_delayed_report(struct mld_group *group, u16_t maxresp_in);
+static void mld6_v2_send(struct netif *netif, struct mld_group *group, u8_t type);
+#endif
 
 /**
  * Stop MLD processing on interface
@@ -128,6 +139,9 @@ mld6_report_groups(struct netif *netif)
 
   while (group != NULL) {
     mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+#if LWIP_IPV6_MLD_V2
+    mld6_v2_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+#endif
     group = group->next;
   }
 }
@@ -177,6 +191,12 @@ mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
     group->last_reporter_flag = 0;
     group->use                = 0;
     group->next               = netif_mld6_data(ifp);
+#if LWIP_IPV6_MLD_V2
+    group->v2_fmode = MLD6_FMODE_INIT;
+    group->v2_last_reporter_flag = 0;
+    group->v2_group_state = MLD6_GROUP_IDLE_MEMBER;
+    group->v2_timer = 0;
+#endif
 
     netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
   }
@@ -244,6 +264,58 @@ mld6_input(struct pbuf *p, struct netif *inp)
 
   switch (mld_hdr->type) {
   case ICMP6_TYPE_MLQ: /* Multicast listener query. */
+#if LWIP_IPV6_MLD_V2
+    if (p->len >= MLD6_V2_MINLEN) {
+      struct mld_v2_query *mld_v2_hdr = (struct mld_v2_query *)p->payload;
+
+      /* Is it a general query? */
+      if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
+          ip6_addr_isany(&(mld_v2_hdr->multicast_address))) {
+        MLD6_STATS_INC(mld6.rx_general);
+        /* Report all groups, except all nodes group, and if-local groups. */
+        group = netif_mld6_data(inp);
+        while (group != NULL) {
+          if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
+              (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
+            mld6_v2_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
+          }
+          group = group->next;
+        }
+      } else { /* query to a specific group ? */
+        if (!ip6_addr_isany(&(mld_v2_hdr->multicast_address))) {
+          u16_t src_num = PP_NTOHS(mld_v2_hdr->src_num);
+          ip6_addr_p_t *src_buf;
+          u16_t src_buf_size = src_num << 4;
+          ip6_addr_t group_addr;
+
+          MLD6_STATS_INC(mld6.rx_group);
+          ip6_addr_copy(group_addr, mld_v2_hdr->multicast_address);
+          group = mld6_lookfor_group(inp, &group_addr);
+
+          if (0 == src_num) {
+            if (group != NULL) {
+              /* Schedule a report. */
+              mld6_v2_delayed_report(group, lwip_ntohs(mld_v2_hdr->max_resp_delay));
+            }
+          } else {
+            src_buf = (ip6_addr_p_t *)mem_malloc(src_buf_size);
+            if (src_buf == NULL) {
+              pbuf_free(p);
+              MLD6_STATS_INC(mld6.memerr);
+              LWIP_DEBUGF(MLD6_DEBUG, ("mld6_input: not enough memory for mld6_input\n"));
+              return;
+            }
+            pbuf_copy_partial(p, src_buf, src_buf_size, MLD_V2_QUERY_HLEN);
+            if (mcast_ip6_filter_interest(inp, (const ip6_addr_t *)&group_addr, src_buf, src_num)) {
+              /* We interest! */
+              mld6_v2_delayed_report(group, lwip_ntohs(mld_v2_hdr->max_resp_delay));
+            }
+          }
+        }
+      }
+
+    }
+#endif
     /* Is it a general query? */
     if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
         ip6_addr_isany(&(mld_hdr->multicast_address))) {
@@ -381,6 +453,10 @@ mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
     MLD6_STATS_INC(mld6.tx_report);
     mld6_send(netif, group, ICMP6_TYPE_MLR);
     mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+#if LWIP_IPV6_MLD_V2
+    mld6_v2_send(netif, group, ICMP6_TYPE_MLR2);
+    mld6_v2_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+#endif
   }
 
   /* Increment group use */
@@ -464,6 +540,13 @@ mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
         MLD6_STATS_INC(mld6.tx_leave);
         mld6_send(netif, group, ICMP6_TYPE_MLD);
       }
+#if LWIP_IPV6_MLD_V2
+      /* If we are the last reporter for this group */
+      if (group->v2_last_reporter_flag) {
+        MLD6_STATS_INC(mld6.tx_leave);
+        mld6_v2_send(netif, group, ICMP6_TYPE_MLD);
+      }
+#endif
 
       /* Disable the group at the MAC level */
       if (netif->mld_mac_filter != NULL) {
@@ -485,6 +568,51 @@ mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
   return ERR_VAL;
 }
 
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an mld_group for which a timeout is reached
+ */
+static void
+mld6_timeout(struct netif *netif, struct mld_group *group)
+{
+  /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
+  if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+    LWIP_DEBUGF(MLD6_DEBUG, ("mld6_timeout: report membership for group with address "));
+    ip6_addr_debug_print_val(MLD6_DEBUG, group->group_address);
+    LWIP_DEBUGF(MLD6_DEBUG, (" on if %p\n", (void *)netif));
+
+    group->group_state = MLD6_GROUP_IDLE_MEMBER;
+
+    MLD6_STATS_INC(mld6.tx_report);
+    mld6_send(netif, group, ICMP6_TYPE_MLR);
+  }
+}
+
+#if LWIP_IPV6_MLD_V2
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an mld_group for which a timeout is reached
+ */
+static void
+mld6_v2_timeout(struct netif *netif, struct mld_group *group)
+{
+  /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
+  if (group->v2_group_state == MLD6_GROUP_DELAYING_MEMBER) {
+    LWIP_DEBUGF(MLD6_DEBUG, ("mld6_v2_timeout: report membership for group with address "));
+    ip6_addr_debug_print_val(MLD6_DEBUG, group->group_address);
+    LWIP_DEBUGF(MLD6_DEBUG, (" on if %p\n", (void *)netif));
+
+    group->v2_group_state = MLD6_GROUP_IDLE_MEMBER;
+
+    MLD6_STATS_INC(mld6.tx_report);
+    mld6_v2_send(netif, group, ICMP6_TYPE_MLR2);
+  }
+}
+#endif /* LWIP_IPV6_MLD_V2 */
 
 /**
  * Periodic timer for mld processing. Must be called every
@@ -504,14 +632,17 @@ mld6_tmr(void)
       if (group->timer > 0) {
         group->timer--;
         if (group->timer == 0) {
-          /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
-          if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
-            MLD6_STATS_INC(mld6.tx_report);
-            mld6_send(netif, group, ICMP6_TYPE_MLR);
-            group->group_state = MLD6_GROUP_IDLE_MEMBER;
-          }
+          mld6_timeout(netif, group);
+        }
+      }
+#if LWIP_IPV6_MLD_V2
+      if (group->v2_timer > 0) {
+        group->v2_timer--;
+        if (group->v2_timer == 0) {
+          mld6_v2_timeout(netif, group);
         }
       }
+#endif
       group = group->next;
     }
   }
@@ -550,6 +681,46 @@ mld6_delayed_report(struct mld_group *group, u16_t maxresp_in)
   }
 }
 
+#if LWIP_IPV6_MLD_V2
+/**
+ * Schedule a delayed membership report for a group
+ *
+ * @param group the mld_group for which "delaying" membership report
+ *              should be sent
+ * @param maxresp_in the max resp delay provided in the query
+ */
+static void
+mld6_v2_delayed_report(struct mld_group *group, u16_t maxresp_in)
+{
+  /* Convert maxresp from milliseconds to tmr ticks */
+  u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL;
+  if (maxresp >= 32768) {
+    maxresp = ((maxresp & 0x0fff) | 0x1000) << (((maxresp >> 12) & 7) + 3);;
+  }
+  if (maxresp == 0) {
+    maxresp = 1;
+  }
+
+#ifdef LWIP_RAND
+  /* Randomize maxresp. (if LWIP_RAND is supported) */
+  maxresp = (u16_t)(LWIP_RAND() % maxresp);
+  if (maxresp == 0) {
+    maxresp = 1;
+  }
+#endif /* LWIP_RAND */
+
+  /* Apply timer value if no report has been scheduled already. */
+  if ((group->v2_group_state == MLD6_GROUP_IDLE_MEMBER) ||
+     ((group->v2_group_state == MLD6_GROUP_DELAYING_MEMBER) &&
+      ((group->v2_timer == 0) || (maxresp < group->v2_timer)))) {
+    group->v2_timer = maxresp;
+    group->v2_group_state = MLD6_GROUP_DELAYING_MEMBER;
+  }
+}
+
+#endif
+
+
 /**
  * Send a MLD message (report or done).
  *
@@ -623,4 +794,181 @@ mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
   pbuf_free(p);
 }
 
+#if LWIP_IPV6_MLD_V2
+/**
+ * Build a mld v2 record to a specific group.
+ *
+ * @param rec record to build
+ * @param group the group to which to send the packet
+ * @param fmode filter mode
+ * @param src_array source addr array
+ * @param src_cnt source addr cnt
+ */
+static void
+mld_v2_build_record(struct mld_v2_record *rec, struct mld_group *group, u8_t fmode, ip6_addr_p_t *src_array, u32_t src_cnt)
+{
+  ip6_addr_p_t *src_copy;
+
+  if (fmode == MCAST_EXCLUDE) {
+    if (group->v2_fmode != MCAST_EXCLUDE) {
+      group->v2_fmode = MCAST_EXCLUDE;
+      rec->type = MLD2_CHANGE_TO_EXCLUDE;
+    } else {
+      rec->type = MLD2_MODE_IS_EXCLUDE;
+    }
+
+  } else {
+    if (group->v2_fmode != MCAST_INCLUDE) {
+      group->v2_fmode = MCAST_INCLUDE;
+      rec->type = MLD2_CHANGE_TO_INCLUDE;
+    } else {
+      rec->type = MLD2_MODE_IS_INCLUDE;
+    }
+  }
+
+  rec->aux_len = 0;
+  rec->src_num = PP_HTONS(src_cnt);
+  ip6_addr_copy_to_packed(rec->multicast_address, group->group_address);
+
+  if (src_cnt) {
+    src_copy = (ip6_addr_p_t *)(rec + 1);
+    MEMCPY(src_copy, src_array, (src_cnt << 4));
+  }
+}
+
+/**
+ * Send a MLD message (report).
+ *
+ * An IPv6 hop-by-hop options header with a router alert option
+ * is prepended.
+ *
+ * @param group the group to report or quit
+ * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
+ */
+static void
+mld6_v2_send(struct netif *netif, struct mld_group *group, u8_t type)
+{
+  struct mld_v2_report *mld_hdr;
+  struct mld_v2_record *rec;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+
+  /* Select our source address. */
+  if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+    /* This is a special case, when we are performing duplicate address detection.
+     * We must join the multicast group, but we don't have a valid address yet. */
+    src_addr = IP6_ADDR_ANY6;
+  } else {
+    /* Use link-local address as source address. */
+    src_addr = netif_ip6_addr(netif, 0);
+  }
+
+  if (ICMP6_TYPE_MLD == type) {
+    p = pbuf_alloc(PBUF_IP, MLD_V2_REPORT_HLEN + MLD_V2_RECORD_LEN(0) + MLD6_HBH_HLEN, PBUF_RAM);
+    if (p == NULL) {
+      LWIP_DEBUGF(MLD6_DEBUG, ("mld6_v2_send: not enough memory for mld6_v2_send\n"));
+      IGMP_STATS_INC(mld6.memerr);
+      return;
+    }
+
+    /* Move to make room for Hop-by-hop options header. */
+    if (pbuf_remove_header(p, MLD6_HBH_HLEN)) {
+      pbuf_free(p);
+      MLD6_STATS_INC(mld6.lenerr);
+      return;
+    }
+
+    mld_hdr = (struct mld_v2_report *)p->payload;
+    mld_hdr->type  = ICMP6_TYPE_MLR2;
+    mld_hdr->reserved1 = 0;
+    mld_hdr->chksum = 0;
+    mld_hdr->reserved2 = 0;
+    mld_hdr->mrec_num   = PP_HTONS(1);
+
+    rec = (struct mld_v2_record *)(mld_hdr + 1);
+    if (group->v2_fmode == MCAST_EXCLUDE) {
+      rec->type = MLD2_CHANGE_TO_INCLUDE;
+    } else {
+      rec->type = MLD2_MODE_IS_INCLUDE;
+    }
+    rec->aux_len = 0;
+    rec->src_num = 0; /* IS_IN (NULL) mean drop group */
+    ip6_addr_copy_to_packed(rec->multicast_address, group->group_address);
+  } else {/* Report a group */
+    ip6_addr_p_t src_array[LWIP_MCAST_SRC_TBL_SIZE];
+    u16_t src_cnt;
+    u8_t fmode;
+
+    src_cnt = mcast_ip6_filter_info(netif, &group->group_address, src_array, LWIP_MCAST_SRC_TBL_SIZE, &fmode);
+    LWIP_ASSERT("mld6_v2_send: multicast filter error!", !(!src_cnt && (fmode == MCAST_INCLUDE)));
+
+    p = pbuf_alloc(PBUF_IP, MLD_V2_REPORT_HLEN + MLD_V2_RECORD_LEN(src_cnt) + MLD6_HBH_HLEN, PBUF_RAM);
+    if (p == NULL) {
+      LWIP_DEBUGF(MLD6_DEBUG, ("mld6_v2_send: not enough memory for mld6_v2_send\n"));
+      IGMP_STATS_INC(mld6.memerr);
+      return;
+    }
+
+    mld_hdr = (struct mld_v2_report *)p->payload;
+    mld_hdr->type  = ICMP6_TYPE_MLR2;
+    mld_hdr->reserved1 = 0;
+    mld_hdr->chksum = 0;
+    mld_hdr->reserved2 = 0;
+    mld_hdr->mrec_num   = PP_HTONS(1);
+
+    rec = (struct mld_v2_record *)(mld_hdr + 1);
+    mld_v2_build_record(rec, group, fmode, src_array, src_cnt);
+    group->v2_last_reporter_flag = 1;/* Remember we were the last to report */
+  }
+
+ #if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
+      src_addr, &(group->group_address));
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Add hop-by-hop headers options: router alert with MLD value. */
+  ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
+
+  /* Send the packet out. */
+  MLD6_STATS_INC(mld6.xmit);
+  ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
+      MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
+  pbuf_free(p);
+}
+
+/**
+ * mld v2 report trigger.
+ */
+void
+mld6_v2_trigger(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+  struct mld_group *group;
+#if LWIP_IPV6_SCOPES
+    ip6_addr_t ip6addr;
+
+    /* If the address has a particular scope but no zone set, use the netif to
+     * set one now. Within the mld6 module, all addresses are properly zoned. */
+    if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+      ip6_addr_set(&ip6addr, groupaddr);
+      ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+      groupaddr = &ip6addr;
+    }
+    IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
+
+  /* find group */
+  group = mld6_lookfor_group(netif, groupaddr);
+  if (group) {
+    mld6_v2_send(netif, group, ICMP6_TYPE_MLR2);
+
+    mld6_v2_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+
+    /* Need to work out where this timer comes from */
+    group->v2_group_state = MLD6_GROUP_DELAYING_MEMBER;
+  }
+}
+#endif
+
 #endif /* LWIP_IPV6 */
diff --git a/src/core/mcast.c b/src/core/mcast.c
index 5e3463b..fa46f10 100644
--- a/src/core/mcast.c
+++ b/src/core/mcast.c
@@ -250,6 +250,204 @@ mcast_ip4_filter_interest(struct netif *netif, const ip4_addr_t *multi_addr, con
 #endif /* LWIP_IGMP_V3 */
 #endif /* LWIP_IPV4 && LWIP_IGMP */
 
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+/**
+ * ipv6 multicast filter find
+ */
+struct ip6_mc *
+mcast_ip6_mc_find(struct ip_mc *ipmc, struct netif *netif, const ip6_addr_t *multi_addr, struct ip6_mc **mc_prev)
+{
+  struct ip6_mc *prev = NULL;
+  struct ip6_mc *mc;
+
+  IP6_MC_FOREACH(ipmc, mc) {
+    if (((mc->if_idx == NETIF_NO_INDEX) || (mc->if_idx == netif_get_index(netif))) && 
+        ip6_addr_cmp_zoneless(&mc->multi_addr, multi_addr)) { /* check interface and multicast address */
+      if (mc_prev) {
+        *mc_prev = prev;
+      }
+      return mc; /* found! */
+    }
+    prev = mc;
+  }
+
+  return NULL; /* not found! */
+}
+
+/**
+ * ipv6 multicast filter find source
+ */
+static struct mld6_src *
+mcast_ip6_mc_src_find(struct ip6_mc *mc, const ip6_addr_t *src_addr, struct mld6_src **src_prev)
+{
+  struct mld6_src *prev = NULL;
+  struct mld6_src *src;
+
+  IP6_MC_SRC_FOREACH(mc, src) {
+    if (ip6_addr_cmp_zoneless(&src->src_addr, src_addr)) { /* check source address */
+      if (src_prev) {
+        *src_prev = prev;
+      }
+      return src; /* found! */
+    }
+    prev = src;
+  }
+
+  return NULL; /* not found! */
+}
+
+/**
+ * ipv6 multicast filter remove all source
+ */
+void
+mcast_ip6_mc_src_remove(struct mld6_src *src)
+{
+  struct mld6_src *next;
+
+  while (src) {
+    next = src->next;
+    mem_free(src);
+    src = next;
+  }
+}
+
+#if LWIP_IPV6_MLD_V2
+/**
+ * ipv6 multicast filter group source infomation (use ip6_addr_p_t for MLDv2 speedup)
+ */
+u16_t
+mcast_ip6_filter_info(struct netif *netif, const ip6_addr_t *multi_addr, ip6_addr_p_t addr_array[], u16_t arr_cnt, u8_t *fmode)
+{
+  static ip6_addr_p_t in_tbl[LWIP_MCAST_SRC_TBL_SIZE];
+  static ip6_addr_p_t ex_tbl[LWIP_MCAST_SRC_TBL_SIZE];
+
+  struct ip6_mc *mc;
+  struct mld6_src *src;
+  ip6_addr_t addr; /* for compare speed */
+  u16_t i, j;
+  u16_t cnt, in_cnt = 0, ex_cnt = 0;
+  u16_t max_cnt = (u16_t)((arr_cnt > LWIP_MCAST_SRC_TBL_SIZE) ? LWIP_MCAST_SRC_TBL_SIZE : arr_cnt);
+  u8_t match = 0;
+
+#define LWIP_IP6_MC_GET(pcb, pcbs, type, tbl, c) \
+  for ((pcb) = (pcbs); (pcb) != NULL; (pcb) = (pcb)->next) { \
+    mc = mcast_ip6_mc_find(&(pcb)->ipmc, netif, multi_addr, NULL); \
+    if ((mc == NULL) || (mc->fmode != (type))) { \
+      continue; \
+    } \
+    match = 1; \
+    IP6_MC_SRC_FOREACH(mc, src) { \
+      if ((c) < max_cnt) { \
+        ip6_addr_copy_to_packed((tbl)[(c)], src->src_addr); /* save a source */ \
+        (c)++; \
+      } else { \
+        *fmode = MCAST_EXCLUDE; /* table overflow, we need all this group packet */ \
+        return (0); \
+      } \
+    } \
+  }
+
+#if LWIP_UDP
+  {
+    struct udp_pcb *pcb;
+    LWIP_IP6_MC_GET(pcb, udp_pcbs, MCAST_INCLUDE, in_tbl, in_cnt); /* copy all udp include source address to in_tbl[] */
+  }
+#endif /* LWIP_UDP */
+
+#if LWIP_RAW
+  {
+    struct raw_pcb *pcb;
+    LWIP_IP6_MC_GET(pcb, raw_pcbs, MCAST_INCLUDE, in_tbl, in_cnt); /* copy all raw include source address to in_tbl[] */
+  }
+#endif /* LWIP_UDP */
+
+#if LWIP_UDP
+  {
+    struct udp_pcb *pcb;
+    LWIP_IP6_MC_GET(pcb, udp_pcbs, MCAST_EXCLUDE, ex_tbl, ex_cnt); /* copy all udp exclude source address to ex_tbl[] */
+  }
+#endif /* LWIP_UDP */
+
+#if LWIP_RAW
+  {
+    struct raw_pcb *pcb;
+    LWIP_IP6_MC_GET(pcb, raw_pcbs, MCAST_EXCLUDE, ex_tbl, ex_cnt); /* copy all raw exclude source address to ex_tbl[] */
+  }
+#endif /* LWIP_UDP */
+
+  if (ex_cnt) { /* at least have one exclude source address */
+    *fmode = MCAST_EXCLUDE;
+    for (i = 0; i < ex_cnt; i++) {
+      ip6_addr_copy_from_packed(addr, ex_tbl[i]);
+      for (j = 0; j < in_cnt; j++) {
+        if (ip6_addr_cmp_zoneless(&addr, &in_tbl[j])) { /* check exclude conflict with include table */
+          ip6_addr_copy_to_packed(ex_tbl[i], *IP6_ADDR_ANY6); /* remove from exclude table */
+          break;
+        }
+      }
+    }
+
+    for (i = 0, cnt = 0; i < ex_cnt; i++) {
+      if (!ip6_addr_isany(&ex_tbl[i])) {
+        ip6_addr_copy_to_packed(addr_array[cnt], ex_tbl[i]);
+        cnt++;
+      }
+    }
+
+  } else if (in_cnt) { /* at least have one include source address */
+    *fmode = MCAST_INCLUDE;
+    for (i = 0; i < in_cnt; i++) {
+      ip6_addr_copy_to_packed(addr_array[i], in_tbl[i]);
+    }
+    cnt = i;
+
+  } else {
+    if (match) { /* at least have one pcb matched */
+      *fmode = MCAST_EXCLUDE;
+    } else { /* no match! */
+      *fmode = MCAST_INCLUDE;
+    }
+    cnt = 0;
+  }
+
+  return (cnt);
+}
+
+/**
+ * ipv6 multicast filter source address interest (use ip6_addr_p_t for MLDv2 speedup)
+ */
+u8_t
+mcast_ip6_filter_interest(struct netif *netif, const ip6_addr_t *multi_addr, const ip6_addr_p_t src_addr[], u16_t arr_cnt)
+{
+  static ip6_addr_p_t ip_tbl[LWIP_MCAST_SRC_TBL_SIZE];
+  u16_t i, j, cnt;
+  u8_t fmode;
+
+  cnt = mcast_ip6_filter_info(netif, multi_addr, ip_tbl, LWIP_MCAST_SRC_TBL_SIZE, &fmode);
+  if (fmode == MCAST_EXCLUDE) {
+    for (i = 0; i < cnt; i++) {
+      for (j = 0; j < arr_cnt; j++) {
+        if (ip6_addr_cmp_zoneless(&src_addr[j], &ip_tbl[i])) {
+          return 0;
+        }
+      }
+    }
+    return 1;
+
+  } else {
+    for (i = 0; i < cnt; i++) {
+      for (j = 0; j < arr_cnt; j++) {
+        if (ip6_addr_cmp_zoneless(&src_addr[j], &ip_tbl[i])) {
+          return 1;
+        }
+      }
+    }
+    return 0;
+  }
+}
+#endif /* LWIP_IPV6_MLD_V2 */
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
 #if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)
 /** Join a multicast group (Can with a source specified)
  *
@@ -303,8 +501,9 @@ mcast_join_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi
         mc->src = src;
         mc->fmode = MCAST_INCLUDE; /* change to include mode */
         IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */
+        return ERR_OK;
       }
-      return ERR_OK;
+      return EADDRINUSE;
     }
 
     mc = (struct ip4_mc *)mem_malloc(sizeof(struct ip4_mc)); /* Make a new mc */
@@ -334,8 +533,68 @@ mcast_join_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi
     mc->next = ipmc->mc4;
     ipmc->mc4 = mc;
     igmp_joingroup_netif(netif, ip_2_ip4(multi_addr));
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct ip6_mc *mc;
+    struct mld6_src *src;
+
+    mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL);
+    if (mc) {
+      if (src_addr) {
+        if ((mc->fmode == MCAST_EXCLUDE) && (mc->src)) {
+          return ERR_VAL; /* filter mode not include mode */
+        }
+        src = mcast_ip6_mc_src_find(mc, ip_2_ip6(src_addr), NULL);
+        if (src) {
+          return ERR_ALREADY; /* already in source list */
+        }
+
+        src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src));
+        if (src == NULL) {
+          return ERR_MEM; /* no memory */
+        }
+        ip6_addr_set(&src->src_addr, ip_2_ip6(src_addr));
+        src->next = mc->src;
+        mc->src = src;
+        mc->fmode = MCAST_INCLUDE; /* change to include mode */
+        IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */
+        return ERR_OK;
+      }
+      return EADDRINUSE;
+    }
+
+    mc = (struct ip6_mc *)mem_malloc(sizeof(struct ip6_mc)); /* Make a new mc */
+    if (mc == NULL) {
+      mld6_leavegroup_netif(netif, ip_2_ip6(multi_addr));
+      return ERR_MEM; /* no memory */
+    }
+    mc->if_idx = netif_get_index(netif);
+    ip6_addr_set(&mc->multi_addr, ip_2_ip6(multi_addr));
+
+    if (src_addr) {
+      mc->fmode = MCAST_INCLUDE;
+      src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src));
+      if (src == NULL) {
+        mld6_leavegroup_netif(netif, ip_2_ip6(multi_addr));
+        mem_free(mc);
+        return ERR_MEM; /* no memory */
+      }
+      ip6_addr_set(&src->src_addr, ip_2_ip6(src_addr));
+      src->next = NULL;
+      mc->src = src;
+
+    } else {
+      mc->fmode = MCAST_EXCLUDE; /* no source specified */
+      mc->src = NULL;
+    }
+
+    mc->next = ipmc->mc6;
+    ipmc->mc6 = mc;
+    mld6_joingroup_netif(netif, ip_2_ip6(multi_addr));
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return ERR_OK;
 }
 
@@ -364,8 +623,23 @@ mcast_join_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *
         }
       }
     }
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct netif *netif;
+
+    NETIF_FOREACH(netif) {
+      if (ip6_addr_isany(ip_2_ip6(if_addr)) ||
+          netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) {
+        err = mcast_join_netif(ipmc, netif, multi_addr, src_addr);
+        if (err != ERR_OK) {
+          return (err);
+        }
+      }
+    }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return err;
 }
 
@@ -423,8 +697,53 @@ mcast_leave_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *mult
       ipmc->mc4 = mc->next;
     }
     mem_free(mc);
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct ip6_mc *mc_prev;
+    struct ip6_mc *mc;
+    struct mld6_src *src_prev;
+    struct mld6_src *src;
+
+    mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), &mc_prev);
+    if (mc == NULL) {
+      return ERR_VAL;
+    }
+
+    if (src_addr) {
+      if ((mc->fmode == MCAST_EXCLUDE) && (mc->src)) {
+        return ERR_VAL; /* drop source membership must in include mode */
+      }
+
+      src = mcast_ip6_mc_src_find(mc, ip_2_ip6(src_addr), &src_prev);
+      if (src) {
+        if (src_prev) {
+          src_prev->next = src->next;
+        } else {
+          mc->src = src->next;
+        }
+        mem_free(src);
+      } else {
+        return ERR_VAL;
+      }
+      if (mc->src) {
+        IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */
+        return ERR_OK;
+      }
+    } else { /* we want drop this group */
+      mcast_ip6_mc_src_remove(mc->src);
+    }
+
+    mld6_leavegroup_netif(netif, ip_2_ip6(multi_addr));
+    if (mc_prev) {
+      mc_prev->next = mc->next;
+    } else {
+      ipmc->mc6 = mc->next;
+    }
+    mem_free(mc);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return ERR_OK;
 }
 
@@ -453,8 +772,23 @@ mcast_leave_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t
         }
       }
     }
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct netif *netif;
+
+    NETIF_FOREACH(netif) {
+      if (ip6_addr_isany(ip_2_ip6(if_addr)) ||
+          netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) {
+        res = mcast_leave_netif(ipmc, netif, multi_addr, src_addr);
+        if (err != ERR_OK) {
+          err = res;
+        }
+      }
+    }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return err;
 }
 
@@ -492,11 +826,36 @@ mcast_block_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *mult
       src->next = mc->src;
       mc->src = src;
       IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */
-      return ERR_OK;
     }
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
-  return EADDRINUSE;
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct ip6_mc *mc;
+    struct mld6_src *src;
+
+    mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL);
+    if (mc == NULL) {
+      return ERR_VAL;
+    }
+    if (mc->fmode != MCAST_EXCLUDE) { /* we must in exclude mode */
+      return ERR_VAL;
+    }
+
+    src = mcast_ip6_mc_src_find(mc, ip_2_ip6(blk_addr), NULL);
+    if (src == NULL) {
+      src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src));
+      if (src == NULL) {
+        return ERR_MEM;
+      }
+      ip6_addr_set(&src->src_addr, ip_2_ip6(blk_addr));
+      src->next = mc->src;
+      mc->src = src;
+      IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */
+    }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
+  return ERR_OK;
 }
 
 /** Add a block source address to a multicast group
@@ -524,8 +883,23 @@ mcast_block_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t
         }
       }
     }
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct netif *netif;
+
+    NETIF_FOREACH(netif) {
+      if (ip6_addr_isany(ip_2_ip6(if_addr)) ||
+          netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) {
+        err = mcast_block_netif(ipmc, netif, multi_addr, blk_addr);
+        if (err != ERR_OK) {
+          return (err);
+        }
+      }
+    }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return err;
 }
 
@@ -565,8 +939,35 @@ mcast_unblock_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *mu
     }
     mem_free(src);
     IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct ip6_mc *mc;
+    struct mld6_src *src_prev;
+    struct mld6_src *src;
+
+    mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL);
+    if (mc == NULL) {
+      return ERR_VAL;
+    }
+    if (mc->fmode != MCAST_EXCLUDE) { /* we must in exclude mode */
+      return ERR_VAL;
+    }
+
+    src = mcast_ip6_mc_src_find(mc, ip_2_ip6(unblk_addr), &src_prev);
+    if (src == NULL) {
+      return ERR_VAL;
+    }
+    if (src_prev) {
+      src_prev->next = src->next;
+    } else {
+      mc->src = src->next;
+    }
+    mem_free(src);
+    IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return ERR_OK;
 }
 
@@ -595,8 +996,23 @@ mcast_unblock_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_
         }
       }
     }
-  } else {}
+  } else
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct netif *netif;
+
+    NETIF_FOREACH(netif) {
+      if (ip6_addr_isany(ip_2_ip6(if_addr)) ||
+          netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) {
+        res = mcast_unblock_netif(ipmc, netif, multi_addr, unblk_addr);
+        if (err != ERR_OK) {
+          err = res;
+        }
+      }
+    }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
   return err;
 }
 
diff --git a/src/core/udp.c b/src/core/udp.c
index e1f9e1b..4a47e99 100644
--- a/src/core/udp.c
+++ b/src/core/udp.c
@@ -195,6 +195,12 @@ udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
     return 0;
   }
 
+#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)
+    if (ip_addr_ismulticast(ip_current_dest_addr())) {
+      return mcast_input_local_match(&pcb->ipmc, inp);
+    }
+#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
   /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
   if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
 #if LWIP_IPV4 && IP_SOF_BROADCAST_RECV
@@ -205,12 +211,6 @@ udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
     return 1;
   }
 
-#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)
-  if (ip_addr_ismulticast(ip_current_dest_addr())) {
-    return mcast_input_local_match(&pcb->ipmc, inp);
-  }
-#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
-
   /* Only need to check PCB if incoming IP version matches PCB IP version */
   if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
 #if LWIP_IPV4
diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h
index d5783dd..319ee09 100644
--- a/src/include/lwip/api.h
+++ b/src/include/lwip/api.h
@@ -103,6 +103,7 @@ extern "C" {
 #define NETCONNTYPE_ISIPV6(t)        (((t)&NETCONN_TYPE_IPV6) != 0)
 #define NETCONNTYPE_ISUDPLITE(t)     (((t)&0xF3) == NETCONN_UDPLITE)
 #define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
+#define NETCONNTYPE_GROUPV6(t)       ((t)&0xFF)
 #else /* LWIP_IPV6 */
 #define NETCONNTYPE_ISIPV6(t)        (0)
 #define NETCONNTYPE_ISUDPLITE(t)     ((t) == NETCONN_UDPLITE)
diff --git a/src/include/lwip/mcast.h b/src/include/lwip/mcast.h
index c7b195d..16a27ae 100644
--- a/src/include/lwip/mcast.h
+++ b/src/include/lwip/mcast.h
@@ -74,6 +74,28 @@ struct ip4_mc {
 #endif /* LWIP_IPV4 && LWIP_IGMP */
 
 
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+/** the IPv6 multicast filter */
+struct ip6_mc {
+  struct ip6_mc *next;
+  /** the interface index */
+  u8_t if_idx;
+  /** the group address */
+  ip6_addr_t multi_addr;
+  /** the source address list filter mode 0: EXCLUDE 1: INCLUDE */
+  u8_t fmode;
+  /** the source address list */
+  struct mld6_src *src;
+};
+
+/** The list of ip6_mc. */
+#define IP6_MC_FOREACH(ipmc, mc) \
+  for ((mc) = (ipmc)->mc6; (mc) != NULL; (mc) = (mc)->next)
+#define IP6_MC_SRC_FOREACH(mc, src) \
+  for ((src) = (mc)->src; (src) != NULL; (src) = (src)->next)
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+
 #if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)
 /* multicast filter control block */
 struct ip_mc {
@@ -85,6 +107,10 @@ struct ip_mc {
 #if LWIP_IPV4 && LWIP_IGMP
   struct ip4_mc *mc4;
 #endif /* LWIP_IPV4 && LWIP_IGMP */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+  struct ip6_mc *mc6;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
 };
 
 #if LWIP_IPV4 && LWIP_IGMP && LWIP_IGMP_V3
@@ -93,6 +119,13 @@ u16_t   mcast_ip4_filter_info(struct netif *netif, const ip4_addr_t *multi_addr,
 u8_t    mcast_ip4_filter_interest(struct netif *netif, const ip4_addr_t *multi_addr, const ip4_addr_p_t src_addr[], u16_t arr_cnt);
 #endif /* LWIP_IPV4 && LWIP_IGMP && LWIP_IGMP_V3 */
 
+#if LWIP_IPV6 && LWIP_IPV6_MLD && LWIP_IPV6_MLD_V2
+/* MLDv2 use the following function to get specified group of total multicast filter source address array */
+u16_t   mcast_ip6_filter_info(struct netif *netif, const ip6_addr_t *multi_addr, ip6_addr_p_t addr_array[], u16_t arr_cnt, u8_t *fmode);
+u8_t    mcast_ip6_filter_interest(struct netif *netif, const ip6_addr_t *multi_addr, const ip6_addr_p_t src_addr[], u16_t arr_cnt);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD && LWIP_IPV6_MLD_V2 */
+
+
 /* UDP or RAW use the following functions */
 void    mcast_pcb_remove(struct ip_mc *ipmc);
 u8_t    mcast_input_local_match(struct ip_mc *ipmc, struct netif *inp);
@@ -109,6 +142,8 @@ err_t   mcast_unblock_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const
 
 struct ip4_mc *mcast_ip4_mc_find(struct ip_mc *ipmc, struct netif *netif, const ip4_addr_t *multi_addr, struct ip4_mc **mc_prev);
 void mcast_ip4_mc_src_remove(struct igmp_src *src);
+struct ip6_mc *mcast_ip6_mc_find(struct ip_mc *ipmc, struct netif *netif, const ip6_addr_t *multi_addr, struct ip6_mc **mc_prev);
+void mcast_ip6_mc_src_remove(struct mld6_src *src);
 
 #endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
 
diff --git a/src/include/lwip/mld6.h b/src/include/lwip/mld6.h
index 2764fdd..7819551 100644
--- a/src/include/lwip/mld6.h
+++ b/src/include/lwip/mld6.h
@@ -54,6 +54,13 @@
 extern "C" {
 #endif
 
+/* Multicast filter support */
+struct mld6_src {
+  struct mld6_src *next;
+  /** the source address */
+  ip6_addr_t src_addr;
+};
+
 /** MLD group */
 struct mld_group {
   /** next link */
@@ -68,6 +75,16 @@ struct mld_group {
   u16_t              timer;
   /** counter of simultaneous uses */
   u8_t               use;
+ #if LWIP_IPV6_MLD_V2
+  /** Last report fmode */
+  u8_t               v2_fmode;
+  /** signifies we were the last person to report */
+  u8_t               v2_last_reporter_flag;
+  /** current state of the group */
+  u8_t               v2_group_state;
+  /** timer for reporting, negative is OFF */
+  u16_t              v2_timer;
+#endif /* LWIP_IPV6_MLD_V2 */
 };
 
 #define MLD6_TMR_INTERVAL              100 /* Milliseconds */
@@ -90,6 +107,19 @@ err_t  mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr);
  */
 #define netif_mld6_data(netif) ((struct mld_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6))
 
+/**
+ * ipv6 multicast filter trigger function
+ */
+#if LWIP_IPV6_MLD_V2
+
+#define MLD6_FMODE_INIT                2 /* Not MCAST_INCLUDE or MCAST_EXCLUDE */
+void mld6_v2_trigger(struct netif *netif, const ip6_addr_t *groupaddr);
+#define IP6_MC_TRIGGER_CALL(netif, multi_addr) mld6_v2_trigger(netif, multi_addr)
+#else
+#define IP6_MC_TRIGGER_CALL(netif, multi_addr)
+#endif /* !LWIP_IPV6_MLD_V2 */
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/netbuf.h b/src/include/lwip/netbuf.h
index e32c9bd..e7f0713 100644
--- a/src/include/lwip/netbuf.h
+++ b/src/include/lwip/netbuf.h
@@ -68,6 +68,9 @@ struct netbuf {
   u16_t toport_chksum;
 #if LWIP_NETBUF_RECVINFO
   ip_addr_t toaddr;
+#if LWIP_IPV6
+  u8_t hoplim;
+#endif /* LWIP_IPV6 */
 #endif /* LWIP_NETBUF_RECVINFO */
 #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
 };
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 06303f4..9a1a32d 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -3584,6 +3584,14 @@
 #if !defined DHCP6_DEBUG || defined __DOXYGEN__
 #define DHCP6_DEBUG                     LWIP_DBG_OFF
 #endif
+
+/**
+ * MLD6_DEBUG: Enable debugging in mld6.c.
+ */
+#if !defined MLD6_DEBUG || defined __DOXYGEN__
+#define MLD6_DEBUG                      LWIP_DBG_OFF
+#endif
+
 /**
  * @}
  */
diff --git a/src/include/lwip/prot/icmp6.h b/src/include/lwip/prot/icmp6.h
index 36989f6..17186b6 100644
--- a/src/include/lwip/prot/icmp6.h
+++ b/src/include/lwip/prot/icmp6.h
@@ -70,6 +70,8 @@ enum icmp6_type {
   ICMP6_TYPE_MLR = 131,
   /** Multicast listener done */
   ICMP6_TYPE_MLD = 132,
+  /** Multicast listener report v2*/
+  ICMP6_TYPE_MLR2 = 143,
   /** Router solicitation */
   ICMP6_TYPE_RS = 133,
   /** Router advertisement */
diff --git a/src/include/lwip/prot/mld6.h b/src/include/lwip/prot/mld6.h
index 71f1dcb..40f1ca6 100644
--- a/src/include/lwip/prot/mld6.h
+++ b/src/include/lwip/prot/mld6.h
@@ -64,6 +64,85 @@ PACK_STRUCT_END
 #  include "arch/epstruct.h"
 #endif
 
+#if LWIP_IPV6_MLD_V2 /* RFC 3810 */
+/**
+ * MLDv2 query packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+  PACK_STRUCT_BEGIN
+  struct mld_v2_query {
+    PACK_STRUCT_FLD_8(u8_t         type);
+    PACK_STRUCT_FLD_8(u8_t code);
+    PACK_STRUCT_FIELD(u16_t chksum);
+    PACK_STRUCT_FIELD(u16_t max_resp_delay);
+    PACK_STRUCT_FIELD(u16_t reserved);
+    PACK_STRUCT_FLD_S(ip6_addr_t multicast_address);
+    PACK_STRUCT_FLD_8(u8_t         resv_s_qrv);
+    PACK_STRUCT_FLD_8(u8_t         qqic);
+    PACK_STRUCT_FIELD(u16_t        src_num);
+  } PACK_STRUCT_STRUCT;
+  PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define MLD_V2_QUERY_HLEN  sizeof(struct mld_v2_query)
+
+/**
+ * MLDv2 report packet header format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+  PACK_STRUCT_BEGIN
+  struct mld_v2_report {
+    PACK_STRUCT_FLD_8(u8_t         type);
+    PACK_STRUCT_FLD_8(u8_t         reserved1);
+    PACK_STRUCT_FIELD(u16_t        chksum);
+    PACK_STRUCT_FIELD(u16_t        reserved2);
+    PACK_STRUCT_FIELD(u16_t        mrec_num);
+  } PACK_STRUCT_STRUCT;
+  PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define MLD_V2_REPORT_HLEN  sizeof(struct mld_v2_report)
+
+/*
+ * Definitions for MLDv2
+ */
+#define MLD2_MODE_IS_INCLUDE    1
+#define MLD2_MODE_IS_EXCLUDE    2
+#define MLD2_CHANGE_TO_INCLUDE  3
+#define MLD2_CHANGE_TO_EXCLUDE  4
+#define MLD2_ALLOW_NEW_SOURCES  5
+#define MLD2_BLOCK_OLD_SOURCES  6
+
+/**
+ * MLDv2 report packet group record format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+  PACK_STRUCT_BEGIN
+  struct mld_v2_record {
+    PACK_STRUCT_FLD_8(u8_t         type);
+    PACK_STRUCT_FLD_8(u8_t         aux_len);
+    PACK_STRUCT_FIELD(u16_t        src_num);
+    PACK_STRUCT_FLD_S(ip6_addr_p_t multicast_address);
+  } PACK_STRUCT_STRUCT;
+  PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define MLD_V2_RECORD_LEN(src_cnt)  (sizeof(struct mld_v2_record) + (src_cnt << 4))
+#endif /* LWIP_IPV6_MLD_V2 */
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 00aca53..f2a1b59 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -114,8 +114,11 @@ struct sockaddr {
   char        sa_data[14];
 };
 
+#ifndef LWIP_IPV6_MLD_V2
 struct sockaddr_storage {
+#if !GAZELLE_ENABLE
   u8_t        s2_len;
+#endif
   sa_family_t ss_family;
   char        s2_data1[2];
   u32_t       s2_data2[3];
@@ -123,6 +126,27 @@ struct sockaddr_storage {
   u32_t       s2_data3[3];
 #endif /* LWIP_IPV6 */
 };
+#else
+#ifndef _SS_SIZE
+#define _SS_SIZE 128
+#endif //_SS_SIZE
+#ifndef __SOCKADDR_COMMON_SIZE
+#define __SOCKADDR_COMMON_SIZE	(sizeof (unsigned short int))
+#endif //__SOCKADDR_COMMON_SIZE
+#ifndef __ss_aligntype
+#define __ss_aligntype	unsigned long int
+#endif //__ss_aligntype
+#ifndef _SS_PADSIZE
+#define _SS_PADSIZE \
+  (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
+#endif //_SS_PADSIZE
+struct sockaddr_storage
+  {
+    sa_family_t ss_family;	/* Address family, etc.  */
+    char __ss_padding[_SS_PADSIZE];
+    __ss_aligntype __ss_align;	/* Force desired alignment.  */
+  };
+#endif //LWIP_IPV6_MLD_V2
 
 /* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED
    to prevent this code from redefining it. */
@@ -335,6 +359,11 @@ struct linger {
  */
 #define IPV6_CHECKSUM       7  /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
 #define IPV6_V6ONLY         27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
+#define IPV6_RECVPKTINFO    49  /* Recv IPV6_PKTINFO message */
+#define IPV6_PKTINFO        50
+#define IPV6_RECVHOPLIMIT   51  /* Recv IPV6_HOPLIMIT message */
+#define IPV6_HOPLIMIT       52
+#define IPV6_MINHOPCNT      99
 #endif /* LWIP_IPV6 */
 
 #if LWIP_UDP && LWIP_UDPLITE
@@ -369,13 +398,24 @@ struct in_pktinfo {
 };
 #endif /* LWIP_IPV4 */
 
+#if LWIP_IPV6
+struct in6_pktinfo {
+  struct in6_addr  ipi6_addr;
+  int              ipi6_ifindex;
+};
+#endif /* LWIP_IPV6 */
+
 #if LWIP_IPV6_MLD
+#define IPV6_UNICAST_HOPS    16 /* IPv6 ttl */
+#define IPV6_MULTICAST_IF    17 /* Set mcast ifindex, (The argument is a pointer to an interface index) */
+#define IPV6_MULTICAST_HOPS  18 /* IPv6 mcast ttl */
+#define IPV6_MULTICAST_LOOP  19 /* Mcast loop */
 /*
  * Options and types related to IPv6 multicast membership
  */
-#define IPV6_JOIN_GROUP      12
+#define IPV6_JOIN_GROUP      20
 #define IPV6_ADD_MEMBERSHIP  IPV6_JOIN_GROUP
-#define IPV6_LEAVE_GROUP     13
+#define IPV6_LEAVE_GROUP     21
 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
 
 typedef struct ipv6_mreq {
@@ -807,6 +847,7 @@ int     mcast_sock_block_unblock_source_group(struct ip_mc *ipmc, int optname, c
 int     mcast_sock_set_groupfilter(struct ip_mc *ipmc, int optname, const struct group_filter *gf);
 int     mcast_sock_get_groupfilter(struct ip_mc *ipmc, int optname, struct group_filter *gf, socklen_t *size);
 
+int     mcast_sock_ipv6_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const struct ipv6_mreq *imr);
 #endif /*(LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)*/
 
 const char *lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size);
diff --git a/src/include/lwipopts.h b/src/include/lwipopts.h
index ab36688..396bcdf 100644
--- a/src/include/lwipopts.h
+++ b/src/include/lwipopts.h
@@ -292,4 +292,9 @@
  */
 #define LWIP_IGMP_V3                    1
 
+ /**
+ * LWIP_IPV6_MLD_V2==1: Enable multicast listener discovery protocol v2.
+ */
+#define LWIP_IPV6_MLD_V2                1
+
 #endif /* __LWIPOPTS_H__ */
-- 
2.25.1

