Skip to content

Commit c598efc

Browse files
mrbffcron2
authored andcommitted
PUSH_UPDATE message sender: enabling the server to send PUSH_UPDATE control messages
Using the management interface you can now target one or more clients (via broadcast or via cid) and send a PUSH_UPDATE control message to update some options. See doc/management-notes.txt for details. Change-Id: Ie82bcc7a8e583de9156b185d71d1a323ed8df3fc Signed-off-by: Marco Baffo <marco@mandelbit.com> Acked-by: Gert Doering <gert@greenie.muc.de> Message-Id: <20250903164826.13284-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg32807.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
1 parent 80c5cde commit c598efc

13 files changed

Lines changed: 741 additions & 18 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -874,9 +874,10 @@ if (BUILD_TESTING)
874874
target_sources(test_push_update_msg PRIVATE
875875
tests/unit_tests/openvpn/mock_msg.c
876876
tests/unit_tests/openvpn/mock_get_random.c
877-
src/openvpn/push_util.c
878-
src/openvpn/options_util.c
879-
src/openvpn/otime.c
877+
src/openvpn/push_util.c
878+
src/openvpn/options_util.c
879+
src/openvpn/otime.c
880+
src/openvpn/list.c
880881
)
881882

882883
if (TARGET test_argv)

doc/management-notes.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,35 @@ This capability is intended to allow the use of certificates
10281028
stored outside of the filesystem (e.g. in Mac OS X Keychain)
10291029
with OpenVPN via the management interface.
10301030

1031+
COMMAND -- push-update-broad (OpenVPN 2.7 or higher)
1032+
----------------------------------------------------
1033+
Send a message to every connected client to update options at runtime.
1034+
The updatable options are: "block-ipv6", "block-outside-dns", "dhcp-option",
1035+
"dns", "ifconfig", "ifconfig-ipv6", "redirect-gateway", "redirect-private",
1036+
"route", "route-gateway", "route-ipv6", "route-metric", "topology",
1037+
"tun-mtu", "keepalive". When a valid option is pushed, the receiving client will
1038+
delete every previous value and set new value, so the update of the option will
1039+
not be incremental even when theoretically possible (ex. with "redirect-gateway").
1040+
The '-' symbol in front of an option means the option should be removed.
1041+
When an option is used with '-', it cannot take any parameter.
1042+
The '?' symbol in front of an option means the option's update is optional
1043+
so if the client do not support it, that option will just be ignored without
1044+
making fail the entire command. The '-' and '?' symbols can be used together.
1045+
1046+
Option Format Ex.
1047+
`-?option`, `-option`, `?option parameters` are valid formats,
1048+
`?-option` is not a valid format.
1049+
1050+
Example
1051+
push-update-broad "route 10.10.10.1 255.255.255.255, -dns, ?tun-mtu 1400"
1052+
1053+
COMMAND -- push-update-cid (OpenVPN 2.7 or higher)
1054+
----------------------------------------------------
1055+
Same as push-update-broad but you must target a single client using client id.
1056+
1057+
Example
1058+
push-update-cid 42 "route 10.10.10.1 255.255.255.255, -dns, ?tun-mtu 1400"
1059+
10311060
OUTPUT FORMAT
10321061
-------------
10331062

src/openvpn/manage.c

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "manage.h"
4242
#include "openvpn.h"
4343
#include "dco.h"
44+
#include "push.h"
4445
#include "multi.h"
4546

4647
#include "memdbg.h"
@@ -132,8 +133,10 @@ man_help(void)
132133
msg(M_CLIENT, "test n : Produce n lines of output for testing/debugging.");
133134
msg(M_CLIENT, "username type u : Enter username u for a queried OpenVPN username.");
134135
msg(M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent.");
135-
msg(M_CLIENT,
136-
"version [n] : Set client's version to n or show current version of daemon.");
136+
msg(M_CLIENT, "version [n] : Set client's version to n or show current version of daemon.");
137+
msg(M_CLIENT, "push-update-broad options : Broadcast a message to update the specified options.");
138+
msg(M_CLIENT, " Ex. push-update-broad \"route something, -dns\"");
139+
msg(M_CLIENT, "push-update-cid CID options : Send an update message to the client identified by CID.");
137140
msg(M_CLIENT, "END");
138141
}
139142

