
This patch implements the following options to getsockopt and setsockopt:

o IP_MULTICAST_TTL for RAW IPv4 sockets
o IP_MULTICAST_IF for RAW IPv4 sockets
o IP_MULTICAST_LOOP for RAW IPv4 sockets
o IPV6_MULTICAST_HOPS for UDP and RAW IPv6 sockets according to RFC3493 section 5.2
o IPV6_MULTICAST_IF for UDP and RAW IPv6 sockets according to RFC3493 section 5.2
o IPV6_MULTICAST_LOOP for UDP and RAW IPv6 sockets according to RFC3493 section 5.2

In addition, all the above options can be used without IGMP or IPv6 MLD support.
Only LWIP_UDP and/or LWIP_RAW is needed (in addition to LWIP_MULTICAST_TX_OPTIONS)
Various *opt*.h files have been modified to reflect this.

This builds on the work done by David van Moolenbroek (patch 9257). Many
thanks to him for reviewing this patch.

diff --git a/contrib/examples/example_app/test_configs/opt_default.h b/contrib/examples/example_app/test_configs/opt_default.h
index 67b69d20..dad804cf 100644
--- a/contrib/examples/example_app/test_configs/opt_default.h
+++ b/contrib/examples/example_app/test_configs/opt_default.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/contrib/examples/example_app/test_configs/opt_dualstack.h b/contrib/examples/example_app/test_configs/opt_dualstack.h
index 4caf490d..006a9200 100644
--- a/contrib/examples/example_app/test_configs/opt_dualstack.h
+++ b/contrib/examples/example_app/test_configs/opt_dualstack.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/contrib/examples/example_app/test_configs/opt_ipv4only.h b/contrib/examples/example_app/test_configs/opt_ipv4only.h
index 82ba4671..a1169735 100644
--- a/contrib/examples/example_app/test_configs/opt_ipv4only.h
+++ b/contrib/examples/example_app/test_configs/opt_ipv4only.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/contrib/examples/example_app/test_configs/opt_ipv6only.h b/contrib/examples/example_app/test_configs/opt_ipv6only.h
index b3cb1e04..6e615970 100644
--- a/contrib/examples/example_app/test_configs/opt_ipv6only.h
+++ b/contrib/examples/example_app/test_configs/opt_ipv6only.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/contrib/examples/example_app/test_configs/opt_nosys_dual.h b/contrib/examples/example_app/test_configs/opt_nosys_dual.h
index 17ee97ff..1ad2a95f 100644
--- a/contrib/examples/example_app/test_configs/opt_nosys_dual.h
+++ b/contrib/examples/example_app/test_configs/opt_nosys_dual.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/contrib/examples/example_app/test_configs/opt_nosys_ipv4.h b/contrib/examples/example_app/test_configs/opt_nosys_ipv4.h
index 024d79be..bf444cfe 100644
--- a/contrib/examples/example_app/test_configs/opt_nosys_ipv4.h
+++ b/contrib/examples/example_app/test_configs/opt_nosys_ipv4.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/contrib/examples/example_app/test_configs/opt_nosys_ipv6.h b/contrib/examples/example_app/test_configs/opt_nosys_ipv6.h
index d0273521..bc0d4bda 100644
--- a/contrib/examples/example_app/test_configs/opt_nosys_ipv6.h
+++ b/contrib/examples/example_app/test_configs/opt_nosys_ipv6.h
@@ -81,7 +81,7 @@
 #define LWIP_DHCP_AUTOIP_COOP           0
 #define LWIP_DHCP_AUTOIP_COOP_TRIES     9
 #define LWIP_MIB2_CALLBACKS             0
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #define LWIP_IGMP                       0
 #define LWIP_DNS                        0
 #define DNS_TABLE_SIZE                  4
diff --git a/src/api/sockets.c b/src/api/sockets.c
index 8d5d1311..77f78d75 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3078,38 +3078,95 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
                                       s, *(int *)optval));
           break;
-#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
         case IP_MULTICAST_TTL:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
-          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP)
+              *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW)
+              *(u8_t *)optval = raw_get_multicast_ttl(sock->conn->pcb.raw);
+          else
+#endif
+          {
             done_socket(sock);
             return ENOPROTOOPT;
           }
