diff --git a/lib/netdev.c b/lib/netdev.c
index b303a7dc558d..00192f0b00da 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -50,6 +50,7 @@
 #include "seq.h"
 #include "openvswitch/shash.h"
 #include "smap.h"
+#include "socket-util.h"
 #include "sset.h"
 #include "svec.h"
 #include "openvswitch/vlog.h"
@@ -2034,29 +2035,13 @@ netdev_get_addrs(const char dev[], struct in6_addr **paddr,
     addr_array = xzalloc(sizeof *addr_array * cnt);
     mask_array = xzalloc(sizeof *mask_array * cnt);
     for (ifa = if_addr_list; ifa; ifa = ifa->ifa_next) {
-        int family;
-
-        if (!ifa->ifa_name || !ifa->ifa_addr || !ifa->ifa_netmask
-            || strncmp(ifa->ifa_name, dev, IFNAMSIZ)) {
-            continue;
-        }
-
-        family = ifa->ifa_addr->sa_family;
-        if (family == AF_INET) {
-            const struct sockaddr_in *sin;
-
-            sin = ALIGNED_CAST(const struct sockaddr_in *, ifa->ifa_addr);
-            in6_addr_set_mapped_ipv4(&addr_array[i], sin->sin_addr.s_addr);
-            sin = ALIGNED_CAST(const struct sockaddr_in *, ifa->ifa_netmask);
-            in6_addr_set_mapped_ipv4(&mask_array[i], sin->sin_addr.s_addr);
-            i++;
-        } else if (family == AF_INET6) {
-            const struct sockaddr_in6 *sin6;
-
-            sin6 = ALIGNED_CAST(const struct sockaddr_in6 *, ifa->ifa_addr);
-            memcpy(&addr_array[i], &sin6->sin6_addr, sizeof *addr_array);
-            sin6 = ALIGNED_CAST(const struct sockaddr_in6 *, ifa->ifa_netmask);
-            memcpy(&mask_array[i], &sin6->sin6_addr, sizeof *mask_array);
+        if (ifa->ifa_name
+            && ifa->ifa_addr
+            && ifa->ifa_netmask
+            && !strncmp(ifa->ifa_name, dev, IFNAMSIZ)
+            && sa_is_ip(ifa->ifa_addr)) {
+            addr_array[i] = sa_get_address(ifa->ifa_addr);
+            mask_array[i] = sa_get_address(ifa->ifa_netmask);
             i++;
         }
     }
diff --git a/lib/socket-util.c b/lib/socket-util.c
index 0964a015e3f9..12d16f582d52 100644
--- a/lib/socket-util.c
+++ b/lib/socket-util.c
@@ -53,6 +53,9 @@ VLOG_DEFINE_THIS_MODULE(socket_util);
 
 static int getsockopt_int(int fd, int level, int option, const char *optname,
                           int *valuep);
+static struct sockaddr_in *sin_cast(const struct sockaddr *);
+static struct sockaddr_in6 *sin6_cast(const struct sockaddr *);
+static const struct sockaddr *sa_cast(const struct sockaddr_storage *);
 
 /* Sets 'fd' to non-blocking mode.  Returns 0 if successful, otherwise a
  * positive errno value. */
@@ -422,7 +425,7 @@ parse_sockaddr_components(struct sockaddr_storage *ss,
                           const char *port_s, uint16_t default_port,
                           const char *s)
 {
-    struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, ss);
+    struct sockaddr_in *sin = sin_cast(sa_cast(ss));
     int port;
 
     if (port_s && port_s[0]) {
@@ -436,9 +439,7 @@ parse_sockaddr_components(struct sockaddr_storage *ss,
 
     memset(ss, 0, sizeof *ss);
     if (host_s && strchr(host_s, ':')) {
-        struct sockaddr_in6 *sin6
-            = ALIGNED_CAST(struct sockaddr_in6 *, ss);
-
+        struct sockaddr_in6 *sin6 = sin6_cast(sa_cast(ss));
         char *addr = strsep(&host_s, "%");
 
         sin6->sin6_family = AF_INET6;
@@ -1017,25 +1018,47 @@ describe_fd(int fd)
 #endif /* _WIN32 */
     return ds_steal_cstr(&string);
 }
-
 
-/* sockaddr_storage helpers. */
+/* sockaddr helpers. */
+
+static struct sockaddr_in *
+sin_cast(const struct sockaddr *sa)
+{
+    return ALIGNED_CAST(struct sockaddr_in *, sa);
+}
+
+static struct sockaddr_in6 *
+sin6_cast(const struct sockaddr *sa)
+{
+    return ALIGNED_CAST(struct sockaddr_in6 *, sa);
+}
+
+/* Returns true if 'sa' represents an IPv4 or IPv6 address, false otherwise. */
+bool
+sa_is_ip(const struct sockaddr *sa)
+{
+    return sa->sa_family == AF_INET || sa->sa_family == AF_INET6;
+}
+
+/* Returns the IPv4 or IPv6 address in 'sa'.  Returns IPv4 addresses as
+ * v6-mapped. */
+struct in6_addr
+sa_get_address(const struct sockaddr *sa)
+{
+    ovs_assert(sa_is_ip(sa));
+    return (sa->sa_family == AF_INET
+            ? in6_addr_mapped_ipv4(sin_cast(sa)->sin_addr.s_addr)
+            : sin6_cast(sa)->sin6_addr);
+}
 
-/* Returns the IPv4 or IPv6 port in 'ss'. */
+/* Returns the IPv4 or IPv6 port in 'sa'. */
 uint16_t
-ss_get_port(const struct sockaddr_storage *ss)
+sa_get_port(const struct sockaddr *sa)
 {
-    if (ss->ss_family == AF_INET) {
-        const struct sockaddr_in *sin
-            = ALIGNED_CAST(const struct sockaddr_in *, ss);
-        return ntohs(sin->sin_port);
-    } else if (ss->ss_family == AF_INET6) {
-        const struct sockaddr_in6 *sin6
-            = ALIGNED_CAST(const struct sockaddr_in6 *, ss);
-        return ntohs(sin6->sin6_port);
-    } else {
-        OVS_NOT_REACHED();
-    }
+    ovs_assert(sa_is_ip(sa));
+    return ntohs(sa->sa_family == AF_INET
+                 ? sin_cast(sa)->sin_port
+                 : sin6_cast(sa)->sin6_port);
 }
 
 /* Returns true if 'name' is safe to include inside a network address field.
@@ -1055,18 +1078,15 @@ is_safe_name(const char *name)
 }
 
 static void
-ss_format_address__(const struct sockaddr_storage *ss,
+sa_format_address__(const struct sockaddr *sa,
                     const char *lbrack, const char *rbrack,
                     struct ds *s)
 {
-    if (ss->ss_family == AF_INET) {
-        const struct sockaddr_in *sin
-            = ALIGNED_CAST(const struct sockaddr_in *, ss);
-
-        ds_put_format(s, IP_FMT, IP_ARGS(sin->sin_addr.s_addr));
-    } else if (ss->ss_family == AF_INET6) {
-        const struct sockaddr_in6 *sin6
-            = ALIGNED_CAST(const struct sockaddr_in6 *, ss);
+    ovs_assert(sa_is_ip(sa));
+    if (sa->sa_family == AF_INET) {
+        ds_put_format(s, IP_FMT, IP_ARGS(sin_cast(sa)->sin_addr.s_addr));
+    } else {
+        const struct sockaddr_in6 *sin6 = sin6_cast(sa);
 
         ds_put_cstr(s, lbrack);
         ds_reserve(s, s->length + INET6_ADDRSTRLEN);
@@ -1089,31 +1109,29 @@ ss_format_address__(const struct sockaddr_storage *ss,
 #endif
 
         ds_put_cstr(s, rbrack);
-    } else {
-        OVS_NOT_REACHED();
     }
 }
 
-/* Formats the IPv4 or IPv6 address in 'ss' into 's'.  If 'ss' is an IPv6
+/* Formats the IPv4 or IPv6 address in 'sa' into 's'.  If 'sa' is an IPv6
  * address, puts square brackets around the address. */
 void
-ss_format_address(const struct sockaddr_storage *ss, struct ds *s)
+sa_format_address(const struct sockaddr *sa, struct ds *s)
 {
-    ss_format_address__(ss, "[", "]", s);
+    sa_format_address__(sa, "[", "]", s);
 }
 
-/* Formats the IPv4 or IPv6 address in 'ss' into 's'.  Does not add square
+/* Formats the IPv4 or IPv6 address in 'sa' into 's'.  Does not add square
  * brackets around IPv6 addresses. */
 void
-ss_format_address_nobracks(const struct sockaddr_storage *ss, struct ds *s)
+sa_format_address_nobracks(const struct sockaddr *sa, struct ds *s)
 {
-    ss_format_address__(ss, "", "", s);
+    sa_format_address__(sa, "", "", s);
 }
 
 size_t
-ss_length(const struct sockaddr_storage *ss)
+sa_length(const struct sockaddr *sa)
 {
-    switch (ss->ss_family) {
+    switch (sa->sa_family) {
     case AF_INET:
         return sizeof(struct sockaddr_in);
 
@@ -1124,7 +1142,51 @@ ss_length(const struct sockaddr_storage *ss)
         OVS_NOT_REACHED();
     }
 }
+
+/* sockaddr_storage helpers.  */
 
+static const struct sockaddr *
+sa_cast(const struct sockaddr_storage *ss)
+{
+    return ALIGNED_CAST(const struct sockaddr *, ss);
+}
+
+bool
+ss_is_ip(const struct sockaddr_storage *ss)
+{
+    return sa_is_ip(sa_cast(ss));
+}
+
+uint16_t
+ss_get_port(const struct sockaddr_storage *ss)
+{
+    return sa_get_port(sa_cast(ss));
+}
+
+struct in6_addr
+ss_get_address(const struct sockaddr_storage *ss)
+{
+    return sa_get_address(sa_cast(ss));
+}
+
+void
+ss_format_address(const struct sockaddr_storage *ss, struct ds *s)
+{
+    sa_format_address(sa_cast(ss), s);
+}
+
+void
+ss_format_address_nobracks(const struct sockaddr_storage *ss, struct ds *s)
+{
+    sa_format_address_nobracks(sa_cast(ss), s);
+}
+
+size_t
+ss_length(const struct sockaddr_storage *ss)
+{
+    return sa_length(sa_cast(ss));
+}
+
 /* For Windows socket calls, 'errno' is not set.  One has to call
  * WSAGetLastError() to get the error number and then pass it to
  * this function to get the correct error string.
diff --git a/lib/socket-util.h b/lib/socket-util.h
index 239d3f22041c..6d386304dc3a 100644
--- a/lib/socket-util.h
+++ b/lib/socket-util.h
@@ -74,12 +74,24 @@ char *describe_fd(int fd);
  * in <netinet/ip.h> is used. */
 #define DSCP_DEFAULT (IPTOS_PREC_INTERNETCONTROL >> 2)
 
+/* Functions for working with sockaddr that might contain an IPv4 or
+ * IPv6 address. */
+bool sa_is_ip(const struct sockaddr *);
+uint16_t sa_get_port(const struct sockaddr *);
+struct in6_addr sa_get_address(const struct sockaddr *);
+void sa_format_address(const struct sockaddr *, struct ds *);
+void sa_format_address_nobracks(const struct sockaddr *, struct ds *);
+size_t sa_length(const struct sockaddr *);
+
 /* Functions for working with sockaddr_storage that might contain an IPv4 or
  * IPv6 address. */
+bool ss_is_ip(const struct sockaddr_storage *);
 uint16_t ss_get_port(const struct sockaddr_storage *);
+struct in6_addr ss_get_address(const struct sockaddr_storage *);
 void ss_format_address(const struct sockaddr_storage *, struct ds *);
 void ss_format_address_nobracks(const struct sockaddr_storage *, struct ds *);
 size_t ss_length(const struct sockaddr_storage *);
+
 const char *sock_strerror(int error);
 
 #ifndef _WIN32
