diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index 66fb06d..e2895c0 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -302,6 +302,8 @@ end
   netinet/tcp.h
   netinet/udp.h
   arpa/inet.h
+  netpacket/packet.h
+  net/ethernet.h
   sys/un.h
   ifaddrs.h
   sys/ioctl.h
@@ -310,6 +312,7 @@ end
   sys/param.h
   sys/ucred.h
   ucred.h
+  net/if_dl.h
   arpa/nameser.h
   resolv.h
 ].each {|h|
@@ -325,6 +328,8 @@ if have_type("struct sockaddr_un", headers) # POSIX
   have_struct_member("struct sockaddr_un", "sun_len", headers) # 4.4BSD
 end
 
+have_type("struct sockaddr_dl", headers) # AF_LINK address.  4.4BSD since Net2
+
 have_type("struct sockaddr_storage", headers)
 
 have_type("struct addrinfo", headers)
@@ -550,7 +555,8 @@ EOS
     "unixserver.#{$OBJEXT}",
     "option.#{$OBJEXT}",
     "ancdata.#{$OBJEXT}",
-    "raddrinfo.#{$OBJEXT}"
+    "raddrinfo.#{$OBJEXT}",
+    "ifaddr.#{$OBJEXT}"
   ]
 
   if getaddr_info_ok == :wide
diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c
new file mode 100644
index 0000000..e69cea7
--- /dev/null
+++ b/ext/socket/ifaddr.c
@@ -0,0 +1,429 @@
+#include "rubysocket.h"
+
+VALUE rb_cSockIfaddr;
+
+typedef struct rb_ifaddr_tag rb_ifaddr_t;
+typedef struct rb_ifaddr_root_tag rb_ifaddr_root_t;
+
+struct rb_ifaddr_tag {
+    int ord;
+    struct ifaddrs *ifaddr;
+    rb_ifaddr_root_t *root;
+};
+
+struct rb_ifaddr_root_tag {
+    int refcount;
+    int numifaddrs;
+    rb_ifaddr_t ary[1];
+};
+
+static rb_ifaddr_root_t *
+get_root(const rb_ifaddr_t *ifaddr)
+{
+    return (rb_ifaddr_root_t *)((char *)&ifaddr[-ifaddr->ord] -
+                                offsetof(rb_ifaddr_root_t, ary));
+}
+
+static void
+ifaddr_mark(void *ptr)
+{
+}
+
+static void
+ifaddr_free(void *ptr)
+{
+    rb_ifaddr_t *ifaddr = ptr;
+    rb_ifaddr_root_t *root = get_root(ifaddr);
+    root->refcount--;
+    if (root->refcount == 0) {
+        freeifaddrs(root->ary[0].ifaddr);
+        xfree(root);
+    }
+}
+
+static size_t
+ifaddr_memsize(const void *ptr)
+{
+    const rb_ifaddr_t *ifaddr;
+    const rb_ifaddr_root_t *root;
+    if (ptr == NULL)
+        return 0;
+    ifaddr = ptr;
+    root = get_root(ifaddr);
+    return sizeof(rb_ifaddr_root_t) + (root->numifaddrs - 1) * sizeof(rb_ifaddr_t);
+}
+
+static const rb_data_type_t ifaddr_type = {
+    "socket/ifaddr",
+    {ifaddr_mark, ifaddr_free, ifaddr_memsize,},
+};
+
+#define IS_IFADDRS(obj) rb_typeddata_is_kind_of((obj), &ifaddr_type)
+static inline rb_ifaddr_t *
+check_ifaddr(VALUE self)
+{
+      return rb_check_typeddata(self, &ifaddr_type);
+}
+
+static rb_ifaddr_t *
+get_ifaddr(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = check_ifaddr(self);
+
+    if (!rifaddr) {
+        rb_raise(rb_eTypeError, "uninitialized ifaddr");
+    }
+    return rifaddr;
+}
+
+static VALUE
+rsock_getifaddrs(void)
+{
+    int ret;
+    int numifaddrs, i;
+    struct ifaddrs *ifaddrs, *ifa;
+    rb_ifaddr_root_t *root;
+    VALUE result;
+
+    ret = getifaddrs(&ifaddrs);
+    if (ret == -1)
+        rb_sys_fail("getifaddrs");
+
+    numifaddrs = 0;
+    for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next)
+        numifaddrs++;
+
+    root = xmalloc(sizeof(rb_ifaddr_root_t) + (numifaddrs-1) * sizeof(rb_ifaddr_t));
+    root->refcount = root->numifaddrs = numifaddrs;
+
+    ifa = ifaddrs;
+    for (i = 0; i < numifaddrs; i++) {
+        root->ary[i].ord = i;
+        root->ary[i].ifaddr = ifa;
+        root->ary[i].root = root;
+        ifa = ifa->ifa_next;
+    }
+
+    result = rb_ary_new2(numifaddrs);
+    for (i = 0; i < numifaddrs; i++) {
+        rb_ary_push(result, TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, &root->ary[i]));
+    }
+
+    return result;
+}
+
+/*
+ * call-seq:
+ *   getifaddr.name => string
+ *
+ * Returns the interface name of _getifaddr_.
+ */
+
+static VALUE
+ifaddr_name(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    return rb_str_new_cstr(ifa->ifa_name);
+}
+
+/*
+ * call-seq:
+ *   getifaddr.ifindex => integer
+ *
+ * Returns the interface index of _getifaddr_.
+ */
+
+static VALUE
+ifaddr_ifindex(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    unsigned int ifindex = if_nametoindex(ifa->ifa_name);
+    if (ifindex == 0) {
+        rb_raise(rb_eArgError, "invalid interface name: %s", ifa->ifa_name);
+    }
+    return UINT2NUM(ifindex);
+}
+
+/*
+ * call-seq:
+ *   getifaddr.flags => integer
+ *
+ * Returns the flags of _getifaddr_.
+ */
+
+static VALUE
+ifaddr_flags(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    return UINT2NUM(ifa->ifa_flags);
+}
+
+/*
+ * call-seq:
+ *   getifaddr.addr => addrinfo
+ *
+ * Returns the address of _getifaddr_.
+ * nil is returned if address is not available in _getifaddr_.
+ */
+
+static VALUE
+ifaddr_addr(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    if (ifa->ifa_addr)
+        return rsock_sockaddr_obj(ifa->ifa_addr, rsock_sockaddr_len(ifa->ifa_addr));
+    return Qnil;
+}
+
+/*
+ * call-seq:
+ *   getifaddr.netmask => addrinfo
+ *
+ * Returns the netmask address of _getifaddr_.
+ * nil is returned if netmask is not available in _getifaddr_.
+ */
+
+static VALUE
+ifaddr_netmask(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    if (ifa->ifa_netmask)
+        return rsock_sockaddr_obj(ifa->ifa_netmask, rsock_sockaddr_len(ifa->ifa_netmask));
+    return Qnil;
+}
+
+/*
+ * call-seq:
+ *   getifaddr.broadaddr => addrinfo
+ *
+ * Returns the broadcast address of _getifaddr_.
+ * nil is returned if the flags doesn't have IFF_BROADCAST.
+ */
+
+static VALUE
+ifaddr_broadaddr(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr)
+        return rsock_sockaddr_obj(ifa->ifa_broadaddr, rsock_sockaddr_len(ifa->ifa_broadaddr));
+    return Qnil;
+}
+
+/*
+ * call-seq:
+ *   getifaddr.dstaddr => addrinfo
+ *
+ * Returns the destination address of _getifaddr_.
+ * nil is returned if the flags doesn't have IFF_POINTOPOINT.
+ */
+
+static VALUE
+ifaddr_dstaddr(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa = rifaddr->ifaddr;
+    if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr)
+        return rsock_sockaddr_obj(ifa->ifa_dstaddr, rsock_sockaddr_len(ifa->ifa_dstaddr));
+    return Qnil;
+}
+
+static void
+ifaddr_inspect_flags(unsigned int flags, VALUE result)
+{
+    char *sep = " ";
+#define INSPECT_BIT(bit, name) \
+    if (flags & (bit)) { rb_str_catf(result, "%s" name, sep); flags &= ~(bit); sep = ","; }
+#ifdef IFF_UP
+    INSPECT_BIT(IFF_UP, "UP")
+#endif
+#ifdef IFF_BROADCAST
+    INSPECT_BIT(IFF_BROADCAST, "BROADCAST")
+#endif
+#ifdef IFF_DEBUG
+    INSPECT_BIT(IFF_DEBUG, "DEBUG")
+#endif
+#ifdef IFF_LOOPBACK
+    INSPECT_BIT(IFF_LOOPBACK, "LOOPBACK")
+#endif
+#ifdef IFF_POINTOPOINT
+    INSPECT_BIT(IFF_POINTOPOINT, "POINTOPOINT")
+#endif
+#ifdef IFF_RUNNING
+    INSPECT_BIT(IFF_RUNNING, "RUNNING")
+#endif
+#ifdef IFF_NOARP
+    INSPECT_BIT(IFF_NOARP, "NOARP")
+#endif
+#ifdef IFF_PROMISC
+    INSPECT_BIT(IFF_PROMISC, "PROMISC")
+#endif
+#ifdef IFF_NOTRAILERS
+    INSPECT_BIT(IFF_NOTRAILERS, "NOTRAILERS")
+#endif
+#ifdef IFF_ALLMULTI
+    INSPECT_BIT(IFF_ALLMULTI, "ALLMULTI")
+#endif
+#ifdef IFF_MASTER
+    INSPECT_BIT(IFF_MASTER, "MASTER")
+#endif
+#ifdef IFF_SLAVE
+    INSPECT_BIT(IFF_SLAVE, "SLAVE")
+#endif
+#ifdef IFF_MULTICAST
+    INSPECT_BIT(IFF_MULTICAST, "MULTICAST")
+#endif
+#ifdef IFF_PORTSEL
+    INSPECT_BIT(IFF_PORTSEL, "PORTSEL")
+#endif
+#ifdef IFF_AUTOMEDIA
+    INSPECT_BIT(IFF_AUTOMEDIA, "AUTOMEDIA")
+#endif
+#ifdef IFF_DYNAMIC
+    INSPECT_BIT(IFF_DYNAMIC, "DYNAMIC")
+#endif
+#ifdef IFF_LOWER_UP
+    INSPECT_BIT(IFF_LOWER_UP, "LOWER_UP")
+#endif
+#ifdef IFF_DORMANT
+    INSPECT_BIT(IFF_DORMANT, "DORMANT")
+#endif
+#ifdef IFF_ECHO
+    INSPECT_BIT(IFF_ECHO, "ECHO")
+#endif
+#undef INSPECT_BIT
+    if (flags) {
+        rb_str_catf(result, "%s%#x", sep, flags);
+    }
+}
+
+/*
+ * call-seq:
+ *   getifaddr.inspect => string
+ *
+ * Returns a string to show contents of _getifaddr_.
+ */
+
+static VALUE
+ifaddr_inspect(VALUE self)
+{
+    rb_ifaddr_t *rifaddr = get_ifaddr(self);
+    struct ifaddrs *ifa;
+    VALUE result;
+
+    ifa = rifaddr->ifaddr;
+
+    result = rb_str_new_cstr("#<");
+
+    rb_str_append(result, rb_class_name(CLASS_OF(self)));
+    rb_str_cat2(result, " ");
+    rb_str_cat2(result, ifa->ifa_name);
+
+    if (ifa->ifa_flags)
+        ifaddr_inspect_flags(ifa->ifa_flags, result);
+
+    if (ifa->ifa_addr) {
+      rb_str_cat2(result, " [");
+      rsock_inspect_sockaddr(ifa->ifa_addr,
+          rsock_sockaddr_len(ifa->ifa_addr),
+          result);
+      rb_str_cat2(result, "]");
+    }
+    if (ifa->ifa_netmask) {
+      rb_str_cat2(result, " netmask:[");
+      rsock_inspect_sockaddr(ifa->ifa_netmask,
+          rsock_sockaddr_len(ifa->ifa_netmask),
+          result);
+      rb_str_cat2(result, "]");
+    }
+
+    if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) {
+      rb_str_cat2(result, " broadcast:[");
+      rsock_inspect_sockaddr(ifa->ifa_broadaddr,
+          rsock_sockaddr_len(ifa->ifa_broadaddr),
+          result);
+      rb_str_cat2(result, "]");
+    }
+
+    if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr) {
+      rb_str_cat2(result, " dstaddr:[");
+      rsock_inspect_sockaddr(ifa->ifa_dstaddr,
+          rsock_sockaddr_len(ifa->ifa_dstaddr),
+          result);
+      rb_str_cat2(result, "]");
+    }
+
+    rb_str_cat2(result, ">");
+    return result;
+}
+
+/*
+ * call-seq:
+ *   Socket.getifaddrs => [ifaddr1, ...]
+ *
+ * Returns an array of interface addresses.
+ * An element of the array is an instance of Socket::Ifaddr.
+ *
+ * This method can be used to find multicast-enabled interfaces:
+ *
+ *   pp Socket.getifaddrs.reject {|ifaddr|
+ *     !ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0)
+ *   }.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] }
+ *   #=> [["eth0", 2, #<Addrinfo: 221.186.184.67>],
+ *   #    ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
+ *
+ * Example result on GNU/Linux:
+ *   pp Socket.getifaddrs
+ *   #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 [PACKET protocol:0 lo hatype:772 HOST hwaddr:00:00:00:00:00:00]>,
+ *   #    #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 [PACKET protocol:0 eth0 hatype:1 HOST hwaddr:00:16:3e:95:88:bb] broadcast:[PACKET protocol:0 eth0 hatype:1 HOST hwaddr:ff:ff:ff:ff:ff:ff]>,
+ *   #    #<Socket::Ifaddr sit0 NOARP [PACKET protocol:0 sit0 hatype:776 HOST hwaddr:00:00:00:00]>,
+ *   #    #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 [127.0.0.1] netmask:[255.0.0.0]>,
+ *   #    #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 [221.186.184.67] netmask:[255.255.255.240] broadcast:[221.186.184.79]>,
+ *   #    #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 [::1] netmask:[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]>,
+ *   #    #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 [fe80::216:3eff:fe95:88bb%eth0] netmask:[ffff:ffff:ffff:ffff::]>]
+ *
+ * Example result on FreeBSD:
+ *   pp Socket.getifaddrs'
+ *   #=> [#<Socket::Ifaddr usbus0 UP,0x10000 [LINK usbus0]>,
+ *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 [LINK re0 3a:d0:40:9a:fe:e8]>,
+ *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 [10.250.10.18] netmask:[255.255.255.? (7 bytes for 16 bytes sockaddr_in)] broadcast:[10.250.10.255]>,
+ *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 [fe80:2::38d0:40ff:fe9a:fee8] netmask:[ffff:ffff:ffff:ffff::]>,
+ *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 [2001:2e8:408:10::12] netmask:[UNSPEC]>,
+ *   #    #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 [LINK plip0]>,
+ *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST [LINK lo0]>,
+ *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST [::1] netmask:[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]>,
+ *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST [fe80:4::1] netmask:[ffff:ffff:ffff:ffff::]>,
+ *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST [127.0.0.1] netmask:[255.?.?.? (5 bytes for 16 bytes sockaddr_in)]>]
+ */
+
+static VALUE
+socket_s_getifaddrs(VALUE self)
+{
+    return rsock_getifaddrs();
+}
+
+void
+rsock_init_sockifaddr(void)
+{
+    /*
+     * Document-class: Socket::Ifaddr
+     *
+     * Socket::Ifaddr represents a result of getifaddrs() function.
+     */
+    rb_cSockIfaddr = rb_define_class_under(rb_cSocket, "Ifaddr", rb_cData);
+    rb_define_method(rb_cSockIfaddr, "inspect", ifaddr_inspect, 0);
+    rb_define_method(rb_cSockIfaddr, "name", ifaddr_name, 0);
+    rb_define_method(rb_cSockIfaddr, "ifindex", ifaddr_ifindex, 0);
+    rb_define_method(rb_cSockIfaddr, "flags", ifaddr_flags, 0);
+    rb_define_method(rb_cSockIfaddr, "addr", ifaddr_addr, 0);
+    rb_define_method(rb_cSockIfaddr, "netmask", ifaddr_netmask, 0);
+    rb_define_method(rb_cSockIfaddr, "broadaddr", ifaddr_broadaddr, 0);
+    rb_define_method(rb_cSockIfaddr, "dstaddr", ifaddr_dstaddr, 0);
+
+    rb_define_singleton_method(rb_cSocket, "getifaddrs", socket_s_getifaddrs, 0);
+}
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 1360800..a5ffe7b 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -627,5 +627,6 @@ rsock_init_socket_init()
     rsock_init_sockopt();
     rsock_init_ancdata();
     rsock_init_addrinfo();