-          *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
-                                      s, *(int *)optval));
+                                      s, *(u8_t *)optval));
           break;
         case IP_MULTICAST_IF:
+        {
+          ip4_addr_t *maddr;
+
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
-          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP)
+          {
+              maddr = udp_get_multicast_netif_addr(sock->conn->pcb.udp);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW)
+          {
+              maddr = raw_get_multicast_netif_addr(sock->conn->pcb.raw);
+          }
+          else
+#endif
+          {
             done_socket(sock);
             return ENOPROTOOPT;
           }
-          inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+
+          inet_addr_from_ip4addr((struct in_addr *)optval, maddr);
+
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
                                       s, *(u32_t *)optval));
-          break;
+        }
+        break;
         case IP_MULTICAST_LOOP:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
-          if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
-            *(u8_t *)optval = 1;
-          } else {
-            *(u8_t *)optval = 0;
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP)
+          {
+              if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_MULTICAST_LOOP) != 0)
+              {
+                *(u8_t *)optval = 1;
+              } else {
+                *(u8_t *)optval = 0;
+              }
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW)
+          {
+              if ((raw_flags(sock->conn->pcb.raw) & UDP_FLAGS_MULTICAST_LOOP) != 0)
+              {
+                *(u8_t *)optval = 1;
+              } else {
+                *(u8_t *)optval = 0;
+              }
+          }
+          else
+#endif
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
           }
+
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
-                                      s, *(int *)optval));
+                                      s, *(u8_t *)optval));
           break;
-#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
         default:
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
                                       s, optname));
@@ -3175,6 +3232,91 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
                                       s, *(int *)optval));
           break;
+#if LWIP_MULTICAST_TX_OPTIONS
+        case IPV6_MULTICAST_HOPS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP_IPV6)
+          {
+              *(int *)optval = (int) udp_get_multicast_ttl(sock->conn->pcb.udp);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW_IPV6)
+          {
+              *(int *)optval = (int) raw_get_multicast_ttl(sock->conn->pcb.raw);
+          }
+          else
+#endif
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_HOPS) = %d\n",
+                                      s, *(int *)optval));
+          break;
+        case IPV6_MULTICAST_IF:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, unsigned int);
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP_IPV6)
+          {
+              *(unsigned int *)optval = (unsigned int) udp_get_multicast_netif_index(sock->conn->pcb.udp);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW_IPV6)
+          {
+              *(unsigned int *)optval = (unsigned int) raw_get_multicast_netif_index(sock->conn->pcb.raw);
+          }
+          else
+#endif
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = %d\n",
+                                      s, *(unsigned int *)optval));
+          break;
+        case IPV6_MULTICAST_LOOP:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP_IPV6)
+          {
+              if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+                *(int *)optval = 1;
+              } else {
+                *(int *)optval = 0;
+              }
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW_IPV6)
+          {
+              if ((raw_flags(sock->conn->pcb.raw) & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+                *(int *)optval = 1;
+              } else {
+                *(int *)optval = 0;
+              }
+          }
+          else
+#endif
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_LOOP) = %d\n",
+                                      s, *(int *)optval));
+          break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
         default:
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
                                       s, optname));
@@ -3533,27 +3675,87 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
           }
           break;
 #endif /* LWIP_NETBUF_RECVINFO */
-#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
         case IP_MULTICAST_TTL:
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
-          udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, u8_t);
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_UDP)
+          {
+              udp_set_multicast_ttl(sock->conn->pcb.udp, *(const u8_t *)optval);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_RAW)
+          {
+              raw_set_multicast_ttl(sock->conn->pcb.raw, *(const u8_t *)optval);
+          }
+          else
+#endif
+          {
+              done_socket(sock);
+              return ENOPROTOOPT;
+          }
           break;
         case IP_MULTICAST_IF: {
           ip4_addr_t if_addr;
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct in_addr);
+
           inet_addr_to_ip4addr(&if_addr, (const struct in_addr *)optval);
