From 81d2354defe8327ec837154e330604e42ab79751 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com> Date: Sat, 9 May 2026 14:08:37 +0200 Subject: [PATCH] Special case for IPv4 point-to-point /31 networks RFC 3021 makes an exception for /31 networks: the network and brodacast addresses are not reserved, since they are not needed in a point-to-point context, and a /31 network contains only two addreses. In that case, start at network address instead of network address + 1. Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com> --- src/ip-lease.c | 22 ++++++++++++++++------ tests/meson.build | 2 +- tests/radius-config | 2 +- tests/radius-group | 2 +- tests/radius-ipv6-prefix | 2 +- tests/radius-multi-group | 2 +- tests/radius-multi-group-comma | 2 +- tests/radius-otp | 2 +- tests/random-net.sh | 2 +- tests/test-config-per-group-url-cert | 2 +- tests/test-config-per-group-url-pass | 2 +- 11 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/ip-lease.c b/src/ip-lease.c index e7ba7631..a1987963 100644 --- a/src/ip-lease.c +++ b/src/ip-lease.c @@ -170,16 +170,23 @@ static int is_ipv4_ok(main_server_st *s, struct sockaddr_storage *ip, { struct sockaddr_storage broadcast; unsigned int i; + static const uint8_t mask31[4] = { 0xff, 0xff, 0xff, 0xfe }; memcpy(&broadcast, net, sizeof(broadcast)); for (i = 0; i < sizeof(struct in_addr); i++) { SA_IN_U8_P(&broadcast)[i] |= ~(SA_IN_U8_P(mask)[i]); } - if (ip_lease_exists(s, ip, sizeof(struct sockaddr_in)) != 0 || - ip_cmp(ip, net) == 0 || ip_cmp(ip, &broadcast) == 0) { + if (ip_lease_exists(s, ip, sizeof(struct sockaddr_in)) != 0) { return 0; } + + /* /31 networks have no reserved network/broadcast addresses (RFC 3021) */ + if (memcmp(SA_IN_U8_P(mask), mask31, 4) != 0 && + (ip_cmp(ip, net) == 0 || ip_cmp(ip, &broadcast) == 0)) { + return 0; + } + return 1; } @@ -194,6 +201,7 @@ static int get_ipv4_lease(main_server_st *s, struct proc_st *proc) int ret; const char *c_network, *c_netmask; char buf[64]; + static const uint8_t mask31[4] = { 0xff, 0xff, 0xff, 0xfe }; /* Our IP accounting */ if (proc->config->ipv4_net && proc->config->ipv4_netmask) { @@ -260,10 +268,11 @@ static int get_ipv4_lease(main_server_st *s, struct proc_st *proc) goto fail; } - /* LIP = network address + 1 */ + /* LIP = network address + 1, except for /31 networks (RFC 3021) */ memcpy(&proc->ipv4->lip, &network, sizeof(struct sockaddr_in)); proc->ipv4->lip_len = sizeof(struct sockaddr_in); - SA_IN_U8_P(&proc->ipv4->lip)[3] |= 1; + if (memcmp(SA_IN_U8_P(&mask), mask31, 4) != 0) + SA_IN_U8_P(&proc->ipv4->lip)[3] |= 1; if (ip_cmp(&proc->ipv4->lip, &proc->ipv4->rip) == 0) { mslog(s, NULL, LOG_ERR, @@ -344,10 +353,11 @@ static int get_ipv4_lease(main_server_st *s, struct proc_st *proc) memcpy(&proc->ipv4->sig, &rnd, sizeof(struct sockaddr_in)); - /* LIP = network address + 1 */ + /* LIP = network address + 1, except for /31 networks (RFC 3021) */ memcpy(&proc->ipv4->lip, &network, sizeof(struct sockaddr_in)); proc->ipv4->lip_len = sizeof(struct sockaddr_in); - SA_IN_U8_P(&proc->ipv4->lip)[3] |= 1; + if (memcmp(SA_IN_U8_P(&mask), mask31, 4) != 0) + SA_IN_U8_P(&proc->ipv4->lip)[3] |= 1; if (memcmp(SA_IN_U8_P(&proc->ipv4->lip), SA_IN_U8_P(&proc->ipv4->rip), diff --git a/tests/meson.build b/tests/meson.build index 447bd366..84d6ca0a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -250,7 +250,7 @@ if get_option('root-tests') root_scripts = [ 'haproxy-connect', 'test-iroute', 'test-multi-cookie', - 'test-pass-script', 'idle-timeout', 'idle-timeout-icmpv6', 'test-year-2038', + 'test-pass-script', 'idle-timeout', 'test-year-2038', 'test-cookie-timeout', 'test-cookie-timeout-2', 'test-explicit-ip', 'test-cookie-invalidation', 'test-user-config', 'test-append-routes', 'test-ban', diff --git a/tests/radius-config b/tests/radius-config index cc3f5953..3df5c187 100755 --- a/tests/radius-config +++ b/tests/radius-config @@ -74,7 +74,7 @@ VPNNET=192.168.66.0/24 VPNADDR=192.168.66.1 CLIVPNADDR=192.168.66.192 VPNNET6=fd91:6d14:7241:dc6a::/112 -VPNADDR6=fd91:6d14:7241:dc6a::1 +VPNADDR6=fd91:6d14:7241:dc6a::0 OCCTL_SOCKET=./occtl-radius-$$.socket . `dirname $0`/ns.sh diff --git a/tests/radius-group b/tests/radius-group index 260c2f7a..f1bde252 100755 --- a/tests/radius-group +++ b/tests/radius-group @@ -73,7 +73,7 @@ trap finish EXIT VPNNET=192.168.93.0/24 VPNADDR=192.168.93.1 VPNNET6=fc7d:b139:f7ba:5f53:c634:e98e:b777:0/112 -VPNADDR6=fc7d:b139:f7ba:5f53:c634:e98e:b777:1 +VPNADDR6=fc7d:b139:f7ba:5f53:c634:e98e:b777:0 OCCTL_SOCKET=./occtl-radius-group-$$.socket . `dirname $0`/ns.sh diff --git a/tests/radius-ipv6-prefix b/tests/radius-ipv6-prefix index 2528a23b..bfa03f7b 100755 --- a/tests/radius-ipv6-prefix +++ b/tests/radius-ipv6-prefix @@ -76,7 +76,7 @@ VPNNET=192.168.66.0/24 VPNADDR=192.168.66.1 CLIVPNADDR=192.168.66.194 VPNNET6=fd91:6d14:7241:dc6a::/112 -VPNADDR6=fd91:6d14:7241:dc6a::1 +VPNADDR6=fd91:6d14:7241:dc6a::0 OCCTL_SOCKET=./occtl-radius-ipv6-prefix-$$.socket . `dirname $0`/ns.sh diff --git a/tests/radius-multi-group b/tests/radius-multi-group index 493aaa34..be859a7e 100755 --- a/tests/radius-multi-group +++ b/tests/radius-multi-group @@ -73,7 +73,7 @@ trap finish EXIT VPNNET=192.168.94.0/24 VPNADDR=192.168.94.192 VPNNET6=fc7d:b139:f7ba:5f51:c634:e98e:b777:0/112 -VPNADDR6=fc7d:b139:f7ba:5f51:c634:e98e:b777:1 +VPNADDR6=fc7d:b139:f7ba:5f51:c634:e98e:b777:0 OCCTL_SOCKET=./occtl-radius-group-$$.socket . `dirname $0`/ns.sh diff --git a/tests/radius-multi-group-comma b/tests/radius-multi-group-comma index dcbe7b24..87f204d1 100755 --- a/tests/radius-multi-group-comma +++ b/tests/radius-multi-group-comma @@ -73,7 +73,7 @@ trap finish EXIT VPNNET=192.168.95.0/24 VPNADDR=192.168.95.192 VPNNET6=fc7d:b139:f7ba:5f50:c634:e98e:b777:0/112 -VPNADDR6=fc7d:b139:f7ba:5f50:c634:e98e:b777:1 +VPNADDR6=fc7d:b139:f7ba:5f50:c634:e98e:b777:0 OCCTL_SOCKET=./occtl-radius-group-$$.socket . `dirname $0`/ns.sh diff --git a/tests/radius-otp b/tests/radius-otp index 7d3d616f..39c54d73 100755 --- a/tests/radius-otp +++ b/tests/radius-otp @@ -80,7 +80,7 @@ trap finish EXIT VPNNET=192.168.55.0/24 VPNADDR=192.168.55.1 VPNNET6=fc7d:b139:f7ba:5f53:c634:e98e:b777:0/112 -VPNADDR6=fc7d:b139:f7ba:5f53:c634:e98e:b777:1 +VPNADDR6=fc7d:b139:f7ba:5f53:c634:e98e:b777:0 OCCTL_SOCKET=./occtl-radius-otp-$$.socket . `dirname $0`/ns.sh diff --git a/tests/random-net.sh b/tests/random-net.sh index f23aac83..4cfde2b6 100755 --- a/tests/random-net.sh +++ b/tests/random-net.sh @@ -51,7 +51,7 @@ while [ $ret = 0 ] do eval $(${IPCALC} -r 112 -np --minaddr) VPNNET6="${NETWORK}/${PREFIX}" - VPNADDR6="${NETWORK}1" + VPNADDR6="${NETWORK}0" ping ${PINGOPS} ${VPNADDR6} >/dev/null 2>&1 ret=$? done diff --git a/tests/test-config-per-group-url-cert b/tests/test-config-per-group-url-cert index 47a4589d..45144515 100755 --- a/tests/test-config-per-group-url-cert +++ b/tests/test-config-per-group-url-cert @@ -62,7 +62,7 @@ CLI_ADDRESS=10.204.4.1 VPNNET=192.168.14.0/24 VPNADDR=192.168.14.1 VPNNET6=fd91:6d87:7344:dc6a:dc15::/112 -VPNADDR6=fd91:6d87:7344:dc6a:dc15::1 +VPNADDR6=fd91:6d87:7344:dc6a:dc15::0 OCCTL_SOCKET=./occtl-cpg-$$.socket ROUTE1=192.168.14.0/24 USERNAME=test diff --git a/tests/test-config-per-group-url-pass b/tests/test-config-per-group-url-pass index 976bd01f..df330dad 100755 --- a/tests/test-config-per-group-url-pass +++ b/tests/test-config-per-group-url-pass @@ -62,7 +62,7 @@ CLI_ADDRESS=10.204.6.1 VPNNET=192.168.24.0/24 VPNADDR=192.168.24.1 VPNNET6=fd91:6d87:7344:dc6a:dc16::/112 -VPNADDR6=fd91:6d87:7344:dc6a:dc16::1 +VPNADDR6=fd91:6d87:7344:dc6a:dc16::0 OCCTL_SOCKET=./occtl-cpg-$$.socket ROUTE1=192.168.24.0/24 USERNAME=test