@@ -1332,6 +1335,48 @@ set_client_version(struct management *man, const char *version)
13321335
}
13331336
}
13341337

1338+
static void
1339+
man_push_update(struct management *man, const char **p, const push_update_type type)
1340+
{
1341+
bool status = false;
1342+
1343+
if (type == UPT_BROADCAST)
1344+
{
1345+
if (!man->persist.callback.push_update_broadcast)
1346+
{
1347+
man_command_unsupported("push-update-broad");
1348+
return;
1349+
}
1350+
1351+
status = (*man->persist.callback.push_update_broadcast)(man->persist.callback.arg, p[1]);
1352+
}
1353+
else if (type == UPT_BY_CID)
1354+
{
1355+
if (!man->persist.callback.push_update_by_cid)
1356+
{
1357+
man_command_unsupported("push-update-cid");
1358+
return;
1359+
}
1360+
1361+
unsigned long cid = 0;
1362+
1363+
if (!parse_cid(p[1], &cid))
1364+
{
1365+
msg(M_CLIENT, "ERROR: push-update-cid fail during cid parsing");
1366+
return;
1367+
}
1368+
1369+
status = (*man->persist.callback.push_update_by_cid)(man->persist.callback.arg, cid, p[2]);
1370+
}
1371+
1372+
if (status)
1373+
{
1374+
msg(M_CLIENT, "SUCCESS: push-update command succeeded");
1375+
return;
1376+
}
1377+
msg(M_CLIENT, "ERROR: push-update command failed");
1378+
}
1379+
13351380
static void
13361381
man_dispatch_command(struct management *man, struct status_output *so, const char **p,
13371382
const int nparms)
@@ -1655,6 +1700,20 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha
16551700
man_remote(man, p);
16561701
}
16571702
}
1703+
else if (streq(p[0], "push-update-broad"))
1704+
{
1705+
if (man_need(man, p, 1, 0))
1706+
{
1707+
man_push_update(man, p, UPT_BROADCAST);
1708+
}
1709+
}
1710+
else if (streq(p[0], "push-update-cid"))
1711+
{
1712+
if (man_need(man, p, 2, 0))
1713+
{
1714+
man_push_update(man, p, UPT_BY_CID);
1715+
}
1716+
}
16581717
#if 1
16591718
else if (streq(p[0], "test"))
16601719
{

src/openvpn/manage.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
#define MF_EXTERNAL_KEY_PSSPAD (1u << 16)
4444
#define MF_EXTERNAL_KEY_DIGEST (1u << 17)
4545

46-
4746
#ifdef ENABLE_MANAGEMENT
4847

4948
#include "misc.h"
@@ -197,6 +196,8 @@ struct management_callback
197196
#endif
198197
unsigned int (*remote_entry_count)(void *arg);
199198
bool (*remote_entry_get)(void *arg, unsigned int index, char **remote);
199+
bool (*push_update_broadcast)(void *arg, const char *options);
200+
bool (*push_update_by_cid)(void *arg, unsigned long cid, const char *options);
200201
};
201202

