diff mbox

[ovs-dev,03/14] packets: Add new functions for IPv4 and IPv6 address parsing.

Message ID 1449623297-31060-4-git-send-email-blp@ovn.org
State Accepted
Headers show

Commit Message

Ben Pfaff Dec. 9, 2015, 1:08 a.m. UTC
These will be used in an upcoming patch to reduce duplicated code.

Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 lib/packets.c | 132 ++++++++++++++++++++++++++++++++++++++++++----------------
 lib/packets.h |   7 ++++
 2 files changed, 102 insertions(+), 37 deletions(-)

Comments

Justin Pettit Dec. 10, 2015, 12:05 a.m. UTC | #1
Acked-by: Justin Pettit <jpettit@ovn.org>

--Justin


> On Dec 8, 2015, at 5:08 PM, Ben Pfaff <blp@ovn.org> wrote:
> 
> These will be used in an upcoming patch to reduce duplicated code.
> 
> Signed-off-by: Ben Pfaff <blp@ovn.org>
> ---
> lib/packets.c | 132 ++++++++++++++++++++++++++++++++++++++++++----------------
> lib/packets.h |   7 ++++
> 2 files changed, 102 insertions(+), 37 deletions(-)
> 
> diff --git a/lib/packets.c b/lib/packets.c
> index 0ad5073..309ec0c 100644
> --- a/lib/packets.c
> +++ b/lib/packets.c
> @@ -416,6 +416,14 @@ ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
>     }
> }
> 
> +/* Parses string 's', which must be an IP address.  Stores the IP address into
> + * '*ip'.  Returns true if successful, otherwise false. */
> +bool
> +ip_parse(const char *s, ovs_be32 *ip)
> +{
> +    return inet_pton(AF_INET, s, ip) == 1;
> +}
> +
> /* Parses string 's', which must be an IP address with an optional netmask or
>  * CIDR prefix length.  Stores the IP address into '*ip' and the netmask into
>  * '*mask'.  (If 's' does not contain a netmask, 255.255.255.255 is
> @@ -447,6 +455,93 @@ ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
>     return NULL;
> }
> 
> +/* Similar to ip_parse_masked(), but the mask, if present, must be a CIDR mask
> + * and is returned as a prefix length in '*plen'. */
> +char * OVS_WARN_UNUSED_RESULT
> +ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
> +{
> +    ovs_be32 mask;
> +    char *error;
> +
> +    error = ip_parse_masked(s, ip, &mask);
> +    if (error) {
> +        return error;
> +    }
> +
> +    if (!ip_is_cidr(mask)) {
> +        return xasprintf("%s: CIDR network required", s);
> +    }
> +    *plen = ip_count_cidr_bits(mask);
> +    return NULL;
> +}
> +
> +/* Parses string 's', which must be an IPv6 address.  Stores the IPv6 address
> + * into '*ip'.  Returns true if successful, otherwise false. */
> +bool
> +ipv6_parse(const char *s, struct in6_addr *ip)
> +{
> +    return inet_pton(AF_INET6, s, ip) == 1;
> +}
> +
> +/* Parses string 's', which must be an IPv6 address with an optional netmask or
> + * CIDR prefix length.  Stores the IPv6 address into '*ip' and the netmask into
> + * '*mask'.  (If 's' does not contain a netmask, all-one-bits is assumed.)
> + *
> + * Returns NULL if successful, otherwise an error message that the caller must
> + * free(). */
> +char * OVS_WARN_UNUSED_RESULT
> +ipv6_parse_masked(const char *s, struct in6_addr *ip, struct in6_addr *mask)
> +{
> +    char ipv6_s[IPV6_SCAN_LEN + 1];
> +    int prefix;
> +    int n;
> +
> +    if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n) && ipv6_parse(ipv6_s, ip)) {
> +        s += n;
> +        if (!*s) {
> +            *mask = in6addr_exact;
> +        } else if (ovs_scan(s, "/%d%n", &prefix, &n) && !s[n]) {
> +            if (prefix <= 0 || prefix > 128) {
> +                return xasprintf("%s: IPv6 network prefix bits not between 0 "
> +                                 "and 128", s);
> +            }
> +            *mask = ipv6_create_mask(prefix);
> +        } else if (ovs_scan(s, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n)
> +                   && !s[n]
> +                   && ipv6_parse(ipv6_s, mask)) {
> +            /* OK. */
> +        } else {
> +            return xasprintf("%s: syntax error expecting IPv6 prefix length "
> +                             "or mask", s);
> +        }
> +        return NULL;
> +    }
> +    return xasprintf("%s: invalid IPv6 address", s);
> +}
> +
> +/* Similar to ipv6_parse_masked(), but the mask, if present, must be a CIDR
> + * mask and is returned as a prefix length in '*plen'. */
> +char * OVS_WARN_UNUSED_RESULT
> +ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
> +{
> +    struct in6_addr mask;
> +    char *error;
> +
> +    error = ipv6_parse_masked(s, ip, &mask);
> +    if (error) {
> +        return error;
> +    }
> +
> +    if (!ipv6_is_cidr(&mask)) {
> +        return xasprintf("%s: IPv6 CIDR network required", s);
> +    }
> +    *plen = ipv6_count_cidr_bits(&mask);
> +    return NULL;
> +}
> +
> +/* Stores the string representation of the IPv6 address 'addr' into the
> + * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
> + * bytes long. */
> void
> ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
> {
> @@ -612,43 +707,6 @@ ipv6_is_cidr(const struct in6_addr *netmask)
>     return true;
> }
> 
> -/* Parses string 's', which must be an IPv6 address with an optional
> - * CIDR prefix length.  Stores the IP address into '*ipv6' and the CIDR
> - * prefix in '*prefix'.  (If 's' does not contain a CIDR length, all-ones
> - * is assumed.)
> - *
> - * Returns NULL if successful, otherwise an error message that the caller must
> - * free(). */
> -char * OVS_WARN_UNUSED_RESULT
> -ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask)
> -{
> -    char ipv6_s[IPV6_SCAN_LEN + 1];
> -    char mask_s[IPV6_SCAN_LEN + 1];
> -    int prefix;
> -    int n;
> -
> -    if (ovs_scan(s, IPV6_SCAN_FMT"/"IPV6_SCAN_FMT"%n", ipv6_s, mask_s, &n)
> -        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
> -        && inet_pton(AF_INET6, mask_s, mask) == 1
> -        && !s[n]) {
> -        /* OK. */
> -    } else if (ovs_scan(s, IPV6_SCAN_FMT"/%d%n", ipv6_s, &prefix, &n)
> -        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
> -        && !s[n]) {
> -        if (prefix <= 0 || prefix > 128) {
> -            return xasprintf("%s: prefix bits not between 0 and 128", s);
> -        }
> -        *mask = ipv6_create_mask(prefix);
> -    } else if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n)
> -               && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
> -               && !s[n]) {
> -        *mask = in6addr_exact;
> -    } else {
> -        return xasprintf("%s: invalid IP address", s);
> -    }
> -    return NULL;
> -}
> -
> /* Populates 'b' with an Ethernet II packet headed with the given 'eth_dst',
>  * 'eth_src' and 'eth_type' parameters.  A payload of 'size' bytes is allocated
>  * in 'b' and returned.  This payload may be populated with appropriate
> diff --git a/lib/packets.h b/lib/packets.h
> index 1e0417a..eaa329f 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -581,8 +581,11 @@ ip_is_local_multicast(ovs_be32 ip)
> }
> int ip_count_cidr_bits(ovs_be32 netmask);
> void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *);
> +bool ip_parse(const char *s, ovs_be32 *ip);
> char *ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
>     OVS_WARN_UNUSED_RESULT;
> +char *ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
> +    OVS_WARN_UNUSED_RESULT;
> 
> #define IP_VER(ip_ihl_ver) ((ip_ihl_ver) >> 4)
> #define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15)
> @@ -1011,8 +1014,12 @@ struct in6_addr ipv6_addr_bitand(const struct in6_addr *src,
> struct in6_addr ipv6_create_mask(int mask);
> int ipv6_count_cidr_bits(const struct in6_addr *netmask);
> bool ipv6_is_cidr(const struct in6_addr *netmask);
> +
> +bool ipv6_parse(const char *s, struct in6_addr *ip);
> char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6,
>                         struct in6_addr *mask);
> +char *ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
> +    OVS_WARN_UNUSED_RESULT;
> 
> void *eth_compose(struct dp_packet *, const struct eth_addr eth_dst,
>                   const struct eth_addr eth_src, uint16_t eth_type,
> -- 
> 2.1.3
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
Ben Pfaff Dec. 16, 2015, 2:01 a.m. UTC | #2
Thanks Justin.  I applied patches 1, 2, and 3 to master.

On Wed, Dec 09, 2015 at 04:05:46PM -0800, Justin Pettit wrote:
> Acked-by: Justin Pettit <jpettit@ovn.org>
> 
> --Justin
> 
> 
> > On Dec 8, 2015, at 5:08 PM, Ben Pfaff <blp@ovn.org> wrote:
> > 
> > These will be used in an upcoming patch to reduce duplicated code.
> > 
> > Signed-off-by: Ben Pfaff <blp@ovn.org>
> > ---
> > lib/packets.c | 132 ++++++++++++++++++++++++++++++++++++++++++----------------
> > lib/packets.h |   7 ++++
> > 2 files changed, 102 insertions(+), 37 deletions(-)
> > 
> > diff --git a/lib/packets.c b/lib/packets.c
> > index 0ad5073..309ec0c 100644
> > --- a/lib/packets.c
> > +++ b/lib/packets.c
> > @@ -416,6 +416,14 @@ ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
> >     }
> > }
> > 
> > +/* Parses string 's', which must be an IP address.  Stores the IP address into
> > + * '*ip'.  Returns true if successful, otherwise false. */
> > +bool
> > +ip_parse(const char *s, ovs_be32 *ip)
> > +{
> > +    return inet_pton(AF_INET, s, ip) == 1;
> > +}
> > +
> > /* Parses string 's', which must be an IP address with an optional netmask or
> >  * CIDR prefix length.  Stores the IP address into '*ip' and the netmask into
> >  * '*mask'.  (If 's' does not contain a netmask, 255.255.255.255 is
> > @@ -447,6 +455,93 @@ ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
> >     return NULL;
> > }
> > 
> > +/* Similar to ip_parse_masked(), but the mask, if present, must be a CIDR mask
> > + * and is returned as a prefix length in '*plen'. */
> > +char * OVS_WARN_UNUSED_RESULT
> > +ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
> > +{
> > +    ovs_be32 mask;
> > +    char *error;
> > +
> > +    error = ip_parse_masked(s, ip, &mask);
> > +    if (error) {
> > +        return error;
> > +    }
> > +
> > +    if (!ip_is_cidr(mask)) {
> > +        return xasprintf("%s: CIDR network required", s);
> > +    }
> > +    *plen = ip_count_cidr_bits(mask);
> > +    return NULL;
> > +}
> > +
> > +/* Parses string 's', which must be an IPv6 address.  Stores the IPv6 address
> > + * into '*ip'.  Returns true if successful, otherwise false. */
> > +bool
> > +ipv6_parse(const char *s, struct in6_addr *ip)
> > +{
> > +    return inet_pton(AF_INET6, s, ip) == 1;
> > +}
> > +
> > +/* Parses string 's', which must be an IPv6 address with an optional netmask or
> > + * CIDR prefix length.  Stores the IPv6 address into '*ip' and the netmask into
> > + * '*mask'.  (If 's' does not contain a netmask, all-one-bits is assumed.)
> > + *
> > + * Returns NULL if successful, otherwise an error message that the caller must
> > + * free(). */
> > +char * OVS_WARN_UNUSED_RESULT
> > +ipv6_parse_masked(const char *s, struct in6_addr *ip, struct in6_addr *mask)
> > +{
> > +    char ipv6_s[IPV6_SCAN_LEN + 1];
> > +    int prefix;
> > +    int n;
> > +
> > +    if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n) && ipv6_parse(ipv6_s, ip)) {
> > +        s += n;
> > +        if (!*s) {
> > +            *mask = in6addr_exact;
> > +        } else if (ovs_scan(s, "/%d%n", &prefix, &n) && !s[n]) {
> > +            if (prefix <= 0 || prefix > 128) {
> > +                return xasprintf("%s: IPv6 network prefix bits not between 0 "
> > +                                 "and 128", s);
> > +            }
> > +            *mask = ipv6_create_mask(prefix);
> > +        } else if (ovs_scan(s, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n)
> > +                   && !s[n]
> > +                   && ipv6_parse(ipv6_s, mask)) {
> > +            /* OK. */
> > +        } else {
> > +            return xasprintf("%s: syntax error expecting IPv6 prefix length "
> > +                             "or mask", s);
> > +        }
> > +        return NULL;
> > +    }
> > +    return xasprintf("%s: invalid IPv6 address", s);
> > +}
> > +
> > +/* Similar to ipv6_parse_masked(), but the mask, if present, must be a CIDR
> > + * mask and is returned as a prefix length in '*plen'. */
> > +char * OVS_WARN_UNUSED_RESULT
> > +ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
> > +{
> > +    struct in6_addr mask;
> > +    char *error;
> > +
> > +    error = ipv6_parse_masked(s, ip, &mask);
> > +    if (error) {
> > +        return error;
> > +    }
> > +
> > +    if (!ipv6_is_cidr(&mask)) {
> > +        return xasprintf("%s: IPv6 CIDR network required", s);
> > +    }
> > +    *plen = ipv6_count_cidr_bits(&mask);
> > +    return NULL;
> > +}
> > +
> > +/* Stores the string representation of the IPv6 address 'addr' into the
> > + * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
> > + * bytes long. */
> > void
> > ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
> > {
> > @@ -612,43 +707,6 @@ ipv6_is_cidr(const struct in6_addr *netmask)
> >     return true;
> > }
> > 
> > -/* Parses string 's', which must be an IPv6 address with an optional
> > - * CIDR prefix length.  Stores the IP address into '*ipv6' and the CIDR
> > - * prefix in '*prefix'.  (If 's' does not contain a CIDR length, all-ones
> > - * is assumed.)
> > - *
> > - * Returns NULL if successful, otherwise an error message that the caller must
> > - * free(). */
> > -char * OVS_WARN_UNUSED_RESULT
> > -ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask)
> > -{
> > -    char ipv6_s[IPV6_SCAN_LEN + 1];
> > -    char mask_s[IPV6_SCAN_LEN + 1];
> > -    int prefix;
> > -    int n;
> > -
> > -    if (ovs_scan(s, IPV6_SCAN_FMT"/"IPV6_SCAN_FMT"%n", ipv6_s, mask_s, &n)
> > -        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
> > -        && inet_pton(AF_INET6, mask_s, mask) == 1
> > -        && !s[n]) {
> > -        /* OK. */
> > -    } else if (ovs_scan(s, IPV6_SCAN_FMT"/%d%n", ipv6_s, &prefix, &n)
> > -        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
> > -        && !s[n]) {
> > -        if (prefix <= 0 || prefix > 128) {
> > -            return xasprintf("%s: prefix bits not between 0 and 128", s);
> > -        }
> > -        *mask = ipv6_create_mask(prefix);
> > -    } else if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n)
> > -               && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
> > -               && !s[n]) {
> > -        *mask = in6addr_exact;
> > -    } else {
> > -        return xasprintf("%s: invalid IP address", s);
> > -    }
> > -    return NULL;
> > -}
> > -
> > /* Populates 'b' with an Ethernet II packet headed with the given 'eth_dst',
> >  * 'eth_src' and 'eth_type' parameters.  A payload of 'size' bytes is allocated
> >  * in 'b' and returned.  This payload may be populated with appropriate
> > diff --git a/lib/packets.h b/lib/packets.h
> > index 1e0417a..eaa329f 100644
> > --- a/lib/packets.h
> > +++ b/lib/packets.h
> > @@ -581,8 +581,11 @@ ip_is_local_multicast(ovs_be32 ip)
> > }
> > int ip_count_cidr_bits(ovs_be32 netmask);
> > void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *);
> > +bool ip_parse(const char *s, ovs_be32 *ip);
> > char *ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
> >     OVS_WARN_UNUSED_RESULT;
> > +char *ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
> > +    OVS_WARN_UNUSED_RESULT;
> > 
> > #define IP_VER(ip_ihl_ver) ((ip_ihl_ver) >> 4)
> > #define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15)
> > @@ -1011,8 +1014,12 @@ struct in6_addr ipv6_addr_bitand(const struct in6_addr *src,
> > struct in6_addr ipv6_create_mask(int mask);
> > int ipv6_count_cidr_bits(const struct in6_addr *netmask);
> > bool ipv6_is_cidr(const struct in6_addr *netmask);
> > +
> > +bool ipv6_parse(const char *s, struct in6_addr *ip);
> > char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6,
> >                         struct in6_addr *mask);
> > +char *ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
> > +    OVS_WARN_UNUSED_RESULT;
> > 
> > void *eth_compose(struct dp_packet *, const struct eth_addr eth_dst,
> >                   const struct eth_addr eth_src, uint16_t eth_type,
> > -- 
> > 2.1.3
> > 
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > http://openvswitch.org/mailman/listinfo/dev
>
diff mbox

Patch

diff --git a/lib/packets.c b/lib/packets.c
index 0ad5073..309ec0c 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -416,6 +416,14 @@  ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
     }
 }
 
+/* Parses string 's', which must be an IP address.  Stores the IP address into
+ * '*ip'.  Returns true if successful, otherwise false. */
+bool
+ip_parse(const char *s, ovs_be32 *ip)
+{
+    return inet_pton(AF_INET, s, ip) == 1;
+}
+
 /* Parses string 's', which must be an IP address with an optional netmask or
  * CIDR prefix length.  Stores the IP address into '*ip' and the netmask into
  * '*mask'.  (If 's' does not contain a netmask, 255.255.255.255 is
@@ -447,6 +455,93 @@  ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
     return NULL;
 }
 
+/* Similar to ip_parse_masked(), but the mask, if present, must be a CIDR mask
+ * and is returned as a prefix length in '*plen'. */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
+{
+    ovs_be32 mask;
+    char *error;
+
+    error = ip_parse_masked(s, ip, &mask);
+    if (error) {
+        return error;
+    }
+
+    if (!ip_is_cidr(mask)) {
+        return xasprintf("%s: CIDR network required", s);
+    }
+    *plen = ip_count_cidr_bits(mask);
+    return NULL;
+}
+
+/* Parses string 's', which must be an IPv6 address.  Stores the IPv6 address
+ * into '*ip'.  Returns true if successful, otherwise false. */
+bool
+ipv6_parse(const char *s, struct in6_addr *ip)
+{
+    return inet_pton(AF_INET6, s, ip) == 1;
+}
+
+/* Parses string 's', which must be an IPv6 address with an optional netmask or
+ * CIDR prefix length.  Stores the IPv6 address into '*ip' and the netmask into
+ * '*mask'.  (If 's' does not contain a netmask, all-one-bits is assumed.)
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_masked(const char *s, struct in6_addr *ip, struct in6_addr *mask)
+{
+    char ipv6_s[IPV6_SCAN_LEN + 1];
+    int prefix;
+    int n;
+
+    if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n) && ipv6_parse(ipv6_s, ip)) {
+        s += n;
+        if (!*s) {
+            *mask = in6addr_exact;
+        } else if (ovs_scan(s, "/%d%n", &prefix, &n) && !s[n]) {
+            if (prefix <= 0 || prefix > 128) {
+                return xasprintf("%s: IPv6 network prefix bits not between 0 "
+                                 "and 128", s);
+            }
+            *mask = ipv6_create_mask(prefix);
+        } else if (ovs_scan(s, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n)
+                   && !s[n]
+                   && ipv6_parse(ipv6_s, mask)) {
+            /* OK. */
+        } else {
+            return xasprintf("%s: syntax error expecting IPv6 prefix length "
+                             "or mask", s);
+        }
+        return NULL;
+    }
+    return xasprintf("%s: invalid IPv6 address", s);
+}
+
+/* Similar to ipv6_parse_masked(), but the mask, if present, must be a CIDR
+ * mask and is returned as a prefix length in '*plen'. */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
+{
+    struct in6_addr mask;
+    char *error;
+
+    error = ipv6_parse_masked(s, ip, &mask);
+    if (error) {
+        return error;
+    }
+
+    if (!ipv6_is_cidr(&mask)) {
+        return xasprintf("%s: IPv6 CIDR network required", s);
+    }
+    *plen = ipv6_count_cidr_bits(&mask);
+    return NULL;
+}
+
+/* Stores the string representation of the IPv6 address 'addr' into the
+ * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
+ * bytes long. */
 void
 ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
 {
@@ -612,43 +707,6 @@  ipv6_is_cidr(const struct in6_addr *netmask)
     return true;
 }
 
