[libvirt] [PATCH 1/3] network: validate DHCP ranges are completely within defined network
John Ferlan
jferlan at redhat.com
Fri May 29 16:30:12 UTC 2015
On 05/26/2015 03:48 PM, Laine Stump wrote:
> virSocketAddrGetRange() has been updated to take the network address
> and prefix, and now checks that both the start and end of the range
> are within that network, thus validating that the entire range of
> addresses is in the network. For IPv4, it also checks that ranges to
> not start with the "network address" of the subnet, nor end with the
> broadcast address of the subnet (this check doesn't apply to IPv6,
> since IPv6 doesn't have a broadcast or network address)
>
> Negative tests have been added to the network update and socket tests
> to verify that bad ranges properly generate an error.
>
> This resolves: https://bugzilla.redhat.com/show_bug.cgi?id=985653
> ---
> src/conf/network_conf.c | 14 ++---
> src/network/bridge_driver.c | 4 +-
> src/util/virsocketaddr.c | 61 ++++++++++++++++++----
> src/util/virsocketaddr.h | 6 ++-
> tests/networkxml2xmlupdatein/dhcp-range-10.xml | 1 +
> tests/networkxml2xmlupdatein/dhcp-range.xml | 2 +-
> .../dhcp6host-routed-network-another-range.xml | 2 +-
> .../dhcp6host-routed-network-range.xml | 2 +-
> tests/networkxml2xmlupdatetest.c | 5 ++
> tests/sockettest.c | 55 ++++++++++++-------
> 10 files changed, 112 insertions(+), 40 deletions(-)
> create mode 100644 tests/networkxml2xmlupdatein/dhcp-range-10.xml
>
> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
> index bc63a3d..f9f3d3d 100644
> --- a/src/conf/network_conf.c
> +++ b/src/conf/network_conf.c
> @@ -832,8 +832,9 @@ int virNetworkIpDefNetmask(const virNetworkIpDef *def,
>
> static int
> virSocketAddrRangeParseXML(const char *networkName,
> - xmlNodePtr node,
> - virSocketAddrRangePtr range)
> + virNetworkIpDefPtr ipdef,
> + xmlNodePtr node,
> + virSocketAddrRangePtr range)
> {
>
>
> @@ -859,7 +860,8 @@ virSocketAddrRangeParseXML(const char *networkName,
> goto cleanup;
>
> /* do a sanity check of the range */
> - if (virSocketAddrGetRange(&range->start, &range->end) < 0) {
> + if (virSocketAddrGetRange(&range->start, &range->end, &ipdef->address,
> + virNetworkIpDefPrefix(ipdef)) < 0) {
> virReportError(VIR_ERR_XML_ERROR,
> _("Invalid dhcp range '%s' to '%s' in network '%s'"),
> start, end, networkName);
> @@ -1009,8 +1011,8 @@ virNetworkDHCPDefParseXML(const char *networkName,
>
> if (VIR_REALLOC_N(def->ranges, def->nranges + 1) < 0)
> return -1;
> - if (virSocketAddrRangeParseXML(networkName, cur,
> - &def->ranges[def->nranges]) < 0) {
> + if (virSocketAddrRangeParseXML(networkName, def, cur,
> + &def->ranges[def->nranges]) < 0) {
> return -1;
> }
> def->nranges++;
> @@ -3608,7 +3610,7 @@ virNetworkDefUpdateIPDHCPRange(virNetworkDefPtr def,
> goto cleanup;
> }
>
> - if (virSocketAddrRangeParseXML(def->name, ctxt->node, &range) < 0)
> + if (virSocketAddrRangeParseXML(def->name, ipdef, ctxt->node, &range) < 0)
> goto cleanup;
>
> /* check if an entry with same name/address/ip already exists */
> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
> index 4b53475..e08a316 100644
> --- a/src/network/bridge_driver.c
> +++ b/src/network/bridge_driver.c
> @@ -1193,7 +1193,9 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
> VIR_FREE(saddr);
> VIR_FREE(eaddr);
> nbleases += virSocketAddrGetRange(&ipdef->ranges[r].start,
> - &ipdef->ranges[r].end);
> + &ipdef->ranges[r].end,
> + &ipdef->address,
> + virNetworkIpDefPrefix(ipdef));
> }
>
> /*
> diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c
> index 67ed330..0edf345 100644
> --- a/src/util/virsocketaddr.c
> +++ b/src/util/virsocketaddr.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (C) 2009-2014 Red Hat, Inc.
> + * Copyright (C) 2009-2015 Red Hat, Inc.
> *
> * This library is free software; you can redistribute it and/or
> * modify it under the terms of the GNU Lesser General Public
> @@ -605,31 +605,69 @@ int virSocketAddrCheckNetmask(virSocketAddrPtr addr1, virSocketAddrPtr addr2,
> * virSocketGetRange:
> * @start: start of an IP range
> * @end: end of an IP range
> + * @network: IP address of network that should completely contain this range
> + * @prefix: prefix of the network
> *
> - * Check the order of the 2 addresses and compute the range, this
> - * will return 1 for identical addresses. Errors can come from incompatible
> - * addresses type, excessive range (>= 2^^16) where the two addresses are
> - * unrelated or inverted start and end.
> + * Check the order of the 2 addresses and compute the range, this will
> + * return 1 for identical addresses. Errors can come from incompatible
> + * addresses type, excessive range (>= 2^^16) where the two addresses
> + * are unrelated, inverted start and end, or a range that is not
> + * within network/prefix.
> *
> * Returns the size of the range or -1 in case of failure
> */
> -int virSocketAddrGetRange(virSocketAddrPtr start, virSocketAddrPtr end)
> +int
> +virSocketAddrGetRange(virSocketAddrPtr start, virSocketAddrPtr end,
> + virSocketAddrPtr network, int prefix)
> {
> int ret = 0;
> size_t i;
> + virSocketAddr netmask;
> +
> + if (start == NULL || end == NULL || network == NULL)
> + return -1;
> +
> + if (VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(end) ||
> + VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(network))
> + return -1;
>
> - if ((start == NULL) || (end == NULL))
> + if (prefix < 0 ||
> + virSocketAddrPrefixToNetmask(prefix, &netmask, VIR_SOCKET_ADDR_FAMILY(network)) < 0)
> return -1;
> - if (start->data.stor.ss_family != end->data.stor.ss_family)
> +
> + /* both start and end of range need to be in the same network as
> + * "network"
> + */
> + if (virSocketAddrCheckNetmask(start, network, &netmask) <= 0 ||
> + virSocketAddrCheckNetmask(start, network, &netmask) <= 0)
Should I assume this is a cut-n-paste (eg, the second call should use
end not start)...
Side note - seems there's no "FAIL" test for this condition -
considering we passed the tests with the check this way...
John
> return -1;
>
> - if (start->data.stor.ss_family == AF_INET) {
> + if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET)) {
> virSocketAddrIPv4 t1, t2;
> + virSocketAddr netaddr, broadcast;
> +
> + if (virSocketAddrBroadcast(network, &netmask, &broadcast) < 0 ||
> + virSocketAddrMask(network, &netmask, &netaddr) < 0)
> + return -1;
> +
> + /* Don't allow the start of the range to be the network
> + * address (usually "...0") or the end of the range to be the
> + * broadcast address (usually "...255"). (the opposite also
> + * isn't allowed, but checking for that is implicit in all the
> + * other combined checks) (IPv6 doesn't have broadcast and
> + * network addresses, so this check is only done for IPv4)
> + */
> + if (virSocketAddrEqual(start, &netaddr) ||
> + virSocketAddrEqual(end, &broadcast))
> + return -1;
>
> if ((virSocketAddrGetIPv4Addr(start, &t1) < 0) ||
> (virSocketAddrGetIPv4Addr(end, &t2) < 0))
> return -1;
>
> + /* legacy check that everything except the last two bytes are
> + * the same
> + */
> for (i = 0; i < 2; i++) {
> if (t1[i] != t2[i])
> return -1;
> @@ -638,13 +676,16 @@ int virSocketAddrGetRange(virSocketAddrPtr start, virSocketAddrPtr end)
> if (ret < 0)
> return -1;
> ret++;
> - } else if (start->data.stor.ss_family == AF_INET6) {
> + } else if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET6)) {
> virSocketAddrIPv6 t1, t2;
>
> if ((virSocketAddrGetIPv6Addr(start, &t1) < 0) ||
> (virSocketAddrGetIPv6Addr(end, &t2) < 0))
> return -1;
>
> + /* legacy check that everything except the last two bytes are
> + * the same
> + */
> for (i = 0; i < 7; i++) {
> if (t1[i] != t2[i])
> return -1;
> diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h
> index 99ab46f..9e2680a 100644
> --- a/src/util/virsocketaddr.h
> +++ b/src/util/virsocketaddr.h
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (C) 2009-2013 Red Hat, Inc.
> + * Copyright (C) 2009-2013, 2015 Red Hat, Inc.
> *
> * This library is free software; you can redistribute it and/or
> * modify it under the terms of the GNU Lesser General Public
> @@ -96,7 +96,9 @@ int virSocketAddrSetPort(virSocketAddrPtr addr, int port);
> int virSocketAddrGetPort(virSocketAddrPtr addr);
>
> int virSocketAddrGetRange(virSocketAddrPtr start,
> - virSocketAddrPtr end);
> + virSocketAddrPtr end,
> + virSocketAddrPtr network,
> + int prefix);
>
> int virSocketAddrIsNetmask(virSocketAddrPtr netmask);
>
> diff --git a/tests/networkxml2xmlupdatein/dhcp-range-10.xml b/tests/networkxml2xmlupdatein/dhcp-range-10.xml
> new file mode 100644
> index 0000000..ed775c8
> --- /dev/null
> +++ b/tests/networkxml2xmlupdatein/dhcp-range-10.xml
> @@ -0,0 +1 @@
> +<range start='10.0.0.10' end='10.0.0.100'/>
> diff --git a/tests/networkxml2xmlupdatein/dhcp-range.xml b/tests/networkxml2xmlupdatein/dhcp-range.xml
> index ed775c8..d5e283c 100644
> --- a/tests/networkxml2xmlupdatein/dhcp-range.xml
> +++ b/tests/networkxml2xmlupdatein/dhcp-range.xml
> @@ -1 +1 @@
> -<range start='10.0.0.10' end='10.0.0.100'/>
> +<range start='192.168.122.10' end='192.168.122.100'/>
> diff --git a/tests/networkxml2xmlupdateout/dhcp6host-routed-network-another-range.xml b/tests/networkxml2xmlupdateout/dhcp6host-routed-network-another-range.xml
> index ee6eb7a..4a1360d 100644
> --- a/tests/networkxml2xmlupdateout/dhcp6host-routed-network-another-range.xml
> +++ b/tests/networkxml2xmlupdateout/dhcp6host-routed-network-another-range.xml
> @@ -8,7 +8,7 @@
> <mac address='12:34:56:78:9a:bc'/>
> <ip address='192.168.122.1' netmask='255.255.255.0'>
> <dhcp>
> - <range start='10.0.0.10' end='10.0.0.100'/>
> + <range start='192.168.122.10' end='192.168.122.100'/>
> <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10'/>
> <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11'/>
> </dhcp>
> diff --git a/tests/networkxml2xmlupdateout/dhcp6host-routed-network-range.xml b/tests/networkxml2xmlupdateout/dhcp6host-routed-network-range.xml
> index ee6eb7a..4a1360d 100644
> --- a/tests/networkxml2xmlupdateout/dhcp6host-routed-network-range.xml
> +++ b/tests/networkxml2xmlupdateout/dhcp6host-routed-network-range.xml
> @@ -8,7 +8,7 @@
> <mac address='12:34:56:78:9a:bc'/>
> <ip address='192.168.122.1' netmask='255.255.255.0'>
> <dhcp>
> - <range start='10.0.0.10' end='10.0.0.100'/>
> + <range start='192.168.122.10' end='192.168.122.100'/>
> <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10'/>
> <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11'/>
> </dhcp>
> diff --git a/tests/networkxml2xmlupdatetest.c b/tests/networkxml2xmlupdatetest.c
> index af697bb..0241378 100644
> --- a/tests/networkxml2xmlupdatetest.c
> +++ b/tests/networkxml2xmlupdatetest.c
> @@ -202,6 +202,11 @@ mymain(void)
> "dhcp6host-routed-network-range",
> VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST,
> 0);
> + DO_TEST_INDEX_FAIL("add-dhcp-range-outside-net",
> + "dhcp-range-10",
> + "dhcp6host-routed-network",
> + VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST,
> + 0);
> DO_TEST_INDEX("append-dhcp-range",
> "dhcp-range",
> "dhcp6host-routed-network",
> diff --git a/tests/sockettest.c b/tests/sockettest.c
> index 0d348d9..84170d5 100644
> --- a/tests/sockettest.c
> +++ b/tests/sockettest.c
> @@ -1,7 +1,7 @@
> /*
> * sockettest.c: Testing for src/util/network.c APIs
> *
> - * Copyright (C) 2010-2011, 2014 Red Hat, Inc.
> + * Copyright (C) 2010-2011, 2014, 2015 Red Hat, Inc.
> *
> * This library is free software; you can redistribute it and/or
> * modify it under the terms of the GNU Lesser General Public
> @@ -86,17 +86,22 @@ static int testFormatHelper(const void *opaque)
> }
>
>
> -static int testRange(const char *saddrstr, const char *eaddrstr, int size, bool pass)
> +static int
> +testRange(const char *saddrstr, const char *eaddrstr,
> + const char *netstr, int prefix, int size, bool pass)
> {
> virSocketAddr saddr;
> virSocketAddr eaddr;
> + virSocketAddr netaddr;
>
> if (virSocketAddrParse(&saddr, saddrstr, AF_UNSPEC) < 0)
> return -1;
> if (virSocketAddrParse(&eaddr, eaddrstr, AF_UNSPEC) < 0)
> return -1;
> + if (virSocketAddrParse(&netaddr, netstr, AF_UNSPEC) < 0)
> + return -1;
>
> - int gotsize = virSocketAddrGetRange(&saddr, &eaddr);
> + int gotsize = virSocketAddrGetRange(&saddr, &eaddr, &netaddr, prefix);
> VIR_DEBUG("Size want %d vs got %d", size, gotsize);
> if (gotsize < 0 || gotsize != size) {
> return pass ? -1 : 0;
> @@ -105,16 +110,23 @@ static int testRange(const char *saddrstr, const char *eaddrstr, int size, bool
> }
> }
>
> +
> struct testRangeData {
> const char *saddr;
> const char *eaddr;
> + const char *netaddr;
> + int prefix;
> int size;
> bool pass;
> };
> +
> +
> static int testRangeHelper(const void *opaque)
> {
> const struct testRangeData *data = opaque;
> - return testRange(data->saddr, data->eaddr, data->size, data->pass);
> + return testRange(data->saddr, data->eaddr,
> + data->netaddr, data->prefix,
> + data->size, data->pass);
> }
>
>
> @@ -287,10 +299,12 @@ mymain(void)
> ret = -1; \
> } while (0)
>
> -#define DO_TEST_RANGE(saddr, eaddr, size, pass) \
> +#define DO_TEST_RANGE(saddr, eaddr, netaddr, prefix, size, pass) \
> do { \
> - struct testRangeData data = { saddr, eaddr, size, pass }; \
> - if (virtTestRun("Test range " saddr " -> " eaddr " size " #size, \
> + struct testRangeData data \
> + = { saddr, eaddr, netaddr, prefix, size, pass }; \
> + if (virtTestRun("Test range " saddr " -> " eaddr "(" netaddr \
> + "/" #prefix") size " #size, \
> testRangeHelper, &data) < 0) \
> ret = -1; \
> } while (0)
> @@ -357,17 +371,22 @@ mymain(void)
> DO_TEST_PARSE_AND_FORMAT("::1", AF_UNIX, false);
> DO_TEST_PARSE_AND_FORMAT("::ffff", AF_UNSPEC, true);
>
> - DO_TEST_RANGE("192.168.122.1", "192.168.122.1", 1, true);
> - DO_TEST_RANGE("192.168.122.1", "192.168.122.20", 20, true);
> - DO_TEST_RANGE("192.168.122.0", "192.168.122.255", 256, true);
> - DO_TEST_RANGE("192.168.122.20", "192.168.122.1", -1, false);
> - DO_TEST_RANGE("10.0.0.1", "192.168.122.20", -1, false);
> - DO_TEST_RANGE("192.168.122.20", "10.0.0.1", -1, false);
> -
> - DO_TEST_RANGE("2000::1", "2000::1", 1, true);
> - DO_TEST_RANGE("2000::1", "2000::2", 2, true);
> - DO_TEST_RANGE("2000::2", "2000::1", -1, false);
> - DO_TEST_RANGE("2000::1", "9001::1", -1, false);
> + DO_TEST_RANGE("192.168.122.1", "192.168.122.1", "192.168.122.1", 24, 1, true);
> + DO_TEST_RANGE("192.168.122.1", "192.168.122.20", "192.168.122.22", 24, 20, true);
> + DO_TEST_RANGE("192.168.122.0", "192.168.122.254", "192.168.122.1", 24, -1, false);
> + DO_TEST_RANGE("192.168.122.1", "192.168.122.255", "192.168.122.1", 24, -1, false);
> + DO_TEST_RANGE("192.168.122.0", "192.168.122.255", "192.168.122.1", 16, 256, true);
> + DO_TEST_RANGE("192.168.122.20", "192.168.122.1", "192.168.122.1", 24, -1, false);
> + DO_TEST_RANGE("10.0.0.1", "192.168.122.20", "192.168.122.1", 24, -1, false);
> + DO_TEST_RANGE("192.168.122.20", "10.0.0.1", "192.168.122.1", 24, -1, false);
> + DO_TEST_RANGE("172.16.0.50", "172.16.0.254", "1.2.3.4", 8, -1, false);
> + DO_TEST_RANGE("192.168.122.1", "192.168.123.20", "192.168.122.22", 24, -1, false);
> + DO_TEST_RANGE("192.168.122.1", "192.168.123.20", "192.168.122.22", 23, 276, true);
> +
> + DO_TEST_RANGE("2000::1", "2000::1", "2000::1", 64, 1, true);
> + DO_TEST_RANGE("2000::1", "2000::2", "2000::1", 64, 2, true);
> + DO_TEST_RANGE("2000::2", "2000::1", "2000::1", 64, -1, false);
> + DO_TEST_RANGE("2000::1", "9001::1", "2000::1", 64, -1, false);
>
> DO_TEST_NETMASK("192.168.122.1", "192.168.122.2",
> "255.255.255.0", true);
>
More information about the libvir-list
mailing list