-          udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_UDP)
+          {
+              udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_RAW)
+          {
+              raw_set_multicast_netif_addr(sock->conn->pcb.raw, &if_addr);
+          }
+          else
+#endif
+          {
+              done_socket(sock);
+              return ENOPROTOOPT;
+          }
         }
         break;
         case IP_MULTICAST_LOOP:
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
-          if (*(const u8_t *)optval) {
-            udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
-          } else {
-            udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, u8_t);
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_UDP)
+          {
+              if (*(const u8_t *)optval) {
+                udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+              } else {
+                udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+              }
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_RAW)
+          {
+              if (*(const u8_t *)optval) {
+                raw_set_flags(sock->conn->pcb.raw, UDP_FLAGS_MULTICAST_LOOP);
+              } else {
+                raw_clear_flags(sock->conn->pcb.raw, UDP_FLAGS_MULTICAST_LOOP);
+              }
+          }
+          else
+#endif
+          {
+              done_socket(sock);
+              return ENOPROTOOPT;
           }
           break;
-#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
 #if LWIP_IGMP
         case IP_ADD_MEMBERSHIP:
         case IP_DROP_MEMBERSHIP: {
@@ -3691,6 +3893,137 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
         }
         break;
 #endif /* LWIP_IPV6_MLD */
+#if LWIP_MULTICAST_TX_OPTIONS
+        case IPV6_MULTICAST_HOPS:
+        {
+          int ttl;
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+
+          ttl = *(const int *)optval;
+
+          /* RFC3493 section 5.2 specifies that argument to IPV6_MULTICAST_HOPS
+           * must be (pointer to) int.
+           * -1 Is a valid option meaning "use system default"
+           */
+          if (ttl < -1 || ttl > 255)
+          {
+              done_socket(sock);
+              return EINVAL;
+          }
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_UDP_IPV6)
+          {
+              if (ttl == -1)
+                  ttl = UDP_TTL;
+              udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)ttl);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_RAW_IPV6)
+          {
+              if (ttl == -1)
+                  ttl = RAW_TTL;
+              raw_set_multicast_ttl(sock->conn->pcb.raw, (u8_t)ttl);
+          }
+          else
+#endif
+          {
+              done_socket(sock);
+              return ENOPROTOOPT;
+          }
+        }
+        break;
+        case IPV6_MULTICAST_IF:
+        {
+          unsigned int opt;
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, unsigned int);
+
+          /* RFC3493 section 5.2 specifies that argument to IPV6_MULTICAST_IF
+           * must be (pointer to) unsigned int.
+           */
+          opt = *(const unsigned int *)optval;
+
+          if (opt > 255 ||
+              opt == NETIF_NO_INDEX ||
+              netif_get_by_index(opt) == NULL)
+          {
+              done_socket(sock);
+              return EINVAL;
+          }
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_UDP_IPV6)
+          {
+              udp_set_multicast_netif_index(sock->conn->pcb.udp, (u8_t)opt);
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_RAW_IPV6)
+          {
+              raw_set_multicast_netif_index(sock->conn->pcb.raw, (u8_t)opt);
+          }
+          else
+#endif
+          {
+              done_socket(sock);
+              return ENOPROTOOPT;
+          }
+        }
+        break;
+        case IPV6_MULTICAST_LOOP:
+        {
+          int opt;
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+
+          /* RFC3493 section 5.2 specifies that argument to IPV6_MULTICAST_LOOP
+           * must be (pointer to) int.
+           * It also states explicitly that any other value than 0 or 1 must
+           * be refused.
+           */
+          opt = *(const int *)optval;
+
+          if (opt != 0 && opt != 1)
+          {
+              done_socket(sock);
+              return EINVAL;
+          }
+
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_UDP_IPV6)
+          {
+              if (opt) {
+                udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+              } else {
+                udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+              }
+          }
+          else
+#endif
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) == NETCONN_RAW_IPV6)
+          {
+              if (opt) {
+                raw_set_flags(sock->conn->pcb.raw, UDP_FLAGS_MULTICAST_LOOP);
+              } else {
+                raw_clear_flags(sock->conn->pcb.raw, UDP_FLAGS_MULTICAST_LOOP);
+              }
+          }
+          else
+#endif
+          {
+              done_socket(sock);
+              return ENOPROTOOPT;
+          }
+
+        }
+        break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
         default:
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
                                       s, optname));
