Apply patch provided by maitainer

Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com>
This commit is contained in:
Dimitri Papadopoulos
2026-07-12 21:44:11 +02:00
parent e41d351d02
commit 5d79d98c37
5 changed files with 186 additions and 27 deletions
+34
View File
@@ -196,6 +196,40 @@ iterations rather than hanging or erroring.
**Links:** REQ-SECMOD-SESSION (predictable_ips / ipv4_seed, see
src/sec-mod-auth.c:570-579)
### REQ-MAIN-NET-004 — With ping-leases enabled, a candidate address is probed with ICMP echo and treated as in-use only on a genuine reply
**Requirement:** The ICMP echo probe (src/icmp-ping.c) is split into a pure
core, `icmp_echo4()`/`icmp_echo6()`, and an ocserv-facing wrapper,
`icmp_ping4()`/`icmp_ping6()`. `icmp_echo4(addr, timeout_secs)` MUST send a
single ICMP echo request to `addr` from a raw socket, and MUST wait up to
`timeout_secs` seconds, or until it has collected two replies (echo reply +
destination unreachable), for a response; it MUST return the number of
`ICMP_ECHOREPLY`/`ICMP6_ECHO_REPLY` messages matching the request's random
`icmp_id` and received from the exact candidate address, and MUST return 0
if only a destination-unreachable, a non-matching reply, or no reply at all
arrived before the timeout. `icmp_echo4()`/`icmp_echo6()` MUST NOT depend on
`main_server_st`, vhost config, or `mslog` — any internal error (RNG or
socket setup failure) is reported via `oc_syslog()` instead. The wrapper
`icmp_ping4(s, addr)`/`icmp_ping6(s, addr)` MUST return 0 immediately
without sending any packet when `GETRCONFIG(s)->ping_leases` is false (the
default); otherwise it MUST call the corresponding `icmp_echo*()` with
`PING_TIMEOUT` (3) seconds, `mslog` the in-use/not-in-use outcome, and
return that result unchanged.
**Strength:** MUST
**Status:** DERIVED
**Source:** src/icmp-ping.c (`icmp_echo4`, `icmp_ping4`, `icmp_echo6`,
`icmp_ping6`)
**Acceptance:** unit, local — (a) call `icmp_echo4()` directly against the
loopback address and confirm it returns non-zero (the kernel answers its
own ICMP echo requests over `lo`), with no `main_server_st` involved; (b)
call `icmp_echo4()` against a non-responding address (e.g. a TEST-NET-1
address unreachable in the test environment) and confirm it returns 0
within the given timeout, not a hang; (c) confirm `icmp_ping4()`/
`icmp_ping6()` (and their `GETRCONFIG`/`mslog` dependency) compile out of
the test binary when built with `-DUNDER_TEST`, so the unit test cannot
accidentally depend on them.
**Links:**
### REQ-MAIN-NET-003 — Reconnecting client (steal) transfers IP leases without re-fetching from the pool
**Requirement:** `steal_ip_leases(proc, thief)` (used when a client
+47 -25
View File
@@ -167,31 +167,27 @@ static ssize_t recvfrom_timeout(int sockfd, void *buf, size_t len, int flags,
return -1;
}
int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1)
int icmp_echo4(const struct sockaddr_in *addr1, unsigned int timeout_secs)
{
struct icmp *pkt;
int pingsock, c, e;
char packet1[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
char buf1[64];
time_t now;
uint16_t id1;
unsigned int gotreply = 0, unreachable = 0;
if (GETRCONFIG(s)->ping_leases == 0)
return 0;
e = gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1));
if (e < 0) {
mslog(s, NULL, LOG_ERR, "error in the random generator: %s",
gnutls_strerror(e));
oc_syslog(LOG_ERR, "error in the random generator: %s",
gnutls_strerror(e));
return 0;
}
pingsock = socket(AF_INET, SOCK_RAW, 1);
if (pingsock == -1) {
e = errno;
mslog(s, NULL, LOG_ERR,
"could not open raw socket for ping: %s", strerror(e));
oc_syslog(LOG_ERR, "could not open raw socket for ping: %s",
strerror(e));
return 0;
}
@@ -208,7 +204,7 @@ int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1)
/* listen for replies */
now = time(NULL);
while (time(NULL) - now < PING_TIMEOUT &&
while (time(NULL) - now < (time_t)timeout_secs &&
(unreachable + gotreply) < 2) {
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
@@ -246,23 +242,37 @@ int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1)
close(pingsock);
return gotreply;
}
#ifndef UNDER_TEST
int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1)
{
char buf1[64];
int gotreply;
if (GETRCONFIG(s)->ping_leases == 0)
return 0;
gotreply = icmp_echo4(addr1, PING_TIMEOUT);
if (gotreply > 0) {
mslog(s, NULL, LOG_INFO, "pinged %s and is in use",
human_addr((void *)addr1, sizeof(struct sockaddr_in),
buf1, sizeof(buf1)));
return gotreply;
} else {
mslog(s, NULL, LOG_INFO, "pinged %s and is not in use",
human_addr((void *)addr1, sizeof(struct sockaddr_in),
buf1, sizeof(buf1)));
return 0;
}
}
int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
return gotreply;
}
#endif /* UNDER_TEST */
int icmp_echo6(const struct sockaddr_in6 *addr1, unsigned int timeout_secs)
{
struct icmp6_hdr *pkt;
char buf1[64];
int pingsock, c, e;
#if defined(SOL_RAW) && defined(IPV6_CHECKSUM)
int sockopt;
@@ -272,21 +282,18 @@ int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
unsigned int gotreply = 0, unreachable = 0;
time_t now;
if (GETRCONFIG(s)->ping_leases == 0)
return 0;
e = gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1));
if (e < 0) {
mslog(s, NULL, LOG_ERR, "error in the random generator: %s",
gnutls_strerror(e));
oc_syslog(LOG_ERR, "error in the random generator: %s",
gnutls_strerror(e));
return 0;
}
pingsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (pingsock == -1) {
e = errno;
mslog(s, NULL, LOG_ERR,
"could not open raw socket for ping: %s", strerror(e));
oc_syslog(LOG_ERR, "could not open raw socket for ping: %s",
strerror(e));
return 0;
}
@@ -306,7 +313,7 @@ int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
/* listen for replies */
now = time(NULL);
while (time(NULL) - now < PING_TIMEOUT &&
while (time(NULL) - now < (time_t)timeout_secs &&
(unreachable + gotreply) < 2) {
struct sockaddr_in6 from;
socklen_t fromlen = sizeof(from);
@@ -334,15 +341,30 @@ int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
close(pingsock);
return gotreply;
}
#ifndef UNDER_TEST
int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
{
char buf1[64];
int gotreply;
if (GETRCONFIG(s)->ping_leases == 0)
return 0;
gotreply = icmp_echo6(addr1, PING_TIMEOUT);
if (gotreply > 0) {
mslog(s, NULL, LOG_INFO, "pinged %s and is in use",
human_addr((void *)addr1, sizeof(struct sockaddr_in6),
buf1, sizeof(buf1)));
return gotreply;
} else {
mslog(s, NULL, LOG_INFO, "pinged %s and is not in use",
human_addr((void *)addr1, sizeof(struct sockaddr_in6),
buf1, sizeof(buf1)));
return 0;
}
return gotreply;
}
#endif /* UNDER_TEST */
+10 -2
View File
@@ -23,8 +23,16 @@
#include "main.h"
/* returns the number of positive replies received or
* 0 if no host with this IP exists. */
/* Pure ICMP echo request/reply probe: no ocserv config or logging
* dependency, so it can be unit tested directly. Returns the number of
* positive replies received within timeout_secs, or 0 if none arrived
* (including the case where a destination-unreachable was received
* instead). */
int icmp_echo4(const struct sockaddr_in *addr1, unsigned int timeout_secs);
int icmp_echo6(const struct sockaddr_in6 *addr1, unsigned int timeout_secs);
/* ocserv-facing wrappers: honor the ping-leases config option and log
* the outcome. Same return convention as icmp_echo4()/icmp_echo6(). */
int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1);
int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1);
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2026 Nikos Mavrogiannopoulos
*
* 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, see <http://www.gnu.org/licenses/>.
*/
/* Exercises REQ-MAIN-NET-004: icmp_echo4()/icmp_echo6() must send an ICMP
* echo request and correctly distinguish a genuine echo reply from "no
* reply"/"unreachable". UNDER_TEST excludes icmp_ping4()/icmp_ping6()
* (the ocserv-facing wrappers, which need a full main_server_st/vhost
* config tree) from this translation unit, so only the pure send/receive
* logic is under test here. Requires CAP_NET_RAW (root), hence gated as a
* root test.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "../src/ip-util.c"
#include "../src/icmp-ping.c"
/* keep the negative-reply tests fast rather than the real 3s PING_TIMEOUT */
#define TEST_TIMEOUT_SECS 1
static void run_test4(const char *desc, const char *ip, int expect_reply)
{
struct sockaddr_in addr;
int ret;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
fprintf(stderr, "FAIL: %s: inet_pton(%s) failed\n", desc, ip);
exit(1);
}
ret = icmp_echo4(&addr, TEST_TIMEOUT_SECS);
if (expect_reply && ret <= 0) {
fprintf(stderr,
"FAIL: %s: expected a ping reply from %s, got ret=%d\n",
desc, ip, ret);
exit(1);
}
if (!expect_reply && ret != 0) {
fprintf(stderr,
"FAIL: %s: expected no ping reply from %s, got ret=%d\n",
desc, ip, ret);
exit(1);
}
printf("PASS: %s (%s) -> %d\n", desc, ip, ret);
}
int main(void)
{
/* positive: the kernel answers ICMP echo requests sent to loopback,
* so a genuine ICMP_ECHOREPLY must be observed and reported. */
run_test4("loopback replies to ping", "127.0.0.1", 1);
/* negative: a TEST-NET-1 (RFC 5737) address is not assigned to any
* host reachable from the test environment, so no ICMP_ECHOREPLY
* can arrive; icmp_echo4() must not mistake silence (or a
* destination-unreachable) for the address being in use, and must
* not hang past the given timeout. */
run_test4("non-responding address is treated as free", "192.0.2.1", 0);
return 0;
}
+11
View File
@@ -287,6 +287,17 @@ if get_option('root-tests')
)
endforeach
# icmp-ping-test: exercises icmp_ping4() directly (REQ-MAIN-NET-004).
# Needs CAP_NET_RAW for a raw ICMP socket, hence gated under root-tests
# alongside the higher-level ping-leases shell test below.
icmp_ping_test_exe = executable('icmp-ping-test', 'icmp-ping-test.c',
c_args: ['-DUNDER_TEST'],
dependencies: test_base_deps + [gnutls_dep],
include_directories: test_inc,
)
test('icmp-ping-test', icmp_ping_test_exe,
env: test_env, workdir: test_workdir, timeout: 30)
root_scripts = [
'haproxy-connect', 'test-iroute', 'test-multi-cookie',
'test-pass-script', 'idle-timeout', 'idle-timeout-icmpv6', 'test-year-2038',