202203
/*

src/openvpn/multi.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3996,7 +3996,7 @@ management_delete_event(void *arg, event_t event)
39963996
}
39973997
}
39983998

3999-
static struct multi_instance *
3999+
struct multi_instance *
40004000
lookup_by_cid(struct multi_context *m, const unsigned long cid)
40014001
{
40024002
if (m)
@@ -4137,6 +4137,8 @@ init_management_callback_multi(struct multi_context *m)
41374137
cb.client_auth = management_client_auth;
41384138
cb.client_pending_auth = management_client_pending_auth;
41394139
cb.get_peer_info = management_get_peer_info;
4140+
cb.push_update_broadcast = management_callback_send_push_update_broadcast;
4141+
cb.push_update_by_cid = management_callback_send_push_update_by_cid;
41404142
management_set_callback(management, &cb);
41414143
}
41424144
#endif /* ifdef ENABLE_MANAGEMENT */
@@ -4261,3 +4263,47 @@ tunnel_server(struct context *top)
42614263
multi_top_free(&multi);
42624264
close_instance(top);
42634265
}
4266+
4267+
/**
4268+
* Update the vhash with new IP/IPv6 addresses in the multi_context when a
4269+
* push-update message containing ifconfig/ifconfig-ipv6 options is sent
4270+
* from the server. This function should be called after a push-update
4271+
* and old_ip/old_ipv6 are the previous addresses of the client in
4272+
* ctx->options.ifconfig_local and ctx->options.ifconfig_ipv6_local.
4273+
*/
4274+
void
4275+
update_vhash(struct multi_context *m, struct multi_instance *mi, const char *old_ip, const char *old_ipv6)
4276+
{
4277+
struct in_addr addr;
4278+
struct in6_addr new_ipv6;
4279+
4280+
if ((mi->context.options.ifconfig_local && (!old_ip || strcmp(old_ip, mi->context.options.ifconfig_local)))
4281+
&& inet_pton(AF_INET, mi->context.options.ifconfig_local, &addr) == 1)
4282+
{
4283+
in_addr_t new_ip = ntohl(addr.s_addr);
4284+
4285+
/* Add new IP */
4286+
multi_learn_in_addr_t(m, mi, new_ip, -1, true);
4287+
}
4288+
4289+
/* TO DO:
4290+
* else if (old_ip)
4291+
* {
4292+
* // remove old IP
4293+
* }
4294+
*/
4295+
4296+
if ((mi->context.options.ifconfig_ipv6_local && (!old_ipv6 || strcmp(old_ipv6, mi->context.options.ifconfig_ipv6_local)))
4297+
&& inet_pton(AF_INET6, mi->context.options.ifconfig_ipv6_local, &new_ipv6) == 1)
4298+
{
4299+
/* Add new IPv6 */
4300+
multi_learn_in6_addr(m, mi, new_ipv6, -1, true);
4301+
}
4302+
4303+
/* TO DO:
4304+
* else if (old_ipv6)
4305+
* {
4306+
* // remove old IPv6
4307+
* }
4308+
*/
4309+
}

src/openvpn/multi.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,5 +686,12 @@ multi_set_pending(struct multi_context *m, struct multi_instance *mi)
686686
*/
687687
void multi_assign_peer_id(struct multi_context *m, struct multi_instance *mi);
688688

689+
#ifdef ENABLE_MANAGEMENT
690+
struct multi_instance *
691+
lookup_by_cid(struct multi_context *m, const unsigned long cid);
692+
#endif
693+
694+
void
695+
update_vhash(struct multi_context *m, struct multi_instance *mi, const char *old_ip, const char *old_ipv6);
689696

690697
#endif /* MULTI_H */

src/openvpn/options.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5488,7 +5488,6 @@ apply_push_options(struct context *c, struct options *options, struct buffer *bu
54885488
{
54895489
continue; /* Ignoring this option */
54905490
}
5491-
throw_signal_soft(SIGUSR1, "Offending option received from server");
54925491
return false; /* Cause push/pull error and stop push processing */
54935492
}
54945493

src/openvpn/options_util.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,11 @@ check_push_update_option_flags(char *line, int *i, unsigned int *flags)
236236
{
237237
if (*flags & PUSH_OPT_OPTIONAL)
238238
{
239-
msg(D_PUSH, "Pushed option is not updatable: '%s'. Ignoring.", line);
239+
msg(D_PUSH, "Pushed dispensable option is not updatable: '%s'. Ignoring.", line);
240240
}
241241
else
242242
{
243-
msg(M_WARN, "Pushed option is not updatable: '%s'. Restarting.", line);
243+
msg(M_WARN, "Pushed option is not updatable: '%s'.", line);
244244
return false;
245245
}
246246
}