-/* Parses string 's', which must be an IPv6 address with an optional
- * CIDR prefix length.  Stores the IP address into '*ipv6' and the CIDR
- * prefix in '*prefix'.  (If 's' does not contain a CIDR length, all-ones
- * is assumed.)
- *
- * Returns NULL if successful, otherwise an error message that the caller must
- * free(). */
-char * OVS_WARN_UNUSED_RESULT
-ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask)
-{
-    char ipv6_s[IPV6_SCAN_LEN + 1];
-    char mask_s[IPV6_SCAN_LEN + 1];
-    int prefix;
-    int n;
-
-    if (ovs_scan(s, IPV6_SCAN_FMT"/"IPV6_SCAN_FMT"%n", ipv6_s, mask_s, &n)
-        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
-        && inet_pton(AF_INET6, mask_s, mask) == 1
-        && !s[n]) {
-        /* OK. */
-    } else if (ovs_scan(s, IPV6_SCAN_FMT"/%d%n", ipv6_s, &prefix, &n)
-        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
-        && !s[n]) {
-        if (prefix <= 0 || prefix > 128) {
-            return xasprintf("%s: prefix bits not between 0 and 128", s);
-        }
-        *mask = ipv6_create_mask(prefix);
-    } else if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n)
-               && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
-               && !s[n]) {
-        *mask = in6addr_exact;
-    } else {
-        return xasprintf("%s: invalid IP address", s);
-    }
-    return NULL;
-}
-
 /* Populates 'b' with an Ethernet II packet headed with the given 'eth_dst',
  * 'eth_src' and 'eth_type' parameters.  A payload of 'size' bytes is allocated
  * in 'b' and returned.  This payload may be populated with appropriate
diff --git a/lib/packets.h b/lib/packets.h
index 1e0417a..eaa329f 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -581,8 +581,11 @@  ip_is_local_multicast(ovs_be32 ip)
 }
 int ip_count_cidr_bits(ovs_be32 netmask);
 void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *);
+bool ip_parse(const char *s, ovs_be32 *ip);
 char *ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
     OVS_WARN_UNUSED_RESULT;
+char *ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
+    OVS_WARN_UNUSED_RESULT;
 
 #define IP_VER(ip_ihl_ver) ((ip_ihl_ver) >> 4)
 #define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15)
@@ -1011,8 +1014,12 @@  struct in6_addr ipv6_addr_bitand(const struct in6_addr *src,
 struct in6_addr ipv6_create_mask(int mask);
 int ipv6_count_cidr_bits(const struct in6_addr *netmask);
 bool ipv6_is_cidr(const struct in6_addr *netmask);
+
+bool ipv6_parse(const char *s, struct in6_addr *ip);
 char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6,
                         struct in6_addr *mask);
+char *ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
+    OVS_WARN_UNUSED_RESULT;
 
 void *eth_compose(struct dp_packet *, const struct eth_addr eth_dst,
                   const struct eth_addr eth_src, uint16_t eth_type,