+    rsock_init_sockifaddr();
     rsock_init_socket_constants();
 }
diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb
index 7f8e119..b86d726 100644
--- a/ext/socket/mkconstants.rb
+++ b/ext/socket/mkconstants.rb
@@ -703,3 +703,62 @@ SCM_UCRED	nil	User credentials
 LOCAL_PEERCRED	nil	Retrieve peer credentials
 LOCAL_CREDS	nil	Pass credentials to receiver
 LOCAL_CONNWAIT	nil	Connect blocks until accepted
+
+IFF_802_1Q_VLAN      nil 802.1Q VLAN device
+IFF_ALLMULTI         nil receive all multicast packets
+IFF_ALTPHYS          nil use alternate physical connection
+IFF_AUTOMEDIA        nil auto media select active
+IFF_BONDING          nil bonding master or slave
+IFF_BRIDGE_PORT      nil device used as bridge port
+IFF_BROADCAST        nil broadcast address valid
+IFF_CANTCONFIG       nil unconfigurable using ioctl(2)
+IFF_DEBUG            nil turn on debugging
+IFF_DISABLE_NETPOLL  nil disable netpoll at run-time
+IFF_DONT_BRIDGE      nil disallow bridging this ether dev
+IFF_DORMANT          nil driver signals dormant
+IFF_DRV_OACTIVE      nil tx hardware queue is full
+IFF_DRV_RUNNING      nil resources allocated
+IFF_DYING            nil interface is winding down
+IFF_DYNAMIC          nil dialup device with changing addresses
+IFF_EBRIDGE          nil ethernet bridging device
+IFF_ECHO             nil echo sent packets
+IFF_ISATAP           nil ISATAP interface (RFC4214)
+IFF_LINK0            nil per link layer defined bit 0
+IFF_LINK1            nil per link layer defined bit 1
+IFF_LINK2            nil per link layer defined bit 2
+IFF_LIVE_ADDR_CHANGE nil hardware address change when it's running
+IFF_LOOPBACK         nil loopback net
+IFF_LOWER_UP         nil driver signals L1 up
+IFF_MACVLAN_PORT     nil device used as macvlan port
+IFF_MASTER           nil master of a load balancer
+IFF_MASTER_8023AD    nil bonding master, 802.3ad.
+IFF_MASTER_ALB       nil bonding master, balance-alb.
+IFF_MASTER_ARPMON    nil bonding master, ARP mon in use
+IFF_MONITOR          nil user-requested monitor mode
+IFF_MULTICAST        nil supports multicast
+IFF_NOARP            nil no address resolution protocol
+IFF_NOTRAILERS       nil avoid use of trailers
+IFF_OACTIVE          nil transmission in progress
+IFF_OVS_DATAPATH     nil device used as Open vSwitch datapath port
+IFF_POINTOPOINT      nil point-to-point link
+IFF_PORTSEL          nil can set media type
+IFF_PPROMISC         nil user-requested promisc mode
+IFF_PROMISC          nil receive all packets
+IFF_RENAMING         nil interface is being renamed
+IFF_ROUTE            nil routing entry installed
+IFF_RUNNING          nil resources allocated
+IFF_SIMPLEX          nil can't hear own transmissions
+IFF_SLAVE            nil slave of a load balancer
+IFF_SLAVE_INACTIVE   nil bonding slave not the curr. active
+IFF_SLAVE_NEEDARP    nil need ARPs for validation
+IFF_SMART            nil interface manages own routes
+IFF_STATICARP        nil static ARP
+IFF_SUPP_NOFCS       nil sending custom FCS
+IFF_TEAM_PORT        nil used as team port
+IFF_TX_SKB_SHARING   nil sharing skbs on transmit
+IFF_UNICAST_FLT      nil unicast filtering
+IFF_UP               nil interface is up
+IFF_WAN_HDLC         nil WAN HDLC device
+IFF_XMIT_DST_RELEASE nil dev_hard_start_xmit() is allowed to release skb->dst
+IFF_VOLATILE         nil volatile flags
+IFF_CANTCHANGE       nil flags not changeable
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index 327218f..8bf85da 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -953,34 +953,62 @@ static VALUE
 inspect_sockaddr(VALUE addrinfo, VALUE ret)
 {
     rb_addrinfo_t *rai = get_addrinfo(addrinfo);
+    union_sockaddr *sockaddr = &rai->addr;
+    socklen_t socklen = rai->sockaddr_len;
+    return rsock_inspect_sockaddr((struct sockaddr *)sockaddr, socklen, ret);
+}
 
