diff --git a/ChangeLog b/ChangeLog
index 65281fd824..e2b28502d8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Sat Dec 25 18:26:55 2010  Tanaka Akira  <akr@fsij.org>
+
+	* ext/socket/option.c (inspect_ipv6_mreq): new function to inspect
+	  struct ipv6_mreq for IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP.
+	  Socket::Option.new(:INET6, :IPV6, :JOIN_GROUP,
+	  [0xff12,0,0,0,0,0,0,1, 2].pack("nnnnnnnnI!")).inspect is now:
+	  "#<Socket::Option: INET6 IPV6 JOIN_GROUP ff12::1 eth0>"
+
+	* ext/socket/extconf.rb: check struct ipv6_mreq.
+
 Sat Dec 25 18:04:30 2010  Nobuyoshi Nakada  <nobu@ruby-lang.org>
 
 	* lib/csv.rb (CSV.foreach): 'rb' mode is defaulted in open.
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index 6d768f8d89..7e7295487d 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -340,6 +340,8 @@ have_func("getpeereid")
 have_header("ucred.h", headers)
 have_func("getpeerucred")
 
+have_type("struct ipv6_mreq", headers)
+
 # workaround for recent Windows SDK
 $defs << "-DIPPROTO_IPV6=IPPROTO_IPV6" if $defs.include?("-DHAVE_CONST_IPPROTO_IPV6") && !have_macro("IPPROTO_IPV6")
 
diff --git a/ext/socket/option.c b/ext/socket/option.c
index 6d437dbcc8..6ebe6c73ef 100644
--- a/ext/socket/option.c
+++ b/ext/socket/option.c
@@ -396,6 +396,30 @@ inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret)
     }
 }
 
+#if defined(IPPROTO_IPV6) && defined(HAVE_TYPE_STRUCT_IPV6_MREQ) /* POSIX, RFC 3493 */
+static int
+inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret)
+{
+    if (RSTRING_LEN(data) == sizeof(struct ipv6_mreq)) {
+        struct ipv6_mreq s;
+        char addrbuf[INET6_ADDRSTRLEN], ifbuf[IFNAMSIZ];
+        memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
+        if (inet_ntop(AF_INET6, &s.ipv6mr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
+            rb_str_cat2(ret, " invalid-address");
+        else
+            rb_str_catf(ret, " %s", addrbuf);
+        if (if_indextoname(s.ipv6mr_interface, ifbuf) == NULL)
+            rb_str_catf(ret, " interface:%u", s.ipv6mr_interface);
+        else
+            rb_str_catf(ret, " %s", ifbuf);
+        return 1;
+    }
+    else {
+        return 0;
+    }
+}
+#endif
+
 #if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
 #if defined(__OpenBSD__)
 #define RUBY_SOCK_PEERCRED struct sockpeercred
@@ -590,7 +614,6 @@ sockopt_inspect(VALUE self)
 #        if defined(IPPROTO_IPV6)
           case IPPROTO_IPV6:
             switch (optname) {
-              /* IPV6_JOIN_GROUP ipv6_mreq, IPV6_LEAVE_GROUP ipv6_mreq */
 #            if defined(IPV6_MULTICAST_HOPS) /* POSIX */
               case IPV6_MULTICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
 #            endif
@@ -600,6 +623,12 @@ sockopt_inspect(VALUE self)
 #            if defined(IPV6_MULTICAST_LOOP) /* POSIX */
               case IPV6_MULTICAST_LOOP: inspected = inspect_uint(level, optname, data, ret); break;
 #            endif
+#            if defined(IPV6_JOIN_GROUP) /* POSIX */
+              case IPV6_JOIN_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
+#            endif
+#            if defined(IPV6_LEAVE_GROUP) /* POSIX */
+              case IPV6_LEAVE_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
+#            endif
 #            if defined(IPV6_UNICAST_HOPS) /* POSIX */
               case IPV6_UNICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
 #            endif