[ovs-dev,1/4] Add general-purpose IP/port parsing function.

Message ID 20170906210839.26091-2-mmichels@redhat.com
State Superseded
Headers show
Series
  • ovn: Add support for IPv6 load balancers
Related show

Commit Message

Mark Michelson Sept. 6, 2017, 9:08 p.m.
OVS has functions for parsing IPv4 addresses, parsing IPv4 addresses
with a port, and parsing IPv6 addresses. What is lacking though is a
function that can take an IPv4 or IPv6 address, with or without a port.

This commit adds ipv46_parse(), which breaks the given input string into
its component parts and stores them in a sockaddr_storage structure. The
function accepts flags that determine how it should behave if a port is
present in the input string.

Signed-off-by: Mark Michelson <mmichels@redhat.com>
---
 lib/packets.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/packets.h | 10 ++++++++
 2 files changed, 88 insertions(+)

Patch

diff --git a/lib/packets.c b/lib/packets.c
index 74d87eda8..51044df4c 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -22,6 +22,8 @@ 
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <netdb.h>
 #include "byte-order.h"
 #include "csum.h"
 #include "crc32c.h"
@@ -656,6 +658,82 @@  ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
     return error;
 }
 
+/* Parses the string into an IPv4 or IPv6 address.
+ * The port flags act as follows:
+ * * PORT_OPTIONAL: A port may be present but is not required
+ * * PORT_REQUIRED: A port must be present
+ * * PORT_FORBIDDEN: A port must not be present
+ */
+char * OVS_WARN_UNUSED_RESULT
+ipv46_parse(const char *s, enum port_flags flags, struct sockaddr_storage *ss)
+{
+    char *error = NULL;
+
+    char *copy;
+    copy = xstrdup(s);
+
+    char *addr;
+    char *port;
+    if (*copy == '[') {
+        char *end;
+
+        addr = copy + 1;
+        end = strchr(addr, ']');
+        if (!end) {
+            error = xasprintf("No closing bracket on address %s", s);
+            goto finish;
+        }
+        *end++ = '\0';
+        if (*end == ':') {
+            port = end + 1;
+        } else {
+            port = NULL;
+        }
+    } else {
+        addr = copy;
+        port = strchr(copy, ':');
+        if (port) {
+            if (strchr(port + 1, ':')) {
+                port = NULL;
+            } else {
+                *port++ = '\0';
+            }
+        }
+    }
+
+    if (port && !*port) {
+        error = xasprintf("Port is an empty string");
+        goto finish;
+    }
+
+    if (port && flags == PORT_FORBIDDEN) {
+        error = xasprintf("Port forbidden in address %s", s);
+        goto finish;
+    } else if (!port && flags == PORT_REQUIRED) {
+        error = xasprintf("Port required in address %s", s);
+        goto finish;
+    }
+
+    struct addrinfo hints = {
+        .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV,
+        .ai_family = AF_UNSPEC,
+    };
+    struct addrinfo *res;
+    int status;
+    status = getaddrinfo(addr, port, &hints, &res);
+    if (status) {
+        error = xasprintf("Error parsing address %s: %s",
+                s, gai_strerror(status));
+        goto finish;
+    }
+    memcpy(ss, res->ai_addr, res->ai_addrlen);
+    freeaddrinfo(res);
+
+finish:
+    free(copy);
+    return error;
+}
+
 /* Parses string 's', which must be an IPv6 address.  Stores the IPv6 address
  * into '*ip'.  Returns true if successful, otherwise false. */
 bool
diff --git a/lib/packets.h b/lib/packets.h
index 705d0b270..866a3335a 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1325,6 +1325,16 @@  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);
 
+enum port_flags {
+    PORT_OPTIONAL,
+    PORT_REQUIRED,
+    PORT_FORBIDDEN,
+};
+
+char *ipv46_parse(const char *s, enum port_flags flags,
+                  struct sockaddr_storage *ss)
+    OVS_WARN_UNUSED_RESULT;
+
 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);