nm-dnsmasq-utils.c 3.68 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Copyright (C) 2013 Red Hat, Inc.
 */

21
#include "nm-default.h"
22

23 24 25 26
#include <string.h>
#include <arpa/inet.h>

#include "nm-dnsmasq-utils.h"
27
#include "platform/nm-platform.h"
28 29 30 31 32 33 34 35 36
#include "nm-utils.h"

gboolean
nm_dnsmasq_utils_get_range (const NMPlatformIP4Address *addr,
                            char *out_first,
                            char *out_last,
                            char **out_error_desc)
{
	guint32 host = addr->address;
37
	guint8 prefix = addr->plen;
38 39 40
	guint32 netmask;
	guint32 first, last, mid, reserved;
	const guint32 NUM = 256;
41

42 43
	g_return_val_if_fail (out_first, FALSE);
	g_return_val_if_fail (out_last, FALSE);
44 45 46 47 48 49 50

	if (prefix > 30) {
		if (out_error_desc)
			*out_error_desc = g_strdup_printf ("Address prefix %d is too small for DHCP.", prefix);
		return FALSE;
	}

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
	if (prefix < 24) {
		/* if the subnet is larger then /24, we partition it and treat it
		 * like it would be a /24.
		 *
		 * Hence, the resulting range will always be between x.x.x.1/24
		 * and x.x.x.254/24, with x.x.x.0 being the network address of the
		 * host.
		 *
		 * In this case, only a /24 portion of the subnet is used.
		 * No particular reason for that, but it's unlikely that a user
		 * would use NetworkManager's shared method when having hundered
		 * of DHCP clients. So, restrict the range to the same /24 in
		 * which the host address lies.
		 */
		prefix = 24;
	}

68
	netmask = _nm_utils_ip4_prefix_to_netmask (prefix);
69 70 71 72

	/* treat addresses in host-order from here on. */
	netmask = ntohl (netmask);
	host = ntohl (host);
73

74 75 76 77 78 79 80
	/* if host is the network or broadcast address, coerce it to
	 * one above or below. Usually, we wouldn't expect the user
	 * to pick such an address. */
	if (host == (host & netmask))
		host++;
	else if (host == (host | ~netmask))
		host--;
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	/* Exclude the network and broadcast address. */
	first = (host &  netmask) + 1;
	last =  (host | ~netmask) - 1;

	/* Depending on whether host is above or below the middle of
	 * the subnet, the larger part if handed out.
	 *
	 * If the host is in the lower half, the range starts
	 * at the lower end with the host (plus reserved), until the
	 * broadcast address
	 *
	 * If the host is in the upper half, the range starts above
	 * the network-address and goes up until the host (except reserved).
	 *
	 * reserved is up to 8 addresses, 10% of the determined range.
97
	 */
98 99 100 101 102 103
	mid =   (host & netmask) | (((first + last) / 2) & ~netmask);
	if (host > mid) {
		/* use lower range */
		reserved = NM_MIN (((host - first) / 10), 8);
		last = host - 1 - reserved;
		first = NM_MAX (first, last > NUM ? last - NUM : 0);
104
	} else {
105 106 107 108
		/* use upper range */
		reserved = NM_MIN (((last - host) / 10), 8);
		first = host + 1 + reserved;
		last = NM_MIN (last, first < 0xFFFFFFFF - NUM ? first + NUM : 0xFFFFFFFF);
109 110
	}

111 112 113
	first = htonl (first);
	last = htonl (last);

114 115 116 117 118 119
	nm_utils_inet4_ntop (first, out_first);
	nm_utils_inet4_ntop (last, out_last);

	return TRUE;
}