1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-31 16:21:26 +03:00

Merge pull request #18653 from yuwata/in-addr-prefix-nth

in-addr-util: fix in_addr_prefix_nth() and use the function to get ip address range in firewall-util
This commit is contained in:
Lennart Poettering 2021-02-17 18:52:24 +01:00 committed by GitHub
commit 50fc7d7036
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 144 additions and 171 deletions

View File

@ -189,8 +189,8 @@ int in_addr_prefix_intersect(
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
assert(u);
/* Increases the network part of an address by one. Returns
* positive if that succeeds, or -ERANGE if this overflows. */
/* Increases the network part of an address by one. Returns 0 if that succeeds, or -ERANGE if
* this overflows. */
return in_addr_prefix_nth(family, u, prefixlen, 1);
}
@ -198,19 +198,17 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen)
/*
* Calculates the nth prefix of size prefixlen starting from the address denoted by u.
*
* On success 1 will be returned and the calculated prefix will be available in
* u. In the case nth == 0 the input will be left unchanged and 1 will be returned.
* In case the calculation cannot be performed (invalid prefix length,
* On success 0 will be returned and the calculated prefix will be available in
* u. In case the calculation cannot be performed (invalid prefix length,
* overflows would occur) -ERANGE is returned. If the address family given isn't
* supported -EAFNOSUPPORT will be returned.
*
*
* Examples:
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 0, writes 192.168.2.0 to u
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 0, no data written
* - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written
* - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written
* - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u
* - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 0, writes 2001:0db8:0000:ff00:: to u
*/
int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) {
assert(u);
@ -218,13 +216,11 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u
if (prefixlen <= 0)
return -ERANGE;
if (nth == 0)
return 1;
if (family == AF_INET) {
uint32_t c, n, t;
if (prefixlen > 32)
prefixlen = 32;
return -ERANGE;
c = be32toh(u->in.s_addr);
@ -238,44 +234,40 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u
n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen);
u->in.s_addr = htobe32(n);
return 1;
return 0;
}
if (family == AF_INET6) {
struct in6_addr result = {};
uint8_t overflow = 0;
uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */
unsigned start_byte = (prefixlen - 1) / 8;
bool overflow = false;
if (prefixlen > 128)
prefixlen = 128;
/* First calculate what we have to add */
delta = nth << ((128 - prefixlen) % 8);
for (unsigned i = 16; i > 0; i--) {
unsigned j = i - 1;
unsigned d = 0;
if (j <= start_byte) {
int16_t t;
d = delta & 0xFF;
delta >>= 8;
t = u->in6.s6_addr[j] + d + overflow;
overflow = t > UINT8_MAX ? t - UINT8_MAX : 0;
result.s6_addr[j] = (uint8_t)t;
} else
result.s6_addr[j] = u->in6.s6_addr[j];
}
if (overflow || delta != 0)
return -ERANGE;
u->in6 = result;
return 1;
for (unsigned i = 16; i > 0; i--) {
unsigned t, j = i - 1, p = j * 8;
if (p >= prefixlen) {
u->in6.s6_addr[j] = 0;
continue;
}
if (prefixlen - p < 8) {
u->in6.s6_addr[j] &= 0xff << (8 - (prefixlen - p));
t = u->in6.s6_addr[j] + ((nth & 0xff) << (8 - (prefixlen - p)));
nth >>= prefixlen - p;
} else {
t = u->in6.s6_addr[j] + (nth & 0xff) + overflow;
nth >>= 8;
}
overflow = t > UINT8_MAX;
u->in6.s6_addr[j] = (uint8_t) (t & 0xff);
}
if (overflow || nth != 0)
return -ERANGE;
return 0;
}
return -EAFNOSUPPORT;
@ -358,6 +350,43 @@ int in_addr_random_prefix(
return -EAFNOSUPPORT;
}
int in_addr_prefix_range(
int family,
const union in_addr_union *in,
unsigned prefixlen,
union in_addr_union *ret_start,
union in_addr_union *ret_end) {
union in_addr_union start, end;
int r;
assert(in);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
if (ret_start) {
start = *in;
r = in_addr_prefix_nth(family, &start, prefixlen, 0);
if (r < 0)
return r;
}
if (ret_end) {
end = *in;
r = in_addr_prefix_nth(family, &end, prefixlen, 1);
if (r < 0)
return r;
}
if (ret_start)
*ret_start = start;
if (ret_end)
*ret_end = end;
return 0;
}
int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
_cleanup_free_ char *x = NULL;
size_t l;

