mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-08-08 09:21:48 +08:00
Apply patch provided by maitainer
Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com>
This commit is contained in:
@@ -196,6 +196,40 @@ iterations rather than hanging or erroring.
|
|||||||
**Links:** REQ-SECMOD-SESSION (predictable_ips / ipv4_seed, see
|
**Links:** REQ-SECMOD-SESSION (predictable_ips / ipv4_seed, see
|
||||||
src/sec-mod-auth.c:570-579)
|
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
|
### 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
|
**Requirement:** `steal_ip_leases(proc, thief)` (used when a client
|
||||||
|
|||||||
+47
-25
@@ -167,31 +167,27 @@ static ssize_t recvfrom_timeout(int sockfd, void *buf, size_t len, int flags,
|
|||||||
return -1;
|
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;
|
struct icmp *pkt;
|
||||||
int pingsock, c, e;
|
int pingsock, c, e;
|
||||||
char packet1[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
|
char packet1[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
|
||||||
char buf1[64];
|
|
||||||
time_t now;
|
time_t now;
|
||||||
uint16_t id1;
|
uint16_t id1;
|
||||||
unsigned int gotreply = 0, unreachable = 0;
|
unsigned int gotreply = 0, unreachable = 0;
|
||||||
|
|
||||||
if (GETRCONFIG(s)->ping_leases == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
e = gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1));
|
e = gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1));
|
||||||
if (e < 0) {
|
if (e < 0) {
|
||||||
mslog(s, NULL, LOG_ERR, "error in the random generator: %s",
|
oc_syslog(LOG_ERR, "error in the random generator: %s",
|
||||||
gnutls_strerror(e));
|
gnutls_strerror(e));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pingsock = socket(AF_INET, SOCK_RAW, 1);
|
pingsock = socket(AF_INET, SOCK_RAW, 1);
|
||||||
if (pingsock == -1) {
|
if (pingsock == -1) {
|
||||||
e = errno;
|
e = errno;
|
||||||
mslog(s, NULL, LOG_ERR,
|
oc_syslog(LOG_ERR, "could not open raw socket for ping: %s",
|
||||||
"could not open raw socket for ping: %s", strerror(e));
|
strerror(e));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +204,7 @@ int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1)
|
|||||||
|
|
||||||
/* listen for replies */
|
/* listen for replies */
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
while (time(NULL) - now < PING_TIMEOUT &&
|
while (time(NULL) - now < (time_t)timeout_secs &&
|
||||||
(unreachable + gotreply) < 2) {
|
(unreachable + gotreply) < 2) {
|
||||||
struct sockaddr_in from;
|
struct sockaddr_in from;
|
||||||
socklen_t fromlen = sizeof(from);
|
socklen_t fromlen = sizeof(from);
|
||||||
@@ -246,23 +242,37 @@ int icmp_ping4(main_server_st *s, struct sockaddr_in *addr1)
|
|||||||
|
|
||||||
close(pingsock);
|
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) {
|
if (gotreply > 0) {
|
||||||
mslog(s, NULL, LOG_INFO, "pinged %s and is in use",
|
mslog(s, NULL, LOG_INFO, "pinged %s and is in use",
|
||||||
human_addr((void *)addr1, sizeof(struct sockaddr_in),
|
human_addr((void *)addr1, sizeof(struct sockaddr_in),
|
||||||
buf1, sizeof(buf1)));
|
buf1, sizeof(buf1)));
|
||||||
return gotreply;
|
|
||||||
} else {
|
} else {
|
||||||
mslog(s, NULL, LOG_INFO, "pinged %s and is not in use",
|
mslog(s, NULL, LOG_INFO, "pinged %s and is not in use",
|
||||||
human_addr((void *)addr1, sizeof(struct sockaddr_in),
|
human_addr((void *)addr1, sizeof(struct sockaddr_in),
|
||||||
buf1, sizeof(buf1)));
|
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;
|
struct icmp6_hdr *pkt;
|
||||||
char buf1[64];
|
|
||||||
int pingsock, c, e;
|
int pingsock, c, e;
|
||||||
#if defined(SOL_RAW) && defined(IPV6_CHECKSUM)
|
#if defined(SOL_RAW) && defined(IPV6_CHECKSUM)
|
||||||
int sockopt;
|
int sockopt;
|
||||||
@@ -272,21 +282,18 @@ int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
|
|||||||
unsigned int gotreply = 0, unreachable = 0;
|
unsigned int gotreply = 0, unreachable = 0;
|
||||||
time_t now;
|
time_t now;
|
||||||
|
|
||||||
if (GETRCONFIG(s)->ping_leases == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
e = gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1));
|
e = gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1));
|
||||||
if (e < 0) {
|
if (e < 0) {
|
||||||
mslog(s, NULL, LOG_ERR, "error in the random generator: %s",
|
oc_syslog(LOG_ERR, "error in the random generator: %s",
|
||||||
gnutls_strerror(e));
|
gnutls_strerror(e));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pingsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
|
pingsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
|
||||||
if (pingsock == -1) {
|
if (pingsock == -1) {
|
||||||
e = errno;
|
e = errno;
|
||||||
mslog(s, NULL, LOG_ERR,
|
oc_syslog(LOG_ERR, "could not open raw socket for ping: %s",
|
||||||
"could not open raw socket for ping: %s", strerror(e));
|
strerror(e));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +313,7 @@ int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
|
|||||||
|
|
||||||
/* listen for replies */
|
/* listen for replies */
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
while (time(NULL) - now < PING_TIMEOUT &&
|
while (time(NULL) - now < (time_t)timeout_secs &&
|
||||||
(unreachable + gotreply) < 2) {
|
(unreachable + gotreply) < 2) {
|
||||||
struct sockaddr_in6 from;
|
struct sockaddr_in6 from;
|
||||||
socklen_t fromlen = sizeof(from);
|
socklen_t fromlen = sizeof(from);
|
||||||
@@ -334,15 +341,30 @@ int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1)
|
|||||||
|
|
||||||
close(pingsock);
|
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) {
|
if (gotreply > 0) {
|
||||||
mslog(s, NULL, LOG_INFO, "pinged %s and is in use",
|
mslog(s, NULL, LOG_INFO, "pinged %s and is in use",
|
||||||
human_addr((void *)addr1, sizeof(struct sockaddr_in6),
|
human_addr((void *)addr1, sizeof(struct sockaddr_in6),
|
||||||
buf1, sizeof(buf1)));
|
buf1, sizeof(buf1)));
|
||||||
return gotreply;
|
|
||||||
} else {
|
} else {
|
||||||
mslog(s, NULL, LOG_INFO, "pinged %s and is not in use",
|
mslog(s, NULL, LOG_INFO, "pinged %s and is not in use",
|
||||||
human_addr((void *)addr1, sizeof(struct sockaddr_in6),
|
human_addr((void *)addr1, sizeof(struct sockaddr_in6),
|
||||||
buf1, sizeof(buf1)));
|
buf1, sizeof(buf1)));
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return gotreply;
|
||||||
}
|
}
|
||||||
|
#endif /* UNDER_TEST */
|
||||||
|
|||||||
+10
-2
@@ -23,8 +23,16 @@
|
|||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
/* returns the number of positive replies received or
|
/* Pure ICMP echo request/reply probe: no ocserv config or logging
|
||||||
* 0 if no host with this IP exists. */
|
* 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_ping4(main_server_st *s, struct sockaddr_in *addr1);
|
||||||
int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1);
|
int icmp_ping6(main_server_st *s, struct sockaddr_in6 *addr1);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -287,6 +287,17 @@ if get_option('root-tests')
|
|||||||
)
|
)
|
||||||
endforeach
|
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 = [
|
root_scripts = [
|
||||||
'haproxy-connect', 'test-iroute', 'test-multi-cookie',
|
'haproxy-connect', 'test-iroute', 'test-multi-cookie',
|
||||||
'test-pass-script', 'idle-timeout', 'idle-timeout-icmpv6', 'test-year-2038',
|
'test-pass-script', 'idle-timeout', 'idle-timeout-icmpv6', 'test-year-2038',
|
||||||
|
|||||||
Reference in New Issue
Block a user