7a3408
From b65e25e3132c9b5b0942b138231440bc8d4dd873 Mon Sep 17 00:00:00 2001
7a3408
Message-Id: <b65e25e3132c9b5b0942b138231440bc8d4dd873@dist-git>
7a3408
From: Laine Stump <laine@laine.org>
7a3408
Date: Mon, 10 Aug 2015 13:43:38 -0400
7a3408
Subject: [PATCH] network: validate network NAT range
7a3408
7a3408
This patch modifies virSocketAddrGetRange() to function properly when
7a3408
the containing network/prefix of the address range isn't known, for
7a3408
example in the case of the NAT range of a virtual network (since it is
7a3408
a range of addresses on the *host*, not within the network itself). We
7a3408
then take advantage of this new functionality to validate the NAT
7a3408
range of a virtual network.
7a3408
7a3408
Extra test cases are also added to verify that virSocketAddrGetRange()
7a3408
works properly in both positive and negative cases when the network
7a3408
pointer is NULL.
7a3408
7a3408
This is the *real* fix for:
7a3408
7a3408
https://bugzilla.redhat.com/show_bug.cgi?id=985653
7a3408
7a3408
Commits 1e334a and 48e8b9 had earlier been pushed as fixes for that
7a3408
bug, but I had neglected to read the report carefully, so instead of
7a3408
fixing validation for the NAT range, I had fixed validation for the
7a3408
DHCP range. sigh.
7a3408
7a3408
(cherry picked from commit a6f9af8292b6462e509892b3a16acbcaaef61e4e)
7a3408
7a3408
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
7a3408
---
7a3408
 src/conf/network_conf.c  |   4 ++
7a3408
 src/util/virsocketaddr.c | 184 ++++++++++++++++++++++++-----------------------
7a3408
 tests/sockettest.c       |  46 +++++++++++-
7a3408
 3 files changed, 144 insertions(+), 90 deletions(-)
7a3408
7a3408
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
7a3408
index 0ebb373..b03c2fd 100644
7a3408
--- a/src/conf/network_conf.c
7a3408
+++ b/src/conf/network_conf.c
7a3408
@@ -1729,6 +1729,10 @@ virNetworkForwardNatDefParseXML(const char *networkName,
7a3408
         goto cleanup;
7a3408
     }
7a3408
 
7a3408
+    /* verify that start <= end */
7a3408
+    if (virSocketAddrGetRange(&def->addr.start, &def->addr.end, NULL, 0) < 0)
7a3408
+        goto cleanup;
7a3408
+
7a3408
     /* ports for SNAT and MASQUERADE */
7a3408
     nNatPorts = virXPathNodeSet("./port", ctxt, &natPortNodes);
7a3408
     if (nNatPorts < 0) {
7a3408
diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c
7a3408
index 81539b3..900aa5b 100644
7a3408
--- a/src/util/virsocketaddr.c
7a3408
+++ b/src/util/virsocketaddr.c
7a3408
@@ -628,126 +628,136 @@ virSocketAddrGetRange(virSocketAddrPtr start, virSocketAddrPtr end,
7a3408
     virSocketAddr netmask;
7a3408
     char *startStr = NULL, *endStr = NULL, *netStr = NULL;
7a3408
 
7a3408
-    if (start == NULL || end == NULL || network == NULL) {
7a3408
+    if (start == NULL || end == NULL) {
7a3408
         virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                       _("NULL argument - %p %p %p"), start, end, network);
7a3408
+                       _("NULL argument - %p %p"), start, end);
7a3408
         goto error;
7a3408
     }
7a3408
 
7a3408
     startStr = virSocketAddrFormat(start);
7a3408
     endStr = virSocketAddrFormat(end);
7a3408
-    netStr = virSocketAddrFormat(network);
7a3408
-    if (!(startStr && endStr && netStr))
7a3408
+    if (!startStr || !endStr)
7a3408
         goto error; /*error already reported */
7a3408
 
7a3408
-    if (VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(end) ||
7a3408
-        VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(network)) {
7a3408
+    if (VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(end)) {
7a3408
         virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                       _("mismatch of address family in "
7a3408
-                         "range %s - %s for network %s"),
7a3408
-                       startStr, endStr, netStr);
7a3408
+                       _("mismatch of address family in range %s - %s"),
7a3408
+                       startStr, endStr);
7a3408
         goto error;
7a3408
     }