View File

@ -41,6 +41,12 @@ int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth);
int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
int in_addr_prefix_range(
int family,
const union in_addr_union *in,
unsigned prefixlen,
union in_addr_union *ret_start,
union in_addr_union *ret_end);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);

View File

@ -18,7 +18,6 @@
#include "firewall-util-private.h"
#include "in-addr-util.h"
#include "macro.h"
#include "memory-util.h"
#include "socket-util.h"
#include "time-util.h"
@ -30,9 +29,6 @@
#define UDP_DPORT_OFFSET 2
void nft_in6addr_to_range(const union in_addr_union *source, unsigned int prefixlen,
struct in6_addr *start, struct in6_addr *end);
static int nfnl_netlink_sendv(sd_netlink *nfnl,
sd_netlink_message *messages[],
size_t msgcount) {
@ -848,55 +844,19 @@ static int fw_nftables_recreate_table(sd_netlink *nfnl, int af, sd_netlink_messa
return 0;
}
void nft_in6addr_to_range(const union in_addr_union *source, unsigned int prefixlen,
struct in6_addr *ret_start, struct in6_addr *ret_end) {
uint8_t carry = 0;
int i, j;
static int nft_message_add_setelem_ip6range(
sd_netlink_message *m,
const union in_addr_union *source,
unsigned int prefixlen) {
assert(prefixlen <= 128);
for (i = 0, j = 15; i < 16; i++) {
uint8_t nm;
nm = 0xFF;
if (prefixlen < 8)
nm = 0xFF << (8 - prefixlen);
ret_start->s6_addr[i] = source->in6.s6_addr[i] & nm;
if (prefixlen <= 8 && j == 15) {
carry = 1u << (8 - prefixlen);
j = i;
}
if (prefixlen >= 8)
prefixlen -= 8;
else
prefixlen = 0;
}
*ret_end = *ret_start;
for (; j >= 0; j--) {
uint16_t overflow = ret_end->s6_addr[j] + carry;
ret_end->s6_addr[j] = overflow;
if (overflow <= 0xff)
break;
carry = 1;
}
if (memcmp(ret_start, ret_end, sizeof(*ret_start)) > 0)
zero(ret_end);
}
static int nft_message_add_setelem_ip6range(sd_netlink_message *m,
const union in_addr_union *source,
unsigned int prefixlen) {
struct in6_addr start, end;
union in_addr_union start, end;
int r;
nft_in6addr_to_range(source, prefixlen, &start, &end);
r = in_addr_prefix_range(AF_INET6, source, prefixlen, &start, &end);
if (r < 0)
return r;
r = sd_nfnl_nft_message_add_setelem(m, 0, &start, sizeof(start), NULL, 0);
r = sd_nfnl_nft_message_add_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0);
if (r < 0)
return r;
@ -904,7 +864,7 @@ static int nft_message_add_setelem_ip6range(sd_netlink_message *m,
if (r < 0)
return r;
r = sd_nfnl_nft_message_add_setelem(m, 1, &end, sizeof(end), NULL, 0);
r = sd_nfnl_nft_message_add_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0);
if (r < 0)
return r;

View File

@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <arpa/inet.h>
#include <stdlib.h>
#include "firewall-util.h"
#include "log.h"
@ -8,32 +6,14 @@
#include "tests.h"
#define MAKE_IN_ADDR_UNION(a,b,c,d) (union in_addr_union) { .in.s_addr = htobe32((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d))}
void nft_in6addr_to_range(const union in_addr_union *source, unsigned int prefixlen,
struct in6_addr *start, struct in6_addr *end);
static void make_in6_addr_union(const char *addr, union in_addr_union *u) {
assert_se(inet_pton(AF_INET6, addr, &u->in6) >= 0);
}
static bool test_in6_eq(const char *addr, const union in_addr_union *u) {
union in_addr_union tmp;
make_in6_addr_union(addr, &tmp);
return memcmp(&tmp.in6, &u->in6, sizeof(tmp.in6)) == 0;
}
static void assert_in6_eq(const union in_addr_union *a, const union in_addr_union *b) {
assert_se(memcmp(&a->in6, &b->in6, sizeof(a->in6)) == 0);
}
#define MAKE_IN6_ADDR_UNION(str, u) assert_se(in_addr_from_string(AF_INET6, str, u) >= 0)
static void test_v6(FirewallContext **ctx) {
union in_addr_union u = {}, u2 = {};
uint8_t prefixlen;
int r;
make_in6_addr_union("dead::beef", &u);
MAKE_IN6_ADDR_UNION("dead::beef", &u);
r = fw_add_masquerade(ctx, true, AF_INET6, &u, 128);
if (r < 0)
@ -55,7 +35,7 @@ static void test_v6(FirewallContext **ctx) {
if (r < 0)
log_error_errno(r, "Failed to modify firewall: %m");
make_in6_addr_union("1c3::c01d", &u2);
MAKE_IN6_ADDR_UNION("1c3::c01d", &u2);
r = fw_add_local_dnat(ctx, true, AF_INET6, IPPROTO_TCP, 4711, &u2, 815, &u);
if (r < 0)
log_error_errno(r, "Failed to modify firewall: %m");
@ -77,52 +57,6 @@ static void test_v6(FirewallContext **ctx) {
log_error_errno(r, "Failed to modify ipv6 firewall: %m");
}
static void test_v6_range(void) {
unsigned int prefixlen = 64;
union in_addr_union a, b, s;
make_in6_addr_union("dead:0:0:beef::", &s);
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_in6_eq(&s, &a);
assert_se(test_in6_eq("dead:0:0:bef0::", &b));
make_in6_addr_union("2001::", &s);
prefixlen = 56;
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_in6_eq(&s, &a);
assert_se(test_in6_eq("2001:0:0:0100::", &b));
prefixlen = 48;
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_in6_eq(&s, &a);
assert_se(test_in6_eq("2001:0:0001::", &b));
prefixlen = 65;
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_se(test_in6_eq("2001::", &a));
assert_se(test_in6_eq("2001::8000:0:0:0", &b));
prefixlen = 66;
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_in6_eq(&s, &a);
assert_se(test_in6_eq("2001::4000:0:0:0", &b));
prefixlen = 127;
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_in6_eq(&s, &a);
assert_se(test_in6_eq("2001::0002", &b));
make_in6_addr_union("dead:beef::1", &s);
prefixlen = 64;
nft_in6addr_to_range(&s, prefixlen, &a.in6, &b.in6);
assert_se(test_in6_eq("dead:beef::", &a));
}
int main(int argc, char *argv[]) {
_cleanup_(fw_ctx_freep) FirewallContext *ctx;
int r;
@ -175,7 +109,6 @@ int main(int argc, char *argv[]) {
log_error_errno(r, "Failed to modify firewall: %m");
test_v6(&ctx);
test_v6_range();
return 0;
}

View File

@ -238,10 +238,12 @@ static void test_in_addr_prefix_intersect(void) {
static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigned pl, const char *after) {
union in_addr_union ubefore, uafter, t;
log_info("/* %s(%s, prefixlen=%u) */", __func__, before, pl);
assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
t = ubefore;
assert_se((in_addr_prefix_next(f, &t, pl) > 0) == !!after);
assert_se((in_addr_prefix_next(f, &t, pl) >= 0) == !!after);
if (after) {
assert_se(in_addr_from_string(f, after, &uafter) >= 0);
@ -250,13 +252,12 @@ static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigne
}
static void test_in_addr_prefix_next(void) {
log_info("/* %s */", __func__);
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 24, "192.168.1.0");
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 16, "192.169.0.0");
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 20, "192.168.16.0");
test_in_addr_prefix_next_one(AF_INET, "0.0.0.0", 32, "0.0.0.1");
test_in_addr_prefix_next_one(AF_INET, "255.255.255.254", 32, "255.255.255.255");
test_in_addr_prefix_next_one(AF_INET, "255.255.255.255", 32, NULL);
test_in_addr_prefix_next_one(AF_INET, "255.255.255.0", 24, NULL);
@ -275,10 +276,12 @@ static void test_in_addr_prefix_next(void) {
static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned pl, uint64_t nth, const char *after) {
union in_addr_union ubefore, uafter, t;
log_info("/* %s(%s, prefixlen=%u, nth=%"PRIu64") */", __func__, before, pl, nth);
assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
t = ubefore;
assert_se((in_addr_prefix_nth(f, &t, pl, nth) > 0) == !!after);
assert_se((in_addr_prefix_nth(f, &t, pl, nth) >= 0) == !!after);
if (after) {
assert_se(in_addr_from_string(f, after, &uafter) >= 0);
@ -287,10 +290,9 @@ static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned
}
static void test_in_addr_prefix_nth(void) {
log_info("/* %s */", __func__);
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 0, "192.168.0.0");
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 1, "192.168.1.0");
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.123", 24, 0, "192.168.0.0");
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.123", 24, 1, "192.168.1.0");
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 4, "192.168.4.0");
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 25, 1, "192.168.0.128");
test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 25, 1, "192.168.255.128");
@ -309,6 +311,48 @@ static void test_in_addr_prefix_nth(void) {
test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 256, NULL);
test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1, NULL);
test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, 1, NULL);
test_in_addr_prefix_nth_one(AF_INET6, "1234:5678:90ab:cdef:1234:5678:90ab:cdef", 12, 1, "1240::");
}
static void test_in_addr_prefix_range_one(
int family,
const char *in,
unsigned prefixlen,
const char *expected_start,
const char *expected_end) {
union in_addr_union a, s, e;
log_info("/* %s(%s, prefixlen=%u) */", __func__, in, prefixlen);
assert_se(in_addr_from_string(family, in, &a) >= 0);
assert_se((in_addr_prefix_range(family, &a, prefixlen, &s, &e) >= 0) == !!expected_start);
if (expected_start) {
union in_addr_union es;
assert_se(in_addr_from_string(family, expected_start, &es) >= 0);
assert_se(in_addr_equal(family, &s, &es) > 0);
}
if (expected_end) {
union in_addr_union ee;
assert_se(in_addr_from_string(family, expected_end, &ee) >= 0);
assert_se(in_addr_equal(family, &e, &ee) > 0);
}
}
static void test_in_addr_prefix_range(void) {
test_in_addr_prefix_range_one(AF_INET, "192.168.123.123", 24, "192.168.123.0", "192.168.124.0");
test_in_addr_prefix_range_one(AF_INET, "192.168.123.123", 16, "192.168.0.0", "192.169.0.0");
test_in_addr_prefix_range_one(AF_INET6, "dead:beef::", 64, "dead:beef::", "dead:beef:0:1::");
test_in_addr_prefix_range_one(AF_INET6, "dead:0:0:beef::", 64, "dead:0:0:beef::", "dead:0:0:bef0::");
test_in_addr_prefix_range_one(AF_INET6, "2001::", 48, "2001::", "2001:0:1::");
test_in_addr_prefix_range_one(AF_INET6, "2001::", 56, "2001::", "2001:0:0:0100::");
test_in_addr_prefix_range_one(AF_INET6, "2001::", 65, "2001::", "2001::8000:0:0:0");
test_in_addr_prefix_range_one(AF_INET6, "2001::", 66, "2001::", "2001::4000:0:0:0");
test_in_addr_prefix_range_one(AF_INET6, "2001::", 127, "2001::", "2001::2");
}
static void test_in_addr_to_string_one(int f, const char *addr) {
@ -339,6 +383,7 @@ int main(int argc, char *argv[]) {
test_in_addr_prefix_intersect();
test_in_addr_prefix_next();
test_in_addr_prefix_nth();
test_in_addr_prefix_range();
test_in_addr_to_string();
return 0;