-    if (rai->sockaddr_len == 0) {
+VALUE
+rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE ret)
+{
+    union_sockaddr *sockaddr = (union_sockaddr *)sockaddr_arg;
+    if (socklen == 0) {
         rb_str_cat2(ret, "empty-sockaddr");
     }
-    else if ((long)rai->sockaddr_len < ((char*)&rai->addr.addr.sa_family + sizeof(rai->addr.addr.sa_family)) - (char*)&rai->addr)
+    else if ((long)socklen < ((char*)&sockaddr->addr.sa_family + sizeof(sockaddr->addr.sa_family)) - (char*)sockaddr)
         rb_str_cat2(ret, "too-short-sockaddr");
     else {
-        switch (rai->addr.addr.sa_family) {
+        switch (sockaddr->addr.sa_family) {
+          case AF_UNSPEC:
+	  {
+	    rb_str_cat2(ret, "UNSPEC");
+            break;
+	  }
+
           case AF_INET:
           {
             struct sockaddr_in *addr;
             int port;
-            if (rai->sockaddr_len < (socklen_t)sizeof(struct sockaddr_in)) {
-                rb_str_cat2(ret, "too-short-AF_INET-sockaddr");
-            }
-            else {
-                addr = &rai->addr.in;
-                rb_str_catf(ret, "%d.%d.%d.%d",
-                            ((unsigned char*)&addr->sin_addr)[0],
-                            ((unsigned char*)&addr->sin_addr)[1],
-                            ((unsigned char*)&addr->sin_addr)[2],
-                            ((unsigned char*)&addr->sin_addr)[3]);
-                port = ntohs(addr->sin_port);
-                if (port)
-                    rb_str_catf(ret, ":%d", port);
-                if ((socklen_t)sizeof(struct sockaddr_in) < rai->sockaddr_len)
-                    rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in)));
-            }
+	    addr = &sockaddr->in;
+	    if (((char*)&addr->sin_addr)-(char*)addr+0+1 <= socklen)
+		rb_str_catf(ret, "%d", ((unsigned char*)&addr->sin_addr)[0]);
+	    else
+		rb_str_cat2(ret, "?");
+	    if (((char*)&addr->sin_addr)-(char*)addr+1+1 <= socklen)
+		rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[1]);
+	    else
+		rb_str_cat2(ret, ".?");
+	    if (((char*)&addr->sin_addr)-(char*)addr+2+1 <= socklen)
+		rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[2]);
+	    else
+		rb_str_cat2(ret, ".?");
+	    if (((char*)&addr->sin_addr)-(char*)addr+3+1 <= socklen)
+		rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[3]);
+	    else
+		rb_str_cat2(ret, ".?");
+
+	    if (((char*)&addr->sin_port)-(char*)addr+sizeof(addr->sin_port) < socklen) {
+		port = ntohs(addr->sin_port);
+		if (port)
+		    rb_str_catf(ret, ":%d", port);
+	    }
+	    else {
+		rb_str_cat2(ret, ":?");
+	    }
+	    if ((socklen_t)sizeof(struct sockaddr_in) != socklen)
+		rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_in)",
+		  (int)socklen,
+		  (int)sizeof(struct sockaddr_in));
             break;
           }
 