7a3408
 
7a3408
-    if (prefix < 0 ||
7a3408
-        virSocketAddrPrefixToNetmask(prefix, &netmask,
7a3408
-                                     VIR_SOCKET_ADDR_FAMILY(network)) < 0) {
7a3408
-        virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                       _("bad prefix %d for network %s when "
7a3408
-                         " checking range %s - %s"),
7a3408
-                       prefix, netStr, startStr, endStr);
7a3408
-        goto error;
7a3408
-    }
7a3408
+    if (network) {
7a3408
+        /* some checks can only be done if we have details of the
7a3408
+         * network the range should be within
7a3408
+         */
7a3408
+        if (!(netStr = virSocketAddrFormat(network)))
7a3408
+            goto error;
7a3408
 
7a3408
-    /* both start and end of range need to be in the same network as
7a3408
-     * "network"
7a3408
-     */
7a3408
-    if (virSocketAddrCheckNetmask(start, network, &netmask) <= 0 ||
7a3408
-        virSocketAddrCheckNetmask(end, network, &netmask) <= 0) {
7a3408
-        virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                       _("range %s - %s is not entirely within "
7a3408
-                         "network %s/%d"),
7a3408
-                       startStr, endStr, netStr, prefix);
7a3408
-        goto error;
7a3408
+        if (VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(network)) {
7a3408
+            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                           _("mismatch of address family in "
7a3408
+                             "range %s - %s for network %s"),
7a3408
+                           startStr, endStr, netStr);
7a3408
+            goto error;
7a3408
+        }
7a3408
+
7a3408
+        if (prefix < 0 ||
7a3408
+            virSocketAddrPrefixToNetmask(prefix, &netmask,
7a3408
+                                         VIR_SOCKET_ADDR_FAMILY(network)) < 0) {
7a3408
+            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                           _("bad prefix %d for network %s when "
7a3408
+                             " checking range %s - %s"),
7a3408
+                           prefix, netStr, startStr, endStr);
7a3408
+            goto error;
7a3408
+        }
7a3408
+
7a3408
+        /* both start and end of range need to be within network */
7a3408
+        if (virSocketAddrCheckNetmask(start, network, &netmask) <= 0 ||
7a3408
+            virSocketAddrCheckNetmask(end, network, &netmask) <= 0) {
7a3408
+            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                           _("range %s - %s is not entirely within "
7a3408
+                             "network %s/%d"),
7a3408
+                           startStr, endStr, netStr, prefix);
7a3408
+            goto error;
7a3408
+        }
7a3408
+
7a3408
+        if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET)) {
7a3408
+            virSocketAddr netaddr, broadcast;
7a3408
+
7a3408
+            if (virSocketAddrBroadcast(network, &netmask, &broadcast) < 0 ||
7a3408
+                virSocketAddrMask(network, &netmask, &netaddr) < 0) {
7a3408
+                virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                               _("failed to construct broadcast or network "
7a3408
+                                 "address for network %s/%d"),
7a3408
+                               netStr, prefix);
7a3408
+                goto error;
7a3408
+            }
7a3408
+
7a3408
+            /* Don't allow the start of the range to be the network
7a3408
+             * address (usually "...0") or the end of the range to be the
7a3408
+             * broadcast address (usually "...255"). (the opposite also
7a3408
+             * isn't allowed, but checking for that is implicit in all the
7a3408
+             * other combined checks) (IPv6 doesn't have broadcast and
7a3408
+             * network addresses, so this check is only done for IPv4)
7a3408
+             */
7a3408
+            if (virSocketAddrEqual(start, &netaddr)) {
7a3408
+                virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                               _("start of range %s - %s in network %s/%d "
7a3408
+                                 "is the network address"),
7a3408
+                               startStr, endStr, netStr, prefix);
7a3408
+                goto error;
7a3408
+            }
7a3408
+
7a3408
+            if (virSocketAddrEqual(end, &broadcast)) {
7a3408
+                virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
+                               _("end of range %s - %s in network %s/%d "
7a3408
+                                 "is the broadcast address"),
7a3408
+                               startStr, endStr, netStr, prefix);
7a3408
+                goto error;
7a3408
+            }
7a3408
+        }
7a3408
     }
