// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2013 Red Hat, Inc. */ #include "nm-default.h" #include #include "nm-dnsmasq-utils.h" #include "platform/nm-platform.h" #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; guint8 prefix = addr->plen; guint32 netmask; guint32 first, last, mid, reserved; const guint32 NUM = 256; g_return_val_if_fail (out_first, FALSE); g_return_val_if_fail (out_last, FALSE); if (prefix > 30) { if (out_error_desc) *out_error_desc = g_strdup_printf ("Address prefix %d is too small for DHCP.", prefix); return FALSE; } 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; } netmask = _nm_utils_ip4_prefix_to_netmask (prefix); /* treat addresses in host-order from here on. */ netmask = ntohl (netmask); host = ntohl (host); /* 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--; /* 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. */ 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); } else { /* use upper range */ reserved = NM_MIN (((last - host) / 10), 8); first = host + 1 + reserved; last = NM_MIN (last, first < 0xFFFFFFFF - NUM ? first + NUM : 0xFFFFFFFF); } first = htonl (first); last = htonl (last); _nm_utils_inet4_ntop (first, out_first); _nm_utils_inet4_ntop (last, out_last); return TRUE; }