@@ -991,16 +1019,16 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret)
             char hbuf[1024];
             int port;
             int error;
-            if (rai->sockaddr_len < (socklen_t)sizeof(struct sockaddr_in6)) {
-                rb_str_cat2(ret, "too-short-AF_INET6-sockaddr");
+            if (socklen < (socklen_t)sizeof(struct sockaddr_in6)) {
+                rb_str_catf(ret, "too-short-AF_INET6-sockaddr %d bytes", (int)socklen);
             }
             else {
-                addr = &rai->addr.in6;
+                addr = &sockaddr->in6;
                 /* use getnameinfo for scope_id.
                  * RFC 4007: IPv6 Scoped Address Architecture
                  * draft-ietf-ipv6-scope-api-00.txt: Scoped Address Extensions to the IPv6 Basic Socket API
                  */
-                error = getnameinfo(&rai->addr.addr, rai->sockaddr_len,
+                error = getnameinfo(&sockaddr->addr, socklen,
                                     hbuf, (socklen_t)sizeof(hbuf), NULL, 0,
                                     NI_NUMERICHOST|NI_NUMERICSERV);
                 if (error) {
@@ -1013,8 +1041,8 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret)
                     port = ntohs(addr->sin6_port);
                     rb_str_catf(ret, "[%s]:%d", hbuf, port);
                 }
-                if ((socklen_t)sizeof(struct sockaddr_in6) < rai->sockaddr_len)
-                    rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in6)));
+                if ((socklen_t)sizeof(struct sockaddr_in6) < socklen)
+                    rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(socklen - sizeof(struct sockaddr_in6)));
             }
             break;
           }