7a3408
 
7a3408
     if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET)) {
7a3408
         virSocketAddrIPv4 t1, t2;
7a3408
-        virSocketAddr netaddr, broadcast;
7a3408
 
7a3408
-        if (virSocketAddrBroadcast(network, &netmask, &broadcast) < 0 ||
7a3408
-            virSocketAddrMask(network, &netmask, &netaddr) < 0) {
7a3408
-            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                           _("failed to construct broadcast or network "
7a3408
-                             "address for network %s/%d"),
7a3408
-                           netStr, prefix);
7a3408
-            goto error;
7a3408
-        }
7a3408
-
7a3408
-        /* Don't allow the start of the range to be the network
7a3408
-         * address (usually "...0") or the end of the range to be the
7a3408
-         * broadcast address (usually "...255"). (the opposite also
7a3408
-         * isn't allowed, but checking for that is implicit in all the
7a3408
-         * other combined checks) (IPv6 doesn't have broadcast and
7a3408
-         * network addresses, so this check is only done for IPv4)
7a3408
-         */
7a3408
-        if (virSocketAddrEqual(start, &netaddr)) {
7a3408
-            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                           _("start of range %s - %s in network %s/%d "
7a3408
-                             "is the network address"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
-            goto error;
7a3408
-        }
7a3408
-
7a3408
-        if (virSocketAddrEqual(end, &broadcast)) {
7a3408
-            virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                           _("end of range %s - %s in network %s/%d "
7a3408
-                             "is the broadcast address"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
-            goto error;
7a3408
-        }
7a3408
-
7a3408
-        if ((virSocketAddrGetIPv4Addr(start, &t1) < 0) ||
7a3408
-            (virSocketAddrGetIPv4Addr(end, &t2) < 0)) {
7a3408
+        if (virSocketAddrGetIPv4Addr(start, &t1) < 0 ||
7a3408
+            virSocketAddrGetIPv4Addr(end, &t2) < 0) {
7a3408
             virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
                            _("failed to get IPv4 address "
7a3408
-                             "for start or end of range %s - %s "
7a3408
-                             "in network %s/%d"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
+                             "for start or end of range %s - %s"),
7a3408
+                           startStr, endStr);
7a3408
             goto error;
7a3408
         }
7a3408
 
7a3408
-        /* legacy check that everything except the last two bytes are
7a3408
-         * the same
7a3408
+        /* legacy check that everything except the last two bytes
7a3408
+         * are the same
7a3408
          */
7a3408
         for (i = 0; i < 2; i++) {
7a3408
             if (t1[i] != t2[i]) {
7a3408
             virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                           _("range %s - %s is too large (> 65535) "
7a3408
-                             "in network %s/%d"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
+                           _("range %s - %s is too large (> 65535)"),
7a3408
+                           startStr, endStr);
7a3408
             goto error;
7a3408
             }
7a3408
         }
7a3408
         ret = (t2[2] - t1[2]) * 256 + (t2[3] - t1[3]);
7a3408
         if (ret < 0) {
7a3408
             virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                           _("range %s - %s is reversed "
7a3408
-                             "in network %s/%d"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
+                           _("range %s - %s is reversed "),
7a3408
+                           startStr, endStr);
7a3408
             goto error;
7a3408
         }
7a3408
         ret++;
7a3408
     } else if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET6)) {
7a3408
         virSocketAddrIPv6 t1, t2;
7a3408
 
7a3408
-        if ((virSocketAddrGetIPv6Addr(start, &t1) < 0) ||
7a3408
-            (virSocketAddrGetIPv6Addr(end, &t2) < 0)) {
7a3408
+        if (virSocketAddrGetIPv6Addr(start, &t1) < 0 ||
7a3408
+            virSocketAddrGetIPv6Addr(end, &t2) < 0) {
7a3408
             virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
                            _("failed to get IPv6 address "
7a3408
-                             "for start or end of range %s - %s "
7a3408
-                             "in network %s/%d"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
+                             "for start or end of range %s - %s"),
7a3408
+                           startStr, endStr);
7a3408
             goto error;
7a3408
         }
7a3408
 
7a3408
@@ -757,29 +767,27 @@ virSocketAddrGetRange(virSocketAddrPtr start, virSocketAddrPtr end,
7a3408
         for (i = 0; i < 7; i++) {
7a3408
             if (t1[i] != t2[i]) {
7a3408
                 virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                               _("range %s - %s is too large (> 65535) "
7a3408
-                                 "in network %s/%d"),
7a3408
-                               startStr, endStr, netStr, prefix);
7a3408
+                               _("range %s - %s is too large (> 65535)"),
7a3408
+                               startStr, endStr);
7a3408
                 goto error;
7a3408
             }
7a3408
         }