src/openvpn/push.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,10 @@ process_incoming_push_reply(struct context *c, unsigned int permission_mask,
10731073
break;
10741074
}
10751075
}
1076+
else
1077+
{
1078+
throw_signal_soft(SIGUSR1, "Offending option received from server");
1079+
}
10761080
}
10771081
else if (ch == '\0')
10781082
{
@@ -1100,7 +1104,7 @@ process_incoming_push_msg(struct context *c, const struct buffer *buffer,
11001104
}
11011105
else if (honor_received_options && buf_string_compare_advance(&buf, push_update_cmd))
11021106
{
1103-
return process_incoming_push_update(c, permission_mask, option_types_found, &buf);
1107+
return process_incoming_push_update(c, permission_mask, option_types_found, &buf, false);
11041108
}
11051109
else
11061110
{

src/openvpn/push.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@
4141
#define PUSH_OPT_TO_REMOVE (1 << 0)
4242
#define PUSH_OPT_OPTIONAL (1 << 1)
4343

44+
#ifdef ENABLE_MANAGEMENT
45+
/* Push-update message sender modes */
46+
typedef enum
47+
{
48+
UPT_BROADCAST = 0,
49+
UPT_BY_CID = 1
50+
} push_update_type;
51+
#endif
52+
4453
int process_incoming_push_request(struct context *c);
4554

4655
/**
@@ -56,6 +65,7 @@ int process_incoming_push_request(struct context *c);
5665
* @param option_types_found A pointer to a variable that will be filled with the types of options
5766
* found in the message.
5867
* @param buf A buffer containing the received message.
68+
* @param msg_sender A boolean indicating if function is called by the message sender (server).
5969
*
6070
* @return
6171
* - `PUSH_MSG_UPDATE`: The message was processed successfully, and the updates were applied.
@@ -65,7 +75,8 @@ int process_incoming_push_request(struct context *c);
6575
*/
6676

6777
int process_incoming_push_update(struct context *c, unsigned int permission_mask,
68-
unsigned int *option_types_found, struct buffer *buf);
78+
unsigned int *option_types_found, struct buffer *buf,
79+
bool msg_sender);
6980

7081
int process_incoming_push_msg(struct context *c, const struct buffer *buffer,
7182
bool honor_received_options, unsigned int permission_mask,
@@ -127,4 +138,28 @@ void send_push_reply_auth_token(struct tls_multi *multi);
127138
*/
128139
void receive_auth_pending(struct context *c, const struct buffer *buffer);
129140

141+
#ifdef ENABLE_MANAGEMENT
142+
/**
143+
* @brief A function to send a PUSH_UPDATE control message from server to client(s).
144+
*
145+
* @param m the multi_context, contains all the clients connected to this server.
146+
* @param target the target to which to send the message. It should be:
147+
* `NULL` if `type == UPT_BROADCAST`,
148+
* a `mroute_addr *` if `type == UPT_BY_ADDR`,
149+
* a `char *` if `type == UPT_BY_CN`,
150+
* an `unsigned long *` if `type == UPT_BY_CID`.
151+
* @param msg a string containing the options to send.
152+
* @param type the way to address the message (broadcast, by cid, by cn, by address).
153+
* @param push_bundle_size the maximum size of a bundle of pushed option. Just use PUSH_BUNDLE_SIZE macro.
154+
* @return the number of clients to which the message was sent.
155+
*/
156+
int
157+
send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const int push_bundle_size);
158+
159+
bool management_callback_send_push_update_broadcast(void *arg, const char *options);
160+
161+
bool management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options);
162+
163+
#endif /* ifdef ENABLE_MANAGEMENT*/
164+
130165
#endif /* ifndef PUSH_H */

0 commit comments

Comments
 (0)