@@ -1023,10 +1051,10 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret)
 #ifdef HAVE_SYS_UN_H
           case AF_UNIX:
           {
-            struct sockaddr_un *addr = &rai->addr.un;
+            struct sockaddr_un *addr = &sockaddr->un;
             char *p, *s, *e;
             s = addr->sun_path;
-            e = (char*)addr + rai->sockaddr_len;
+            e = (char*)addr + socklen;
             while (s < e && *(e-1) == '\0')
                 e--;
             if (e < s)
@@ -1042,11 +1070,11 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret)
                 }
                 if (printable_only) { /* only printable, no space */
                     if (s[0] != '/') /* relative path */
-                        rb_str_cat2(ret, "AF_UNIX ");
+                        rb_str_cat2(ret, "UNIX ");
                     rb_str_cat(ret, s, p - s);
                 }
                 else {
-                    rb_str_cat2(ret, "AF_UNIX");
+                    rb_str_cat2(ret, "UNIX");
                     while (s < e)
                         rb_str_catf(ret, ":%02x", (unsigned char)*s++);
                 }
@@ -1055,11 +1083,132 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret)
           }
 #endif
 
+#ifdef AF_PACKET
+          /* GNU/Linux */
+          case AF_PACKET:
+          {
+            struct sockaddr_ll *addr;
+            addr = (struct sockaddr_ll *)sockaddr;
+            rb_str_cat2(ret, "PACKET");
+
+            if (offsetof(struct sockaddr_ll, sll_protocol) + sizeof(addr->sll_protocol) <= socklen) {
+                rb_str_catf(ret, " protocol:%d", ntohs(addr->sll_protocol));
+            }
+            if (offsetof(struct sockaddr_ll, sll_ifindex) + sizeof(addr->sll_ifindex) <= socklen) {
+                char buf[IFNAMSIZ];
+                if (if_indextoname(addr->sll_ifindex, buf) == NULL)
+                    rb_str_catf(ret, " ifindex:%d", addr->sll_ifindex);
+                else
+                    rb_str_catf(ret, " %s", buf);
+            }
+            if (offsetof(struct sockaddr_ll, sll_hatype) + sizeof(addr->sll_hatype) <= socklen) {
+                rb_str_catf(ret, " hatype:%d", addr->sll_hatype);
+            }
+            if (offsetof(struct sockaddr_ll, sll_pkttype) + sizeof(addr->sll_pkttype) <= socklen) {
+                if (addr->sll_pkttype == PACKET_HOST)
+                    rb_str_cat2(ret, " HOST");
+                else if (addr->sll_pkttype == PACKET_BROADCAST)
+                    rb_str_cat2(ret, " BROADCAST");
+                else if (addr->sll_pkttype == PACKET_MULTICAST)
+                    rb_str_cat2(ret, " MULTICAST");
+                else if (addr->sll_pkttype == PACKET_OTHERHOST)
+                    rb_str_cat2(ret, " OTHERHOST");
+                else if (addr->sll_pkttype == PACKET_OUTGOING)
+                    rb_str_cat2(ret, " OUTGOING");
+                else
+                    rb_str_catf(ret, " pkttype:%d", addr->sll_pkttype);
+            }
+            if (socklen != (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + addr->sll_halen)) {
+                if (offsetof(struct sockaddr_ll, sll_halen) + sizeof(addr->sll_halen) <= socklen) {
+                    rb_str_catf(ret, " halen:%d", addr->sll_halen);
+                }
+            }
+            if (offsetof(struct sockaddr_ll, sll_addr) < socklen) {
+                socklen_t len, i;
+                rb_str_cat2(ret, " hwaddr");
+                len = addr->sll_halen;
+                if (socklen < offsetof(struct sockaddr_ll, sll_addr) + len)
+                    len = socklen - offsetof(struct sockaddr_ll, sll_addr);
+                for (i = 0; i < len; i++) {
+                    rb_str_catf(ret, ":%02x", addr->sll_addr[i]);
+                }
+            }
+
+            if (socklen < (socklen_t)(offsetof(struct sockaddr_ll, sll_halen) + sizeof(addr->sll_halen)) ||
+                (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + addr->sll_halen) != socklen)
+                rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_ll)",
+                    (int)socklen, (int)sizeof(struct sockaddr_ll));
+
+            break;
+          }
+#endif
+
+#ifdef AF_LINK
+	  /* AF_LINK is defined in 4.4BSD derivations since Net2.
+	     link_ntoa is also defined at Net2.
+             However Debian GNU/kFreeBSD defines AF_LINK but
+             don't have link_ntoa.  */
+          case AF_LINK:
+	  {
+	    /*
+	     * Simple implementation using link_ntoa():
+	     * This doesn't work on Debian GNU/kFreeBSD 6.0.7 (squeeze).
+             * Also, the format is bit different.
+	     *
+	     * rb_str_catf(ret, "LINK %s", link_ntoa(&sockaddr->dl));
+	     * break;
+	     */
+            struct sockaddr_dl *addr = &sockaddr->dl;
+            char *np = NULL, *ap = NULL, *endp;
+            int nlen = 0, alen = 0;
+            int i, off;
+
+            endp = ((char *)addr) + socklen;
+
+            rb_str_cat2(ret, "LINK");
+
+            if (offsetof(struct sockaddr_dl, sdl_data) < socklen) {
+                np = addr->sdl_data;
+                nlen = addr->sdl_nlen;
+                if (endp - np < nlen)
+                    nlen = endp - np;
+            }
+            off = addr->sdl_nlen;
+
+            if (offsetof(struct sockaddr_dl, sdl_data) + off < socklen) {
+                ap = addr->sdl_data + off;
+                alen = addr->sdl_alen;
+                if (endp - ap < alen)
+                    alen = endp - ap;
+            }
+
+            if (np)
+                rb_str_catf(ret, " %.*s", nlen, np);
+            else
+                rb_str_cat2(ret, " ?");
+
+            if (ap) {
+                for (i = 0; i < alen; i++)
+                    rb_str_catf(ret, "%s%02x", i == 0 ? " " : ":", (unsigned char)ap[i]);
+            }
+
+            if (socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_nlen) + sizeof(addr->sdl_nlen)) ||
+                socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_alen) + sizeof(addr->sdl_alen)) ||
+                socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_slen) + sizeof(addr->sdl_slen)) ||
+                /* longer length is possible behavior because struct sockaddr_dl has "minimum work area, can be larger" as the last field.
+                 * cf. Net2:/usr/src/sys/net/if_dl.h. */
+                socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_data) + addr->sdl_nlen + addr->sdl_alen + addr->sdl_slen))
+                rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_dl)",
+                    (int)socklen, (int)sizeof(struct sockaddr_dl));
+            break;
+          }
+#endif
+
           default:
           {
-            ID id = rsock_intern_family(rai->addr.addr.sa_family);
+            ID id = rsock_intern_family(sockaddr->addr.sa_family);
             if (id == 0)
-                rb_str_catf(ret, "unknown address family %d", rai->addr.addr.sa_family);
+                rb_str_catf(ret, "unknown address family %d", sockaddr->addr.sa_family);
             else
                 rb_str_catf(ret, "%s address format unknown", rb_id2name(id));
             break;
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 68f2734..d10bbac 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -44,6 +44,13 @@
 #  include <netdb.h>
 #endif
 
+#ifdef HAVE_NETPACKET_PACKET_H
+#  include <netpacket/packet.h>
+#endif
+#ifdef HAVE_NET_ETHERNET_H
+#  include <net/ethernet.h>
+#endif
+
 #include <errno.h>
 
 #ifdef HAVE_SYS_UN_H
@@ -87,6 +94,9 @@
 #ifdef HAVE_UCRED_H
 #  include <ucred.h>
 #endif
+#ifdef HAVE_NET_IF_DL_H
+#  include <net/if_dl.h>
+#endif
 
 #ifndef HAVE_TYPE_SOCKLEN_T
 typedef int socklen_t;
@@ -169,6 +179,9 @@ typedef union {
 #ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
   struct sockaddr_un un;
 #endif
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_DL
+  struct sockaddr_dl dl; /* AF_LINK */
+#endif
   struct sockaddr_storage storage;
   char place_holder[2048]; /* sockaddr_storage is not enough for Unix domain sockets on SunOS and Darwin. */
 } union_sockaddr;
@@ -267,6 +280,9 @@ VALUE rsock_addrinfo_inspect_sockaddr(VALUE rai);
 VALUE rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen);
 VALUE rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup);
 VALUE rsock_make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, socklen_t));