7a3408
         ret = t2[7] - t1[7];
7a3408
         if (ret < 0) {
7a3408
             virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
-                           _("range %s - %s start larger than end "
7a3408
-                             "in network %s/%d"),
7a3408
-                           startStr, endStr, netStr, prefix);
7a3408
+                           _("range %s - %s start larger than end"),
7a3408
+                           startStr, endStr);
7a3408
             goto error;
7a3408
         }
7a3408
         ret++;
7a3408
     } else {
7a3408
         virReportError(VIR_ERR_INTERNAL_ERROR,
7a3408
                        _("unsupported address family "
7a3408
-                         "for range %s - %s "
7a3408
-                         "in network %s/%d, must be ipv4 or ipv6"),
7a3408
-                       startStr, endStr, netStr, prefix);
7a3408
+                         "for range %s - %s, must be ipv4 or ipv6"),
7a3408
+                       startStr, endStr);
7a3408
         goto error;
7a3408
     }
7a3408
+
7a3408
  cleanup:
7a3408
     VIR_FREE(startStr);
7a3408
     VIR_FREE(endStr);
7a3408
diff --git a/tests/sockettest.c b/tests/sockettest.c
7a3408
index 292edb6..8f46218 100644
7a3408
--- a/tests/sockettest.c
7a3408
+++ b/tests/sockettest.c
7a3408
@@ -98,10 +98,11 @@ testRange(const char *saddrstr, const char *eaddrstr,
7a3408
         return -1;
7a3408
     if (virSocketAddrParse(&eaddr, eaddrstr, AF_UNSPEC) < 0)
7a3408
         return -1;
7a3408
-    if (virSocketAddrParse(&netaddr, netstr, AF_UNSPEC) < 0)
7a3408
+    if (netstr && virSocketAddrParse(&netaddr, netstr, AF_UNSPEC) < 0)
7a3408
         return -1;
7a3408
 
7a3408
-    int gotsize = virSocketAddrGetRange(&saddr, &eaddr, &netaddr, prefix);
7a3408
+    int gotsize = virSocketAddrGetRange(&saddr, &eaddr,
7a3408
+                                        netstr ? &netaddr : NULL, prefix);
7a3408
     VIR_DEBUG("Size want %d vs got %d", size, gotsize);
7a3408
     if (pass) {
7a3408
         /* fail if virSocketAddrGetRange returns failure, or unexpected size */
7a3408
@@ -311,6 +312,15 @@ mymain(void)
7a3408
             ret = -1;                                                   \
7a3408
     } while (0)
7a3408
 
7a3408
+#define DO_TEST_RANGE_SIMPLE(saddr, eaddr, size, pass)                  \
7a3408
+    do {                                                                \
7a3408
+        struct testRangeData data                                       \
7a3408
+           = { saddr, eaddr, NULL, 0, size, pass };                     \
7a3408
+        if (virtTestRun("Test range " saddr " -> " eaddr "size " #size, \
7a3408
+                        testRangeHelper, &data) < 0)                    \
7a3408
+            ret = -1;                                                   \
7a3408
+    } while (0)
7a3408
+
7a3408
 #define DO_TEST_NETMASK(addr1, addr2, netmask, pass)                    \
7a3408
     do {                                                                \
7a3408
         struct testNetmaskData data = { addr1, addr2, netmask, pass };  \
7a3408
@@ -373,23 +383,55 @@ mymain(void)
7a3408
     DO_TEST_PARSE_AND_FORMAT("::1", AF_UNIX, false);
7a3408
     DO_TEST_PARSE_AND_FORMAT("::ffff", AF_UNSPEC, true);
7a3408
 
7a3408
+    /* tests that specify a network that should contain the range */
7a3408
     DO_TEST_RANGE("192.168.122.1", "192.168.122.1", "192.168.122.1", 24, 1, true);
7a3408
     DO_TEST_RANGE("192.168.122.1", "192.168.122.20", "192.168.122.22", 24, 20, true);
7a3408
+    /* start of range is "network address" */
7a3408
     DO_TEST_RANGE("192.168.122.0", "192.168.122.254", "192.168.122.1", 24, -1, false);
7a3408
+    /* end of range is "broadcast address" */
7a3408
     DO_TEST_RANGE("192.168.122.1", "192.168.122.255", "192.168.122.1", 24, -1, false);
7a3408
     DO_TEST_RANGE("192.168.122.0", "192.168.122.255", "192.168.122.1", 16, 256, true);
7a3408
+    /* range is reversed */
7a3408
     DO_TEST_RANGE("192.168.122.20", "192.168.122.1", "192.168.122.1", 24, -1, false);
7a3408
+    /* start address outside network */
7a3408
     DO_TEST_RANGE("10.0.0.1", "192.168.122.20", "192.168.122.1", 24, -1, false);
7a3408
+    /* end address outside network and range reversed */
7a3408
     DO_TEST_RANGE("192.168.122.20", "10.0.0.1", "192.168.122.1", 24, -1, false);
7a3408
+    /* entire range outside network */
7a3408
     DO_TEST_RANGE("172.16.0.50", "172.16.0.254", "1.2.3.4", 8, -1, false);
7a3408
+    /* end address outside network */
7a3408
     DO_TEST_RANGE("192.168.122.1", "192.168.123.20", "192.168.122.22", 24, -1, false);
7a3408
     DO_TEST_RANGE("192.168.122.1", "192.168.123.20", "192.168.122.22", 23, 276, true);
7a3408
 
7a3408
     DO_TEST_RANGE("2000::1", "2000::1", "2000::1", 64, 1, true);
7a3408
     DO_TEST_RANGE("2000::1", "2000::2", "2000::1", 64, 2, true);
7a3408
+    /* range reversed */
7a3408
     DO_TEST_RANGE("2000::2", "2000::1", "2000::1", 64, -1, false);
7a3408
+    /* range too large (> 65536) */
7a3408
     DO_TEST_RANGE("2000::1", "9001::1", "2000::1", 64, -1, false);
7a3408
 
7a3408
+    /* tests that *don't* specify a containing network
7a3408
+     * (so fewer things can be checked)
7a3408
+     */
7a3408
+    DO_TEST_RANGE_SIMPLE("192.168.122.1", "192.168.122.1", 1, true);
7a3408
+    DO_TEST_RANGE_SIMPLE("192.168.122.1", "192.168.122.20", 20, true);
7a3408
+    DO_TEST_RANGE_SIMPLE("192.168.122.0", "192.168.122.255", 256, true);
7a3408
+    /* range is reversed */
7a3408
+    DO_TEST_RANGE_SIMPLE("192.168.122.20", "192.168.122.1", -1, false);
7a3408
+    /* range too large (> 65536) */
7a3408
+    DO_TEST_RANGE_SIMPLE("10.0.0.1", "192.168.122.20", -1, false);
7a3408
+    /* range reversed */
7a3408
+    DO_TEST_RANGE_SIMPLE("192.168.122.20", "10.0.0.1", -1, false);
7a3408
+    DO_TEST_RANGE_SIMPLE("172.16.0.50", "172.16.0.254", 205, true);
7a3408
+    DO_TEST_RANGE_SIMPLE("192.168.122.1", "192.168.123.20", 276, true);
7a3408
+
7a3408
+    DO_TEST_RANGE_SIMPLE("2000::1", "2000::1", 1, true);
7a3408
+    DO_TEST_RANGE_SIMPLE("2000::1", "2000::2", 2, true);
7a3408
+    /* range reversed */
7a3408
+    DO_TEST_RANGE_SIMPLE("2000::2", "2000::1", -1, false);
7a3408
+    /* range too large (> 65536) */
7a3408
+    DO_TEST_RANGE_SIMPLE("2000::1", "9001::1", -1, false);
7a3408
+
7a3408
     DO_TEST_NETMASK("192.168.122.1", "192.168.122.2",
7a3408
                     "255.255.255.0", true);
7a3408
     DO_TEST_NETMASK("192.168.122.1", "192.168.122.4",
7a3408
-- 
7a3408
2.5.0
7a3408