mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-10-06 22:32:05 +08:00
Merge branch 'tmp-lease' into 'master'
Use network address as IPv6 lease start Closes #714 See merge request openconnect/ocserv!543
This commit is contained in:
@@ -23,6 +23,12 @@
|
||||
- IPv4 /31 networks (netmask 255.255.255.254) can now be used for leases as
|
||||
point-to-point links (RFC 3021); previously no address could be leased from
|
||||
them. The server takes the network address and the client the other one.
|
||||
- The server-side IPv6 tunnel address is now the network address of
|
||||
ipv6-network (previously the network address + 1). Firewall rules, DNS
|
||||
settings or scripts that reference the server's ::1 tunnel address must be
|
||||
updated. This makes /127 point-to-point networks (RFC 6164) lease the
|
||||
expected address to the client, and a client can no longer be assigned
|
||||
the server's address as part of the first subnet (#714)
|
||||
|
||||
|
||||
* Version 1.5.0 (released 2026-06-07)
|
||||
|
||||
@@ -250,6 +250,29 @@ and `explicit-ipv4 = 192.168.1.0`, confirm the session is rejected (no IP).
|
||||
**Links:** REQ-MAIN-NET-001, OC-PROTO-CONN-007 (server address first in the
|
||||
network, SHOULD)
|
||||
|
||||
### REQ-MAIN-NET-006 — Server-side IPv6 TUN address is the network address
|
||||
|
||||
**Requirement:** `get_ipv6_lease()` MUST set the server-side (local) TUN
|
||||
address `lip` to the network address of the configured `ipv6-network` (all
|
||||
host bits zero) and MUST NOT lease to the client (`rip`) an address whose
|
||||
`ipv6-subnet-prefix` subnet equals `lip`. IPv6 has no reserved network
|
||||
address, so for a `/127` network (RFC 6164) the server takes the network
|
||||
address and the client is leased the other one (`network + 1`). An explicit
|
||||
per-user IPv6 address (`explicit-ipv6`) whose subnet equals `lip` MUST be
|
||||
rejected with `ERR_NO_IP`.
|
||||
**Strength:** MUST
|
||||
**Status:** DERIVED
|
||||
**Source:** src/ip-lease.c:442-443 (`lip`), src/ip-lease.c:447-481
|
||||
(explicit IP), src/ip-lease.c:545 (pool allocation), src/ip-lease.c:150-164
|
||||
(`is_ipv6_ok`)
|
||||
**Acceptance:** local — with `ipv6-network = N/127`, confirm the server TUN
|
||||
address is `N` and the client receives `N+1`; with the same `/127` and
|
||||
`explicit-ipv6 = N`, confirm the session is rejected (no IP). With a wider
|
||||
network (e.g. `/112`), confirm the server TUN address is `N` and an
|
||||
`explicit-ipv6` equal to `N` is rejected.
|
||||
**Links:** REQ-MAIN-NET-001, REQ-MAIN-NET-004, OC-PROTO-CONN-007 (server
|
||||
address first in the network, SHOULD)
|
||||
|
||||
---
|
||||
|
||||
### REQ-MAIN-NET-005 — A user's iroutes are applied with `route-add-cmd` when the user connects, with `%{R}`, `%{RI}` and `%{D}` substituted
|
||||
|
||||
@@ -366,8 +366,9 @@ test sources after `common.sh`:
|
||||
same kind into `NAME`. For every allocated `NAME` it MUST set
|
||||
`NAME_BASE`, the network address without its last all-zero octet
|
||||
(IPv4, e.g. `10.22.134`) or group (IPv6, ending in `:`), and
|
||||
`NAME_ADDR`, the first host address (`NAME_BASE` followed by `.1`
|
||||
or `1`), and it MUST append `NAME` to `VPNNET_VARS` so that
|
||||
`NAME_ADDR`, the server address: the first host address
|
||||
(`NAME_BASE` followed by `.1`) for IPv4, and the network address
|
||||
itself for IPv6 (`REQ-MAIN-NET-006`), and it MUST append `NAME` to `VPNNET_VARS` so that
|
||||
`update_config` substitutes it (`REQ-GEN-TEST-008`). It MUST also
|
||||
set `VPNADDR` and `VPNADDR6` to `VPNNET_ADDR` and `VPNNET6_ADDR`.
|
||||
It MUST NOT modify `ADDRESS` or `CLI_ADDRESS`, so a test using
|
||||
@@ -414,7 +415,7 @@ draw space is about 70,000 `/24` networks).
|
||||
`random-vpnnet.sh`, call `alloc_vpnnet4 VPNNET2`, and confirm: `ADDRESS`
|
||||
is unchanged and `CLI_ADDRESS` unset; `"${VPNNET_BASE}.0/24" = "$VPNNET"`
|
||||
and `VPNADDR = VPNNET_ADDR = ${VPNNET_BASE}.1`; `VPNNET6` ends in `/112`
|
||||
and `VPNADDR6 = ${VPNNET6_BASE}1`; `VPNNET2 != VPNNET`; and a template
|
||||
and `VPNADDR6` equals the network address of `VPNNET6`; `VPNNET2 != VPNNET`; and a template
|
||||
containing `@VPNNET2@ @VPNNET2_BASE@.9 @VPNNET2_ADDR@` is materialized by
|
||||
`update_config` with those values. Source `random-net.sh` and confirm
|
||||
`ADDRESS` and `CLI_ADDRESS` are set to addresses different from
|
||||
|
||||
@@ -113,9 +113,12 @@ each individually optional depending on what's been requested/negotiated
|
||||
**Notes:** "X-CSTP-Address-IP6 ... prefix length is RECOMMENDED to be set to
|
||||
127-bits" (RFC 6164) and "X-CSTP-Netmask ... RECOMMENDED the server address to
|
||||
be the first in defined network" are both SHOULD-strength conventions, not
|
||||
MUST — `[CANDIDATE: check whether ocserv's `ip-lease.c` (REQ-MAIN-NET-001)
|
||||
follows the /127 and "server address first" recommendations for IPv6/IPv4
|
||||
respectively.]`
|
||||
MUST. ocserv follows "server address first": the IPv4 server address is
|
||||
the network address + 1, or the network address for a `/31`
|
||||
(REQ-MAIN-NET-004), and the IPv6 server address is the network address
|
||||
(REQ-MAIN-NET-006). ocserv does not follow the /127 recommendation: the
|
||||
prefix sent in `X-CSTP-Address-IP6` is `ipv6-subnet-prefix` (default 128),
|
||||
not 127 — see REQ-PROTO-CONN-007.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -181,12 +181,15 @@ choice depends on `user_agent_type`, see REQ-PROTO-CFG-002); OCSERV
|
||||
`X-CSTP-Split-Include*` header is sent and the client tunnels all traffic.
|
||||
**Divergence**: the *choice of DNS header name* (`X-CSTP-DNS` vs
|
||||
`X-CSTP-DNS-IP6`) for IPv6 entries is AnyConnect-vs-OpenConnect divergent — see
|
||||
REQ-PROTO-CFG-002 for the dedicated entry. The "/127 for IPv6, server-address-
|
||||
first for IPv4" RECOMMENDED conventions from OC-PROTO-CONN-007's Notes were
|
||||
**not verified** against `src/ip-lease.c` in this pass — `[REVIEW]`: confirm
|
||||
`ip-lease.c`'s allocation order against these SHOULD-strength conventions in a
|
||||
follow-up.
|
||||
**Links**: REQ-PROTO-CFG-001, REQ-PROTO-CFG-002, REQ-MAIN-NET-001
|
||||
REQ-PROTO-CFG-002 for the dedicated entry. Of the SHOULD-strength conventions
|
||||
in OC-PROTO-CONN-007's Notes, ocserv follows "server address first" for both
|
||||
families (REQ-MAIN-NET-004 for IPv4, REQ-MAIN-NET-006 for IPv6). It diverges
|
||||
from "/127 for IPv6": `X-CSTP-Address-IP6` carries `ipv6-subnet-prefix`
|
||||
(`src/worker-vpn.c`, default 128, or e.g. 64 for a delegated subnet), because
|
||||
ocserv leases a client its own subnet rather than one side of a shared `/127`
|
||||
link. Informational.
|
||||
**Links**: REQ-PROTO-CFG-001, REQ-PROTO-CFG-002, REQ-MAIN-NET-001,
|
||||
REQ-MAIN-NET-004, REQ-MAIN-NET-006
|
||||
|
||||
---
|
||||
|
||||
|
||||
+6
-1
@@ -633,7 +633,12 @@ ipv4-netmask = 255.255.255.0
|
||||
# An alternative way of specifying the network:
|
||||
#ipv4-network = 192.168.1.0/24
|
||||
|
||||
# The IPv6 subnet that leases will be given from.
|
||||
# The IPv6 subnet that leases will be given from. The server uses the
|
||||
# network address (e.g., fda9:4efe:7e3b:03ea:: for the network below) for
|
||||
# its side of the tunnel; versions prior to 1.5.1 used the network
|
||||
# address + 1. A /127 network is suitable for point-to-point links
|
||||
# (RFC 6164): the server takes the network address and the client the
|
||||
# other address; this is typically useful as a per-user setting.
|
||||
# [scope: vhost user]
|
||||
#ipv6-network = fda9:4efe:7e3b:03ea::/48
|
||||
|
||||
|
||||
@@ -439,9 +439,7 @@ static int get_ipv6_lease(main_server_st *s, struct proc_st *proc)
|
||||
if (proc->ipv6 == NULL)
|
||||
return ERR_MEM;
|
||||
|
||||
/* LIP = network address + 1 */
|
||||
memcpy(&proc->ipv6->lip, &network, sizeof(struct sockaddr_in6));
|
||||
SA_IN6_U8_P(&proc->ipv6->lip)[15] |= 1;
|
||||
|
||||
proc->ipv6->lip_len = sizeof(struct sockaddr_in6);
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Used by test-ipv6-p2p; the per-user files in user-config-ipv6-p2p/
|
||||
# override ipv6-network to exercise /127 point-to-point networks.
|
||||
auth = "plain[@SRCDIR@/data/test1.passwd]"
|
||||
isolate-workers = @ISOLATE_WORKERS@
|
||||
use-dbus = no
|
||||
max-clients = 16
|
||||
max-same-clients = 2
|
||||
tcp-port = @PORT@
|
||||
udp-port = @PORT@
|
||||
keepalive = 32400
|
||||
dpd = 440
|
||||
try-mtu-discovery = false
|
||||
server-cert = @SRCDIR@/certs/server-cert.pem
|
||||
server-key = @SRCDIR@/certs/server-key.pem
|
||||
tls-priorities = "PERFORMANCE:%SERVER_PRECEDENCE:%COMPAT"
|
||||
auth-timeout = 40
|
||||
pid-file = /var/run/ocserv.pid
|
||||
socket-file = /var/run/ocserv-socket
|
||||
run-as-user = nobody
|
||||
run-as-group = daemon
|
||||
device = vpns
|
||||
default-domain = example.com
|
||||
ipv4-network = @VPNNET@
|
||||
ipv6-network = @VPNNET6@
|
||||
ping-leases = false
|
||||
config-per-user = @CONFIG_PER_USER_DIR@/
|
||||
occtl-socket-file = @OCCTL_SOCKET@
|
||||
use-occtl = true
|
||||
@@ -1 +1 @@
|
||||
explicit-ipv6 = @VPNNET6_BASE@1
|
||||
explicit-ipv6 = @VPNNET6_BASE@0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ipv6-network = @VPNNET6_BASE@0/127
|
||||
@@ -0,0 +1,2 @@
|
||||
ipv6-network = @VPNNET6_BASE@2/127
|
||||
explicit-ipv6 = @VPNNET6_BASE@2
|
||||
@@ -0,0 +1,2 @@
|
||||
ipv6-network = @VPNNET6B@
|
||||
explicit-ipv6 = @VPNNET6B_BASE@1
|
||||
+2
-1
@@ -301,7 +301,8 @@ if get_option('root-tests')
|
||||
'haproxy-connect', 'test-iroute', 'test-multi-cookie',
|
||||
'test-pass-script', 'idle-timeout', 'idle-timeout-icmpv6', 'test-year-2038',
|
||||
'test-cookie-timeout', 'test-cookie-timeout-2',
|
||||
'test-explicit-ip', 'test-ipv4-p2p', 'test-cookie-invalidation',
|
||||
'test-explicit-ip', 'test-ipv4-p2p', 'test-ipv6-p2p',
|
||||
'test-cookie-invalidation',
|
||||
'test-user-config', 'test-append-routes', 'test-ban',
|
||||
'multiple-routes', 'json', 'test-udp-listen-host',
|
||||
'test-max-same-1', 'test-vhost-udp-port-inheritance',
|
||||
|
||||
+3
-3
@@ -27,9 +27,9 @@
|
||||
# VPNADDR=192.168.1.1
|
||||
#
|
||||
# Provides:
|
||||
# ${NSCMD1} - to run on NS1
|
||||
# ${NSCMD2} - to run on NS2
|
||||
# ${NSCMD3} - to run on NS3
|
||||
# ${CMDNS1} - to run on NS1
|
||||
# ${CMDNS2} - to run on NS2
|
||||
# ${CMDNS3} - to run on NS3
|
||||
#
|
||||
# Cleanup is automatic via a trap
|
||||
# Requires: finish() to be defined
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
# to derive addresses and sub-networks within VPNNET
|
||||
# VPNNET_ADDR - the first host address of VPNNET (also as VPNADDR)
|
||||
# VPNNET6 - random IPv6 /112 network
|
||||
# VPNNET6_BASE, VPNNET6_ADDR (also as VPNADDR6) - likewise for VPNNET6
|
||||
# VPNNET6_BASE, VPNNET6_ADDR (also as VPNADDR6) - likewise for VPNNET6,
|
||||
# except that VPNNET6_ADDR is the network address itself
|
||||
# alloc_vpnnet4 NAME, alloc_vpnnet6 NAME - allocate a further network,
|
||||
# distinct from the ones already allocated, into NAME,
|
||||
# NAME_BASE and NAME_ADDR. Every allocated NAME is
|
||||
@@ -115,13 +116,14 @@ _alloc_vpnnet() {
|
||||
echo "VPN network $1: ${NETWORK}/${PREFIX} overlaps a local route, redrawing"
|
||||
done
|
||||
# the network address minus its last all-zero group or octet;
|
||||
# for IPv6 it ends in ':', so BASE + host number is an address
|
||||
# for IPv6 it ends in ':', so BASE + host number is an address.
|
||||
# The IPv6 server address is the network address (REQ-MAIN-NET-006).
|
||||
if test "$2" = 24; then
|
||||
_base="${NETWORK%.0}"
|
||||
_addr="${_base}.1"
|
||||
else
|
||||
_base="${NETWORK%0}"
|
||||
_addr="${_base}1"
|
||||
_addr="${NETWORK}"
|
||||
fi
|
||||
_VPNNETS_ALLOCATED="${_VPNNETS_ALLOCATED} ${NETWORK}"
|
||||
VPNNET_VARS="${VPNNET_VARS} $1"
|
||||
|
||||
Executable
+137
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2026 Nikos Mavrogiannopoulos
|
||||
#
|
||||
# This file is part of ocserv.
|
||||
#
|
||||
# ocserv 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.
|
||||
#
|
||||
# ocserv 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Tests that the server side of the IPv6 tunnel takes the network address:
|
||||
# on a /127 point-to-point network (RFC 6164) the client is leased the
|
||||
# other address, and an explicit IP equal to the network address is
|
||||
# rejected while network + 1 is usable. See REQ-MAIN-NET-006.
|
||||
#
|
||||
# The server runs in its own network namespace (ns.sh) because the host
|
||||
# namespace of CI containers may have IPv6 disabled, which makes setting
|
||||
# the tun device's IPv6 address fail and the session fall back to IPv4.
|
||||
|
||||
SERV="${SERV:-../src/ocserv}"
|
||||
srcdir=${srcdir:-.}
|
||||
TMPFILE=outfile.$$
|
||||
OCCTL_SOCKET=./occtl-ipv6-p2p-$$.socket
|
||||
SERVERCERT=pin-sha256:xp3scfzy3rOQsv9NcOve/8YVVv+pHr4qNCXEXrNl5s8=
|
||||
|
||||
. `dirname $0`/common.sh
|
||||
. `dirname $0`/random-net.sh
|
||||
|
||||
# a second, wider network for the explicit network + 1 case
|
||||
alloc_vpnnet6 VPNNET6B
|
||||
|
||||
eval "${GETPORT}"
|
||||
|
||||
function finish {
|
||||
set +e
|
||||
test -n "${CPID}" && kill ${CPID} >/dev/null 2>&1
|
||||
test -n "${PID}" && kill ${PID} >/dev/null 2>&1
|
||||
rm -f ${TMPFILE} ${CONFIG}
|
||||
rm -rf ${CONFIG_PER_USER_DIR}
|
||||
}
|
||||
|
||||
. `dirname $0`/ns.sh
|
||||
|
||||
# connect <user>: on success leaves the client running as $CPID with its
|
||||
# verbose output in $TMPFILE
|
||||
connect()
|
||||
{
|
||||
user=$1
|
||||
COOKIE=''
|
||||
eval `echo "$user" | ${CMDNS1} $OPENCONNECT -q $ADDRESS:$PORT -u $user --servercert=$SERVERCERT --authenticate`
|
||||
if [ -z "$COOKIE" ];then
|
||||
return 1
|
||||
fi
|
||||
|
||||
rm -f $TMPFILE
|
||||
echo "$user" | ${CMDNS1} $OPENCONNECT -v $ADDRESS:$PORT -u $user -C "$COOKIE" --servercert=$SERVERCERT --script=/bin/true >$TMPFILE 2>&1 &
|
||||
CPID=$!
|
||||
|
||||
sleep 3
|
||||
if ! grep "Established DTLS" $TMPFILE >/dev/null 2>&1;then
|
||||
kill $CPID 2>/dev/null
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# check_header <header> <expected value>
|
||||
check_header()
|
||||
{
|
||||
got=`grep "$1:" $TMPFILE | head -1 | sed 's/.*'"$1"': *//' | tr -d '\r'`
|
||||
if test "$got" != "$2";then
|
||||
kill $CPID
|
||||
fail $PID "FAIL: expected $1: $2, got '$got'"
|
||||
fi
|
||||
}
|
||||
|
||||
# check_server_ip <user> <expected server address>
|
||||
check_server_ip()
|
||||
{
|
||||
got=`${OCCTL} -s ${OCCTL_SOCKET} show user $1 2>&1 | grep "P-t-P IPv6:" | head -1 | sed 's/.*P-t-P IPv6: *//' | awk '{print $1}'`
|
||||
if test "$got" != "$2";then
|
||||
kill $CPID
|
||||
fail $PID "FAIL: expected P-t-P IPv6: $2, got '$got'"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Testing the server side IPv6 address and /127 point-to-point networks... "
|
||||
|
||||
update_config_dir user-config-ipv6-p2p
|
||||
update_config test-ipv6-p2p.config
|
||||
${CMDNS2} ${SERV} -d 1 -f -c "${CONFIG}" & PID=$!
|
||||
wait_server $PID
|
||||
|
||||
echo -n "Connecting with a /127 network... "
|
||||
connect test
|
||||
if test $? != 0;then
|
||||
cat $TMPFILE
|
||||
fail $PID "FAIL: expected a lease from ${VPNNET6_BASE}0/127, got no connection"
|
||||
fi
|
||||
check_header X-CSTP-Address-IP6 ${VPNNET6_BASE}1/128
|
||||
check_server_ip test ${VPNNET6_ADDR}
|
||||
kill $CPID
|
||||
echo ok
|
||||
|
||||
echo -n "Connecting with the server address of a /127 as explicit IP... "
|
||||
connect test2
|
||||
if test $? = 0;then
|
||||
kill $CPID
|
||||
fail $PID "FAIL: expected rejection of ${VPNNET6_BASE}2 (server address), got a connection"
|
||||
fi
|
||||
echo ok
|
||||
|
||||
echo -n "Connecting with network + 1 of a /112 as explicit IP... "
|
||||
connect test3
|
||||
if test $? != 0;then
|
||||
cat $TMPFILE
|
||||
fail $PID "FAIL: expected a connection with ${VPNNET6B_BASE}1, got none"
|
||||
fi
|
||||
check_header X-CSTP-Address-IP6 ${VPNNET6B_BASE}1/128
|
||||
check_server_ip test3 ${VPNNET6B_ADDR}
|
||||
kill $CPID
|
||||
echo ok
|
||||
|
||||
kill $PID
|
||||
wait
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user