@@ -778,6 +778,7 @@
target_sources(test_ssl PRIVATE
tests/unit_tests/openvpn/mock_management.c
+ tests/unit_tests/openvpn/mock_signal.c
tests/unit_tests/openvpn/mock_ssl_dependencies.c
tests/unit_tests/openvpn/mock_win32_execve.c
src/openvpn/argv.c
@@ -794,6 +795,7 @@
src/openvpn/otime.c
src/openvpn/packet_id.c
src/openvpn/run_command.c
+ src/openvpn/socket_util.c
src/openvpn/ssl_mbedtls.c
src/openvpn/ssl_openssl.c
src/openvpn/ssl_util.c
@@ -802,6 +804,7 @@
src/openvpn/xkey_helper.c
src/openvpn/xkey_provider.c
)
+ target_link_libraries(test_ssl PUBLIC ${RESOLV_LIBRARIES})
target_sources(test_mbuf PRIVATE
tests/unit_tests/openvpn/mock_get_random.c
@@ -811,10 +814,15 @@
target_sources(test_misc PRIVATE
tests/unit_tests/openvpn/mock_get_random.c
+ tests/unit_tests/openvpn/mock_management.c
+ tests/unit_tests/openvpn/mock_setenv.c
+ tests/unit_tests/openvpn/mock_signal.c
src/openvpn/options_util.c
+ src/openvpn/socket_util.c
src/openvpn/ssl_util.c
src/openvpn/list.c
)
+ target_link_libraries(test_misc PUBLIC ${RESOLV_LIBRARIES})
target_sources(test_ncp PRIVATE
src/openvpn/crypto_epoch.c
@@ -829,9 +837,14 @@
target_sources(test_options_parse PRIVATE
tests/unit_tests/openvpn/mock_get_random.c
+ tests/unit_tests/openvpn/mock_management.c
+ tests/unit_tests/openvpn/mock_setenv.c
+ tests/unit_tests/openvpn/mock_signal.c
src/openvpn/options_parse.c
src/openvpn/options_util.c
+ src/openvpn/socket_util.c
)
+ target_link_libraries(test_options_parse PUBLIC ${RESOLV_LIBRARIES})
target_sources(test_packet_id PRIVATE
tests/unit_tests/openvpn/mock_get_random.c
@@ -888,10 +901,15 @@
target_sources(test_push_update_msg PRIVATE
tests/unit_tests/openvpn/mock_msg.c
tests/unit_tests/openvpn/mock_get_random.c
+ tests/unit_tests/openvpn/mock_management.c
+ tests/unit_tests/openvpn/mock_setenv.c
+ tests/unit_tests/openvpn/mock_signal.c
src/openvpn/options_util.c
src/openvpn/otime.c
+ src/openvpn/socket_util.c
src/openvpn/list.c
)
+ target_link_libraries(test_push_update_msg PUBLIC ${RESOLV_LIBRARIES})
target_sources(test_argv PRIVATE
tests/unit_tests/openvpn/mock_get_random.c
@@ -132,21 +132,23 @@
ifconfig settings pushed to the client would create an IP numbering
conflict.
- Valid syntax:
+ Valid syntaxes:
::
client-nat snat|dnat network netmask alias
+ client-nat snat|dnat network/bits alias
Examples:
::
client-nat snat 192.168.0.0 255.255.0.0 10.64.0.0
- client-nat dnat 10.64.0.0 255.255.0.0 192.168.0.0
+ client-nat dnat 10.64.0.0/16 192.168.0.0
- ``network`` and ``netmask`` (for example :code:`192.168.0.0
- 255.255.0.0`) define the local view of a resource from the client
- perspective, while ``alias`` (for example :code:`10.64.0.0`) defines the
- remote view from the server perspective using the same netmask.
+ ``network netmask`` (for example :code:`192.168.0.0 255.255.0.0`) or
+ ``network/bits`` (for example :code:`192.168.0.0/16`) defines the local
+ view of a resource from the client perspective, while ``alias`` (for
+ example :code:`10.64.0.0`) defines the remote view from the server
+ perspective using the same netmask/bits.
Use :code:`snat` (source NAT) for resources owned by the client and
:code:`dnat` (destination NAT) for remote resources.
@@ -286,23 +286,28 @@
Push virtual IP endpoints for client tunnel, overriding the
``--ifconfig-pool`` dynamic allocation.
- Valid syntax:
+ Valid syntaxes:
::
- ifconfig-push local remote-netmask [alias]
+ ifconfig-push local remote [alias]
+ ifconfig-push local netmask [alias]
+ ifconfig-push local/bits [alias]
- The parameters ``local`` and ``remote-netmask`` are set according to the
- ``--ifconfig`` directive which you want to execute on the client machine
- to configure the remote end of the tunnel. Note that the parameters
- ``local`` and ``remote-netmask`` are from the perspective of the client,
- not the server. They may be DNS names rather than IP addresses, in which
- case they will be resolved on the server at the time of client
- connection.
+ The parameters ``local remote`` or ``local netmask`` are set according to
+ the ``--ifconfig`` directive which you want to execute on the client
+ machine to configure the remote end of the tunnel. As a compact form,
+ ``local/bits`` can be used, in which case the netmask is derived from
+ ``bits``. Note that the parameters ``local`` and ``remote`` are from the
+ perspective of the client, not the server. They may be DNS names rather
+ than IP addresses, in which case they will be resolved on the server at
+ the time of client connection.
The optional ``alias`` parameter may be used in cases where NAT causes
the client view of its local endpoint to differ from the server view. In
- this case ``local/remote-netmask`` will refer to the server view while
- ``alias/remote-netmask`` will refer to the client view.
+ this case ``local/netmask`` (or ``local/bits``) will refer to the server
+ view while ``alias/netmask`` (or ``alias/bits``) will refer to the client
+ view, so the server will actually push ``ifconfig alias remote/netmask`` to
+ the client.
This option must be associated with a specific client instance, which
means that it must be specified either in a client instance config file
@@ -328,6 +333,15 @@
by ``--ifconfig``, OpenVPN will install a /32 host route for the ``local``
IP address.
+--ifconfig-push-constraint args
+ Restrict static IPv4 addresses from ``--ifconfig-push`` to a specific subnet.
+
+ Valid syntaxes:
+ ::
+
+ ifconfig-push-constraint network netmask
+ ifconfig-push-constraint network/bits
+
--ifconfig-ipv6-push args
for ``--client-config-dir`` per-client static IPv6 interface
configuration, see ``--client-config-dir`` and ``--ifconfig-push`` for
@@ -367,13 +381,15 @@
applies only to IPv6.
--iroute args
- Generate an internal route to a specific client. The ``netmask``
- parameter, if omitted, defaults to :code:`255.255.255.255`.
+ Generate an internal route to a specific client. If both ``netmask`` and
+ ``bits`` are omitted, the default netmask is :code:`255.255.255.255`
+ (equivalent to :code:`/32` in CIDR notation).
- Valid syntax:
+ Valid syntaxes:
::
iroute network [netmask]
+ iroute network[/bits]
This directive can be used to route a fixed subnet from the server to a
particular client, regardless of where the client is connecting from.
@@ -555,10 +571,11 @@
optional :code:`nopool` flag is given, no dynamic IP address pool will
prepared for VPN clients.
- Valid syntax:
+ Valid syntaxes:
::
server network netmask [nopool]
+ server network/bits [nopool]
For example, ``--server 10.8.0.0 255.255.255.0`` expands as follows:
::
@@ -597,6 +614,7 @@
::
server-bridge gateway netmask pool-start-IP pool-end-IP
+ server-bridge gateway/bits pool-start-IP pool-end-IP
server-bridge [nogw]
If ``--server-bridge`` is used without any parameters, it will enable a
@@ -223,19 +223,28 @@
Android 10 or later.
--ifconfig args
+ Valid syntaxes:
+ ::
+
+ ifconfig local remote
+ ifconfig local netmask
+ ifconfig local/bits
+
Set TUN/TAP adapter parameters. It requires the *IP address* of the local
VPN endpoint. For TUN devices in point-to-point mode, the next argument
must be the VPN IP address of the remote VPN endpoint. For TAP devices,
or TUN devices used with ``--topology subnet``, the second argument
is the subnet mask of the virtual network segment which is being created
- or connected to.
+ or connected to. In this netmask case, ``local/bits`` may be used as a
+ compact form.
For TUN devices, which facilitate virtual point-to-point IP connections
(when used in ``--topology net30`` or ``p2p`` mode), the proper usage of
``--ifconfig`` is to use two private IP addresses which are not a member
- of any existing subnet which is in use. The IP addresses may be
+ of any existing subnet which is in use. In this mode, use the explicit
+ ``local remote`` form (CIDR form is not applicable). The IP addresses may be
consecutive and should have their order reversed on the remote peer.
- After the VPN is established, by pinging ``rn``, you will be pinging
+ After the VPN is established, by pinging ``remote``, you will be pinging
across the VPN.
For TAP devices, which provide the ability to create virtual ethernet
@@ -267,6 +276,9 @@
# tun/tap device in subnet mode
ifconfig 10.8.0.2 255.255.255.0
+ # equivalent subnet-mode shorthand
+ ifconfig 10.8.0.2/24
+
--ifconfig-ipv6 args
Configure an IPv6 address on the *tun* device.
@@ -405,17 +417,16 @@
Valid syntaxes:
::
- route network/IP
- route network/IP netmask
- route network/IP netmask gateway
- route network/IP netmask gateway metric
+ route network [netmask] [gateway] [metric]
+ route ipv4addr [netmask] [gateway] [metric]
+ route ipv4addr[/bits] [gateway] [metric]
This option is intended as a convenience proxy for the ``route``\(8)
shell command, while at the same time providing portable semantics
across OpenVPN's platform space.
- ``netmask``
- defaults to :code:`255.255.255.255` when not given
+ ``netmask`` (or ``bits``)
+ defaults to :code:`255.255.255.255` (or :code:`/32`) when not given
``gateway``
default taken from ``--route-gateway`` or the second
@@ -431,6 +442,9 @@
DNS or :code:`/etc/hosts` file resolvable name, or as one of three special
keywords:
+ The ``ipv4addr`` parameter may be specified using CIDR notation, thus
+ omitting the ``netmask`` parameter.
+
:code:`vpn_gateway`
The remote VPN endpoint address (derived either from
``--route-gateway`` or the second parameter to ``--ifconfig``
@@ -208,6 +208,8 @@
" addresses outside of the subnets used by either peer.\n"
" TAP: configure device to use IP address l as a local\n"
" endpoint and rn as a subnet mask.\n"
+ " In netmask mode (TAP, or TUN with --topology subnet),\n"
+ " l may be specified as l/bits.\n"
"--ifconfig-ipv6 l r : configure device to use IPv6 address l as local\n"
" endpoint (as a /64) and r as remote endpoint\n"
"--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
@@ -221,6 +223,7 @@
"--route network [netmask] [gateway] [metric] :\n"
" Add route to routing table after connection\n"
" is established. Multiple routes can be specified.\n"
+ " network netmask can also be specified as network/bits.\n"
" netmask default: 255.255.255.255\n"
" gateway default: taken from --route-gateway or --ifconfig\n"
" Specify default by leaving blank or setting to \"default\".\n"
@@ -259,6 +262,7 @@
" (Server) Instead of forwarding IPv6 packets send\n"
" ICMPv6 host unreachable packets to the client.\n"
"--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
+ " network netmask can also be specified as network/bits.\n"
"--push-peer-info : (client only) push client info to server.\n"
"--setenv name value : Set a custom environmental variable to pass to script.\n"
"--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to allow\n"
@@ -425,10 +429,12 @@
"--vlan-pvid v : Sets the Port VLAN Identifier. Defaults to 1.\n"
"\n"
"Multi-Client Server options (when --mode server is used):\n"
- "--server network netmask : Helper option to easily configure server mode.\n"
+ "--server network netmask [nopool] : Helper option to easily configure server mode.\n"
+ " network netmask can also be specified as network/bits.\n"
"--server-ipv6 network/bits : Configure IPv6 server mode.\n"
"--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
" easily configure ethernet bridging server mode.\n"
+ " IP netmask can also be specified as IP/bits.\n"
"--push \"option\" : Push a config file option back to the peer for remote\n"
" execution. Peer must specify --pull in its config file.\n"
"--push-reset : Don't inherit global push list for specific\n"
@@ -442,13 +448,15 @@
" If seconds=0, file will be treated as read-only.\n"
"--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
" to be dynamically allocated to connecting clients.\n"
- "--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
+ "--ifconfig-push local remote-netmask [alias] : Push an ifconfig option to remote,\n"
" overrides --ifconfig-pool dynamic allocation.\n"
+ " local remote-netmask can also be specified as local/bits.\n"
" Only valid in a client-specific config file.\n"
"--ifconfig-ipv6-push local/bits remote : Push an ifconfig-ipv6 option to\n"
" remote, overrides --ifconfig-ipv6-pool allocation.\n"
" Only valid in a client-specific config file.\n"
"--iroute network [netmask] : Route subnet to client.\n"
+ " network netmask can also be specified as network/bits.\n"
"--iroute-ipv6 network/bits : Route IPv6 subnet to client.\n"
" Sets up internal routes only.\n"
" Only valid in a client-specific config file.\n"
@@ -5365,6 +5373,28 @@
return true;
}
+static bool
+ipv4_cidr_parms_checked(char *p[], int network_idx, int max_idx, char *normalized[],
+ const char *cidr_label, const msglvl_t msglevel, struct gc_arena *gc)
+{
+ const int res = convert_ipv4_cidr_parms(p, network_idx, max_idx, normalized, gc);
+ if (res < 0)
+ {
+ msg(msglevel, "%s parameter %s '%s' has invalid CIDR notation",
+ p[0], cidr_label, p[network_idx]);
+ return false;
+ }
+
+ if (res == 1 && p[max_idx])
+ {
+ msg(msglevel, "%s parameter has too many arguments when using CIDR %s",
+ p[0], cidr_label);
+ return false;
+ }
+
+ return true;
+}
+
void
update_option(struct context *c, struct options *options, char *p[], bool is_inline,
const char *file, int line, const int level, const msglvl_t msglevel,
@@ -5378,8 +5408,15 @@
{
if (!(options->push_update_options_found & OPT_P_U_ROUTE))
{
+ char *route_parms[MAX_PARMS + 1] = { 0 };
+
VERIFY_PERMISSION(OPT_P_ROUTE);
- if (!check_route_option(options, p, msglevel, pull_mode))
+ if (!ipv4_cidr_parms_checked(p, 1, 4, route_parms, "network/IP", msglevel, &options->gc))
+ {
+ goto err;
+ }
+
+ if (!check_route_option(options, route_parms, msglevel, pull_mode))
{
goto err;
}
@@ -5899,18 +5936,32 @@
iproute_path = p[1];
}
#endif
- else if (streq(p[0], "ifconfig") && p[1] && p[2] && !p[3])
+ else if (streq(p[0], "ifconfig") && p[1] && !p[3])
{
+ char *ifconfig_parms[MAX_PARMS + 1] = { 0 };
+
VERIFY_PERMISSION(OPT_P_UP);
- if (ip_or_dns_addr_safe(p[1], options->allow_pull_fqdn)
- && ip_or_dns_addr_safe(p[2], options->allow_pull_fqdn)) /* FQDN -- may be DNS name */
+ if (!ipv4_cidr_parms_checked(p, 1, 2, ifconfig_parms, "local/IP", msglevel, &options->gc))
{
- options->ifconfig_local = p[1];
- options->ifconfig_remote_netmask = p[2];
+ goto err;
+ }
+ if (!ifconfig_parms[2])
+ {
+ msg(msglevel, "--ifconfig requires 'local remote/netmask' or 'local/bits'");
+ goto err;
+ }
+
+ if (ip_or_dns_addr_safe(ifconfig_parms[1], options->allow_pull_fqdn)
+ && ip_or_dns_addr_safe(ifconfig_parms[2],
+ options->allow_pull_fqdn)) /* FQDN -- may be DNS name */
+ {
+ options->ifconfig_local = ifconfig_parms[1];
+ options->ifconfig_remote_netmask = ifconfig_parms[2];
}
else
{
- msg(msglevel, "ifconfig parms '%s' and '%s' must be valid addresses", p[1], p[2]);
+ msg(msglevel, "ifconfig parms '%s' and '%s' must be valid addresses",
+ ifconfig_parms[1], ifconfig_parms[2]);
goto err;
}
}
@@ -6804,11 +6855,24 @@
VERIFY_PERMISSION(OPT_P_PERSIST_IP);
options->persist_remote_ip = true;
}
- else if (streq(p[0], "client-nat") && p[1] && p[2] && p[3] && p[4] && !p[5])
+ else if (streq(p[0], "client-nat") && p[1] && p[2] && !p[5])
{
+ char *client_nat_parms[MAX_PARMS + 1] = { 0 };
+
VERIFY_PERMISSION(OPT_P_ROUTE);
+ if (!ipv4_cidr_parms_checked(p, 2, 4, client_nat_parms, "network/IP", msglevel, &options->gc))
+ {
+ goto err;
+ }
+ if (!client_nat_parms[3] || !client_nat_parms[4])
+ {
+ msg(msglevel, "--client-nat requires 'snat|dnat network netmask alias' or "
+ "'snat|dnat network/bits alias'");
+ goto err;
+ }
cnol_check_alloc(options);
- add_client_nat_to_option_list(options->client_nat, p[1], p[2], p[3], p[4], msglevel);
+ add_client_nat_to_option_list(options->client_nat, client_nat_parms[1], client_nat_parms[2],
+ client_nat_parms[3], client_nat_parms[4], msglevel);
}
else if (streq(p[0], "route-table") && p[1] && !p[2])
{
@@ -6821,11 +6885,19 @@
else if (streq(p[0], "route") && p[1] && !p[5])
{
VERIFY_PERMISSION(OPT_P_ROUTE);
- if (!check_route_option(options, p, msglevel, pull_mode))
+
+ char *route_parms[MAX_PARMS + 1] = { 0 };
+ if (!ipv4_cidr_parms_checked(p, 1, 4, route_parms, "network/IP", msglevel, &options->gc))
{
goto err;
}
- add_route_to_option_list(options->routes, p[1], p[2], p[3], p[4],
+
+ if (!check_route_option(options, route_parms, msglevel, pull_mode))
+ {
+ goto err;
+ }
+ add_route_to_option_list(options->routes, route_parms[1], route_parms[2], route_parms[3],
+ route_parms[4],
options->route_default_table_id);
}
else if (streq(p[0], "route-ipv6") && p[1] && !p[4])
@@ -7145,15 +7217,26 @@
VERIFY_PERMISSION(OPT_P_GENERAL);
options->occ = false;
}
- else if (streq(p[0], "server") && p[1] && p[2] && !p[4])
+ else if (streq(p[0], "server") && p[1] && !p[4])
{
- const int lev = M_WARN;
+ const msglvl_t lev = M_WARN;
bool error = false;
in_addr_t network, netmask;
+ char *server_parms[MAX_PARMS + 1] = { 0 };
VERIFY_PERMISSION(OPT_P_GENERAL);
- network = get_ip_addr(p[1], lev, &error);
- netmask = get_ip_addr(p[2], lev, &error);
+ if (!ipv4_cidr_parms_checked(p, 1, 3, server_parms, "network/IP", msglevel, &options->gc))
+ {
+ goto err;
+ }
+ if (!server_parms[2])
+ {
+ msg(msglevel, "--server requires 'network netmask [nopool]' or 'network/bits [nopool]'");
+ goto err;
+ }
+
+ network = get_ip_addr(server_parms[1], lev, &error);
+ netmask = get_ip_addr(server_parms[2], lev, &error);
if (error || !network || !netmask)
{
msg(msglevel, "error parsing --server parameters");
@@ -7163,22 +7246,23 @@
options->server_network = network;
options->server_netmask = netmask;
- if (p[3])
+ if (server_parms[3])
{
- if (streq(p[3], "nopool"))
+ if (streq(server_parms[3], "nopool"))
{
options->server_flags |= SF_NOPOOL;
}
else
{
- msg(msglevel, "error parsing --server: %s is not a recognized flag", p[3]);
+ msg(msglevel, "error parsing --server: %s is not a recognized flag",
+ server_parms[3]);
goto err;
}
}
}
else if (streq(p[0], "server-ipv6") && p[1] && !p[2])
{
- const int lev = M_WARN;
+ const msglvl_t lev = M_WARN;
struct in6_addr network;
unsigned int netbits = 0;
@@ -7199,17 +7283,29 @@
options->server_network_ipv6 = network;
options->server_netbits_ipv6 = netbits;
}
- else if (streq(p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4] && !p[5])
+ else if (streq(p[0], "server-bridge") && p[1] && p[2] && p[3] && !p[5])
{
- const int lev = M_WARN;
+ const msglvl_t lev = M_WARN;
bool error = false;
in_addr_t ip, netmask, pool_start, pool_end;
+ char *server_bridge_parms[MAX_PARMS + 1] = { 0 };
VERIFY_PERMISSION(OPT_P_GENERAL);
- ip = get_ip_addr(p[1], lev, &error);
- netmask = get_ip_addr(p[2], lev, &error);
- pool_start = get_ip_addr(p[3], lev, &error);
- pool_end = get_ip_addr(p[4], lev, &error);
+ if (!ipv4_cidr_parms_checked(p, 1, 4, server_bridge_parms, "gateway/IP", msglevel, &options->gc))
+ {
+ goto err;
+ }
+ if (!server_bridge_parms[4])
+ {
+ msg(msglevel, "--server-bridge requires 'gateway netmask pool-start-IP pool-end-IP' "
+ "or 'gateway/bits pool-start-IP pool-end-IP'");
+ goto err;
+ }
+
+ ip = get_ip_addr(server_bridge_parms[1], lev, &error);
+ netmask = get_ip_addr(server_bridge_parms[2], lev, &error);
+ pool_start = get_ip_addr(server_bridge_parms[3], lev, &error);
+ pool_end = get_ip_addr(server_bridge_parms[4], lev, &error);
if (error || !ip || !netmask || !pool_start || !pool_end)
{
msg(msglevel, "error parsing --server-bridge parameters");
@@ -7250,7 +7346,7 @@
}
else if (streq(p[0], "ifconfig-pool") && p[1] && p[2] && !p[4])
{
- const int lev = M_WARN;
+ const msglvl_t lev = M_WARN;
bool error = false;
in_addr_t start, end, netmask = 0;
@@ -7290,7 +7386,7 @@
}
else if (streq(p[0], "ifconfig-ipv6-pool") && p[1] && !p[2])
{
- const int lev = M_WARN;
+ const msglvl_t lev = M_WARN;
struct in6_addr network;
unsigned int netbits = 0;
@@ -7556,30 +7652,51 @@
}
else if (streq(p[0], "iroute") && p[1] && !p[3])
{
+ char *iroute_parms[MAX_PARMS + 1] = { 0 };
+
VERIFY_PERMISSION(OPT_P_INSTANCE);
- option_iroute(options, p[1], p[2], msglevel);
+ if (!ipv4_cidr_parms_checked(p, 1, 2, iroute_parms, "network/IP", msglevel, &options->gc))
+ {
+ goto err;
+ }
+ option_iroute(options, iroute_parms[1], iroute_parms[2], msglevel);
}
else if (streq(p[0], "iroute-ipv6") && p[1] && !p[2])
{
VERIFY_PERMISSION(OPT_P_INSTANCE);
option_iroute_ipv6(options, p[1], msglevel);
}
- else if (streq(p[0], "ifconfig-push") && p[1] && p[2] && !p[4])
+ else if (streq(p[0], "ifconfig-push") && p[1] && !p[4])
{
in_addr_t local, remote_netmask;
+ char *ifconfig_push_parms[MAX_PARMS + 1] = { 0 };
VERIFY_PERMISSION(OPT_P_INSTANCE);
- local = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, p[1], 0, NULL, NULL);
- remote_netmask = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, p[2], 0, NULL, NULL);
+ if (!ipv4_cidr_parms_checked(p, 1, 3, ifconfig_push_parms, "local/IP", msglevel, &options->gc))
+ {
+ goto err;
+ }
+ if (!ifconfig_push_parms[2])
+ {
+ msg(msglevel, "--ifconfig-push requires 'local remote-netmask [alias]' or "
+ "'local/bits [alias]'");
+ goto err;
+ }
+
+ local = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, ifconfig_push_parms[1], 0, NULL,
+ NULL);
+ remote_netmask = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, ifconfig_push_parms[2], 0,
+ NULL, NULL);
if (local && remote_netmask)
{
options->push_ifconfig_defined = true;
options->push_ifconfig_local = local;
options->push_ifconfig_remote_netmask = remote_netmask;
- if (p[3])
+ if (ifconfig_push_parms[3])
{
options->push_ifconfig_local_alias =
- getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, p[3], 0, NULL, NULL);
+ getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, ifconfig_push_parms[3], 0,
+ NULL, NULL);
}
}
else
@@ -7588,13 +7705,26 @@
goto err;
}
}
- else if (streq(p[0], "ifconfig-push-constraint") && p[1] && p[2] && !p[3])
+ else if (streq(p[0], "ifconfig-push-constraint") && p[1] && !p[3])
{
in_addr_t network, netmask;
+ char *ifconfig_push_constraint_parms[MAX_PARMS + 1] = { 0 };
VERIFY_PERMISSION(OPT_P_GENERAL);
- network = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, p[1], 0, NULL, NULL);
- netmask = getaddr(GETADDR_HOST_ORDER, p[2], 0, NULL, NULL);
+ if (!ipv4_cidr_parms_checked(p, 1, 2, ifconfig_push_constraint_parms, "network/IP", msglevel,
+ &options->gc))
+ {
+ goto err;
+ }
+ if (!ifconfig_push_constraint_parms[2])
+ {
+ msg(msglevel, "--ifconfig-push-constraint requires 'network netmask' or 'network/bits'");
+ goto err;
+ }
+
+ network = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, ifconfig_push_constraint_parms[1],
+ 0, NULL, NULL);
+ netmask = getaddr(GETADDR_HOST_ORDER, ifconfig_push_constraint_parms[2], 0, NULL, NULL);
if (network && netmask)
{
options->push_ifconfig_constraint_defined = true;
@@ -30,6 +30,7 @@
#include "options_util.h"
#include "push.h"
+#include "socket_util.h"
const char *
parse_auth_failed_temp(struct options *o, const char *reason)
@@ -193,6 +194,96 @@
return true;
}
+static int
+ipv4_cidr_to_netmask(const char *in_cidr, const char *slash, const char **out_network,
+ const char **out_netmask, struct gc_arena *gc)
+{
+ ASSERT(in_cidr);
+ ASSERT(slash);
+ ASSERT(slash[0] == '/');
+
+ if (slash[1] == '\0')
+ {
+ return -EINVAL;
+ }
+
+ /* extract and validate the prefix value */
+ const char *prefix_str = slash + 1;
+ if (strspn(prefix_str, "0123456789") != strlen(prefix_str))
+ {
+ return -EINVAL;
+ }
+
+ errno = 0;
+ char *endptr = NULL;
+ const unsigned long prefix = strtoul(prefix_str, &endptr, 10);
+ if (errno != 0 || endptr == prefix_str || *endptr != '\0' || prefix > 32)
+ {
+ return -EINVAL;
+ }
+
+ const size_t slash_offset = (size_t)(slash - in_cidr);
+ if (slash_offset == 0)
+ {
+ return -EINVAL;
+ }
+
+ /* build the network string */
+ char *network_buf = gc_malloc(slash_offset + 1, true, gc);
+ memcpy(network_buf, in_cidr, slash_offset);
+ network_buf[slash_offset] = '\0';
+
+ /* build the netmask string */
+ const char *netmask_buf = print_in_addr_t(netbits_to_netmask((int)prefix), 0, gc);
+
+ *out_network = network_buf;
+ *out_netmask = netmask_buf;
+
+ return 0;
+}
+
+int
+convert_ipv4_cidr_parms(char *p[], int network_idx, int max_idx, char *normalized[],
+ struct gc_arena *gc)
+{
+ ASSERT(max_idx < MAX_PARMS);
+ ASSERT(max_idx >= (network_idx + 1));
+ ASSERT(network_idx >= 1);
+ ASSERT(p[network_idx]);
+
+ /* map normalized parameters to the user provided ones */
+ for (int i = 0; i <= max_idx && p[i]; ++i)
+ {
+ normalized[i] = p[i];
+ }
+
+ const char *slash = strchr(p[network_idx], '/');
+ if (!slash)
+ {
+ return 0;
+ }
+
+ const char *network = NULL;
+ const char *netmask = NULL;
+ const int err = ipv4_cidr_to_netmask(p[network_idx], slash, &network, &netmask, gc);
+ if (err)
+ {
+ return err;
+ }
+
+ /* insert the netmask and shift the next parameters */
+ normalized[network_idx] = (char *)network;
+ normalized[network_idx + 1] = (char *)netmask;
+ for (int src = network_idx + 1, dst = network_idx + 2; dst <= max_idx; ++src, ++dst)
+ {
+ normalized[dst] = p[src];
+ }
+ /* keep argv-style null termination to avoid stale tail entries */
+ normalized[max_idx + 1] = NULL;
+
+ return 1;
+}
+
static const char *updatable_options[] = { "block-ipv6", "block-outside-dns",
"dhcp-option", "dns",
"ifconfig", "ifconfig-ipv6",
@@ -108,4 +108,37 @@
*/
bool check_push_update_option_flags(char *line, int *i, unsigned int *flags);
+/**
+ * Convert option parameters whose first IPv4 parameter may be in CIDR notation.
+ *
+ * The input tokens are read from \p p and the normalized output tokens are
+ * written to \p normalized.
+ *
+ * When \p p[network_idx] is in CIDR form (for example, ``10.8.0.0/24``), this
+ * function:
+ * - splits the network and prefix length,
+ * - converts the prefix length to dotted-quad netmask,
+ * - writes ``network`` and ``netmask`` into ``normalized[network_idx]`` and
+ * ``normalized[network_idx + 1]``,
+ * - shifts remaining parameters one position to preserve legacy
+ * ``network netmask ...`` layout.
+ *
+ * When \p p[network_idx] is not CIDR, \p normalized receives \p p unchanged
+ * for the copied range (indices ``0..max_idx`` until ``NULL``), and no
+ * conversion is applied.
+ *
+ * @param p Input option tokens (argv-style, NULL-terminated).
+ * @param network_idx Index in \p p of the parameter that may contain CIDR.
+ * @param max_idx Maximum parameter index to normalize.
+ * @param normalized Output token array receiving normalized parameters
+ * (argv-style, NULL-terminated in CIDR case).
+ * @param gc GC arena used for converted string allocations.
+ *
+ * @return 0 when no CIDR notation was present and no conversion was needed.
+ * @return 1 when CIDR notation was present and conversion was applied.
+ * @return -EINVAL on malformed CIDR input.
+ */
+int convert_ipv4_cidr_parms(char *p[], int network_idx, int max_idx,
+ char *normalized[], struct gc_arena *gc);
+
#endif /* ifndef OPTIONS_UTIL_H_ */
@@ -98,10 +98,11 @@
ssl_testdriver_CFLAGS = \
-I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \
@TEST_CFLAGS@
-ssl_testdriver_LDFLAGS = @TEST_LDFLAGS@ $(OPTIONAL_CRYPTO_LIBS)
+ssl_testdriver_LDFLAGS = @TEST_LDFLAGS@ $(OPTIONAL_CRYPTO_LIBS) $(SOCKETS_LIBS)
ssl_testdriver_SOURCES = test_ssl.c \
mock_msg.c mock_msg.h test_common.h \
mock_management.c \
+ mock_signal.c \
mock_ssl_dependencies.c mock_win32_execve.c \
$(top_srcdir)/src/openvpn/argv.c \
$(top_srcdir)/src/openvpn/base64.c \
@@ -121,6 +122,7 @@
$(top_srcdir)/src/openvpn/packet_id.c \
$(top_srcdir)/src/openvpn/platform.c \
$(top_srcdir)/src/openvpn/run_command.c \
+ $(top_srcdir)/src/openvpn/socket_util.c \
$(top_srcdir)/src/openvpn/ssl_openssl.c \
$(top_srcdir)/src/openvpn/ssl_mbedtls.c \
$(top_srcdir)/src/openvpn/ssl_util.c \
@@ -221,13 +223,18 @@
endif
options_parse_testdriver_CFLAGS = -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat @TEST_CFLAGS@
-options_parse_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn
+options_parse_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn \
+ $(SOCKETS_LIBS)
options_parse_testdriver_SOURCES = test_options_parse.c \
mock_msg.c mock_msg.h test_common.h \
mock_get_random.c \
+ mock_management.c \
+ mock_setenv.c \
+ mock_signal.c \
$(top_srcdir)/src/openvpn/options_parse.c \
$(top_srcdir)/src/openvpn/options_util.c \
$(top_srcdir)/src/openvpn/buffer.c \
+ $(top_srcdir)/src/openvpn/socket_util.c \
$(top_srcdir)/src/openvpn/win32-util.c \
$(top_srcdir)/src/openvpn/platform.c
@@ -365,12 +372,16 @@
-I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \
-DSOURCEDIR=\"$(top_srcdir)\" @TEST_CFLAGS@
-misc_testdriver_LDFLAGS = @TEST_LDFLAGS@
+misc_testdriver_LDFLAGS = @TEST_LDFLAGS@ $(SOCKETS_LIBS)
misc_testdriver_SOURCES = test_misc.c \
mock_msg.c test_common.h \
mock_get_random.c \
+ mock_management.c \
+ mock_setenv.c \
+ mock_signal.c \
$(top_srcdir)/src/openvpn/buffer.c \
+ $(top_srcdir)/src/openvpn/socket_util.c \
$(top_srcdir)/src/openvpn/options_util.c \
$(top_srcdir)/src/openvpn/ssl_util.c \
$(top_srcdir)/src/openvpn/win32-util.c \
@@ -384,15 +395,20 @@
push_update_msg_testdriver_LDFLAGS = \
@TEST_LDFLAGS@ \
- -L$(top_srcdir)/src/openvpn
+ -L$(top_srcdir)/src/openvpn \
+ $(SOCKETS_LIBS)
push_update_msg_testdriver_SOURCES = test_push_update_msg.c \
mock_msg.c \
mock_get_random.c \
+ mock_management.c \
+ mock_setenv.c \
+ mock_signal.c \
$(top_srcdir)/src/openvpn/buffer.c \
$(top_srcdir)/src/openvpn/platform.c \
$(top_srcdir)/src/openvpn/options_util.c \
$(top_srcdir)/src/openvpn/otime.c \
+ $(top_srcdir)/src/openvpn/socket_util.c \
$(top_srcdir)/src/openvpn/list.c
socket_testdriver_CFLAGS = \
new file mode 100644
@@ -0,0 +1,39 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "env_set.h"
+
+void
+setenv_str(struct env_set *es, const char *name, const char *value)
+{
+}
+
+void
+setenv_int(struct env_set *es, const char *name, int value)
+{
+}
new file mode 100644
@@ -0,0 +1,47 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "socket.h"
+
+struct signal_info siginfo_static; /* GLOBAL */
+
+int
+signal_reset(struct signal_info *si, int signum)
+{
+ return signum;
+}
+
+#ifdef _WIN32
+struct win32_signal win32_signal; /* GLOBAL */
+
+int
+win32_signal_get(struct win32_signal *ws)
+{
+ return 0;
+}
+#endif
@@ -34,6 +34,7 @@
#include <cmocka.h>
#include "options.h"
+#include "options_util.h"
#include "test_common.h"
#include "mock_msg.h"
@@ -249,20 +250,9 @@
gc_init(&o.gc);
gc_init(&o.dns_options.gc);
- char *p_expect_someopt[MAX_PARMS];
- char *p_expect_otheropt[MAX_PARMS];
- char *p_expect_inlineopt[MAX_PARMS];
- CLEAR(p_expect_someopt);
- CLEAR(p_expect_otheropt);
- CLEAR(p_expect_inlineopt);
- p_expect_someopt[0] = "someopt";
- p_expect_someopt[1] = "parm1";
- p_expect_someopt[2] = "parm2";
- p_expect_otheropt[0] = "otheropt";
- p_expect_otheropt[1] = "1";
- p_expect_otheropt[2] = "2";
- p_expect_inlineopt[0] = "inlineopt";
- p_expect_inlineopt[1] = "some text\nother text\n";
+ char *p_expect_someopt[MAX_PARMS] = { "someopt", "parm1", "parm2", NULL };
+ char *p_expect_otheropt[MAX_PARMS] = { "otheropt", "1", "2", NULL };
+ char *p_expect_inlineopt[MAX_PARMS] = { "inlineopt", "some text\nother text\n", NULL };
/* basic test */
expect_function_call(add_option);
@@ -299,12 +289,209 @@
gc_free(&o.dns_options.gc);
}
+static void
+assert_tokens(char *actual[], const char *expected[], int max_idx)
+{
+ for (int i = 0; i <= max_idx + 1; ++i)
+ {
+ if (!expected[i])
+ {
+ assert_null(actual[i]);
+ continue;
+ }
+ assert_non_null(actual[i]);
+ assert_string_equal(actual[i], expected[i]);
+ }
+}
+
+static void
+test_convert_ipv4_cidr_parms_core(void **state)
+{
+ struct gc_arena gc = gc_new();
+ char *normalized[MAX_PARMS + 1] = { 0 };
+
+ /* non-CIDR input is returned unchanged */
+ char *legacy[MAX_PARMS + 1] = { "route", "10.8.0.0", "255.255.255.0", NULL };
+ const char *legacy_expected[] = { "route", "10.8.0.0", "255.255.255.0", NULL, NULL, NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(legacy, 1, 4, normalized, &gc), 0);
+ assert_tokens(normalized, legacy_expected, 4);
+
+ /* CIDR input is split and netmask is materialized */
+ CLEAR(normalized);
+ char *cidr[MAX_PARMS + 1] = { "route", "10.8.0.0/24", NULL };
+ const char *cidr_expected[] = { "route", "10.8.0.0", "255.255.255.0", NULL, NULL, NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(cidr, 1, 4, normalized, &gc), 1);
+ assert_tokens(normalized, cidr_expected, 4);
+
+ gc_free(&gc);
+}
+
+static void
+test_convert_ipv4_cidr_parms_coverage(void **state)
+{
+ struct gc_arena gc = gc_new();
+ char *normalized[MAX_PARMS + 1] = { 0 };
+
+ /* success paths */
+
+ /* route */
+ char *route[] = { "route", "10.1.2.0/24", "192.0.2.1", "7", NULL };
+ const char *route_expected[] = { "route", "10.1.2.0", "255.255.255.0", "192.0.2.1", "7",
+ NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route, 1, 4, normalized, &gc), 1);
+ assert_tokens(normalized, route_expected, 4);
+
+ /* ifconfig */
+ CLEAR(normalized);
+ char *ifconfig[] = { "ifconfig", "10.8.0.1/24", NULL };
+ const char *ifconfig_expected[] = { "ifconfig", "10.8.0.1", "255.255.255.0", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(ifconfig, 1, 2, normalized, &gc), 1);
+ assert_tokens(normalized, ifconfig_expected, 2);
+
+ /* server */
+ CLEAR(normalized);
+ char *server[] = { "server", "10.8.0.0/24", "nopool", NULL };
+ const char *server_expected[] = { "server", "10.8.0.0", "255.255.255.0", "nopool", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(server, 1, 3, normalized, &gc), 1);
+ assert_tokens(normalized, server_expected, 3);
+
+ /* server-bridge */
+ CLEAR(normalized);
+ char *server_bridge[] = { "server-bridge", "10.8.0.1/24", "10.8.0.10", "10.8.0.20", NULL };
+ const char *server_bridge_expected[] = { "server-bridge", "10.8.0.1", "255.255.255.0",
+ "10.8.0.10", "10.8.0.20", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(server_bridge, 1, 4, normalized, &gc), 1);
+ assert_tokens(normalized, server_bridge_expected, 4);
+
+ /* iroute */
+ CLEAR(normalized);
+ char *iroute[] = { "iroute", "172.16.0.0/16", NULL };
+ const char *iroute_expected[] = { "iroute", "172.16.0.0", "255.255.0.0", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(iroute, 1, 2, normalized, &gc), 1);
+ assert_tokens(normalized, iroute_expected, 2);
+
+ /* ifconfig-push */
+ CLEAR(normalized);
+ char *ifconfig_push[] = { "ifconfig-push", "10.8.0.6/24", "10.8.0.7", NULL };
+ const char *ifconfig_push_expected[] = { "ifconfig-push", "10.8.0.6", "255.255.255.0",
+ "10.8.0.7", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(ifconfig_push, 1, 3, normalized, &gc), 1);
+ assert_tokens(normalized, ifconfig_push_expected, 3);
+
+ /* ifconfig-push-constraint */
+ CLEAR(normalized);
+ char *ifconfig_push_constraint[] = { "ifconfig-push-constraint", "10.8.0.0/24", NULL };
+ const char *ifconfig_push_constraint_expected[] = { "ifconfig-push-constraint", "10.8.0.0",
+ "255.255.255.0", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(ifconfig_push_constraint, 1, 2, normalized, &gc),
+ 1);
+ assert_tokens(normalized, ifconfig_push_constraint_expected, 2);
+
+ /* client-nat */
+ CLEAR(normalized);
+ char *client_nat[] = { "client-nat", "snat", "192.168.0.0/16", "10.0.0.0", NULL };
+ const char *client_nat_expected[] = { "client-nat", "snat", "192.168.0.0", "255.255.0.0",
+ "10.0.0.0", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(client_nat, 2, 4, normalized, &gc), 1);
+ assert_tokens(normalized, client_nat_expected, 4);
+
+ /* CIDR prefix boundaries */
+
+ /* /0 */
+ CLEAR(normalized);
+ char *route_default[MAX_PARMS + 1] = { "route", "0.0.0.0/0", NULL };
+ const char *route_default_expected[] = { "route", "0.0.0.0", "0.0.0.0", NULL, NULL, NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_default, 1, 4, normalized, &gc), 1);
+ assert_tokens(normalized, route_default_expected, 4);
+
+ /* /32 */
+ CLEAR(normalized);
+ char *route_host[MAX_PARMS + 1] = { "route", "198.51.100.42/32", NULL };
+ const char *route_host_expected[] = { "route", "198.51.100.42", "255.255.255.255", NULL,
+ NULL, NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_host, 1, 4, normalized, &gc), 1);
+ assert_tokens(normalized, route_host_expected, 4);
+
+ /* CIDR with DNS-style network token */
+ CLEAR(normalized);
+ char *route_dns_cidr[] = { "route", "vpn.example/24", "192.0.2.1", NULL };
+ const char *route_dns_cidr_expected[] = { "route", "vpn.example", "255.255.255.0",
+ "192.0.2.1", NULL, NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_dns_cidr, 1, 4, normalized, &gc), 1);
+ assert_tokens(normalized, route_dns_cidr_expected, 4);
+
+ /* non-CIDR passthrough (DNS-style network token) */
+ CLEAR(normalized);
+ char *route_dns[] = { "route", "vpn.example", "255.255.255.0", "192.0.2.1", NULL };
+ const char *route_dns_expected[] = { "route", "vpn.example", "255.255.255.0", "192.0.2.1",
+ NULL, NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_dns, 1, 4, normalized, &gc), 0);
+ assert_tokens(normalized, route_dns_expected, 4);
+
+ /* varied network_idx/max_idx shapes */
+
+ CLEAR(normalized);
+ char *generic_shift[] = { "opt", "x", "y", "10.9.0.0/25", "tail", NULL };
+ const char *generic_shift_expected[] = { "opt", "x", "y", "10.9.0.0", "255.255.255.128",
+ "tail", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(generic_shift, 3, 5, normalized, &gc), 1);
+ assert_tokens(normalized, generic_shift_expected, 5);
+
+ CLEAR(normalized);
+ char *near_limit[MAX_PARMS + 1] = { 0 };
+ near_limit[0] = "opt";
+ near_limit[MAX_PARMS - 2] = "203.0.113.0/24";
+ assert_int_equal(
+ convert_ipv4_cidr_parms(near_limit, MAX_PARMS - 2, MAX_PARMS - 1, normalized, &gc), 1);
+ assert_string_equal(normalized[0], "opt");
+ assert_string_equal(normalized[MAX_PARMS - 2], "203.0.113.0");
+ assert_string_equal(normalized[MAX_PARMS - 1], "255.255.255.0");
+ assert_null(normalized[MAX_PARMS]);
+
+ /* error paths */
+
+ char *route_invalid[] = { "route", "10.1.2.0/33", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid, 1, 4, normalized, &gc), -EINVAL);
+ char *route_invalid_negative_prefix[] = { "route", "10.1.2.0/-1", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_negative_prefix, 1, 4, normalized,
+ &gc),
+ -EINVAL);
+ char *route_invalid_plus_prefix[] = { "route", "10.1.2.0/+24", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_plus_prefix, 1, 4, normalized, &gc),
+ -EINVAL);
+ char *route_invalid_malformed_slash[] = { "route", "10.1.2.0//24", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_malformed_slash, 1, 4, normalized,
+ &gc),
+ -EINVAL);
+ char *route_invalid_overflow_prefix[] = { "route", "10.1.2.0/999999999999999999999", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_overflow_prefix, 1, 4, normalized,
+ &gc),
+ -EINVAL);
+ char *route_invalid_empty_network[] = { "route", "/24", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_empty_network, 1, 4, normalized, &gc),
+ -EINVAL);
+ char *route_invalid_no_prefix[] = { "route", "10.1.2.0/", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_no_prefix, 1, 4, normalized, &gc),
+ -EINVAL);
+ char *route_invalid_alpha_prefix[] = { "route", "10.1.2.0/2x", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(route_invalid_alpha_prefix, 1, 4, normalized, &gc),
+ -EINVAL);
+
+ char *client_nat_invalid[] = { "client-nat", "snat", "192.168.0.0/35", "10.0.0.0", NULL };
+ assert_int_equal(convert_ipv4_cidr_parms(client_nat_invalid, 2, 4, normalized, &gc),
+ -EINVAL);
+
+ gc_free(&gc);
+}
+
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_parse_line),
cmocka_unit_test(test_read_config),
+ cmocka_unit_test(test_convert_ipv4_cidr_parms_core),
+ cmocka_unit_test(test_convert_ipv4_cidr_parms_coverage),
};
return cmocka_run_group_tests_name("options_parse", tests, NULL, NULL);
@@ -51,8 +51,6 @@
/* Mock function to be allowed to include win32.c which is required for
* getting the temp directory */
#ifdef _WIN32
-struct signal_info siginfo_static; /* GLOBAL */
-
const char *
strerror_win32(DWORD errnum, struct gc_arena *gc)
{