diff --git a/src/core/raw.c b/src/core/raw.c
index 40a442a7..9a85ad2f 100644
--- a/src/core/raw.c
+++ b/src/core/raw.c
@@ -368,10 +368,32 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
 #if LWIP_MULTICAST_TX_OPTIONS
     netif = NULL;
     if (ip_addr_ismulticast(ipaddr)) {
-      /* For multicast-destined packets, use the user-provided interface index to
-       * determine the outgoing interface, if an interface index is set and a
-       * matching netif can be found. Otherwise, fall back to regular routing. */
-      netif = netif_get_by_index(pcb->mcast_ifindex);
+      /* For IPv6, the interface to use for packets with a multicast destination
+       * is specified using an interface index. The same approach may be used for
+       * IPv4 as well, in which case it overrides the IPv4 multicast override
+       * address below. Here we have to look up the netif by going through the
+       * list, but by doing so we skip a route lookup. If the interface index has
+       * gone stale, we fall through and do the regular route lookup after all. */
+      if (pcb->mcast_ifindex != NETIF_NO_INDEX) {
+          netif = netif_get_by_index(pcb->mcast_ifindex);
+      }
+#if LWIP_IPV4
+      else
+#if LWIP_IPV6
+        if (IP_IS_V4(ipaddr))
+#endif /* LWIP_IPV6 */
+        {
+          /* IPv4 does not use source-based routing by default, so we use an
+             administratively selected interface for multicast by default.
+             However, this can be overridden by setting an interface address
+             in pcb->mcast_ip4 that is used for routing. If this routing lookup
+             fails, we try regular routing as though no override was set. */
+          if (!ip4_addr_isany_val(pcb->mcast_ip4) &&
+              !ip4_addr_eq(&pcb->mcast_ip4, IP4_ADDR_BROADCAST)) {
+            netif = ip4_route_src(ip_2_ip4(&pcb->local_ip), &pcb->mcast_ip4);
+          }
+        }
+#endif /* LWIP_IPV4 */
     }
 
     if (netif == NULL)
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 9fe0bf04..e3cf6217 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -1077,7 +1077,7 @@
  * core support for the corresponding IPv6 options.
  */
 #if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__
-#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#define LWIP_MULTICAST_TX_OPTIONS       (LWIP_UDP || LWIP_RAW)
 #endif
 /**
  * @}
diff --git a/src/include/lwip/raw.h b/src/include/lwip/raw.h
index f916ff6b..2561b446 100644
--- a/src/include/lwip/raw.h
+++ b/src/include/lwip/raw.h
@@ -82,6 +82,10 @@ struct raw_pcb {
   u8_t flags;
 
 #if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4
+  /** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */
+  ip4_addr_t mcast_ip4;
+#endif /* LWIP_IPV4 */
   /** outgoing network interface for multicast packets, by interface index (if nonzero) */
   u8_t mcast_ifindex;
   /** TTL for outgoing multicast packets */
@@ -128,6 +132,10 @@ void             raw_recv       (struct raw_pcb *pcb, raw_recv_fn recv, void *re
 #define raw_new_ip6(proto) raw_new_ip_type(IPADDR_TYPE_V6, proto)
 
 #if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4
+#define raw_set_multicast_netif_addr(pcb, ip4addr) ip4_addr_copy((pcb)->mcast_ip4, *(ip4addr))
+#define raw_get_multicast_netif_addr(pcb)          (&(pcb)->mcast_ip4)
+#endif /* LWIP_IPV4 */
 #define raw_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx))
 #define raw_get_multicast_netif_index(pcb)      ((pcb)->mcast_ifindex)
 #define raw_set_multicast_ttl(pcb, value)       ((pcb)->mcast_ttl = (value))
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index b6f3d524..85cbbc17 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -311,11 +311,18 @@ struct linger {
 
 #if LWIP_MULTICAST_TX_OPTIONS
 /*
- * Options and types for UDP multicast traffic handling
+ * Options and types for UDP & raw multicast traffic handling
  */
 #define IP_MULTICAST_TTL   5
 #define IP_MULTICAST_IF    6
 #define IP_MULTICAST_LOOP  7
+
+#if LWIP_IPV6
+#define IPV6_MULTICAST_IF   17
+#define IPV6_MULTICAST_HOPS 18
+#define IPV6_MULTICAST_LOOP 19
+#endif
+
 #endif /* LWIP_MULTICAST_TX_OPTIONS */
 
 #if LWIP_IGMP