+VALUE rsock_inspect_sockaddr(struct sockaddr *addr, socklen_t socklen, VALUE ret);
+socklen_t rsock_sockaddr_len(struct sockaddr *addr);
+VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len);
 
 int rsock_revlookup_flag(VALUE revlookup, int *norevlookup);
 
@@ -344,6 +360,7 @@ void rsock_init_socket_constants(void);
 void rsock_init_ancdata(void);
 void rsock_init_addrinfo(void);
 void rsock_init_sockopt(void);
+void rsock_init_sockifaddr(void);
 void rsock_init_socket_init(void);
 
 NORETURN(void rsock_sys_fail_host_port(const char *, VALUE, VALUE));
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 3d59b6c..726d4e6 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -1584,11 +1584,22 @@ sockaddr_len(struct sockaddr *addr)
         return (socklen_t)sizeof(struct sockaddr_un);
 #endif
 
+#ifdef AF_PACKET
+      case AF_PACKET:
+        return (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + ((struct sockaddr_ll *)addr)->sll_halen);
+#endif
+
       default:
         return (socklen_t)(offsetof(struct sockaddr, sa_family) + sizeof(addr->sa_family));
     }
 }
 
+socklen_t
+rsock_sockaddr_len(struct sockaddr *addr)
+{
+    return sockaddr_len(addr);
+}
+
 static VALUE
 sockaddr_obj(struct sockaddr *addr, socklen_t len)
 {
@@ -1622,6 +1633,12 @@ sockaddr_obj(struct sockaddr *addr, socklen_t len)
     return rsock_addrinfo_new(addr, len, addr->sa_family, 0, 0, Qnil, Qnil);
 }
 
+VALUE
+rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len)
+{
+    return sockaddr_obj(addr, len);
+}
+
 #endif
 
 #if defined(HAVE_GETIFADDRS) || (defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && !defined(__hpux)) || defined(SIOCGIFCONF) ||  defined(_WIN32)
