/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2013 Red Hat, Inc. */ #include "src/core/nm-default-daemon.h" #include "nm-dnsmasq-utils.h" #include #include "libnm-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 hundred * of DHCP clients. So, restrict the range to the same /24 in * which the host address lies. */ prefix = 24; } netmask = nm_ip4_addr_netmask_from_prefix(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) / 10u), 8u); last = host - 1 - reserved; first = NM_MAX(first, last > NUM ? last - NUM : 0); } else { /* use upper range */ reserved = NM_MIN(((last - host) / 10u), 8u); first = host + 1 + reserved; last = NM_MIN(last, first < 0xFFFFFFFF - NUM ? first + NUM : 0xFFFFFFFF); } first = htonl(first); last = htonl(last); nm_inet4_ntop(first, out_first); nm_inet4_ntop(last, out_last); return TRUE; }