You can subscribe to this list here.
2002 |
Jan
|
Feb
|
Mar
|
Apr
(24) |
May
(14) |
Jun
(29) |
Jul
(33) |
Aug
(3) |
Sep
(8) |
Oct
(18) |
Nov
(1) |
Dec
(10) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2003 |
Jan
(3) |
Feb
(33) |
Mar
(7) |
Apr
(28) |
May
(30) |
Jun
(5) |
Jul
(10) |
Aug
(7) |
Sep
(32) |
Oct
(41) |
Nov
(20) |
Dec
(10) |
2004 |
Jan
(24) |
Feb
(18) |
Mar
(57) |
Apr
(40) |
May
(55) |
Jun
(48) |
Jul
(77) |
Aug
(15) |
Sep
(56) |
Oct
(80) |
Nov
(74) |
Dec
(52) |
2005 |
Jan
(38) |
Feb
(42) |
Mar
(39) |
Apr
(56) |
May
(79) |
Jun
(73) |
Jul
(16) |
Aug
(23) |
Sep
(68) |
Oct
(77) |
Nov
(52) |
Dec
(27) |
2006 |
Jan
(27) |
Feb
(18) |
Mar
(51) |
Apr
(62) |
May
(28) |
Jun
(50) |
Jul
(36) |
Aug
(33) |
Sep
(47) |
Oct
(50) |
Nov
(77) |
Dec
(13) |
2007 |
Jan
(15) |
Feb
(8) |
Mar
(14) |
Apr
(18) |
May
(25) |
Jun
(16) |
Jul
(16) |
Aug
(19) |
Sep
(32) |
Oct
(17) |
Nov
(5) |
Dec
(5) |
2008 |
Jan
(64) |
Feb
(25) |
Mar
(25) |
Apr
(6) |
May
(28) |
Jun
(20) |
Jul
(10) |
Aug
(27) |
Sep
(28) |
Oct
(59) |
Nov
(37) |
Dec
(43) |
2009 |
Jan
(40) |
Feb
(25) |
Mar
(12) |
Apr
(57) |
May
(46) |
Jun
(29) |
Jul
(39) |
Aug
(10) |
Sep
(20) |
Oct
(42) |
Nov
(50) |
Dec
(57) |
2010 |
Jan
(82) |
Feb
(165) |
Mar
(256) |
Apr
(260) |
May
(36) |
Jun
(87) |
Jul
(53) |
Aug
(89) |
Sep
(107) |
Oct
(51) |
Nov
(88) |
Dec
(117) |
2011 |
Jan
(69) |
Feb
(60) |
Mar
(113) |
Apr
(71) |
May
(67) |
Jun
(90) |
Jul
(88) |
Aug
(90) |
Sep
(48) |
Oct
(64) |
Nov
(69) |
Dec
(118) |
2012 |
Jan
(49) |
Feb
(528) |
Mar
(351) |
Apr
(190) |
May
(238) |
Jun
(193) |
Jul
(104) |
Aug
(100) |
Sep
(57) |
Oct
(41) |
Nov
(47) |
Dec
(51) |
2013 |
Jan
(94) |
Feb
(57) |
Mar
(96) |
Apr
(105) |
May
(77) |
Jun
(102) |
Jul
(27) |
Aug
(81) |
Sep
(32) |
Oct
(53) |
Nov
(127) |
Dec
(65) |
2014 |
Jan
(113) |
Feb
(59) |
Mar
(104) |
Apr
(259) |
May
(70) |
Jun
(70) |
Jul
(146) |
Aug
(45) |
Sep
(58) |
Oct
(149) |
Nov
(77) |
Dec
(83) |
2015 |
Jan
(53) |
Feb
(66) |
Mar
(86) |
Apr
(50) |
May
(135) |
Jun
(76) |
Jul
(151) |
Aug
(83) |
Sep
(97) |
Oct
(262) |
Nov
(245) |
Dec
(231) |
2016 |
Jan
(131) |
Feb
(233) |
Mar
(97) |
Apr
(138) |
May
(221) |
Jun
(254) |
Jul
(92) |
Aug
(248) |
Sep
(168) |
Oct
(275) |
Nov
(477) |
Dec
(445) |
2017 |
Jan
(218) |
Feb
(217) |
Mar
(146) |
Apr
(172) |
May
(216) |
Jun
(252) |
Jul
(164) |
Aug
(192) |
Sep
(190) |
Oct
(143) |
Nov
(255) |
Dec
(182) |
2018 |
Jan
(295) |
Feb
(164) |
Mar
(113) |
Apr
(147) |
May
(64) |
Jun
(262) |
Jul
(184) |
Aug
(90) |
Sep
(69) |
Oct
(364) |
Nov
(102) |
Dec
(101) |
2019 |
Jan
(119) |
Feb
(64) |
Mar
(64) |
Apr
(102) |
May
(57) |
Jun
(154) |
Jul
(84) |
Aug
(81) |
Sep
(76) |
Oct
(102) |
Nov
(233) |
Dec
(89) |
2020 |
Jan
(38) |
Feb
(170) |
Mar
(155) |
Apr
(172) |
May
(120) |
Jun
(223) |
Jul
(461) |
Aug
(227) |
Sep
(268) |
Oct
(113) |
Nov
(56) |
Dec
(124) |
2021 |
Jan
(121) |
Feb
(48) |
Mar
(334) |
Apr
(345) |
May
(207) |
Jun
(136) |
Jul
(71) |
Aug
(112) |
Sep
(122) |
Oct
(173) |
Nov
(184) |
Dec
(223) |
2022 |
Jan
(197) |
Feb
(206) |
Mar
(156) |
Apr
(212) |
May
(192) |
Jun
(170) |
Jul
(143) |
Aug
(380) |
Sep
(182) |
Oct
(148) |
Nov
(128) |
Dec
(269) |
2023 |
Jan
(248) |
Feb
(196) |
Mar
(264) |
Apr
(36) |
May
(123) |
Jun
(66) |
Jul
(120) |
Aug
(48) |
Sep
(157) |
Oct
(198) |
Nov
(300) |
Dec
(273) |
2024 |
Jan
(271) |
Feb
(147) |
Mar
(207) |
Apr
(78) |
May
(107) |
Jun
(168) |
Jul
(151) |
Aug
(51) |
Sep
(438) |
Oct
(221) |
Nov
(302) |
Dec
(357) |
2025 |
Jan
(451) |
Feb
(219) |
Mar
(326) |
Apr
(232) |
May
(306) |
Jun
(181) |
Jul
(452) |
Aug
(153) |
Sep
|
Oct
|
Nov
|
Dec
|
From: plaisthos (C. Review) <ge...@op...> - 2025-08-15 13:59:05
|
Attention is currently required from: flichtenheld, plaisthos. Hello flichtenheld, I'd like you to reexamine a change. Please visit http://gerrit.openvpn.net/c/openvpn/+/1067?usp=email to look at the new patch set (#6). Change subject: Check message id/acked ids too when doing sessionid cookie checks ...................................................................... Check message id/acked ids too when doing sessionid cookie checks This fixes that control packets on a floating client can trigger creating a new session in special circumstances: To trigger this circumstance a connection needs to - starts on IP A - successfully floats to IP B by data packet - then has a control packet from IP A before any data packet can trigger the float back to IP A and all of this needs to happen in the 60s time that hmac cookie is valid in the default configuration. In this scenario we would trigger a new connection as the HMAC session id would be valid. This patch adds checking also of the message-id and acked ids to discern packet from the initial three-way handshake where these ids 0 or 1 from any later packet. This will now trigger (at verb 4 or higher) a messaged like: Packet (P_ACK_V1) with invalid or missing SID instead. Also remove a few duplicated free_tls_pre_decrypt_state in test_ssl. Reported-By: Walter Doekes <wal...@wj...> Tested-By: Walter Doekes <wal...@wj...> Change-Id: I6752dcd5aff3e5cea2b439366479e86751a1c403 Signed-off-by: Arne Schwabe <ar...@rf...> --- M src/openvpn/mudp.c M src/openvpn/ssl_pkt.c M src/openvpn/ssl_pkt.h M tests/unit_tests/openvpn/test_pkt.c 4 files changed, 129 insertions(+), 24 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/67/1067/6 diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c index 7259a4b..31134be 100644 --- a/src/openvpn/mudp.c +++ b/src/openvpn/mudp.c @@ -151,7 +151,8 @@ * need to contain the peer id */ struct gc_arena gc = gc_new(); - bool ret = check_session_id_hmac(state, from, hmac, handwindow); + bool pkt_is_ack = (verdict == VERDICT_VALID_ACK_V1); + bool ret = check_session_hmac_and_pkt_id(state, from, hmac, handwindow, pkt_is_ack); const char *peer = print_link_socket_actual(&m->top.c2.from, &gc); uint8_t pkt_firstbyte = *BPTR(&m->top.c2.buf); @@ -159,7 +160,8 @@ if (!ret) { - msg(D_MULTI_MEDIUM, "Packet (%s) with invalid or missing SID from %s", + msg(D_MULTI_MEDIUM, "Packet (%s) with invalid or missing SID from" + " %s or wrong packet id", packet_opcode_name(op), peer); } else diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c index b901f87..6ec05a7 100644 --- a/src/openvpn/ssl_pkt.c +++ b/src/openvpn/ssl_pkt.c @@ -496,8 +496,11 @@ } bool -check_session_id_hmac(struct tls_pre_decrypt_state *state, const struct openvpn_sockaddr *from, - hmac_ctx_t *hmac, int handwindow) +check_session_hmac_and_pkt_id(struct tls_pre_decrypt_state *state, + const struct openvpn_sockaddr *from, + hmac_ctx_t *hmac, + int handwindow, + bool pkt_is_ack) { if (!from) { @@ -512,6 +515,36 @@ return false; } + /* Check if the packet ID of the packet or ACKED packet is <= 1 */ + for (int i = 0; i < ack.len; i++) + { + /* This packet ACKs a packet that has a higher packet id than the + * ones expected in the three-way handshake, consider it as invalid + * for the session */ + if (ack.packet_id[i] > 1) + { + return false; + } + } + + if (!pkt_is_ack) + { + packet_id_type message_id; + /* Extract the packet ID from the packet */ + if (!reliable_ack_read_packet_id(&buf, &message_id)) + { + return false; + } + + /* similar check. Anything larger than 1 is not considered part of the + * three-way handshake */ + if (message_id > 1) + { + return false; + } + } + + /* check adjacent timestamps too */ for (int offset = -2; offset <= 1; offset++) { diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h index 8fe4880..96cdd68 100644 --- a/src/openvpn/ssl_pkt.h +++ b/src/openvpn/ssl_pkt.h @@ -178,14 +178,20 @@ /** * Checks if a control packet has a correct HMAC server session id * + * This will also consider packets that have a packet id higher + * than 1 or ack packets higher than 1 to be invalid as they are + * not part of the initial three way handshake of OpenVPN and should + * not create a new connection. + * * @param state session information * @param from link_socket from the client * @param hmac the hmac context to use for the calculation * @param handwindow the quantisation of the current time + * @param pkt_is_ack the packet being checked is a P_ACK_V1 * @return the expected server session id */ -bool check_session_id_hmac(struct tls_pre_decrypt_state *state, const struct openvpn_sockaddr *from, - hmac_ctx_t *hmac, int handwindow); +bool check_session_hmac_and_pkt_id(struct tls_pre_decrypt_state *state, const struct openvpn_sockaddr *from, + hmac_ctx_t *hmac, int handwindow, bool pkt_is_ack); /* * Write a control channel authentication record. diff --git a/tests/unit_tests/openvpn/test_pkt.c b/tests/unit_tests/openvpn/test_pkt.c index 65b31e7..5122766 100644 --- a/tests/unit_tests/openvpn/test_pkt.c +++ b/tests/unit_tests/openvpn/test_pkt.c @@ -139,6 +139,27 @@ 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x85, 0xdb, 0x53, 0x56, 0x23, 0xb0, 0x2e }; +/* no tls-auth, P_ACK_V1, acks 0,1, and 2 */ +const uint8_t client_ack_123_none_random_id[] = { + 0x28, + 0xae, 0xb9, 0xaf, 0xe1, 0xf0, 0x1d, 0x79, 0xc8, + 0x03, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0xdd, 0x85, 0xdb, 0x53, 0x56, 0x23, 0xb0, 0x2e +}; + +/* no tls-auth, P_CONTROL_V1, acks 0, msg-id 2 */ +const uint8_t client_control_none_random_id[] = { + 0x20, + 0xae, 0xb9, 0xaf, 0xe1, 0xf0, 0x1d, 0x79, 0xc8, + 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x02 +}; + + struct tls_auth_standalone init_tas_auth(int key_direction) { @@ -256,12 +277,10 @@ assert_int_equal(verdict, VERDICT_VALID_RESET_V2); free_tls_pre_decrypt_state(&state); - free_tls_pre_decrypt_state(&state); /* The pre decrypt function should not modify the buffer, so calling it * again should have the same result */ verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf); assert_int_equal(verdict, VERDICT_VALID_RESET_V2); - free_tls_pre_decrypt_state(&state); /* and buf memory should be equal */ assert_memory_equal(BPTR(&buf), client_reset_v2_tls_auth, sizeof(client_reset_v2_tls_auth)); @@ -279,7 +298,6 @@ assert_int_equal(verdict, VERDICT_INVALID); free_tls_pre_decrypt_state(&state); - free_tls_pre_decrypt_state(&state); /* Wrong key direction gives a wrong hmac key and should not validate */ free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi); free_tas(&tas); @@ -319,15 +337,12 @@ assert_int_equal(verdict, VERDICT_VALID_RESET_V2); free_tls_pre_decrypt_state(&state); - free_tls_pre_decrypt_state(&state); buf_reset_len(&buf); buf_write(&buf, client_reset_v2_tls_crypt, sizeof(client_reset_v2_none)); verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf); assert_int_equal(verdict, VERDICT_VALID_RESET_V2); free_tls_pre_decrypt_state(&state); - free_tls_pre_decrypt_state(&state); - /* This is not a reset packet and should trigger the other response */ buf_reset_len(&buf); buf_write(&buf, client_ack_tls_auth_randomid, sizeof(client_ack_tls_auth_randomid)); @@ -405,7 +420,7 @@ assert_int_equal(verdict, VERDICT_VALID_CONTROL_V1); /* This is a valid packet but containing a random id instead of an HMAC id*/ - bool valid = check_session_id_hmac(&state, &from.dest, hmac, 30); + bool valid = check_session_hmac_and_pkt_id(&state, &from.dest, hmac, 30, false); assert_false(valid); free_tls_pre_decrypt_state(&state); @@ -436,7 +451,7 @@ verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf); assert_int_equal(verdict, VERDICT_VALID_ACK_V1); - bool valid = check_session_id_hmac(&state, &from.dest, hmac, 30); + bool valid = check_session_hmac_and_pkt_id(&state, &from.dest, hmac, 30, true); assert_true(valid); free_tls_pre_decrypt_state(&state); @@ -445,6 +460,51 @@ hmac_ctx_free(hmac); } +static void +test_verify_hmac_none_out_of_range_ack(void **ut_state) +{ + hmac_ctx_t *hmac = session_id_hmac_init(); + + struct link_socket_actual from = { 0 }; + from.dest.addr.sa.sa_family = AF_INET; + + struct tls_auth_standalone tas = { 0 }; + struct tls_pre_decrypt_state state = { 0 }; + + struct buffer buf = alloc_buf(1024); + enum first_packet_verdict verdict; + + tas.tls_wrap.mode = TLS_WRAP_NONE; + + buf_reset_len(&buf); + buf_write(&buf, client_ack_123_none_random_id, sizeof(client_ack_123_none_random_id)); + + + verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf); + assert_int_equal(verdict, VERDICT_VALID_ACK_V1); + + /* should fail because it acks 2 */ + bool valid = check_session_hmac_and_pkt_id(&state, &from.dest, hmac, 30, true); + assert_false(valid); + free_tls_pre_decrypt_state(&state); + + /* Try test with the control with a too high message id now */ + buf_reset_len(&buf); + buf_write(&buf, client_control_none_random_id, sizeof(client_control_none_random_id)); + + verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf); + assert_int_equal(verdict, VERDICT_VALID_CONTROL_V1); + + /* should fail because it has message id 2 */ + valid = check_session_hmac_and_pkt_id(&state, &from.dest, hmac, 30, true); + assert_false(valid); + + free_tls_pre_decrypt_state(&state); + free_buf(&buf); + hmac_ctx_cleanup(hmac); + hmac_ctx_free(hmac); +} + static hmac_ctx_t * init_static_hmac(void) { @@ -634,16 +694,20 @@ main(void) { openvpn_unit_test_setup(); - const struct CMUnitTest tests[] = { cmocka_unit_test(test_tls_decrypt_lite_none), - cmocka_unit_test(test_tls_decrypt_lite_auth), - cmocka_unit_test(test_tls_decrypt_lite_crypt), - cmocka_unit_test(test_parse_ack), - cmocka_unit_test(test_calc_session_id_hmac_static), - cmocka_unit_test(test_verify_hmac_none), - cmocka_unit_test(test_verify_hmac_tls_auth), - cmocka_unit_test(test_generate_reset_packet_plain), - cmocka_unit_test(test_generate_reset_packet_tls_auth), - cmocka_unit_test(test_extract_control_message) }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_tls_decrypt_lite_none), + cmocka_unit_test(test_tls_decrypt_lite_auth), + cmocka_unit_test(test_tls_decrypt_lite_crypt), + cmocka_unit_test(test_parse_ack), + cmocka_unit_test(test_calc_session_id_hmac_static), + cmocka_unit_test(test_verify_hmac_none), + cmocka_unit_test(test_verify_hmac_tls_auth), + cmocka_unit_test(test_verify_hmac_none_out_of_range_ack), + cmocka_unit_test(test_generate_reset_packet_plain), + cmocka_unit_test(test_generate_reset_packet_tls_auth), + cmocka_unit_test(test_extract_control_message) + }; #if defined(ENABLE_CRYPTO_OPENSSL) OpenSSL_add_all_algorithms(); -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1067?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: I6752dcd5aff3e5cea2b439366479e86751a1c403 Gerrit-Change-Number: 1067 Gerrit-PatchSet: 6 Gerrit-Owner: plaisthos <arn...@rf...> Gerrit-Reviewer: flichtenheld <fr...@li...> Gerrit-CC: MaxF <ma...@ma...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-Attention: flichtenheld <fr...@li...> Gerrit-MessageType: newpatchset |
From: Johan D. <jo...@op...> - 2025-08-14 13:32:52
|
Meeting summary for 13 August 2025: * *Updated: Release 2.7* DCO for Windows multipeer statistics driver part is done - userspace part is waiting review. The FreeBSD patch for float was merged, should show up in 14.4 probably. The tentative plan for release is now: August 20: beta1 September 3: release candidate 1 September 17: stable release These are some known remaining tasks: Epoch data keys for Linux DCO and Windows DCO - can be done in a minor release after 2.7.0. Small fix to check message id/acked ids too when doing sessionid cookie checks - should go into 2.7.0. PUSH_UPDATE review and server-side support - cron2 wants to discuss changes. mi prefix handling, int/uint fixes, and the new 'real route' gateway handling * *Updated: OpenVPN community meetup 2025* https://community.openvpn.net/openvpn/wiki/CommunityMeetup2025 When: 25 and 26 october (saturday and sunday), with travel days on the friday before and monday after. Where: Napoli, Italy. Meeting room:https://www.hotelparadisonapoli.it/en/home-page.aspxsponsored by company. Hotel: Paradiso Napoli Hotel. Beer: yes. T-shirts: yes. Should send out invites. To include people like jan just, reynir, kristof, syzzer, max, rein, selva, perhaps leon from netgate who contributed multipeer to dco-win. novaflash will make a draft, let cron2 review it, then send it out to some peeps. If we forgot anyone we can forward a copy after. * *Updated: Github repo for open sourced MCP project* MCP is a protocol that AI agents can speak, like a universal API, so that if other programs also speak MCP, then AI can work with it. Company is going to do some MCP project to work with CloudConnexa, maybe in the future Access Server. They want to open source this and ask for a repo to publish it in. This is a bit similar to this project;https://github.com/OpenVPN/terraform-provider-cloudconnexawhich was started elsewhere but got published to open source. Seems like community has no objections to this, so will relay that approval back to company. As always you're welcome to join at #openvpn-meeting on Libera IRC network every Wednesday at 14:00 Central European Time. Kind regards, Johan Draaisma |
From: d12fk (C. Review) <ge...@op...> - 2025-08-13 14:12:37
|
Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/1144?usp=email to review the following change. Change subject: dns: fix systemd dns-updown script ...................................................................... dns: fix systemd dns-updown script In the resolvconf part of the script there was one instance of a dynamic variable using _* left. The _* ones do not work as the regular ones, but only when you directly place them within ${!}, not indirectly using a variable. Convert the code to use a loop and a check, like in all the other places in the script. Change-Id: Id800cad0e92e0abc0d96079fdb5a9d57578e1446 Signed-off-by: Heiko Hund <he...@is...> --- M distro/dns-scripts/systemd-dns-updown.sh 1 file changed, 5 insertions(+), 3 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/44/1144/1 diff --git a/distro/dns-scripts/systemd-dns-updown.sh b/distro/dns-scripts/systemd-dns-updown.sh index 9006e28..ed3947a 100644 --- a/distro/dns-scripts/systemd-dns-updown.sh +++ b/distro/dns-scripts/systemd-dns-updown.sh @@ -189,11 +189,13 @@ domains+="${!domain_var} " done { + local i=1 local maxns=3 - local server_var=dns_server_${n}_address_* - for addr_var in ${!server_var}; do - [ $((maxns--)) -gt 0 ] || break + while [ "${i}" -le "${maxns}" ]; do + local addr_var=dns_server_${n}_address_${i} + [ -n "${!addr_var}" ] || break echo "nameserver ${!addr_var}" + i=$((i+1)) done [ -z "$domains" ] || echo "search $domains" } | /sbin/resolvconf -a "$dev" -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1144?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Id800cad0e92e0abc0d96079fdb5a9d57578e1446 Gerrit-Change-Number: 1144 Gerrit-PatchSet: 1 Gerrit-Owner: d12fk <he...@op...> Gerrit-Reviewer: flichtenheld <fr...@li...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-Attention: flichtenheld <fr...@li...> Gerrit-MessageType: newchange |
From: MaxF (C. Review) <ge...@op...> - 2025-08-13 14:01:49
|
Attention is currently required from: flichtenheld, plaisthos. MaxF has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/1067?usp=email ) Change subject: Check message id/acked ids too when doing sessionid cookie checks ...................................................................... Patch Set 5: (3 comments) Patchset: PS5: The change makes sense to me, just some small nitpicks. If you strongly prefer doing it this way, I'm willing to approve. File src/openvpn/mudp.c: http://gerrit.openvpn.net/c/openvpn/+/1067/comment/b8f6989e_8a8a01d2 : PS5, Line 163: msg(D_MULTI_MEDIUM, "Packet (%s) with invalid or missing SID from %s", : packet_opcode_name(op), peer); This debug message may now be incorrect. The packet might have a valid SID, but a wrong packet ID. File src/openvpn/ssl_pkt.c: http://gerrit.openvpn.net/c/openvpn/+/1067/comment/3d6e4bec_68a33598 : PS5, Line 518: /* Check if the packet ID of the packet or ACKED packet is <= 1 */ : for (int i = 0; i < ack.len; i++) : { : /* This packet ACKs a packet that has a higher packet id than the : * ones expected in the three-way handshake, consider it as invalid : * for the session */ : if (ack.packet_id[i] > 1) : { : return false; : } : } : : if (!pkt_is_ack) : { : packet_id_type message_id; : /* Extract the packet ID from the packet */ : if (!reliable_ack_read_packet_id(&buf, &message_id)) : { : return false; : } : : /* similar check. Anything larger than 1 is not considered part of the : * three-way handshake */ : if (message_id > 1) : { : return false; : } : } Maybe nitpicky, but this seems like scope creep for this function. Now check_session_id_hmac() does more than checking the hmac. I'd prefer to extract this check to its own function, or rename this one. -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1067?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: I6752dcd5aff3e5cea2b439366479e86751a1c403 Gerrit-Change-Number: 1067 Gerrit-PatchSet: 5 Gerrit-Owner: plaisthos <arn...@rf...> Gerrit-Reviewer: flichtenheld <fr...@li...> Gerrit-CC: MaxF <ma...@ma...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-Attention: flichtenheld <fr...@li...> Gerrit-Comment-Date: Wed, 13 Aug 2025 14:01:34 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: No Gerrit-MessageType: comment |
From: Илья Ш. <chi...@gm...> - 2025-08-12 14:28:59
|
вт, 12 авг. 2025 г. в 16:21, Arne Schwabe <ar...@rf...>: > > Am 12.08.2025 um 16:09 schrieb Ilia Shipitsin: > > Reference: https://github.blog/changelog/2024-09-03-github-actions-arm64-linux-and-windows-runners-are-now-generally-available/ > > Do you have a Github Actions run that shows that it actually runs? > GHA: introduce additional arm64 builds · chipitsine/openvpn@348a30e <https://github.com/chipitsine/openvpn/actions/runs/16911396903/job/47913763811#step:1:9> > From the linked page: > > > Arm64 Linux and Windows GitHub-hosted runners for Actions are now > generally available. This new addition to our suite of hosted runners > provides power, performance & sustainability improvements for all your > Actions jobs. Arm64 runners are available to customers on* our Team and > Enterprise Cloud plans*. > > Which sounds like they are only available on certain (paid) Github plans. > > > Arne > |
From: Arne S. <ar...@rf...> - 2025-08-12 14:21:16
|
Am 12.08.2025 um 16:09 schrieb Ilia Shipitsin: > Reference:https://github.blog/changelog/2024-09-03-github-actions-arm64-linux-and-windows-runners-are-now-generally-available/ > Do you have a Github Actions run that shows that it actually runs? From the linked page: > Arm64 Linux and Windows GitHub-hosted runners for Actions are now > generally available. This new addition to our suite of hosted runners > provides power, performance & sustainability improvements for all your > Actions jobs. Arm64 runners are available to customers on*our Team and > Enterprise Cloud plans*. Which sounds like they are only available on certain (paid) Github plans. Arne |
From: Ilia S. <chi...@gm...> - 2025-08-12 14:10:14
|
Reference: https://github.blog/changelog/2024-09-03-github-actions-arm64-linux-and-windows-runners-are-now-generally-available/ Signed-off-by: Ilia Shipitsin <chi...@gm...> --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f7883c70..6429f87d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -135,7 +135,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, ubuntu-24.04] + os: [ubuntu-22.04, ubuntu-24.04, ubuntu-24.04-arm] sslpkg: [libmbedtls-dev] ssllib: [mbedtls] libname: [mbed TLS] @@ -181,7 +181,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, ubuntu-24.04] + os: [ubuntu-22.04, ubuntu-24.04, ubuntu-24.04-arm] ssllib: [mbedtls, openssl] name: "clang-asan - ${{matrix.os}} - ${{matrix.ssllib}}" -- 2.46.0.windows.1 |
From: flichtenheld (C. Review) <ge...@op...> - 2025-08-12 13:40:13
|
Attention is currently required from: plaisthos. Hello plaisthos, I'd like you to reexamine a change. Please visit http://gerrit.openvpn.net/c/openvpn/+/1135?usp=email to look at the new patch set (#7). Change subject: openvpn_PRF: Change API to use size_t for lenghts ...................................................................... openvpn_PRF: Change API to use size_t for lenghts Basically all users already wanted that anyway. And most of the library functions also take size_t nowadays. Change-Id: Ic88cd6e143bc48cab3c9ebb7c7007513803bd199 Signed-off-by: Frank Lichtenheld <fr...@li...> --- M src/openvpn/crypto.c M src/openvpn/crypto_backend.h M src/openvpn/crypto_mbedtls.c M src/openvpn/crypto_openssl.c M src/openvpn/ssl.c M tests/unit_tests/openvpn/test_crypto.c 6 files changed, 25 insertions(+), 25 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/35/1135/7 diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index af7583a..f0c7939 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -1903,8 +1903,8 @@ uint8_t out[8]; uint8_t expected_out[] = { 'q', 'D', '\xfe', '%', '@', 's', 'u', '\x95' }; - int ret = ssl_tls1_PRF((uint8_t *)seed, (int)strlen(seed), (uint8_t *)secret, - (int)strlen(secret), out, sizeof(out)); + int ret = ssl_tls1_PRF((uint8_t *)seed, strlen(seed), (uint8_t *)secret, + strlen(secret), out, sizeof(out)); return (ret && memcmp(out, expected_out, sizeof(out)) == 0); } diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h index 59418f6..b74cb7f 100644 --- a/src/openvpn/crypto_backend.h +++ b/src/openvpn/crypto_backend.h @@ -716,7 +716,7 @@ * * @return true if successful, false on any error */ -bool ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret, int secret_len, - uint8_t *output, int output_len); +bool ssl_tls1_PRF(const uint8_t *seed, size_t seed_len, const uint8_t *secret, size_t secret_len, + uint8_t *output, size_t output_len); #endif /* CRYPTO_BACKEND_H_ */ diff --git a/src/openvpn/crypto_mbedtls.c b/src/openvpn/crypto_mbedtls.c index 86317dd..ce6cfe8 100644 --- a/src/openvpn/crypto_mbedtls.c +++ b/src/openvpn/crypto_mbedtls.c @@ -983,8 +983,8 @@ * from recent versions, so we use our own implementation if necessary. */ #if defined(HAVE_MBEDTLS_SSL_TLS_PRF) && defined(MBEDTLS_SSL_TLS_PRF_TLS1) bool -ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret, int secret_len, - uint8_t *output, int output_len) +ssl_tls1_PRF(const uint8_t *seed, size_t seed_len, const uint8_t *secret, size_t secret_len, + uint8_t *output, size_t output_len) { return mbed_ok(mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1, secret, secret_len, "", seed, seed_len, output, output_len)); @@ -1002,8 +1002,8 @@ * @param olen Length of the output buffer */ static void -tls1_P_hash(const mbedtls_md_info_t *md_kt, const uint8_t *sec, int sec_len, const uint8_t *seed, - int seed_len, uint8_t *out, int olen) +tls1_P_hash(const mbedtls_md_info_t *md_kt, const uint8_t *sec, size_t sec_len, const uint8_t *seed, + size_t seed_len, uint8_t *out, size_t olen) { struct gc_arena gc = gc_new(); uint8_t A1[MAX_HMAC_KEY_LENGTH]; @@ -1089,8 +1089,8 @@ * (2) The pre-master secret is generated by the client. */ bool -ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec, int slen, uint8_t *out1, - int olen) +ssl_tls1_PRF(const uint8_t *label, size_t label_len, const uint8_t *sec, size_t slen, uint8_t *out1, + size_t olen) { struct gc_arena gc = gc_new(); const md_kt_t *md5 = md_get("MD5"); @@ -1113,7 +1113,7 @@ secure_memzero(out2, olen); - dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc)); + dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%zu]: %s", olen, format_hex(out1, olen, 0, &gc)); gc_free(&gc); return true; diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index 2351bfd..75af4f5 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -1341,8 +1341,8 @@ } #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(LIBRESSL_VERSION_NUMBER) bool -ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret, int secret_len, - uint8_t *output, int output_len) +ssl_tls1_PRF(const uint8_t *seed, size_t seed_len, const uint8_t *secret, size_t secret_len, + uint8_t *output, size_t output_len) { bool ret = true; EVP_KDF_CTX *kctx = NULL; @@ -1368,9 +1368,9 @@ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_md5_sha1, strlen(SN_md5_sha1)); params[1] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET, (uint8_t *)secret, - (size_t)secret_len); + secret_len); params[2] = - OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED, (uint8_t *)seed, (size_t)seed_len); + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED, (uint8_t *)seed, seed_len); params[3] = OSSL_PARAM_construct_end(); if (EVP_KDF_derive(kctx, output, output_len, params) <= 0) @@ -1392,15 +1392,15 @@ } #elif defined(OPENSSL_IS_AWSLC) bool -ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec, int slen, uint8_t *out1, - int olen) +ssl_tls1_PRF(const uint8_t *label, size_t label_len, const uint8_t *sec, size_t slen, uint8_t *out1, + size_t olen) { CRYPTO_tls1_prf(EVP_md5_sha1(), out1, olen, sec, slen, label, label_len, NULL, 0, NULL, 0); } #elif !defined(LIBRESSL_VERSION_NUMBER) && !defined(ENABLE_CRYPTO_WOLFSSL) bool -ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret, int secret_len, - uint8_t *output, int output_len) +ssl_tls1_PRF(const uint8_t *seed, size_t seed_len, const uint8_t *secret, size_t secret_len, + uint8_t *output, size_t output_len) { EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_TLS1_PRF, NULL); if (!pctx) @@ -1448,8 +1448,8 @@ * OpenSSL does. As result they will only be able to support * peers that support TLS EKM like when running with OpenSSL 3.x FIPS */ bool -ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec, int slen, uint8_t *out1, - int olen) +ssl_tls1_PRF(const uint8_t *label, size_t label_len, const uint8_t *sec, size_t slen, uint8_t *out1, + size_t olen) { return false; } diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 85b018b..284d951 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1294,10 +1294,10 @@ } static bool -openvpn_PRF(const uint8_t *secret, int secret_len, const char *label, const uint8_t *client_seed, - int client_seed_len, const uint8_t *server_seed, int server_seed_len, +openvpn_PRF(const uint8_t *secret, size_t secret_len, const char *label, const uint8_t *client_seed, + size_t client_seed_len, const uint8_t *server_seed, size_t server_seed_len, const struct session_id *client_sid, const struct session_id *server_sid, - uint8_t *output, int output_len) + uint8_t *output, size_t output_len) { /* concatenate seed components */ diff --git a/tests/unit_tests/openvpn/test_crypto.c b/tests/unit_tests/openvpn/test_crypto.c index 12ddaba..5df1046 100644 --- a/tests/unit_tests/openvpn/test_crypto.c +++ b/tests/unit_tests/openvpn/test_crypto.c @@ -161,7 +161,7 @@ uint8_t out[32]; - bool ret = ssl_tls1_PRF(seed, (int)seed_len, secret, (int)secret_len, out, sizeof(out)); + bool ret = ssl_tls1_PRF(seed, seed_len, secret, secret_len, out, sizeof(out)); #if defined(LIBRESSL_VERSION_NUMBER) || defined(ENABLE_CRYPTO_WOLFSSL) /* No TLS1 PRF support in these libraries */ -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1135?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Ic88cd6e143bc48cab3c9ebb7c7007513803bd199 Gerrit-Change-Number: 1135 Gerrit-PatchSet: 7 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-MessageType: newpatchset |
From: Gert D. <ge...@gr...> - 2025-08-12 09:49:29
|
For whatever reason, I never had problems with this (I think it was explained to me how to disable this particular action) - but indeed, this is a useful change, because it does not make sense outside the main openvpn repo. Your patch has been applied to the master branch. commit c4f4f26d48babdf4f15a6cde837317c7216abbba Author: Ilia Shipitsin Date: Mon Aug 11 18:13:23 2025 +0200 GHA: limit 'Deploy Doxygen documentation' to main repo only Signed-off-by: Ilia Shipitsin <chi...@gm...> Acked-by: Frank Lichtenheld <fr...@li...> Message-Id: <202...@gm...> URL: https://www.mail-archive.com/ope...@li.../msg32598.html Signed-off-by: Gert Doering <ge...@gr...> -- kind regards, Gert Doering |
From: Frank L. <fr...@li...> - 2025-08-12 09:44:15
|
On Mon, Aug 11, 2025 at 06:13:23PM +0200, Ilia Shipitsin wrote: > that workflow was accidently triggered in fork repo, where > there's no github pages set Makes sense to me Acked-by: Frank Lichtenheld <fr...@li...> Regards, -- Frank Lichtenheld |
From: Ilia S. <chi...@gm...> - 2025-08-11 16:13:52
|
that workflow was accidently triggered in fork repo, where there's no github pages set Signed-off-by: Ilia Shipitsin <chi...@gm...> --- .github/workflows/doxygen.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index ffd1b826..751258a9 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -9,6 +9,7 @@ concurrency: jobs: build: runs-on: ubuntu-24.04 + if: ${{ github.repository_owner == 'openvpn' || github.event_name == 'workflow_dispatch' }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: -- 2.46.0.windows.1 |
From: stipa (C. Review) <ge...@op...> - 2025-08-11 12:09:36
|
Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/1143?usp=email to review the following change. Change subject: dco-win: add support for multipeer stats ...................................................................... dco-win: add support for multipeer stats Use the new driver API to fetch per-peer link and VPN byte counters in both client and server modes. Two usage modes are supported: - Single peer: pass the peer ID and a fixed-size output buffer. If the IOCTL is not supported (old driver), fall back to the legacy API. - All peers: first call the IOCTL with a small output buffer to get the required size, then allocate a buffer and call again to fetch stats for all peers. Change-Id: I525d7300e49f9a5a18e7146ee35ccc2af8184b8a Signed-off-by: Lev Stipakov <le...@op...> --- M src/openvpn/dco_win.c M src/openvpn/dco_win.h M src/openvpn/ovpn_dco_win.h 3 files changed, 184 insertions(+), 3 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/43/1143/1 diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index 5317ac1..bf3f151 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -30,6 +30,7 @@ #include "forward.h" #include "tun.h" #include "crypto.h" +#include "multi.h" #include "ssl_common.h" #include "openvpn.h" @@ -190,6 +191,8 @@ { dco_context_t *dco = &c->c1.tuntap->dco; + dco->c = c; + switch (c->mode) { case MODE_POINT_TO_POINT: @@ -714,12 +717,132 @@ int dco_get_peer_stats_multi(dco_context_t *dco, const bool raise_sigusr1_on_err) { - /* Not implemented. */ - return 0; + struct gc_arena gc = gc_new(); + + int ret = 0; + struct tuntap *tt = dco->tt; + + if (!tuntap_defined(tt)) + { + ret = -1; + goto done; + } + + OVPN_GET_PEER_STATS ps = { + .PeerId = -1 + }; + + DWORD required_size = 0, bytes_returned = 0; + /* first, figure out buffer size */ + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_GET_PEER_STATS, &ps, sizeof(ps), &required_size, sizeof(DWORD), &bytes_returned, NULL)) + { + if (GetLastError() == ERROR_MORE_DATA) + { + if (bytes_returned != sizeof(DWORD)) + { + msg(M_WARN, "%s: invalid bytes returned for size query (%lu, expected %zu)", __func__, bytes_returned, sizeof(DWORD)); + ret = -1; + goto done; + } + /* required_size now contains the size written by the driver */ + if (required_size == 0) + { + ret = 0; /* no peers to process */ + goto done; + } + if (required_size < sizeof(OVPN_PEER_STATS)) + { + msg(M_WARN, "%s: invalid required size %lu (minimum %zu)", __func__, required_size, sizeof(OVPN_PEER_STATS)); + ret = -1; + goto done; + } + } + else + { + msg(M_WARN | M_ERRNO, "%s: failed to fetch required buffer size", __func__); + ret = -1; + goto done; + } + } + else + { + /* unexpected success? */ + if (bytes_returned == 0) + { + ret = 0; /* no peers to process */ + goto done; + } + + msg(M_WARN, "%s: first DeviceIoControl call succeeded unexpectedly (%lu bytes returned)", __func__, bytes_returned); + ret = -1; + goto done; + } + + + /* allocate the buffer and fetch stats */ + OVPN_PEER_STATS *peer_stats = gc_malloc(required_size, true, &gc); + if (!peer_stats) + { + msg(M_WARN, "%s: failed to allocate buffer of size %lu", __func__, required_size); + ret = -1; + goto done; + } + + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_GET_PEER_STATS, &ps, sizeof(ps), peer_stats, required_size, &bytes_returned, NULL)) + { + /* unlikely case when a peer has been added since fetching buffer size, not an error! */ + if (GetLastError() == ERROR_MORE_DATA) + { + msg(M_WARN, "%s: peer has been added, skip fetching stats", __func__); + ret = 0; + goto done; + } + + msg(M_WARN | M_ERRNO, "%s: failed to fetch multipeer stats", __func__); + ret = -1; + goto done; + } + + /* iterate over stats and update peers */ + for (int i = 0; i < bytes_returned / sizeof(OVPN_PEER_STATS); ++i) + { + OVPN_PEER_STATS *stat = &peer_stats[i]; + + if (stat->PeerId >= dco->c->multi->max_clients) + { + msg(M_WARN, "%s: received out of bound peer_id %u (max=%u)", __func__, stat->PeerId, + dco->c->multi->max_clients); + continue; + } + + struct multi_instance *mi = dco->c->multi->instances[stat->PeerId]; + if (!mi) + { + msg(M_WARN, "%s: received data for a non-existing peer %u", __func__, stat->PeerId); + continue; + } + + /* update peer stats */ + struct context_2 *c2 = &mi->context.c2; + c2->dco_read_bytes = stat->LinkRxBytes; + c2->dco_write_bytes = stat->LinkTxBytes; + c2->tun_read_bytes = stat->VpnRxBytes; + c2->tun_write_bytes = stat->VpnTxBytes; + } + +done: + gc_free(&gc); + + if (raise_sigusr1_on_err && ret < 0) + { + register_signal(dco->c->sig, SIGUSR1, "dco peer stats error"); + } + + return ret; } int -dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err) +dco_get_peer_stats_fallback(struct context *c, const bool raise_sigusr1_on_err) { struct tuntap *tt = c->c1.tuntap; @@ -747,6 +870,48 @@ return 0; } +int +dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err) +{ + struct tuntap *tt = c->c1.tuntap; + + if (!tuntap_defined(tt)) + { + return -1; + } + + /* first, try a new ioctl */ + OVPN_GET_PEER_STATS ps = { .PeerId = c->c2.tls_multi->dco_peer_id }; + + OVPN_PEER_STATS peer_stats = { 0 }; + DWORD bytes_returned = 0; + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_GET_PEER_STATS, &ps, sizeof(ps), &peer_stats, sizeof(peer_stats), + &bytes_returned, NULL)) + { + if (GetLastError() == ERROR_INVALID_FUNCTION) + { + /* are we using the old driver? */ + return dco_get_peer_stats_fallback(c, raise_sigusr1_on_err); + } + + msg(M_WARN | M_ERRNO, "%s: DeviceIoControl(OVPN_IOCTL_GET_PEER_STATS) failed", __func__); + return -1; + } + + if (bytes_returned != sizeof(OVPN_PEER_STATS)) + { + msg(M_WARN | M_ERRNO, "%s: DeviceIoControl(OVPN_IOCTL_GET_PEER_STATS) returnted invalid size", __func__); + return -1; + } + + c->c2.dco_read_bytes = peer_stats.LinkRxBytes; + c->c2.dco_write_bytes = peer_stats.LinkTxBytes; + c->c2.tun_read_bytes = peer_stats.VpnRxBytes; + c->c2.tun_write_bytes = peer_stats.VpnTxBytes; + + return 0; +} + void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { diff --git a/src/openvpn/dco_win.h b/src/openvpn/dco_win.h index a7f4865..4f3f028 100644 --- a/src/openvpn/dco_win.h +++ b/src/openvpn/dco_win.h @@ -57,6 +57,8 @@ uint64_t dco_read_bytes; uint64_t dco_write_bytes; + + struct context *c; }; typedef struct dco_context dco_context_t; diff --git a/src/openvpn/ovpn_dco_win.h b/src/openvpn/ovpn_dco_win.h index baf7214..9e1378a 100644 --- a/src/openvpn/ovpn_dco_win.h +++ b/src/openvpn/ovpn_dco_win.h @@ -83,6 +83,14 @@ LONG64 TunBytesReceived; } OVPN_STATS, * POVPN_STATS; +typedef struct _OVPN_PEER_STATS { + int PeerId; + LONG64 LinkRxBytes; + LONG64 LinkTxBytes; + LONG64 VpnRxBytes; + LONG64 VpnTxBytes; +} OVPN_PEER_STATS, * POVPN_PEER_STATS; + typedef enum _OVPN_KEY_SLOT { OVPN_KEY_SLOT_PRIMARY, OVPN_KEY_SLOT_SECONDARY @@ -185,6 +193,10 @@ int IPv6; } OVPN_MP_IROUTE, * POVPN_MP_IROUTE; +typedef struct _OVPN_GET_PEER_STATS { + int PeerId; // -1 for all peers stats +} OVPN_GET_PEER_STATS, * POVPN_GET_PEER_STATS; + #define OVPN_IOCTL_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) #define OVPN_IOCTL_GET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) #define OVPN_IOCTL_NEW_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -207,3 +219,5 @@ #define OVPN_IOCTL_MP_ADD_IROUTE CTL_CODE(FILE_DEVICE_UNKNOWN, 17, METHOD_BUFFERED, FILE_ANY_ACCESS) #define OVPN_IOCTL_MP_DEL_IROUTE CTL_CODE(FILE_DEVICE_UNKNOWN, 18, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define OVPN_IOCTL_GET_PEER_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 19, METHOD_BUFFERED, FILE_ANY_ACCESS) -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1143?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: I525d7300e49f9a5a18e7146ee35ccc2af8184b8a Gerrit-Change-Number: 1143 Gerrit-PatchSet: 1 Gerrit-Owner: stipa <lst...@gm...> Gerrit-Reviewer: flichtenheld <fr...@li...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-Attention: flichtenheld <fr...@li...> Gerrit-MessageType: newchange |
From: Arne S. <ar...@rf...> - 2025-08-08 23:48:52
|
Am 08.08.2025 um 16:04 schrieb Jon Chiappetta: > Hi Arne, > > You are correct, I didn't do a very good job of explaining the code in > my blog post, I usually keep those short with more screen captures > because I figure that not many people would actually take the time to > read through it there. Also, I didn't really add many comments either > but I did try to copy the present style of the code to try and make it > match and be more consistent throughout, even if not perfect yet. I'm > still testing out the change myself in my own home setup here to see > if I run into any bad edge cases along the way. > > I can always try to explain the different code parts as I am indeed > modifying the core parts of the read and write operations for tun and > tcp so it's a big change to make to the code base. Basically this > change is important to me in particular because of my setup and > requirements in specific. I have WiFi LAN clients which all assume a > 1500 byte MTU on their side and I have a router WAN client which > enforces a 1500 byte MTU on the internet's side. In the middle of my > core network is a VPN box and almost every VPN software will operate > in UDP mode with a sub-1500 MTU in the middle of this network > pipeline. This is not a good design to have in general as I don't want > to waste cycles fragmenting and/or compressing the data into smaller > sized UDP packets. With the code change I am presenting, I am able to > specify a true 1500 byte VPN MTU interface with the exact matching > 1500 byte read calls to the TUN interface itself (the code base had to > be modified to allow for this because it was adding onto the > payload_size used in the read call which I didn't want as I am > operating on exact multiples of 1500 bytes in specific). OpenVPN already supports arbitrary MTU sizes just fine. The default is even MTU 1500. mssfix is also enabled by default to avoid fragmentation, which especially for UDP is bad. So I am not sure what you are modifying here because what you are describing is already well supported by OpenVPN. > > With this change, my network pipeline is a true 1500 byte MTU which > matches all the way from the client side to the vpn link to the > internet side and to the server side (end to end to end to end). In > addition, I also added the ability to batch together multiple 1500 > byte read calls (specifically 6 x 1500 bytes into 9000 bytes) into one > single encrypt sign call and one single TCP write call. So this needs a different wire format and some extra headers/framing as you need to split this jumbo packet again into 1500 bytes packets on the receiver. In order to justify introducing a new framing/packet format, a clear benefit should be shown. It would be for example to show what the performance benefit here actually is, e.g. by doing a simple test. do a simple client server OpenVPN connection and run iperf between client and server - without VPN - unmodified OpenVPN with --mtu 1500 - unmodified OpenVPN with --mtu 9000 - your approach That would give an indication what kind of gains we are talking here about. > This allows the encryption method to operate only once on a much > larger payload size as well as allow the linux kernel to efficiently > transfer the data with order and delivery guaranteed as fast as > possible. The code base had to be modified to allow for all of this as > well as it was preventing me from performing this much larger sized > ssl sign and encrypt + tcp read and write (the code base assumes you > are operating on only 1 tun read call worth of data at a time > everywhere). > > This is exactly why I prefer using TCP to tunnel encrypted network > data as my solution provided can properly set a full sized 1500 byte > MTU as well as perform an exact matching read call of 1500 bytes to > get the full amount of data from the interface and then bulk it > together to efficiently encrypt it and then use the magic of TCP to > transfer that data all at once as quickly as possible without any need > for fragmentation or compression. I don't think any other VPN product > on the market offers that kind of functionality as far as I am aware > as most other VPN products use a smaller sized MTU as well as the > packet size limitations of UDP. I believe that this could be a > distinguishing feature for OpenVPN as well as automatically solve some > of the issues that folks run into when inserting a VPN appliance into > the middle of their network setups. In TCP mode can you get away with a lot of thing you are doing. In UDP mode, having tunnel inside MTU of 1500 or pushing large fragmented IP/UDP packets will be very detrimental to VPN performance. > > I've been running this change on my own setup to at least make sure it > works and it seems to be running pretty nicely so far. I haven't > experienced any fragmentation or performance issues as any sized data > that comes off the clients LAN side is fully taken care of now through > the VPN side and onto the WAN and server side. This whole MTU fragment and tun must have 1500 MTU to not see these problems is not a thing that I observe and also not a logical conclusion for me. > > If this is something you are not interested in I can understand that, > I can stop posting here and the most I can do is at least submit a > pull request in case anyone in the future is indeed interested in such > work. It'd be nice to contribute to a good quality open source project > that I have used for many many years and something which may help > solve other community member's issues with regards to the small sized > MTU + UDP problem which does exist in practice and really hampers > connections along the way in a network design. It would be good to understand what you are actually referring to here. Small sized MTU + UDP problem is quite vague as there are multiple things that can cause probelems with an UDP VPN. > > I also don't mind explaining my code parts if you actually want, I > just need to take time to write them out and describe what they are > doing and why. As you can see, I am trying to achieve a very specific > and exact design goal that the code base wasn't originally allowing > for, so I had to make some modifications to be able to accomplish it. > |
From: Jon C. <ro...@fo...> - 2025-08-08 18:50:23
|
[one last self reply] I did some work today to fix a potential issue with my read tun method where it may not read from the file descriptor upon reconnection. I believe it should be fixed now just in case anyone is still interested in a change like this. Example POC Pull Request Link: https://github.com/OpenVPN/openvpn/pull/814/files $ cat 0001-bulk-mode.patch >From 28d43ff49984f2d3c787fa791d0a7a548ac80fad Mon Sep 17 00:00:00 2001 From: Jon Chiappetta <ro...@fo...> Date: Wed, 6 Aug 2025 16:33:18 -0400 Subject: [PATCH] bulk mode --- src/openvpn/forward.c | 225 ++++++++++++++++++++++++++++++++++++++++-- src/openvpn/forward.h | 4 + src/openvpn/init.c | 57 +++++++++++ src/openvpn/mtu.c | 10 +- src/openvpn/mtu.h | 13 +++ src/openvpn/multi.c | 7 +- src/openvpn/openvpn.h | 10 ++ src/openvpn/options.c | 8 ++ src/openvpn/options.h | 3 + 9 files changed, 325 insertions(+), 12 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 75ca9d5c..b9d96482 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -46,6 +46,9 @@ #include "mstats.h" +#include <sys/select.h> +#include <sys/time.h> + counter_type link_read_bytes_global; /* GLOBAL */ counter_type link_write_bytes_global; /* GLOBAL */ @@ -78,6 +81,34 @@ show_wait_status(struct context *c) #endif /* ifdef ENABLE_DEBUG */ +bool check_bulk_mode(struct context *c) +{ + if ((c->c2.frame.bulk_size > 0) && (c->c1.tuntap != NULL) && (c->c2.buffers != NULL)) + { + return true; + } + return false; +} + +void xfer_io(struct context *c, struct context *b) +{ + //dmsg(M_INFO, "BULK MODE xfer_io c [%d] [%p] [%p] [%d] [%d] [%d]", c->c2.frame.bulk_size, c->c1.tuntap, c->c2.buffers, BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.to_link)); + //dmsg(M_INFO, "BULK MODE xfer_io b [%d] [%p] [%p] [%d] [%d] [%d]", b->c2.frame.bulk_size, b->c1.tuntap, b->c2.buffers, BLEN(&b->c2.buf), BLEN(&b->c2.to_tun), BLEN(&b->c2.to_link)); + int plen = 0; + if (check_bulk_mode(b)) + { + int leng = (b->c2.buffers->bufs_indx + 1); + for (int x = 0; x < leng; ++x) + { + plen = BLEN(&b->c2.bufs[x]); + if (plen < 1) { c->c2.bufs[x].len = 0; } + else { c->c2.bufs[x] = b->c2.bufs[x]; } + } + c->c2.buffers->bufs_indx = b->c2.buffers->bufs_indx; + b->c2.buffers->bufs_indx = -1; + } +} + static void check_tls_errors_co(struct context *c) { @@ -605,6 +636,21 @@ buffer_turnover(const uint8_t *orig_buf, struct buffer *dest_stub, struct buffer } } +uint8_t *buff_prepsize(uint8_t *buff, int *size) +{ + buff[0] = ((*size >> 8) & 0xff); + buff[1] = ((*size >> 0) & 0xff); + buff += 2; + return buff; +} + +uint8_t *buff_postsize(uint8_t *buff, int *size) +{ + *size = ((buff[0] << 8) + (buff[1] << 0)); + buff += 2; + return buff; +} + /* * Compress, fragment, encrypt and HMAC-sign an outgoing packet. * Input: c->c2.buf @@ -1031,6 +1077,7 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo fprintf(stderr, "R"); } #endif + msg(D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii(lsi->proto, lsi->af, true), BLEN(&c->c2.buf), print_link_socket_actual(&c->c2.from, &gc), PROTO_DUMP(&c->c2.buf, &gc)); @@ -1211,6 +1258,26 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, } } +void process_incoming_link_part3(struct context *c) +{ + int leng = BLEN(&c->c2.buf); + if (leng > 0) + { + if (check_bulk_mode(c)) + { + c->c2.buffers->send_tun_max.offset = TUN_BAT_OFF; + c->c2.buffers->send_tun_max.len = leng; + bcopy(BPTR(&c->c2.buf), BPTR(&c->c2.buffers->send_tun_max), leng); + c->c2.to_tun.offset += 2; + c->c2.buf.offset += 2; + } + } + else + { + buf_reset(&c->c2.to_tun); + } +} + static void process_incoming_link(struct context *c, struct link_socket *sock) { @@ -1221,6 +1288,7 @@ process_incoming_link(struct context *c, struct link_socket *sock) process_incoming_link_part1(c, lsi, false); process_incoming_link_part2(c, lsi, orig_buf); + process_incoming_link_part3(c); perf_pop(); } @@ -1321,7 +1389,7 @@ process_incoming_dco(struct context *c) */ void -read_incoming_tun(struct context *c) +read_incoming_tun_part2(struct context *c) { /* * Setup for read() call on TUN/TAP device. @@ -1382,6 +1450,54 @@ read_incoming_tun(struct context *c) perf_pop(); } +void read_incoming_tun_part3(struct context *c) +{ + fd_set rfds; + struct timeval timo; + if (check_bulk_mode(c)) + { + int plen = 0, pidx = -1; + int fdno = c->c1.tuntap->fd; + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + int leng = plen, indx = (pidx + 1); + if (indx >= TUN_BAT_MIN) { break; } + if (leng < 1) + { + FD_ZERO(&rfds); + FD_SET(fdno, &rfds); + timo.tv_sec = 0; + timo.tv_usec = 0; + select(fdno+1, &rfds, NULL, NULL, &timo); + if (FD_ISSET(fdno, &rfds)) + { + read_incoming_tun_part2(c); + plen = BLEN(&c->c2.buf); + } else { break; } + } + leng = plen; + if (leng > 0) + { + c->c2.buffers->read_tun_bufs[indx].offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_bufs[indx].len = leng; + bcopy(BPTR(&c->c2.buf), BPTR(&c->c2.buffers->read_tun_bufs[indx]), leng); + c->c2.bufs[indx] = c->c2.buffers->read_tun_bufs[indx]; + pidx = indx; + } else { break; } + plen = 0; + } + c->c2.buffers->bufs_indx = pidx; + } +} + +void read_incoming_tun(struct context *c) +{ + if (c->c2.frame.bulk_size <= 0) { + read_incoming_tun_part2(c); + } + read_incoming_tun_part3(c); +} + /** * Drops UDP packets which OS decided to route via tun. * @@ -1469,7 +1585,7 @@ drop_if_recursive_routing(struct context *c, struct buffer *buf) */ void -process_incoming_tun(struct context *c, struct link_socket *out_sock) +process_incoming_tun_part2(struct context *c, struct link_socket *out_sock) { struct gc_arena gc = gc_new(); @@ -1488,7 +1604,7 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) #endif /* Show packet content */ - dmsg(D_TUN_RW, "TUN READ [%d]", BLEN(&c->c2.buf)); + dmsg(D_TUN_RW, "TUN READ [%d] [%d]", BLEN(&c->c2.buf), c->c2.frame.buf.payload_size); if (c->c2.buf.len > 0) { @@ -1512,7 +1628,9 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) } if (c->c2.buf.len > 0) { + if ((c->c2.buffers == NULL) || (c->c2.buffers->flag_ciph != -2)) { encrypt_sign(c, true); + } } else { @@ -1522,6 +1640,65 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) gc_free(&gc); } +void process_incoming_tun_part3(struct context *c, struct link_socket *out_sock) +{ + if (c->c2.buf.len > 0) + { + if (check_bulk_mode(c)) + { + c->c2.buffers->flag_ciph = -2; + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_max.len = 0; + uint8_t *temp = BPTR(&c->c2.buffers->read_tun_max); + int plen = 0, fdno = c->c1.tuntap->fd; + int maxl = 0, leng = (c->c2.buffers->bufs_indx + 1); + if ((fdno > 0) && (leng > 0)) + { + for (int x = 0; x < leng; ++x) + { + c->c2.buf = c->c2.bufs[x]; + process_incoming_tun_part2(c, out_sock); + if (BLEN(&c->c2.buf) < 1) + { + c->c2.bufs[x].len = 0; + } + } + for (int x = 0; x < leng; ++x) + { + plen = c->c2.bufs[x].len; + if (plen > 0) + { + temp = buff_prepsize(temp, &plen); + bcopy(BPTR(&c->c2.bufs[x]), temp, plen); + temp += plen; maxl += (plen + 2); + } + } + if (maxl > 0) + { + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_max.len = maxl; + c->c2.buf = c->c2.buffers->read_tun_max; + encrypt_sign(c, true); + } + } + c->c2.buffers->bufs_indx = -1; + c->c2.buffers->flag_ciph = -1; + } + } + else + { + buf_reset(&c->c2.to_link); + } +} + +void process_incoming_tun(struct context *c, struct link_socket *out_sock) +{ + if (c->c2.frame.bulk_size <= 0) { + process_incoming_tun_part2(c, out_sock); + } + process_incoming_tun_part3(c, out_sock); +} + /** * Forges a IPv6 ICMP packet with a no route to host error code from the * IPv6 packet in buf and sends it directly back to the client via the tun @@ -1748,7 +1925,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) perf_push(PERF_PROC_OUT_LINK); - if (c->c2.to_link.len > 0 && c->c2.to_link.len <= c->c2.frame.buf.payload_size) + if (c->c2.to_link.len > 0 && (c->c2.to_link.len <= c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0)) { /* * Setup for call to send/sendto which will send @@ -1793,6 +1970,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) fprintf(stderr, "W"); } #endif + msg(D_LINK_RW, "%s WRITE [%d] to %s: %s", proto2ascii(sock->info.proto, sock->info.af, true), BLEN(&c->c2.to_link), print_link_socket_actual(c->c2.to_link_addr, &gc), PROTO_DUMP(&c->c2.to_link, &gc)); @@ -1892,7 +2070,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) */ void -process_outgoing_tun(struct context *c, struct link_socket *in_sock) +process_outgoing_tun_part2(struct context *c, struct link_socket *in_sock) { /* * Set up for write() call to TUN/TAP @@ -1912,7 +2090,7 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) process_ip_header(c, PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | PIPV4_CLIENT_NAT | PIP_OUTGOING, &c->c2.to_tun, in_sock); - if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size) + if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0) { /* * Write to TUN/TAP device. @@ -1925,7 +2103,8 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) fprintf(stderr, "w"); } #endif - dmsg(D_TUN_RW, "TUN WRITE [%d]", BLEN(&c->c2.to_tun)); + + dmsg(D_TUN_RW, "TUN WRITE [%d] [%d]", BLEN(&c->c2.to_tun), c->c2.frame.buf.payload_size); #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify(BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun), TUNNEL_TYPE(c->c1.tuntap), @@ -1981,6 +2160,38 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) perf_pop(); } +void process_outgoing_tun_part3(struct context *c, struct link_socket *in_sock) +{ + if (check_bulk_mode(c)) + { + int maxl = 0, plen = 0; + int leng = BLEN(&c->c2.buffers->send_tun_max); + uint8_t *temp = BPTR(&c->c2.buffers->send_tun_max); + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + temp = buff_postsize(temp, &plen); + if ((leng > 0) && (plen > 0) && ((maxl + plen) < leng)) + { + c->c2.to_tun = c->c2.buffers->to_tun_max; + c->c2.to_tun.offset = TUN_BAT_OFF; + c->c2.to_tun.len = plen; + bcopy(temp, BPTR(&c->c2.to_tun), plen); + temp += plen; maxl += (plen + 2); + process_outgoing_tun_part2(c, in_sock); + } else { break; } + } + } + buf_reset(&c->c2.to_tun); +} + +void process_outgoing_tun(struct context *c, struct link_socket *in_sock) +{ + if (c->c2.frame.bulk_size <= 0) { + process_outgoing_tun_part2(c, in_sock); + } + process_outgoing_tun_part3(c, in_sock); +} + void pre_select(struct context *c) { diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index d5641491..9fda1583 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -79,6 +79,8 @@ void pre_select(struct context *c); void process_io(struct context *c, struct link_socket *sock); +void xfer_io(struct context *c, struct context *b); + /**********************************************************************/ /** @@ -196,6 +198,8 @@ bool process_incoming_link_part1(struct context *c, struct link_socket_info *lsi void process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf); +void process_incoming_link_part3(struct context *c); + /** * Transfers \c float_sa data extracted from an incoming DCO * PEER_FLOAT_NTF to \c out_osaddr for later processing. diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 40ae2c8c..bbdbad46 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2971,6 +2971,10 @@ frame_finalize_options(struct context *c, const struct options *o) tailroom += COMP_EXTRA_BUFFER(payload_size); #endif + if (frame->bulk_size > 0) { + payload_size = frame->tun_mtu; + } + frame->buf.payload_size = payload_size; frame->buf.headroom = headroom; frame->buf.tailroom = tailroom; @@ -3473,6 +3477,9 @@ do_init_frame_tls(struct context *c) if (c->c2.tls_multi) { tls_multi_init_finalize(c->c2.tls_multi, c->options.ce.tls_mtu); + if (c->c2.frame.bulk_size > 0) { + c->c2.tls_multi->opt.frame.buf.payload_size = c->c2.frame.tun_mtu; + } ASSERT(c->c2.tls_multi->opt.frame.buf.payload_size <= c->c2.frame.buf.payload_size); frame_print(&c->c2.tls_multi->opt.frame, D_MTU_INFO, "Control Channel MTU parms"); @@ -3536,6 +3543,14 @@ do_init_frame(struct context *c) c->c2.frame.extra_tun += c->options.ce.tun_mtu_extra; } + /* + * Adjust bulk size based on the --bulk-mode parameter. + */ + if (c->options.ce.bulk_mode) + { + c->c2.frame.bulk_size = c->options.ce.tun_mtu; + } + /* * Fill in the blanks in the frame parameters structure, * make sure values are rational, etc. @@ -3676,9 +3691,41 @@ init_context_buffers(const struct frame *frame) size_t buf_size = BUF_SIZE(frame); + if (frame->bulk_size > 0) { + size_t off_size = (frame->buf.headroom + TUN_BAT_OFF + frame->buf.tailroom); + buf_size = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, off_size); + } + + dmsg(M_INFO, "MEM NEW [%ld] [%d+%d+%d]", buf_size, frame->buf.headroom, frame->buf.payload_size, frame->buf.tailroom); + b->read_link_buf = alloc_buf(buf_size); b->read_tun_buf = alloc_buf(buf_size); + if (frame->bulk_size > 0) { + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + size_t part_size = BUF_SIZE(frame); + b->read_tun_bufs[x] = alloc_buf(part_size); + b->read_tun_bufs[x].offset = TUN_BAT_OFF; + b->read_tun_bufs[x].len = 0; + } + + b->read_tun_max = alloc_buf(buf_size); + b->read_tun_max.offset = TUN_BAT_OFF; + b->read_tun_max.len = 0; + + b->send_tun_max = alloc_buf(buf_size); + b->send_tun_max.offset = TUN_BAT_OFF; + b->send_tun_max.len = 0; + + b->to_tun_max = alloc_buf(buf_size); + b->to_tun_max.offset = TUN_BAT_OFF; + b->to_tun_max.len = 0; + } + + b->bufs_indx = -1; + b->flag_ciph = -1; + b->aux_buf = alloc_buf(buf_size); b->encrypt_buf = alloc_buf(buf_size); @@ -3701,6 +3748,16 @@ free_context_buffers(struct context_buffers *b) free_buf(&b->read_tun_buf); free_buf(&b->aux_buf); + if (b->to_tun_max.data) { + free_buf(&b->to_tun_max); + free_buf(&b->send_tun_max); + free_buf(&b->read_tun_max); + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + free_buf(&b->read_tun_bufs[x]); + } + } + #ifdef USE_COMP free_buf(&b->compress_buf); free_buf(&b->decompress_buf); diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c index a419e32d..7e35c837 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -41,9 +41,15 @@ void alloc_buf_sock_tun(struct buffer *buf, const struct frame *frame) { /* allocate buffer for overlapped I/O */ - *buf = alloc_buf(BUF_SIZE(frame)); + size_t alen = BUF_SIZE(frame); + size_t blen = frame->buf.payload_size; + if (frame->bulk_size > 0) { + alen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_OFF); + blen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_NOP); + } + *buf = alloc_buf(alen); ASSERT(buf_init(buf, frame->buf.headroom)); - buf->len = frame->buf.payload_size; + buf->len = blen; ASSERT(buf_safe(buf, 0)); } diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index 925ef0bf..eb799fb3 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -58,6 +58,14 @@ */ #define TUN_MTU_MIN 100 +/* + * Bulk mode static define values. + */ +#define TUN_BAT_MIN 6 +#define TUN_BAT_MAX 9 +#define TUN_BAT_OFF 256 +#define TUN_BAT_NOP 0 + /* * Default MTU of network over which tunnel data will pass by TCP/UDP. */ @@ -152,6 +160,10 @@ struct frame * which defaults to 0 for tun and 32 * (\c TAP_MTU_EXTRA_DEFAULT) for tap. * */ + + int bulk_size; /**< Signal to the init frame function + * to allow for bulk mode TCP transfers. + * */ }; /* Forward declarations, to prevent includes */ @@ -171,6 +183,7 @@ struct options; * larger than the headroom. */ #define BUF_SIZE(f) ((f)->buf.headroom + (f)->buf.payload_size + (f)->buf.tailroom) +#define BAT_SIZE(a, b, c) ((a * b) + c) /* * Function prototypes. diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index e1ce32ab..9e089703 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -3414,6 +3414,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } process_incoming_link_part2(c, lsi, orig_buf); + process_incoming_link_part3(c); } perf_pop(); @@ -3558,9 +3559,7 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags const int dev_type = TUNNEL_TYPE(m->top.c1.tuntap); int16_t vid = 0; -#ifdef MULTI_DEBUG_EVENT_LOOP - printf("TUN -> TCP/UDP [%d]\n", BLEN(&m->top.c2.buf)); -#endif + msg(D_MULTI_DEBUG, "TUN -> TCP/UDP [%d]", BLEN(&m->top.c2.buf)); if (m->pending) { @@ -3610,6 +3609,8 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags { /* transfer packet pointer from top-level context buffer to instance */ c->c2.buf = m->top.c2.buf; + /* todo determine if to call this (multi_process_incoming_tun) for each bulk item read? */ + xfer_io(c, &m->top); } else { diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index cd99cd40..21fa8967 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -112,6 +112,14 @@ struct context_buffers */ struct buffer read_link_buf; struct buffer read_tun_buf; + + struct buffer read_tun_bufs[TUN_BAT_MAX]; + struct buffer read_tun_max; + struct buffer send_tun_max; + struct buffer to_tun_max; + + int bufs_indx; + int flag_ciph; }; /* @@ -376,6 +384,8 @@ struct context_2 struct buffer to_tun; struct buffer to_link; + struct buffer bufs[TUN_BAT_MAX]; + /* should we print R|W|r|w to console on packet transfers? */ bool log_rw; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index c54032d8..041d17d0 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -304,6 +304,7 @@ static const char usage_message[] = " 'maybe' -- Use per-route hints\n" " 'yes' -- Always DF (Don't Fragment)\n" "--mtu-test : Empirically measure and report MTU.\n" + "--bulk-mode : Use bulk TUN/TCP reads/writes.\n" #ifdef ENABLE_FRAGMENT "--fragment max : Enable internal datagram fragmentation so that no UDP\n" " datagrams are sent which are larger than max bytes.\n" @@ -3005,6 +3006,9 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) ce->tun_mtu_extra_defined = true; ce->tun_mtu_extra = TAP_MTU_EXTRA_DEFAULT; } + if (ce->proto != PROTO_TCP && ce->proto != PROTO_TCP_SERVER && ce->proto != PROTO_TCP_CLIENT) { + ce->bulk_mode = false; + } } /* @@ -9926,6 +9930,10 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, goto err; } } + else if (streq(p[0], "bulk-mode")) + { + options->ce.bulk_mode = true; + } else { int i; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 38e67c8d..d1b0586d 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -174,6 +174,9 @@ struct connection_entry /* Allow only client that support resending the wrapped client key */ bool tls_crypt_v2_force_cookie; + + /* Bulk mode allows for multiple tun reads + larger tcp writes */ + bool bulk_mode; }; struct remote_entry -- 2.39.5 (Apple Git-154) On Fri, Aug 8, 2025 at 10:23 AM Jon Chiappetta <ro...@fo...> wrote: > [replying to my own update thread] > > I pushed an update to the PR with the following small changes: > > - rebased to the latest master commit > - increased the data buffer size to be slightly bigger than the read > buffer size > - copied two more buf resets so that the additional bulk forward functions > match the present logic checks > > Example updated pull request: > https://github.com/OpenVPN/openvpn/pull/814/files > > $ cat 0001-bulk-mode.patch > From 2ce0b023d105e7ecc289a414cd26f7ebc8bcbcaf Mon Sep 17 00:00:00 2001 > From: Jon Chiappetta <ro...@fo...> > Date: Wed, 6 Aug 2025 16:33:18 -0400 > Subject: [PATCH] bulk mode > > --- > src/openvpn/forward.c | 228 ++++++++++++++++++++++++++++++++++++++++-- > src/openvpn/forward.h | 4 + > src/openvpn/init.c | 57 +++++++++++ > src/openvpn/mtu.c | 10 +- > src/openvpn/mtu.h | 13 +++ > src/openvpn/multi.c | 7 +- > src/openvpn/openvpn.h | 10 ++ > src/openvpn/options.c | 8 ++ > src/openvpn/options.h | 3 + > 9 files changed, 328 insertions(+), 12 deletions(-) > > diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c > index 75ca9d5c..0af983e9 100644 > --- a/src/openvpn/forward.c > +++ b/src/openvpn/forward.c > @@ -46,6 +46,9 @@ > > #include "mstats.h" > > +#include <sys/select.h> > +#include <sys/time.h> > + > counter_type link_read_bytes_global; /* GLOBAL */ > counter_type link_write_bytes_global; /* GLOBAL */ > > @@ -78,6 +81,32 @@ show_wait_status(struct context *c) > > #endif /* ifdef ENABLE_DEBUG */ > > +bool check_bulk_mode(struct context *c) > +{ > + if ((c->c2.frame.bulk_size > 0) && (c->c1.tuntap != NULL) && > (c->c2.buffers != NULL)) > + { > + return true; > + } > + return false; > +} > + > +void xfer_io(struct context *c, struct context *b) > +{ > + int plen = 0; > + if (check_bulk_mode(b)) > + { > + int leng = (b->c2.buffers->bufs_indx + 1); > + for (int x = 0; x < leng; ++x) > + { > + plen = BLEN(&b->c2.bufs[x]); > + if (plen < 1) { c->c2.bufs[x].len = 0; } > + else { c->c2.bufs[x] = b->c2.bufs[x]; } > + } > + c->c2.buffers->bufs_indx = b->c2.buffers->bufs_indx; > + b->c2.buffers->bufs_indx = -1; > + } > +} > + > static void > check_tls_errors_co(struct context *c) > { > @@ -605,6 +634,21 @@ buffer_turnover(const uint8_t *orig_buf, struct > buffer *dest_stub, struct buffer > } > } > > +uint8_t *buff_prepsize(uint8_t *buff, int *size) > +{ > + buff[0] = ((*size >> 8) & 0xff); > + buff[1] = ((*size >> 0) & 0xff); > + buff += 2; > + return buff; > +} > + > +uint8_t *buff_postsize(uint8_t *buff, int *size) > +{ > + *size = ((buff[0] << 8) + (buff[1] << 0)); > + buff += 2; > + return buff; > +} > + > /* > * Compress, fragment, encrypt and HMAC-sign an outgoing packet. > * Input: c->c2.buf > @@ -1031,6 +1075,7 @@ process_incoming_link_part1(struct context *c, > struct link_socket_info *lsi, boo > fprintf(stderr, "R"); > } > #endif > + > msg(D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii(lsi->proto, > lsi->af, true), > BLEN(&c->c2.buf), print_link_socket_actual(&c->c2.from, &gc), > PROTO_DUMP(&c->c2.buf, &gc)); > > @@ -1211,6 +1256,27 @@ process_incoming_link_part2(struct context *c, > struct link_socket_info *lsi, > } > } > > +void process_incoming_link_part3(struct context *c) > +{ > + int leng = BLEN(&c->c2.buf); > + if (leng > 0) > + { > + if (check_bulk_mode(c)) > + { > + c->c2.buffers->send_tun_max.offset = TUN_BAT_OFF; > + c->c2.buffers->send_tun_max.len = leng; > + bcopy(BPTR(&c->c2.buf), BPTR(&c->c2.buffers->send_tun_max), > leng); > + //dmsg(M_INFO, "FWD BAT LINK 0 [%d] [%d] [%d] [%d] [%d]", > BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf), > BLEN(&c->c2.buffers->read_link_buf), BLEN(&c->c2.buffers->send_tun_max)); > + c->c2.to_tun.offset += 2; > + c->c2.buf.offset += 2; > + } > + } > + else > + { > + buf_reset(&c->c2.to_tun); > + } > +} > + > static void > process_incoming_link(struct context *c, struct link_socket *sock) > { > @@ -1221,6 +1287,7 @@ process_incoming_link(struct context *c, struct > link_socket *sock) > > process_incoming_link_part1(c, lsi, false); > process_incoming_link_part2(c, lsi, orig_buf); > + process_incoming_link_part3(c); > > perf_pop(); > } > @@ -1321,7 +1388,7 @@ process_incoming_dco(struct context *c) > */ > > void > -read_incoming_tun(struct context *c) > +read_incoming_tun_part2(struct context *c) > { > /* > * Setup for read() call on TUN/TAP device. > @@ -1382,6 +1449,55 @@ read_incoming_tun(struct context *c) > perf_pop(); > } > > +void read_incoming_tun_part3(struct context *c) > +{ > + fd_set rfds; > + struct timeval timo; > + if (check_bulk_mode(c)) > + { > + int plen = 0; > + int fdno = c->c1.tuntap->fd; > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + int leng = plen; > + int indx = (c->c2.buffers->bufs_indx + 1); > + if (indx >= TUN_BAT_MIN) { break; } > + if (leng < 1) > + { > + FD_ZERO(&rfds); > + FD_SET(fdno, &rfds); > + timo.tv_sec = 0; > + timo.tv_usec = 0; > + select(fdno+1, &rfds, NULL, NULL, &timo); > + if (FD_ISSET(fdno, &rfds)) > + { > + read_incoming_tun_part2(c); > + plen = BLEN(&c->c2.buf); > + } else { break; } > + } > + //dmsg(M_INFO, "FWD BAT READ 0 [%d] [%d] [%d] [%d] [%d]", > c->c2.buffers->bufs_indx + 1, fdno, BLEN(&c->c2.buf), > BLEN(&c->c2.buffers->read_tun_buf), BLEN(&c->c2.buffers->read_tun_max)); > + leng = plen; > + if (leng > 0) > + { > + c->c2.buffers->read_tun_bufs[indx].offset = TUN_BAT_OFF; > + c->c2.buffers->read_tun_bufs[indx].len = leng; > + bcopy(BPTR(&c->c2.buf), > BPTR(&c->c2.buffers->read_tun_bufs[indx]), leng); > + c->c2.bufs[indx] = c->c2.buffers->read_tun_bufs[indx]; > + c->c2.buffers->bufs_indx = indx; > + } else { break; } > + plen = 0; > + } > + } > +} > + > +void read_incoming_tun(struct context *c) > +{ > + if (c->c2.frame.bulk_size <= 0) { > + read_incoming_tun_part2(c); > + } > + read_incoming_tun_part3(c); > +} > + > /** > * Drops UDP packets which OS decided to route via tun. > * > @@ -1469,7 +1585,7 @@ drop_if_recursive_routing(struct context *c, struct > buffer *buf) > */ > > void > -process_incoming_tun(struct context *c, struct link_socket *out_sock) > +process_incoming_tun_part2(struct context *c, struct link_socket > *out_sock) > { > struct gc_arena gc = gc_new(); > > @@ -1488,7 +1604,7 @@ process_incoming_tun(struct context *c, struct > link_socket *out_sock) > #endif > > /* Show packet content */ > - dmsg(D_TUN_RW, "TUN READ [%d]", BLEN(&c->c2.buf)); > + dmsg(D_TUN_RW, "TUN READ [%d] [%d]", BLEN(&c->c2.buf), > c->c2.frame.buf.payload_size); > > if (c->c2.buf.len > 0) > { > @@ -1512,7 +1628,9 @@ process_incoming_tun(struct context *c, struct > link_socket *out_sock) > } > if (c->c2.buf.len > 0) > { > + if ((c->c2.buffers == NULL) || (c->c2.buffers->flag_ciph != -2)) { > encrypt_sign(c, true); > + } > } > else > { > @@ -1522,6 +1640,67 @@ process_incoming_tun(struct context *c, struct > link_socket *out_sock) > gc_free(&gc); > } > > +void process_incoming_tun_part3(struct context *c, struct link_socket > *out_sock) > +{ > + if (c->c2.buf.len > 0) > + { > + if (check_bulk_mode(c)) > + { > + c->c2.buffers->flag_ciph = -2; > + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; > + c->c2.buffers->read_tun_max.len = 0; > + uint8_t *temp = BPTR(&c->c2.buffers->read_tun_max); > + int plen = 0, fdno = c->c1.tuntap->fd; > + int maxl = 0, leng = (c->c2.buffers->bufs_indx + 1); > + if ((fdno > 0) && (leng > 0)) > + { > + for (int x = 0; x < leng; ++x) > + { > + c->c2.buf = c->c2.bufs[x]; > + //dmsg(M_INFO, "FWD BAT INPT 0 [%d] [%d] [%d] [%d] > [%d]", x, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), > BLEN(&c->c2.bufs[x])); > + process_incoming_tun_part2(c, out_sock); > + if (BLEN(&c->c2.buf) < 1) > + { > + c->c2.bufs[x].len = 0; > + } > + } > + for (int x = 0; x < leng; ++x) > + { > + plen = c->c2.bufs[x].len; > + if (plen > 0) > + { > + temp = buff_prepsize(temp, &plen); > + bcopy(BPTR(&c->c2.bufs[x]), temp, plen); > + temp += plen; maxl += (plen + 2); > + } > + } > + if (maxl > 0) > + { > + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; > + c->c2.buffers->read_tun_max.len = maxl; > + c->c2.buf = c->c2.buffers->read_tun_max; > + //dmsg(M_INFO, "FWD BAT INPT 1 [%d] [%d] [%d] [%d] > [%d]", maxl, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), > BLEN(&c->c2.buffers->read_tun_max)); > + encrypt_sign(c, true); > + } > + } > + c->c2.buffers->bufs_indx = -1; > + c->c2.buffers->flag_ciph = -1; > + } > + } > + else > + { > + buf_reset(&c->c2.to_link); > + } > +} > + > +void process_incoming_tun(struct context *c, struct link_socket *out_sock) > +{ > + if (c->c2.frame.bulk_size <= 0) { > + process_incoming_tun_part2(c, out_sock); > + } > + process_incoming_tun_part3(c, out_sock); > +} > + > /** > * Forges a IPv6 ICMP packet with a no route to host error code from the > * IPv6 packet in buf and sends it directly back to the client via the tun > @@ -1748,7 +1927,7 @@ process_outgoing_link(struct context *c, struct > link_socket *sock) > > perf_push(PERF_PROC_OUT_LINK); > > - if (c->c2.to_link.len > 0 && c->c2.to_link.len <= > c->c2.frame.buf.payload_size) > + if (c->c2.to_link.len > 0 && (c->c2.to_link.len <= > c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0)) > { > /* > * Setup for call to send/sendto which will send > @@ -1793,6 +1972,7 @@ process_outgoing_link(struct context *c, struct > link_socket *sock) > fprintf(stderr, "W"); > } > #endif > + > msg(D_LINK_RW, "%s WRITE [%d] to %s: %s", > proto2ascii(sock->info.proto, sock->info.af, true), > BLEN(&c->c2.to_link), > print_link_socket_actual(c->c2.to_link_addr, &gc), > PROTO_DUMP(&c->c2.to_link, &gc)); > @@ -1892,7 +2072,7 @@ process_outgoing_link(struct context *c, struct > link_socket *sock) > */ > > void > -process_outgoing_tun(struct context *c, struct link_socket *in_sock) > +process_outgoing_tun_part2(struct context *c, struct link_socket *in_sock) > { > /* > * Set up for write() call to TUN/TAP > @@ -1912,7 +2092,7 @@ process_outgoing_tun(struct context *c, struct > link_socket *in_sock) > process_ip_header(c, PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | > PIPV4_CLIENT_NAT | PIP_OUTGOING, > &c->c2.to_tun, in_sock); > > - if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size) > + if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size || > c->c2.frame.bulk_size > 0) > { > /* > * Write to TUN/TAP device. > @@ -1925,7 +2105,8 @@ process_outgoing_tun(struct context *c, struct > link_socket *in_sock) > fprintf(stderr, "w"); > } > #endif > - dmsg(D_TUN_RW, "TUN WRITE [%d]", BLEN(&c->c2.to_tun)); > + > + dmsg(D_TUN_RW, "TUN WRITE [%d] [%d]", BLEN(&c->c2.to_tun), > c->c2.frame.buf.payload_size); > > #ifdef PACKET_TRUNCATION_CHECK > ipv4_packet_size_verify(BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun), > TUNNEL_TYPE(c->c1.tuntap), > @@ -1981,6 +2162,39 @@ process_outgoing_tun(struct context *c, struct > link_socket *in_sock) > perf_pop(); > } > > +void process_outgoing_tun_part3(struct context *c, struct link_socket > *in_sock) > +{ > + if (check_bulk_mode(c)) > + { > + int maxl = 0, plen = 0; > + int leng = BLEN(&c->c2.buffers->send_tun_max); > + uint8_t *temp = BPTR(&c->c2.buffers->send_tun_max); > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + temp = buff_postsize(temp, &plen); > + if ((leng > 0) && (plen > 0) && ((maxl + plen) < leng)) > + { > + c->c2.to_tun = c->c2.buffers->to_tun_max; > + c->c2.to_tun.offset = TUN_BAT_OFF; > + c->c2.to_tun.len = plen; > + bcopy(temp, BPTR(&c->c2.to_tun), plen); > + temp += plen; maxl += (plen + 2); > + //dmsg(M_INFO, "FWD BAT OUTP 1 [%d] [%d] [%d] [%d]", x, > BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf)); > + process_outgoing_tun_part2(c, in_sock); > + } else { break; } > + } > + } > + buf_reset(&c->c2.to_tun); > +} > + > +void process_outgoing_tun(struct context *c, struct link_socket *in_sock) > +{ > + if (c->c2.frame.bulk_size <= 0) { > + process_outgoing_tun_part2(c, in_sock); > + } > + process_outgoing_tun_part3(c, in_sock); > +} > + > void > pre_select(struct context *c) > { > diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h > index d5641491..9fda1583 100644 > --- a/src/openvpn/forward.h > +++ b/src/openvpn/forward.h > @@ -79,6 +79,8 @@ void pre_select(struct context *c); > > void process_io(struct context *c, struct link_socket *sock); > > +void xfer_io(struct context *c, struct context *b); > + > > /**********************************************************************/ > /** > @@ -196,6 +198,8 @@ bool process_incoming_link_part1(struct context *c, > struct link_socket_info *lsi > void process_incoming_link_part2(struct context *c, struct > link_socket_info *lsi, > const uint8_t *orig_buf); > > +void process_incoming_link_part3(struct context *c); > + > /** > * Transfers \c float_sa data extracted from an incoming DCO > * PEER_FLOAT_NTF to \c out_osaddr for later processing. > diff --git a/src/openvpn/init.c b/src/openvpn/init.c > index 40ae2c8c..bbdbad46 100644 > --- a/src/openvpn/init.c > +++ b/src/openvpn/init.c > @@ -2971,6 +2971,10 @@ frame_finalize_options(struct context *c, const > struct options *o) > tailroom += COMP_EXTRA_BUFFER(payload_size); > #endif > > + if (frame->bulk_size > 0) { > + payload_size = frame->tun_mtu; > + } > + > frame->buf.payload_size = payload_size; > frame->buf.headroom = headroom; > frame->buf.tailroom = tailroom; > @@ -3473,6 +3477,9 @@ do_init_frame_tls(struct context *c) > if (c->c2.tls_multi) > { > tls_multi_init_finalize(c->c2.tls_multi, c->options.ce.tls_mtu); > + if (c->c2.frame.bulk_size > 0) { > + c->c2.tls_multi->opt.frame.buf.payload_size = > c->c2.frame.tun_mtu; > + } > ASSERT(c->c2.tls_multi->opt.frame.buf.payload_size <= > c->c2.frame.buf.payload_size); > frame_print(&c->c2.tls_multi->opt.frame, D_MTU_INFO, "Control > Channel MTU parms"); > > @@ -3536,6 +3543,14 @@ do_init_frame(struct context *c) > c->c2.frame.extra_tun += c->options.ce.tun_mtu_extra; > } > > + /* > + * Adjust bulk size based on the --bulk-mode parameter. > + */ > + if (c->options.ce.bulk_mode) > + { > + c->c2.frame.bulk_size = c->options.ce.tun_mtu; > + } > + > /* > * Fill in the blanks in the frame parameters structure, > * make sure values are rational, etc. > @@ -3676,9 +3691,41 @@ init_context_buffers(const struct frame *frame) > > size_t buf_size = BUF_SIZE(frame); > > + if (frame->bulk_size > 0) { > + size_t off_size = (frame->buf.headroom + TUN_BAT_OFF + > frame->buf.tailroom); > + buf_size = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, off_size); > + } > + > + dmsg(M_INFO, "MEM NEW [%ld] [%d+%d+%d]", buf_size, > frame->buf.headroom, frame->buf.payload_size, frame->buf.tailroom); > + > b->read_link_buf = alloc_buf(buf_size); > b->read_tun_buf = alloc_buf(buf_size); > > + if (frame->bulk_size > 0) { > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + size_t part_size = BUF_SIZE(frame); > + b->read_tun_bufs[x] = alloc_buf(part_size); > + b->read_tun_bufs[x].offset = TUN_BAT_OFF; > + b->read_tun_bufs[x].len = 0; > + } > + > + b->read_tun_max = alloc_buf(buf_size); > + b->read_tun_max.offset = TUN_BAT_OFF; > + b->read_tun_max.len = 0; > + > + b->send_tun_max = alloc_buf(buf_size); > + b->send_tun_max.offset = TUN_BAT_OFF; > + b->send_tun_max.len = 0; > + > + b->to_tun_max = alloc_buf(buf_size); > + b->to_tun_max.offset = TUN_BAT_OFF; > + b->to_tun_max.len = 0; > + } > + > + b->bufs_indx = -1; > + b->flag_ciph = -1; > + > b->aux_buf = alloc_buf(buf_size); > > b->encrypt_buf = alloc_buf(buf_size); > @@ -3701,6 +3748,16 @@ free_context_buffers(struct context_buffers *b) > free_buf(&b->read_tun_buf); > free_buf(&b->aux_buf); > > + if (b->to_tun_max.data) { > + free_buf(&b->to_tun_max); > + free_buf(&b->send_tun_max); > + free_buf(&b->read_tun_max); > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + free_buf(&b->read_tun_bufs[x]); > + } > + } > + > #ifdef USE_COMP > free_buf(&b->compress_buf); > free_buf(&b->decompress_buf); > diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c > index a419e32d..7e35c837 100644 > --- a/src/openvpn/mtu.c > +++ b/src/openvpn/mtu.c > @@ -41,9 +41,15 @@ void > alloc_buf_sock_tun(struct buffer *buf, const struct frame *frame) > { > /* allocate buffer for overlapped I/O */ > - *buf = alloc_buf(BUF_SIZE(frame)); > + size_t alen = BUF_SIZE(frame); > + size_t blen = frame->buf.payload_size; > + if (frame->bulk_size > 0) { > + alen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_OFF); > + blen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_NOP); > + } > + *buf = alloc_buf(alen); > ASSERT(buf_init(buf, frame->buf.headroom)); > - buf->len = frame->buf.payload_size; > + buf->len = blen; > ASSERT(buf_safe(buf, 0)); > } > > diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h > index 925ef0bf..eb799fb3 100644 > --- a/src/openvpn/mtu.h > +++ b/src/openvpn/mtu.h > @@ -58,6 +58,14 @@ > */ > #define TUN_MTU_MIN 100 > > +/* > + * Bulk mode static define values. > + */ > +#define TUN_BAT_MIN 6 > +#define TUN_BAT_MAX 9 > +#define TUN_BAT_OFF 256 > +#define TUN_BAT_NOP 0 > + > /* > * Default MTU of network over which tunnel data will pass by TCP/UDP. > */ > @@ -152,6 +160,10 @@ struct frame > * which defaults to 0 for tun and 32 > * (\c TAP_MTU_EXTRA_DEFAULT) for tap. > * */ > + > + int bulk_size; /**< Signal to the init frame function > + * to allow for bulk mode TCP transfers. > + * */ > }; > > /* Forward declarations, to prevent includes */ > @@ -171,6 +183,7 @@ struct options; > * larger than the headroom. > */ > #define BUF_SIZE(f) ((f)->buf.headroom + (f)->buf.payload_size + > (f)->buf.tailroom) > +#define BAT_SIZE(a, b, c) ((a * b) + c) > > /* > * Function prototypes. > diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c > index e1ce32ab..9e089703 100644 > --- a/src/openvpn/multi.c > +++ b/src/openvpn/multi.c > @@ -3414,6 +3414,7 @@ multi_process_incoming_link(struct multi_context *m, > struct multi_instance *inst > } > > process_incoming_link_part2(c, lsi, orig_buf); > + process_incoming_link_part3(c); > } > perf_pop(); > > @@ -3558,9 +3559,7 @@ multi_process_incoming_tun(struct multi_context *m, > const unsigned int mpp_flags > const int dev_type = TUNNEL_TYPE(m->top.c1.tuntap); > int16_t vid = 0; > > -#ifdef MULTI_DEBUG_EVENT_LOOP > - printf("TUN -> TCP/UDP [%d]\n", BLEN(&m->top.c2.buf)); > -#endif > + msg(D_MULTI_DEBUG, "TUN -> TCP/UDP [%d]", BLEN(&m->top.c2.buf)); > > if (m->pending) > { > @@ -3610,6 +3609,8 @@ multi_process_incoming_tun(struct multi_context *m, > const unsigned int mpp_flags > { > /* transfer packet pointer from top-level > context buffer to instance */ > c->c2.buf = m->top.c2.buf; > + /* todo determine if to call this > (multi_process_incoming_tun) for each bulk item read? */ > + xfer_io(c, &m->top); > } > else > { > diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h > index cd99cd40..21fa8967 100644 > --- a/src/openvpn/openvpn.h > +++ b/src/openvpn/openvpn.h > @@ -112,6 +112,14 @@ struct context_buffers > */ > struct buffer read_link_buf; > struct buffer read_tun_buf; > + > + struct buffer read_tun_bufs[TUN_BAT_MAX]; > + struct buffer read_tun_max; > + struct buffer send_tun_max; > + struct buffer to_tun_max; > + > + int bufs_indx; > + int flag_ciph; > }; > > /* > @@ -376,6 +384,8 @@ struct context_2 > struct buffer to_tun; > struct buffer to_link; > > + struct buffer bufs[TUN_BAT_MAX]; > + > /* should we print R|W|r|w to console on packet transfers? */ > bool log_rw; > > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index c54032d8..041d17d0 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -304,6 +304,7 @@ static const char usage_message[] = > " 'maybe' -- Use per-route hints\n" > " 'yes' -- Always DF (Don't Fragment)\n" > "--mtu-test : Empirically measure and report MTU.\n" > + "--bulk-mode : Use bulk TUN/TCP reads/writes.\n" > #ifdef ENABLE_FRAGMENT > "--fragment max : Enable internal datagram fragmentation so that no > UDP\n" > " datagrams are sent which are larger than max > bytes.\n" > @@ -3005,6 +3006,9 @@ options_postprocess_mutate_ce(struct options *o, > struct connection_entry *ce) > ce->tun_mtu_extra_defined = true; > ce->tun_mtu_extra = TAP_MTU_EXTRA_DEFAULT; > } > + if (ce->proto != PROTO_TCP && ce->proto != PROTO_TCP_SERVER && > ce->proto != PROTO_TCP_CLIENT) { > + ce->bulk_mode = false; > + } > } > > /* > @@ -9926,6 +9930,10 @@ add_option(struct options *options, char *p[], bool > is_inline, const char *file, > goto err; > } > } > + else if (streq(p[0], "bulk-mode")) > + { > + options->ce.bulk_mode = true; > + } > else > { > int i; > diff --git a/src/openvpn/options.h b/src/openvpn/options.h > index 38e67c8d..d1b0586d 100644 > --- a/src/openvpn/options.h > +++ b/src/openvpn/options.h > @@ -174,6 +174,9 @@ struct connection_entry > > /* Allow only client that support resending the wrapped client key */ > bool tls_crypt_v2_force_cookie; > + > + /* Bulk mode allows for multiple tun reads + larger tcp writes */ > + bool bulk_mode; > }; > > struct remote_entry > -- > 2.39.5 (Apple Git-154) > > > > > On Thu, Aug 7, 2025 at 2:29 PM Jon Chiappetta <ro...@fo...> wrote: > >> Thanks to Gert's help on this, I was able to finally configure and >> compile and run and test the bulk mode changes against the latest git >> source code to ensure everything still works correctly. >> >> I also fixed up some other issues like properly freeing the extra buffer >> allocations and removing the unneeded batched data prefixes and converting >> a remaining while loop to a max limited for loop and properly resetting the >> outgoing tun buffer pointer at the end of the write method when finished. >> >> Thanks, >> Jon C >> >> Example updated pull request: >> https://github.com/OpenVPN/openvpn/pull/814/files >> >> git formatted diff patch: >> >> From 985e88a9af26a39554f113f37ee18032a2f41c3e Mon Sep 17 00:00:00 2001 >> From: Jon Chiappetta <ro...@fo...> >> Date: Wed, 6 Aug 2025 16:33:18 -0400 >> Subject: [PATCH] bulk mode >> >> --- >> src/openvpn/forward.c | 217 ++++++++++++++++++++++++++++++++++++++++-- >> src/openvpn/forward.h | 4 + >> src/openvpn/init.c | 56 +++++++++++ >> src/openvpn/mtu.c | 10 +- >> src/openvpn/mtu.h | 13 +++ >> src/openvpn/multi.c | 7 +- >> src/openvpn/openvpn.h | 10 ++ >> src/openvpn/options.c | 8 ++ >> src/openvpn/options.h | 3 + >> 9 files changed, 316 insertions(+), 12 deletions(-) >> >> diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c >> index 75ca9d5c..d9a98607 100644 >> --- a/src/openvpn/forward.c >> +++ b/src/openvpn/forward.c >> @@ -46,6 +46,9 @@ >> >> #include "mstats.h" >> >> +#include <sys/select.h> >> +#include <sys/time.h> >> + >> counter_type link_read_bytes_global; /* GLOBAL */ >> counter_type link_write_bytes_global; /* GLOBAL */ >> >> @@ -78,6 +81,32 @@ show_wait_status(struct context *c) >> >> #endif /* ifdef ENABLE_DEBUG */ >> >> +bool check_bulk_mode(struct context *c) >> +{ >> + if ((c->c2.frame.bulk_size > 0) && (c->c1.tuntap != NULL) && >> (c->c2.buffers != NULL)) >> + { >> + return true; >> + } >> + return false; >> +} >> + >> +void xfer_io(struct context *c, struct context *b) >> +{ >> + int plen = 0; >> + if (check_bulk_mode(b)) >> + { >> + int leng = (b->c2.buffers->bufs_indx + 1); >> + for (int x = 0; x < leng; ++x) >> + { >> + plen = BLEN(&b->c2.bufs[x]); >> + if (plen < 1) { c->c2.bufs[x].len = 0; } >> + else { c->c2.bufs[x] = b->c2.bufs[x]; } >> + } >> + c->c2.buffers->bufs_indx = b->c2.buffers->bufs_indx; >> + b->c2.buffers->bufs_indx = -1; >> + } >> +} >> + >> static void >> check_tls_errors_co(struct context *c) >> { >> @@ -605,6 +634,21 @@ buffer_turnover(const uint8_t *orig_buf, struct >> buffer *dest_stub, struct buffer >> } >> } >> >> +uint8_t *buff_prepsize(uint8_t *buff, int *size) >> +{ >> + buff[0] = ((*size >> 8) & 0xff); >> + buff[1] = ((*size >> 0) & 0xff); >> + buff += 2; >> + return buff; >> +} >> + >> +uint8_t *buff_postsize(uint8_t *buff, int *size) >> +{ >> + *size = ((buff[0] << 8) + (buff[1] << 0)); >> + buff += 2; >> + return buff; >> +} >> + >> /* >> * Compress, fragment, encrypt and HMAC-sign an outgoing packet. >> * Input: c->c2.buf >> @@ -1031,6 +1075,7 @@ process_incoming_link_part1(struct context *c, >> struct link_socket_info *lsi, boo >> fprintf(stderr, "R"); >> } >> #endif >> + >> msg(D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii(lsi->proto, >> lsi->af, true), >> BLEN(&c->c2.buf), print_link_socket_actual(&c->c2.from, &gc), >> PROTO_DUMP(&c->c2.buf, &gc)); >> >> @@ -1211,6 +1256,23 @@ process_incoming_link_part2(struct context *c, >> struct link_socket_info *lsi, >> } >> } >> >> +void process_incoming_link_part3(struct context *c) >> +{ >> + int leng = BLEN(&c->c2.to_tun); >> + if (leng > 0) >> + { >> + if (check_bulk_mode(c)) >> + { >> + c->c2.buffers->send_tun_max.offset = TUN_BAT_OFF; >> + c->c2.buffers->send_tun_max.len = leng; >> + bcopy(BPTR(&c->c2.to_tun), >> BPTR(&c->c2.buffers->send_tun_max), leng); >> + //dmsg(M_INFO, "FWD BAT LINK 0 [%d] [%d] [%d] [%d] [%d]", >> BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf), >> BLEN(&c->c2.buffers->read_link_buf), BLEN(&c->c2.buffers->send_tun_max)); >> + c->c2.to_tun.offset += 2; >> + c->c2.buf.offset += 2; >> + } >> + } >> +} >> + >> static void >> process_incoming_link(struct context *c, struct link_socket *sock) >> { >> @@ -1221,6 +1283,7 @@ process_incoming_link(struct context *c, struct >> link_socket *sock) >> >> process_incoming_link_part1(c, lsi, false); >> process_incoming_link_part2(c, lsi, orig_buf); >> + process_incoming_link_part3(c); >> >> perf_pop(); >> } >> @@ -1321,7 +1384,7 @@ process_incoming_dco(struct context *c) >> */ >> >> void >> -read_incoming_tun(struct context *c) >> +read_incoming_tun_part2(struct context *c) >> { >> /* >> * Setup for read() call on TUN/TAP device. >> @@ -1382,6 +1445,55 @@ read_incoming_tun(struct context *c) >> perf_pop(); >> } >> >> +void read_incoming_tun_part3(struct context *c) >> +{ >> + fd_set rfds; >> + struct timeval timo; >> + if (check_bulk_mode(c)) >> + { >> + int plen = 0; >> + int fdno = c->c1.tuntap->fd; >> + for (int x = 0; x < TUN_BAT_MAX; ++x) >> + { >> + int leng = plen; >> + int indx = (c->c2.buffers->bufs_indx + 1); >> + if (indx >= TUN_BAT_MIN) { break; } >> + if (leng < 1) >> + { >> + FD_ZERO(&rfds); >> + FD_SET(fdno, &rfds); >> + timo.tv_sec = 0; >> + timo.tv_usec = 0; >> + select(fdno+1, &rfds, NULL, NULL, &timo); >> + if (FD_ISSET(fdno, &rfds)) >> + { >> + read_incoming_tun_part2(c); >> + plen = BLEN(&c->c2.buf); >> + } else { break; } >> + } >> + //dmsg(M_INFO, "FWD BAT READ 0 [%d] [%d] [%d] [%d] [%d]", >> c->c2.buffers->bufs_indx + 1, fdno, BLEN(&c->c2.buf), >> BLEN(&c->c2.buffers->read_tun_buf), BLEN(&c->c2.buffers->read_tun_max)); >> + leng = plen; >> + if (leng > 0) >> + { >> + c->c2.buffers->read_tun_bufs[indx].offset = TUN_BAT_OFF; >> + c->c2.buffers->read_tun_bufs[indx].len = leng; >> + bcopy(BPTR(&c->c2.buf), >> BPTR(&c->c2.buffers->read_tun_bufs[indx]), leng); >> + c->c2.bufs[indx] = c->c2.buffers->read_tun_bufs[indx]; >> + c->c2.buffers->bufs_indx = indx; >> + } else { break; } >> + plen = 0; >> + } >> + } >> +} >> + >> +void read_incoming_tun(struct context *c) >> +{ >> + if (c->c2.frame.bulk_size <= 0) { >> + read_incoming_tun_part2(c); >> + } >> + read_incoming_tun_part3(c); >> +} >> + >> /** >> * Drops UDP packets which OS decided to route via tun. >> * >> @@ -1469,7 +1581,7 @@ drop_if_recursive_routing(struct context *c, struct >> buffer *buf) >> */ >> >> void >> -process_incoming_tun(struct context *c, struct link_socket *out_sock) >> +process_incoming_tun_part2(struct context *c, struct link_socket >> *out_sock) >> { >> struct gc_arena gc = gc_new(); >> >> @@ -1488,7 +1600,7 @@ process_incoming_tun(struct context *c, struct >> link_socket *out_sock) >> #endif >> >> /* Show packet content */ >> - dmsg(D_TUN_RW, "TUN READ [%d]", BLEN(&c->c2.buf)); >> + dmsg(D_TUN_RW, "TUN READ [%d] [%d]", BLEN(&c->c2.buf), >> c->c2.frame.buf.payload_size); >> >> if (c->c2.buf.len > 0) >> { >> @@ -1512,7 +1624,9 @@ process_incoming_tun(struct context *c, struct >> link_socket *out_sock) >> } >> if (c->c2.buf.len > 0) >> { >> + if ((c->c2.buffers == NULL) || (c->c2.buffers->flag_ciph != -2)) >> { >> encrypt_sign(c, true); >> + } >> } >> else >> { >> @@ -1522,6 +1636,60 @@ process_incoming_tun(struct context *c, struct >> link_socket *out_sock) >> gc_free(&gc); >> } >> >> +void process_incoming_tun_part3(struct context *c, struct link_socket >> *out_sock) >> +{ >> + if (check_bulk_mode(c)) >> + { >> + c->c2.buffers->flag_ciph = -2; >> + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; >> + c->c2.buffers->read_tun_max.len = 0; >> + uint8_t *temp = BPTR(&c->c2.buffers->read_tun_max); >> + int plen = 0, fdno = c->c1.tuntap->fd; >> + int maxl = 0, leng = (c->c2.buffers->bufs_indx + 1); >> + if ((fdno > 0) && (leng > 0)) >> + { >> + for (int x = 0; x < leng; ++x) >> + { >> + c->c2.buf = c->c2.bufs[x]; >> + //dmsg(M_INFO, "FWD BAT INPT 0 [%d] [%d] [%d] [%d] >> [%d]", x, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), >> BLEN(&c->c2.bufs[x])); >> + process_incoming_tun_part2(c, out_sock); >> + if (BLEN(&c->c2.buf) < 1) >> + { >> + c->c2.bufs[x].len = 0; >> + } >> + } >> + for (int x = 0; x < leng; ++x) >> + { >> + plen = c->c2.bufs[x].len; >> + if (plen > 0) >> + { >> + temp = buff_prepsize(temp, &plen); >> + bcopy(BPTR(&c->c2.bufs[x]), temp, plen); >> + temp += plen; maxl += (plen + 2); >> + } >> + } >> + if (maxl > 0) >> + { >> + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; >> + c->c2.buffers->read_tun_max.len = maxl; >> + c->c2.buf = c->c2.buffers->read_tun_max; >> + //dmsg(M_INFO, "FWD BAT INPT 1 [%d] [%d] [%d] [%d] >> [%d]", maxl, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), >> BLEN(&c->c2.buffers->read_tun_max)); >> + encrypt_sign(c, true); >> + } >> + } >> + c->c2.buffers->bufs_indx = -1; >> + c->c2.buffers->flag_ciph = -1; >> + } >> +} >> + >> +void process_incoming_tun(struct context *c, struct link_socket >> *out_sock) >> +{ >> + if (c->c2.frame.bulk_size <= 0) { >> + process_incoming_tun_part2(c, out_sock); >> + } >> + process_incoming_tun_part3(c, out_sock); >> +} >> + >> /** >> * Forges a IPv6 ICMP packet with a no route to host error code from the >> * IPv6 packet in buf and sends it directly back to the client via the >> tun >> @@ -1748,7 +1916,7 @@ process_outgoing_link(struct context *c, struct >> link_socket *sock) >> >> perf_push(PERF_PROC_OUT_LINK); >> >> - if (c->c2.to_link.len > 0 && c->c2.to_link.len <= >> c->c2.frame.buf.payload_size) >> + if (c->c2.to_link.len > 0 && (c->c2.to_link.len <= >> c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0)) >> { >> /* >> * Setup for call to send/sendto which will send >> @@ -1793,6 +1961,7 @@ process_outgoing_link(struct context *c, struct >> link_socket *sock) >> fprintf(stderr, "W"); >> } >> #endif >> + >> msg(D_LINK_RW, "%s WRITE [%d] to %s: %s", >> proto2ascii(sock->info.proto, sock->info.af, true), >> BLEN(&c->c2.to_link), >> print_link_socket_actual(c->c2.to_link_addr, &gc), >> PROTO_DUMP(&c->c2.to_link, &gc)); >> @@ -1892,7 +2061,7 @@ process_outgoing_link(struct context *c, struct >> link_socket *sock) >> */ >> >> void >> -process_outgoing_tun(struct context *c, struct link_socket *in_sock) >> +process_outgoing_tun_part2(struct context *c, struct link_socket >> *in_sock) >> { >> /* >> * Set up for write() call to TUN/TAP >> @@ -1912,7 +2081,7 @@ process_outgoing_tun(struct context *c, struct >> link_socket *in_sock) >> process_ip_header(c, PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | >> PIPV4_CLIENT_NAT | PIP_OUTGOING, >> &c->c2.to_tun, in_sock); >> >> - if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size) >> + if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size || >> c->c2.frame.bulk_size > 0) >> { >> /* >> * Write to TUN/TAP device. >> @@ -1925,7 +2094,8 @@ process_outgoing_tun(struct context *c, struct >> link_socket *in_sock) >> fprintf(stderr, "w"); >> } >> #endif >> - dmsg(D_TUN_RW, "TUN WRITE [%d]", BLEN(&c->c2.to_tun)); >> + >> + dmsg(D_TUN_RW, "TUN WRITE [%d] [%d]", BLEN(&c->c2.to_tun), >> c->c2.frame.buf.payload_size); >> >> #ifdef PACKET_TRUNCATION_CHECK >> ipv4_packet_size_verify(BPTR(&c->c2.to_tun), >> BLEN(&c->c2.to_tun), TUNNEL_TYPE(c->c1.tuntap), >> @@ -1981,6 +2151,39 @@ process_outgoing_tun(struct context *c, struct >> link_socket *in_sock) >> perf_pop(); >> } >> >> +void process_outgoing_tun_part3(struct context *c, struct link_socket >> *in_sock) >> +{ >> + if (check_bulk_mode(c)) >> + { >> + int maxl = 0, plen = 0; >> + int leng = BLEN(&c->c2.buffers->send_tun_max); >> + uint8_t *temp = BPTR(&c->c2.buffers->send_tun_max); >> + for (int x = 0; x < TUN_BAT_MAX; ++x) >> + { >> + temp = buff_postsize(temp, &plen); >> + if ((leng > 0) && (plen > 0) && ((maxl + plen) < leng)) >> + { >> + c->c2.to_tun = c->c2.buffers->to_tun_max; >> + c->c2.to_tun.offset = TUN_BAT_OFF; >> + c->c2.to_tun.len = plen; >> + bcopy(temp, BPTR(&c->c2.to_tun), plen); >> + temp += plen; maxl += (plen + 2); >> + //dmsg(M_INFO, "FWD BAT OUTP 1 [%d] [%d] [%d] [%d]", x, >> BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf)); >> + process_outgoing_tun_part2(c, in_sock); >> + } else { break; } >> + } >> +... [truncated message content] |
From: Jon C. <ro...@fo...> - 2025-08-08 14:33:41
|
[replying to my own update thread] I pushed an update to the PR with the following small changes: - rebased to the latest master commit - increased the data buffer size to be slightly bigger than the read buffer size - copied two more buf resets so that the additional bulk forward functions match the present logic checks Example updated pull request: https://github.com/OpenVPN/openvpn/pull/814/files $ cat 0001-bulk-mode.patch >From 2ce0b023d105e7ecc289a414cd26f7ebc8bcbcaf Mon Sep 17 00:00:00 2001 From: Jon Chiappetta <ro...@fo...> Date: Wed, 6 Aug 2025 16:33:18 -0400 Subject: [PATCH] bulk mode --- src/openvpn/forward.c | 228 ++++++++++++++++++++++++++++++++++++++++-- src/openvpn/forward.h | 4 + src/openvpn/init.c | 57 +++++++++++ src/openvpn/mtu.c | 10 +- src/openvpn/mtu.h | 13 +++ src/openvpn/multi.c | 7 +- src/openvpn/openvpn.h | 10 ++ src/openvpn/options.c | 8 ++ src/openvpn/options.h | 3 + 9 files changed, 328 insertions(+), 12 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 75ca9d5c..0af983e9 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -46,6 +46,9 @@ #include "mstats.h" +#include <sys/select.h> +#include <sys/time.h> + counter_type link_read_bytes_global; /* GLOBAL */ counter_type link_write_bytes_global; /* GLOBAL */ @@ -78,6 +81,32 @@ show_wait_status(struct context *c) #endif /* ifdef ENABLE_DEBUG */ +bool check_bulk_mode(struct context *c) +{ + if ((c->c2.frame.bulk_size > 0) && (c->c1.tuntap != NULL) && (c->c2.buffers != NULL)) + { + return true; + } + return false; +} + +void xfer_io(struct context *c, struct context *b) +{ + int plen = 0; + if (check_bulk_mode(b)) + { + int leng = (b->c2.buffers->bufs_indx + 1); + for (int x = 0; x < leng; ++x) + { + plen = BLEN(&b->c2.bufs[x]); + if (plen < 1) { c->c2.bufs[x].len = 0; } + else { c->c2.bufs[x] = b->c2.bufs[x]; } + } + c->c2.buffers->bufs_indx = b->c2.buffers->bufs_indx; + b->c2.buffers->bufs_indx = -1; + } +} + static void check_tls_errors_co(struct context *c) { @@ -605,6 +634,21 @@ buffer_turnover(const uint8_t *orig_buf, struct buffer *dest_stub, struct buffer } } +uint8_t *buff_prepsize(uint8_t *buff, int *size) +{ + buff[0] = ((*size >> 8) & 0xff); + buff[1] = ((*size >> 0) & 0xff); + buff += 2; + return buff; +} + +uint8_t *buff_postsize(uint8_t *buff, int *size) +{ + *size = ((buff[0] << 8) + (buff[1] << 0)); + buff += 2; + return buff; +} + /* * Compress, fragment, encrypt and HMAC-sign an outgoing packet. * Input: c->c2.buf @@ -1031,6 +1075,7 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo fprintf(stderr, "R"); } #endif + msg(D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii(lsi->proto, lsi->af, true), BLEN(&c->c2.buf), print_link_socket_actual(&c->c2.from, &gc), PROTO_DUMP(&c->c2.buf, &gc)); @@ -1211,6 +1256,27 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, } } +void process_incoming_link_part3(struct context *c) +{ + int leng = BLEN(&c->c2.buf); + if (leng > 0) + { + if (check_bulk_mode(c)) + { + c->c2.buffers->send_tun_max.offset = TUN_BAT_OFF; + c->c2.buffers->send_tun_max.len = leng; + bcopy(BPTR(&c->c2.buf), BPTR(&c->c2.buffers->send_tun_max), leng); + //dmsg(M_INFO, "FWD BAT LINK 0 [%d] [%d] [%d] [%d] [%d]", BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf), BLEN(&c->c2.buffers->read_link_buf), BLEN(&c->c2.buffers->send_tun_max)); + c->c2.to_tun.offset += 2; + c->c2.buf.offset += 2; + } + } + else + { + buf_reset(&c->c2.to_tun); + } +} + static void process_incoming_link(struct context *c, struct link_socket *sock) { @@ -1221,6 +1287,7 @@ process_incoming_link(struct context *c, struct link_socket *sock) process_incoming_link_part1(c, lsi, false); process_incoming_link_part2(c, lsi, orig_buf); + process_incoming_link_part3(c); perf_pop(); } @@ -1321,7 +1388,7 @@ process_incoming_dco(struct context *c) */ void -read_incoming_tun(struct context *c) +read_incoming_tun_part2(struct context *c) { /* * Setup for read() call on TUN/TAP device. @@ -1382,6 +1449,55 @@ read_incoming_tun(struct context *c) perf_pop(); } +void read_incoming_tun_part3(struct context *c) +{ + fd_set rfds; + struct timeval timo; + if (check_bulk_mode(c)) + { + int plen = 0; + int fdno = c->c1.tuntap->fd; + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + int leng = plen; + int indx = (c->c2.buffers->bufs_indx + 1); + if (indx >= TUN_BAT_MIN) { break; } + if (leng < 1) + { + FD_ZERO(&rfds); + FD_SET(fdno, &rfds); + timo.tv_sec = 0; + timo.tv_usec = 0; + select(fdno+1, &rfds, NULL, NULL, &timo); + if (FD_ISSET(fdno, &rfds)) + { + read_incoming_tun_part2(c); + plen = BLEN(&c->c2.buf); + } else { break; } + } + //dmsg(M_INFO, "FWD BAT READ 0 [%d] [%d] [%d] [%d] [%d]", c->c2.buffers->bufs_indx + 1, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), BLEN(&c->c2.buffers->read_tun_max)); + leng = plen; + if (leng > 0) + { + c->c2.buffers->read_tun_bufs[indx].offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_bufs[indx].len = leng; + bcopy(BPTR(&c->c2.buf), BPTR(&c->c2.buffers->read_tun_bufs[indx]), leng); + c->c2.bufs[indx] = c->c2.buffers->read_tun_bufs[indx]; + c->c2.buffers->bufs_indx = indx; + } else { break; } + plen = 0; + } + } +} + +void read_incoming_tun(struct context *c) +{ + if (c->c2.frame.bulk_size <= 0) { + read_incoming_tun_part2(c); + } + read_incoming_tun_part3(c); +} + /** * Drops UDP packets which OS decided to route via tun. * @@ -1469,7 +1585,7 @@ drop_if_recursive_routing(struct context *c, struct buffer *buf) */ void -process_incoming_tun(struct context *c, struct link_socket *out_sock) +process_incoming_tun_part2(struct context *c, struct link_socket *out_sock) { struct gc_arena gc = gc_new(); @@ -1488,7 +1604,7 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) #endif /* Show packet content */ - dmsg(D_TUN_RW, "TUN READ [%d]", BLEN(&c->c2.buf)); + dmsg(D_TUN_RW, "TUN READ [%d] [%d]", BLEN(&c->c2.buf), c->c2.frame.buf.payload_size); if (c->c2.buf.len > 0) { @@ -1512,7 +1628,9 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) } if (c->c2.buf.len > 0) { + if ((c->c2.buffers == NULL) || (c->c2.buffers->flag_ciph != -2)) { encrypt_sign(c, true); + } } else { @@ -1522,6 +1640,67 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) gc_free(&gc); } +void process_incoming_tun_part3(struct context *c, struct link_socket *out_sock) +{ + if (c->c2.buf.len > 0) + { + if (check_bulk_mode(c)) + { + c->c2.buffers->flag_ciph = -2; + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_max.len = 0; + uint8_t *temp = BPTR(&c->c2.buffers->read_tun_max); + int plen = 0, fdno = c->c1.tuntap->fd; + int maxl = 0, leng = (c->c2.buffers->bufs_indx + 1); + if ((fdno > 0) && (leng > 0)) + { + for (int x = 0; x < leng; ++x) + { + c->c2.buf = c->c2.bufs[x]; + //dmsg(M_INFO, "FWD BAT INPT 0 [%d] [%d] [%d] [%d] [%d]", x, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), BLEN(&c->c2.bufs[x])); + process_incoming_tun_part2(c, out_sock); + if (BLEN(&c->c2.buf) < 1) + { + c->c2.bufs[x].len = 0; + } + } + for (int x = 0; x < leng; ++x) + { + plen = c->c2.bufs[x].len; + if (plen > 0) + { + temp = buff_prepsize(temp, &plen); + bcopy(BPTR(&c->c2.bufs[x]), temp, plen); + temp += plen; maxl += (plen + 2); + } + } + if (maxl > 0) + { + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_max.len = maxl; + c->c2.buf = c->c2.buffers->read_tun_max; + //dmsg(M_INFO, "FWD BAT INPT 1 [%d] [%d] [%d] [%d] [%d]", maxl, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), BLEN(&c->c2.buffers->read_tun_max)); + encrypt_sign(c, true); + } + } + c->c2.buffers->bufs_indx = -1; + c->c2.buffers->flag_ciph = -1; + } + } + else + { + buf_reset(&c->c2.to_link); + } +} + +void process_incoming_tun(struct context *c, struct link_socket *out_sock) +{ + if (c->c2.frame.bulk_size <= 0) { + process_incoming_tun_part2(c, out_sock); + } + process_incoming_tun_part3(c, out_sock); +} + /** * Forges a IPv6 ICMP packet with a no route to host error code from the * IPv6 packet in buf and sends it directly back to the client via the tun @@ -1748,7 +1927,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) perf_push(PERF_PROC_OUT_LINK); - if (c->c2.to_link.len > 0 && c->c2.to_link.len <= c->c2.frame.buf.payload_size) + if (c->c2.to_link.len > 0 && (c->c2.to_link.len <= c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0)) { /* * Setup for call to send/sendto which will send @@ -1793,6 +1972,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) fprintf(stderr, "W"); } #endif + msg(D_LINK_RW, "%s WRITE [%d] to %s: %s", proto2ascii(sock->info.proto, sock->info.af, true), BLEN(&c->c2.to_link), print_link_socket_actual(c->c2.to_link_addr, &gc), PROTO_DUMP(&c->c2.to_link, &gc)); @@ -1892,7 +2072,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) */ void -process_outgoing_tun(struct context *c, struct link_socket *in_sock) +process_outgoing_tun_part2(struct context *c, struct link_socket *in_sock) { /* * Set up for write() call to TUN/TAP @@ -1912,7 +2092,7 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) process_ip_header(c, PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | PIPV4_CLIENT_NAT | PIP_OUTGOING, &c->c2.to_tun, in_sock); - if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size) + if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0) { /* * Write to TUN/TAP device. @@ -1925,7 +2105,8 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) fprintf(stderr, "w"); } #endif - dmsg(D_TUN_RW, "TUN WRITE [%d]", BLEN(&c->c2.to_tun)); + + dmsg(D_TUN_RW, "TUN WRITE [%d] [%d]", BLEN(&c->c2.to_tun), c->c2.frame.buf.payload_size); #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify(BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun), TUNNEL_TYPE(c->c1.tuntap), @@ -1981,6 +2162,39 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) perf_pop(); } +void process_outgoing_tun_part3(struct context *c, struct link_socket *in_sock) +{ + if (check_bulk_mode(c)) + { + int maxl = 0, plen = 0; + int leng = BLEN(&c->c2.buffers->send_tun_max); + uint8_t *temp = BPTR(&c->c2.buffers->send_tun_max); + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + temp = buff_postsize(temp, &plen); + if ((leng > 0) && (plen > 0) && ((maxl + plen) < leng)) + { + c->c2.to_tun = c->c2.buffers->to_tun_max; + c->c2.to_tun.offset = TUN_BAT_OFF; + c->c2.to_tun.len = plen; + bcopy(temp, BPTR(&c->c2.to_tun), plen); + temp += plen; maxl += (plen + 2); + //dmsg(M_INFO, "FWD BAT OUTP 1 [%d] [%d] [%d] [%d]", x, BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf)); + process_outgoing_tun_part2(c, in_sock); + } else { break; } + } + } + buf_reset(&c->c2.to_tun); +} + +void process_outgoing_tun(struct context *c, struct link_socket *in_sock) +{ + if (c->c2.frame.bulk_size <= 0) { + process_outgoing_tun_part2(c, in_sock); + } + process_outgoing_tun_part3(c, in_sock); +} + void pre_select(struct context *c) { diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index d5641491..9fda1583 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -79,6 +79,8 @@ void pre_select(struct context *c); void process_io(struct context *c, struct link_socket *sock); +void xfer_io(struct context *c, struct context *b); + /**********************************************************************/ /** @@ -196,6 +198,8 @@ bool process_incoming_link_part1(struct context *c, struct link_socket_info *lsi void process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf); +void process_incoming_link_part3(struct context *c); + /** * Transfers \c float_sa data extracted from an incoming DCO * PEER_FLOAT_NTF to \c out_osaddr for later processing. diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 40ae2c8c..bbdbad46 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2971,6 +2971,10 @@ frame_finalize_options(struct context *c, const struct options *o) tailroom += COMP_EXTRA_BUFFER(payload_size); #endif + if (frame->bulk_size > 0) { + payload_size = frame->tun_mtu; + } + frame->buf.payload_size = payload_size; frame->buf.headroom = headroom; frame->buf.tailroom = tailroom; @@ -3473,6 +3477,9 @@ do_init_frame_tls(struct context *c) if (c->c2.tls_multi) { tls_multi_init_finalize(c->c2.tls_multi, c->options.ce.tls_mtu); + if (c->c2.frame.bulk_size > 0) { + c->c2.tls_multi->opt.frame.buf.payload_size = c->c2.frame.tun_mtu; + } ASSERT(c->c2.tls_multi->opt.frame.buf.payload_size <= c->c2.frame.buf.payload_size); frame_print(&c->c2.tls_multi->opt.frame, D_MTU_INFO, "Control Channel MTU parms"); @@ -3536,6 +3543,14 @@ do_init_frame(struct context *c) c->c2.frame.extra_tun += c->options.ce.tun_mtu_extra; } + /* + * Adjust bulk size based on the --bulk-mode parameter. + */ + if (c->options.ce.bulk_mode) + { + c->c2.frame.bulk_size = c->options.ce.tun_mtu; + } + /* * Fill in the blanks in the frame parameters structure, * make sure values are rational, etc. @@ -3676,9 +3691,41 @@ init_context_buffers(const struct frame *frame) size_t buf_size = BUF_SIZE(frame); + if (frame->bulk_size > 0) { + size_t off_size = (frame->buf.headroom + TUN_BAT_OFF + frame->buf.tailroom); + buf_size = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, off_size); + } + + dmsg(M_INFO, "MEM NEW [%ld] [%d+%d+%d]", buf_size, frame->buf.headroom, frame->buf.payload_size, frame->buf.tailroom); + b->read_link_buf = alloc_buf(buf_size); b->read_tun_buf = alloc_buf(buf_size); + if (frame->bulk_size > 0) { + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + size_t part_size = BUF_SIZE(frame); + b->read_tun_bufs[x] = alloc_buf(part_size); + b->read_tun_bufs[x].offset = TUN_BAT_OFF; + b->read_tun_bufs[x].len = 0; + } + + b->read_tun_max = alloc_buf(buf_size); + b->read_tun_max.offset = TUN_BAT_OFF; + b->read_tun_max.len = 0; + + b->send_tun_max = alloc_buf(buf_size); + b->send_tun_max.offset = TUN_BAT_OFF; + b->send_tun_max.len = 0; + + b->to_tun_max = alloc_buf(buf_size); + b->to_tun_max.offset = TUN_BAT_OFF; + b->to_tun_max.len = 0; + } + + b->bufs_indx = -1; + b->flag_ciph = -1; + b->aux_buf = alloc_buf(buf_size); b->encrypt_buf = alloc_buf(buf_size); @@ -3701,6 +3748,16 @@ free_context_buffers(struct context_buffers *b) free_buf(&b->read_tun_buf); free_buf(&b->aux_buf); + if (b->to_tun_max.data) { + free_buf(&b->to_tun_max); + free_buf(&b->send_tun_max); + free_buf(&b->read_tun_max); + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + free_buf(&b->read_tun_bufs[x]); + } + } + #ifdef USE_COMP free_buf(&b->compress_buf); free_buf(&b->decompress_buf); diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c index a419e32d..7e35c837 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -41,9 +41,15 @@ void alloc_buf_sock_tun(struct buffer *buf, const struct frame *frame) { /* allocate buffer for overlapped I/O */ - *buf = alloc_buf(BUF_SIZE(frame)); + size_t alen = BUF_SIZE(frame); + size_t blen = frame->buf.payload_size; + if (frame->bulk_size > 0) { + alen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_OFF); + blen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_NOP); + } + *buf = alloc_buf(alen); ASSERT(buf_init(buf, frame->buf.headroom)); - buf->len = frame->buf.payload_size; + buf->len = blen; ASSERT(buf_safe(buf, 0)); } diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index 925ef0bf..eb799fb3 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -58,6 +58,14 @@ */ #define TUN_MTU_MIN 100 +/* + * Bulk mode static define values. + */ +#define TUN_BAT_MIN 6 +#define TUN_BAT_MAX 9 +#define TUN_BAT_OFF 256 +#define TUN_BAT_NOP 0 + /* * Default MTU of network over which tunnel data will pass by TCP/UDP. */ @@ -152,6 +160,10 @@ struct frame * which defaults to 0 for tun and 32 * (\c TAP_MTU_EXTRA_DEFAULT) for tap. * */ + + int bulk_size; /**< Signal to the init frame function + * to allow for bulk mode TCP transfers. + * */ }; /* Forward declarations, to prevent includes */ @@ -171,6 +183,7 @@ struct options; * larger than the headroom. */ #define BUF_SIZE(f) ((f)->buf.headroom + (f)->buf.payload_size + (f)->buf.tailroom) +#define BAT_SIZE(a, b, c) ((a * b) + c) /* * Function prototypes. diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index e1ce32ab..9e089703 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -3414,6 +3414,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } process_incoming_link_part2(c, lsi, orig_buf); + process_incoming_link_part3(c); } perf_pop(); @@ -3558,9 +3559,7 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags const int dev_type = TUNNEL_TYPE(m->top.c1.tuntap); int16_t vid = 0; -#ifdef MULTI_DEBUG_EVENT_LOOP - printf("TUN -> TCP/UDP [%d]\n", BLEN(&m->top.c2.buf)); -#endif + msg(D_MULTI_DEBUG, "TUN -> TCP/UDP [%d]", BLEN(&m->top.c2.buf)); if (m->pending) { @@ -3610,6 +3609,8 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags { /* transfer packet pointer from top-level context buffer to instance */ c->c2.buf = m->top.c2.buf; + /* todo determine if to call this (multi_process_incoming_tun) for each bulk item read? */ + xfer_io(c, &m->top); } else { diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index cd99cd40..21fa8967 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -112,6 +112,14 @@ struct context_buffers */ struct buffer read_link_buf; struct buffer read_tun_buf; + + struct buffer read_tun_bufs[TUN_BAT_MAX]; + struct buffer read_tun_max; + struct buffer send_tun_max; + struct buffer to_tun_max; + + int bufs_indx; + int flag_ciph; }; /* @@ -376,6 +384,8 @@ struct context_2 struct buffer to_tun; struct buffer to_link; + struct buffer bufs[TUN_BAT_MAX]; + /* should we print R|W|r|w to console on packet transfers? */ bool log_rw; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index c54032d8..041d17d0 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -304,6 +304,7 @@ static const char usage_message[] = " 'maybe' -- Use per-route hints\n" " 'yes' -- Always DF (Don't Fragment)\n" "--mtu-test : Empirically measure and report MTU.\n" + "--bulk-mode : Use bulk TUN/TCP reads/writes.\n" #ifdef ENABLE_FRAGMENT "--fragment max : Enable internal datagram fragmentation so that no UDP\n" " datagrams are sent which are larger than max bytes.\n" @@ -3005,6 +3006,9 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) ce->tun_mtu_extra_defined = true; ce->tun_mtu_extra = TAP_MTU_EXTRA_DEFAULT; } + if (ce->proto != PROTO_TCP && ce->proto != PROTO_TCP_SERVER && ce->proto != PROTO_TCP_CLIENT) { + ce->bulk_mode = false; + } } /* @@ -9926,6 +9930,10 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, goto err; } } + else if (streq(p[0], "bulk-mode")) + { + options->ce.bulk_mode = true; + } else { int i; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 38e67c8d..d1b0586d 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -174,6 +174,9 @@ struct connection_entry /* Allow only client that support resending the wrapped client key */ bool tls_crypt_v2_force_cookie; + + /* Bulk mode allows for multiple tun reads + larger tcp writes */ + bool bulk_mode; }; struct remote_entry -- 2.39.5 (Apple Git-154) On Thu, Aug 7, 2025 at 2:29 PM Jon Chiappetta <ro...@fo...> wrote: > Thanks to Gert's help on this, I was able to finally configure and compile > and run and test the bulk mode changes against the latest git source code > to ensure everything still works correctly. > > I also fixed up some other issues like properly freeing the extra buffer > allocations and removing the unneeded batched data prefixes and converting > a remaining while loop to a max limited for loop and properly resetting the > outgoing tun buffer pointer at the end of the write method when finished. > > Thanks, > Jon C > > Example updated pull request: > https://github.com/OpenVPN/openvpn/pull/814/files > > git formatted diff patch: > > From 985e88a9af26a39554f113f37ee18032a2f41c3e Mon Sep 17 00:00:00 2001 > From: Jon Chiappetta <ro...@fo...> > Date: Wed, 6 Aug 2025 16:33:18 -0400 > Subject: [PATCH] bulk mode > > --- > src/openvpn/forward.c | 217 ++++++++++++++++++++++++++++++++++++++++-- > src/openvpn/forward.h | 4 + > src/openvpn/init.c | 56 +++++++++++ > src/openvpn/mtu.c | 10 +- > src/openvpn/mtu.h | 13 +++ > src/openvpn/multi.c | 7 +- > src/openvpn/openvpn.h | 10 ++ > src/openvpn/options.c | 8 ++ > src/openvpn/options.h | 3 + > 9 files changed, 316 insertions(+), 12 deletions(-) > > diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c > index 75ca9d5c..d9a98607 100644 > --- a/src/openvpn/forward.c > +++ b/src/openvpn/forward.c > @@ -46,6 +46,9 @@ > > #include "mstats.h" > > +#include <sys/select.h> > +#include <sys/time.h> > + > counter_type link_read_bytes_global; /* GLOBAL */ > counter_type link_write_bytes_global; /* GLOBAL */ > > @@ -78,6 +81,32 @@ show_wait_status(struct context *c) > > #endif /* ifdef ENABLE_DEBUG */ > > +bool check_bulk_mode(struct context *c) > +{ > + if ((c->c2.frame.bulk_size > 0) && (c->c1.tuntap != NULL) && > (c->c2.buffers != NULL)) > + { > + return true; > + } > + return false; > +} > + > +void xfer_io(struct context *c, struct context *b) > +{ > + int plen = 0; > + if (check_bulk_mode(b)) > + { > + int leng = (b->c2.buffers->bufs_indx + 1); > + for (int x = 0; x < leng; ++x) > + { > + plen = BLEN(&b->c2.bufs[x]); > + if (plen < 1) { c->c2.bufs[x].len = 0; } > + else { c->c2.bufs[x] = b->c2.bufs[x]; } > + } > + c->c2.buffers->bufs_indx = b->c2.buffers->bufs_indx; > + b->c2.buffers->bufs_indx = -1; > + } > +} > + > static void > check_tls_errors_co(struct context *c) > { > @@ -605,6 +634,21 @@ buffer_turnover(const uint8_t *orig_buf, struct > buffer *dest_stub, struct buffer > } > } > > +uint8_t *buff_prepsize(uint8_t *buff, int *size) > +{ > + buff[0] = ((*size >> 8) & 0xff); > + buff[1] = ((*size >> 0) & 0xff); > + buff += 2; > + return buff; > +} > + > +uint8_t *buff_postsize(uint8_t *buff, int *size) > +{ > + *size = ((buff[0] << 8) + (buff[1] << 0)); > + buff += 2; > + return buff; > +} > + > /* > * Compress, fragment, encrypt and HMAC-sign an outgoing packet. > * Input: c->c2.buf > @@ -1031,6 +1075,7 @@ process_incoming_link_part1(struct context *c, > struct link_socket_info *lsi, boo > fprintf(stderr, "R"); > } > #endif > + > msg(D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii(lsi->proto, > lsi->af, true), > BLEN(&c->c2.buf), print_link_socket_actual(&c->c2.from, &gc), > PROTO_DUMP(&c->c2.buf, &gc)); > > @@ -1211,6 +1256,23 @@ process_incoming_link_part2(struct context *c, > struct link_socket_info *lsi, > } > } > > +void process_incoming_link_part3(struct context *c) > +{ > + int leng = BLEN(&c->c2.to_tun); > + if (leng > 0) > + { > + if (check_bulk_mode(c)) > + { > + c->c2.buffers->send_tun_max.offset = TUN_BAT_OFF; > + c->c2.buffers->send_tun_max.len = leng; > + bcopy(BPTR(&c->c2.to_tun), > BPTR(&c->c2.buffers->send_tun_max), leng); > + //dmsg(M_INFO, "FWD BAT LINK 0 [%d] [%d] [%d] [%d] [%d]", > BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf), > BLEN(&c->c2.buffers->read_link_buf), BLEN(&c->c2.buffers->send_tun_max)); > + c->c2.to_tun.offset += 2; > + c->c2.buf.offset += 2; > + } > + } > +} > + > static void > process_incoming_link(struct context *c, struct link_socket *sock) > { > @@ -1221,6 +1283,7 @@ process_incoming_link(struct context *c, struct > link_socket *sock) > > process_incoming_link_part1(c, lsi, false); > process_incoming_link_part2(c, lsi, orig_buf); > + process_incoming_link_part3(c); > > perf_pop(); > } > @@ -1321,7 +1384,7 @@ process_incoming_dco(struct context *c) > */ > > void > -read_incoming_tun(struct context *c) > +read_incoming_tun_part2(struct context *c) > { > /* > * Setup for read() call on TUN/TAP device. > @@ -1382,6 +1445,55 @@ read_incoming_tun(struct context *c) > perf_pop(); > } > > +void read_incoming_tun_part3(struct context *c) > +{ > + fd_set rfds; > + struct timeval timo; > + if (check_bulk_mode(c)) > + { > + int plen = 0; > + int fdno = c->c1.tuntap->fd; > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + int leng = plen; > + int indx = (c->c2.buffers->bufs_indx + 1); > + if (indx >= TUN_BAT_MIN) { break; } > + if (leng < 1) > + { > + FD_ZERO(&rfds); > + FD_SET(fdno, &rfds); > + timo.tv_sec = 0; > + timo.tv_usec = 0; > + select(fdno+1, &rfds, NULL, NULL, &timo); > + if (FD_ISSET(fdno, &rfds)) > + { > + read_incoming_tun_part2(c); > + plen = BLEN(&c->c2.buf); > + } else { break; } > + } > + //dmsg(M_INFO, "FWD BAT READ 0 [%d] [%d] [%d] [%d] [%d]", > c->c2.buffers->bufs_indx + 1, fdno, BLEN(&c->c2.buf), > BLEN(&c->c2.buffers->read_tun_buf), BLEN(&c->c2.buffers->read_tun_max)); > + leng = plen; > + if (leng > 0) > + { > + c->c2.buffers->read_tun_bufs[indx].offset = TUN_BAT_OFF; > + c->c2.buffers->read_tun_bufs[indx].len = leng; > + bcopy(BPTR(&c->c2.buf), > BPTR(&c->c2.buffers->read_tun_bufs[indx]), leng); > + c->c2.bufs[indx] = c->c2.buffers->read_tun_bufs[indx]; > + c->c2.buffers->bufs_indx = indx; > + } else { break; } > + plen = 0; > + } > + } > +} > + > +void read_incoming_tun(struct context *c) > +{ > + if (c->c2.frame.bulk_size <= 0) { > + read_incoming_tun_part2(c); > + } > + read_incoming_tun_part3(c); > +} > + > /** > * Drops UDP packets which OS decided to route via tun. > * > @@ -1469,7 +1581,7 @@ drop_if_recursive_routing(struct context *c, struct > buffer *buf) > */ > > void > -process_incoming_tun(struct context *c, struct link_socket *out_sock) > +process_incoming_tun_part2(struct context *c, struct link_socket > *out_sock) > { > struct gc_arena gc = gc_new(); > > @@ -1488,7 +1600,7 @@ process_incoming_tun(struct context *c, struct > link_socket *out_sock) > #endif > > /* Show packet content */ > - dmsg(D_TUN_RW, "TUN READ [%d]", BLEN(&c->c2.buf)); > + dmsg(D_TUN_RW, "TUN READ [%d] [%d]", BLEN(&c->c2.buf), > c->c2.frame.buf.payload_size); > > if (c->c2.buf.len > 0) > { > @@ -1512,7 +1624,9 @@ process_incoming_tun(struct context *c, struct > link_socket *out_sock) > } > if (c->c2.buf.len > 0) > { > + if ((c->c2.buffers == NULL) || (c->c2.buffers->flag_ciph != -2)) { > encrypt_sign(c, true); > + } > } > else > { > @@ -1522,6 +1636,60 @@ process_incoming_tun(struct context *c, struct > link_socket *out_sock) > gc_free(&gc); > } > > +void process_incoming_tun_part3(struct context *c, struct link_socket > *out_sock) > +{ > + if (check_bulk_mode(c)) > + { > + c->c2.buffers->flag_ciph = -2; > + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; > + c->c2.buffers->read_tun_max.len = 0; > + uint8_t *temp = BPTR(&c->c2.buffers->read_tun_max); > + int plen = 0, fdno = c->c1.tuntap->fd; > + int maxl = 0, leng = (c->c2.buffers->bufs_indx + 1); > + if ((fdno > 0) && (leng > 0)) > + { > + for (int x = 0; x < leng; ++x) > + { > + c->c2.buf = c->c2.bufs[x]; > + //dmsg(M_INFO, "FWD BAT INPT 0 [%d] [%d] [%d] [%d] [%d]", > x, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), > BLEN(&c->c2.bufs[x])); > + process_incoming_tun_part2(c, out_sock); > + if (BLEN(&c->c2.buf) < 1) > + { > + c->c2.bufs[x].len = 0; > + } > + } > + for (int x = 0; x < leng; ++x) > + { > + plen = c->c2.bufs[x].len; > + if (plen > 0) > + { > + temp = buff_prepsize(temp, &plen); > + bcopy(BPTR(&c->c2.bufs[x]), temp, plen); > + temp += plen; maxl += (plen + 2); > + } > + } > + if (maxl > 0) > + { > + c->c2.buffers->read_tun_max.offset = TUN_BAT_OFF; > + c->c2.buffers->read_tun_max.len = maxl; > + c->c2.buf = c->c2.buffers->read_tun_max; > + //dmsg(M_INFO, "FWD BAT INPT 1 [%d] [%d] [%d] [%d] [%d]", > maxl, fdno, BLEN(&c->c2.buf), BLEN(&c->c2.buffers->read_tun_buf), > BLEN(&c->c2.buffers->read_tun_max)); > + encrypt_sign(c, true); > + } > + } > + c->c2.buffers->bufs_indx = -1; > + c->c2.buffers->flag_ciph = -1; > + } > +} > + > +void process_incoming_tun(struct context *c, struct link_socket *out_sock) > +{ > + if (c->c2.frame.bulk_size <= 0) { > + process_incoming_tun_part2(c, out_sock); > + } > + process_incoming_tun_part3(c, out_sock); > +} > + > /** > * Forges a IPv6 ICMP packet with a no route to host error code from the > * IPv6 packet in buf and sends it directly back to the client via the tun > @@ -1748,7 +1916,7 @@ process_outgoing_link(struct context *c, struct > link_socket *sock) > > perf_push(PERF_PROC_OUT_LINK); > > - if (c->c2.to_link.len > 0 && c->c2.to_link.len <= > c->c2.frame.buf.payload_size) > + if (c->c2.to_link.len > 0 && (c->c2.to_link.len <= > c->c2.frame.buf.payload_size || c->c2.frame.bulk_size > 0)) > { > /* > * Setup for call to send/sendto which will send > @@ -1793,6 +1961,7 @@ process_outgoing_link(struct context *c, struct > link_socket *sock) > fprintf(stderr, "W"); > } > #endif > + > msg(D_LINK_RW, "%s WRITE [%d] to %s: %s", > proto2ascii(sock->info.proto, sock->info.af, true), > BLEN(&c->c2.to_link), > print_link_socket_actual(c->c2.to_link_addr, &gc), > PROTO_DUMP(&c->c2.to_link, &gc)); > @@ -1892,7 +2061,7 @@ process_outgoing_link(struct context *c, struct > link_socket *sock) > */ > > void > -process_outgoing_tun(struct context *c, struct link_socket *in_sock) > +process_outgoing_tun_part2(struct context *c, struct link_socket *in_sock) > { > /* > * Set up for write() call to TUN/TAP > @@ -1912,7 +2081,7 @@ process_outgoing_tun(struct context *c, struct > link_socket *in_sock) > process_ip_header(c, PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | > PIPV4_CLIENT_NAT | PIP_OUTGOING, > &c->c2.to_tun, in_sock); > > - if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size) > + if (c->c2.to_tun.len <= c->c2.frame.buf.payload_size || > c->c2.frame.bulk_size > 0) > { > /* > * Write to TUN/TAP device. > @@ -1925,7 +2094,8 @@ process_outgoing_tun(struct context *c, struct > link_socket *in_sock) > fprintf(stderr, "w"); > } > #endif > - dmsg(D_TUN_RW, "TUN WRITE [%d]", BLEN(&c->c2.to_tun)); > + > + dmsg(D_TUN_RW, "TUN WRITE [%d] [%d]", BLEN(&c->c2.to_tun), > c->c2.frame.buf.payload_size); > > #ifdef PACKET_TRUNCATION_CHECK > ipv4_packet_size_verify(BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun), > TUNNEL_TYPE(c->c1.tuntap), > @@ -1981,6 +2151,39 @@ process_outgoing_tun(struct context *c, struct > link_socket *in_sock) > perf_pop(); > } > > +void process_outgoing_tun_part3(struct context *c, struct link_socket > *in_sock) > +{ > + if (check_bulk_mode(c)) > + { > + int maxl = 0, plen = 0; > + int leng = BLEN(&c->c2.buffers->send_tun_max); > + uint8_t *temp = BPTR(&c->c2.buffers->send_tun_max); > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + temp = buff_postsize(temp, &plen); > + if ((leng > 0) && (plen > 0) && ((maxl + plen) < leng)) > + { > + c->c2.to_tun = c->c2.buffers->to_tun_max; > + c->c2.to_tun.offset = TUN_BAT_OFF; > + c->c2.to_tun.len = plen; > + bcopy(temp, BPTR(&c->c2.to_tun), plen); > + temp += plen; maxl += (plen + 2); > + //dmsg(M_INFO, "FWD BAT OUTP 1 [%d] [%d] [%d] [%d]", x, > BLEN(&c->c2.buf), BLEN(&c->c2.to_tun), BLEN(&c->c2.buffers->read_link_buf)); > + process_outgoing_tun_part2(c, in_sock); > + } else { break; } > + } > + buf_reset(&c->c2.to_tun); > + } > +} > + > +void process_outgoing_tun(struct context *c, struct link_socket *in_sock) > +{ > + if (c->c2.frame.bulk_size <= 0) { > + process_outgoing_tun_part2(c, in_sock); > + } > + process_outgoing_tun_part3(c, in_sock); > +} > + > void > pre_select(struct context *c) > { > diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h > index d5641491..9fda1583 100644 > --- a/src/openvpn/forward.h > +++ b/src/openvpn/forward.h > @@ -79,6 +79,8 @@ void pre_select(struct context *c); > > void process_io(struct context *c, struct link_socket *sock); > > +void xfer_io(struct context *c, struct context *b); > + > > /**********************************************************************/ > /** > @@ -196,6 +198,8 @@ bool process_incoming_link_part1(struct context *c, > struct link_socket_info *lsi > void process_incoming_link_part2(struct context *c, struct > link_socket_info *lsi, > const uint8_t *orig_buf); > > +void process_incoming_link_part3(struct context *c); > + > /** > * Transfers \c float_sa data extracted from an incoming DCO > * PEER_FLOAT_NTF to \c out_osaddr for later processing. > diff --git a/src/openvpn/init.c b/src/openvpn/init.c > index 40ae2c8c..0849dfce 100644 > --- a/src/openvpn/init.c > +++ b/src/openvpn/init.c > @@ -2971,6 +2971,10 @@ frame_finalize_options(struct context *c, const > struct options *o) > tailroom += COMP_EXTRA_BUFFER(payload_size); > #endif > > + if (frame->bulk_size > 0) { > + payload_size = frame->tun_mtu; > + } > + > frame->buf.payload_size = payload_size; > frame->buf.headroom = headroom; > frame->buf.tailroom = tailroom; > @@ -3473,6 +3477,9 @@ do_init_frame_tls(struct context *c) > if (c->c2.tls_multi) > { > tls_multi_init_finalize(c->c2.tls_multi, c->options.ce.tls_mtu); > + if (c->c2.frame.bulk_size > 0) { > + c->c2.tls_multi->opt.frame.buf.payload_size = > c->c2.frame.tun_mtu; > + } > ASSERT(c->c2.tls_multi->opt.frame.buf.payload_size <= > c->c2.frame.buf.payload_size); > frame_print(&c->c2.tls_multi->opt.frame, D_MTU_INFO, "Control > Channel MTU parms"); > > @@ -3536,6 +3543,14 @@ do_init_frame(struct context *c) > c->c2.frame.extra_tun += c->options.ce.tun_mtu_extra; > } > > + /* > + * Adjust bulk size based on the --bulk-mode parameter. > + */ > + if (c->options.ce.bulk_mode) > + { > + c->c2.frame.bulk_size = c->options.ce.tun_mtu; > + } > + > /* > * Fill in the blanks in the frame parameters structure, > * make sure values are rational, etc. > @@ -3676,9 +3691,40 @@ init_context_buffers(const struct frame *frame) > > size_t buf_size = BUF_SIZE(frame); > > + if (frame->bulk_size > 0) { > + buf_size = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, > frame->buf.headroom + frame->buf.tailroom); > + } > + > + dmsg(M_INFO, "MEM NEW [%ld] [%d+%d+%d]", buf_size, > frame->buf.headroom, frame->buf.payload_size, frame->buf.tailroom); > + > b->read_link_buf = alloc_buf(buf_size); > b->read_tun_buf = alloc_buf(buf_size); > > + if (frame->bulk_size > 0) { > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + size_t part_size = BUF_SIZE(frame); > + b->read_tun_bufs[x] = alloc_buf(part_size); > + b->read_tun_bufs[x].offset = TUN_BAT_OFF; > + b->read_tun_bufs[x].len = 0; > + } > + > + b->read_tun_max = alloc_buf(buf_size); > + b->read_tun_max.offset = TUN_BAT_OFF; > + b->read_tun_max.len = 0; > + > + b->send_tun_max = alloc_buf(buf_size); > + b->send_tun_max.offset = TUN_BAT_OFF; > + b->send_tun_max.len = 0; > + > + b->to_tun_max = alloc_buf(buf_size); > + b->to_tun_max.offset = TUN_BAT_OFF; > + b->to_tun_max.len = 0; > + } > + > + b->bufs_indx = -1; > + b->flag_ciph = -1; > + > b->aux_buf = alloc_buf(buf_size); > > b->encrypt_buf = alloc_buf(buf_size); > @@ -3701,6 +3747,16 @@ free_context_buffers(struct context_buffers *b) > free_buf(&b->read_tun_buf); > free_buf(&b->aux_buf); > > + if (b->to_tun_max.data) { > + free_buf(&b->to_tun_max); > + free_buf(&b->send_tun_max); > + free_buf(&b->read_tun_max); > + for (int x = 0; x < TUN_BAT_MAX; ++x) > + { > + free_buf(&b->read_tun_bufs[x]); > + } > + } > + > #ifdef USE_COMP > free_buf(&b->compress_buf); > free_buf(&b->decompress_buf); > diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c > index a419e32d..7e35c837 100644 > --- a/src/openvpn/mtu.c > +++ b/src/openvpn/mtu.c > @@ -41,9 +41,15 @@ void > alloc_buf_sock_tun(struct buffer *buf, const struct frame *frame) > { > /* allocate buffer for overlapped I/O */ > - *buf = alloc_buf(BUF_SIZE(frame)); > + size_t alen = BUF_SIZE(frame); > + size_t blen = frame->buf.payload_size; > + if (frame->bulk_size > 0) { > + alen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_OFF); > + blen = BAT_SIZE(TUN_BAT_MAX, frame->tun_mtu, TUN_BAT_NOP); > + } > + *buf = alloc_buf(alen); > ASSERT(buf_init(buf, frame->buf.headroom)); > - buf->len = frame->buf.payload_size; > + buf->len = blen; > ASSERT(buf_safe(buf, 0)); > } > > diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h > index 925ef0bf..eb799fb3 100644 > --- a/src/openvpn/mtu.h > +++ b/src/openvpn/mtu.h > @@ -58,6 +58,14 @@ > */ > #define TUN_MTU_MIN 100 > > +/* > + * Bulk mode static define values. > + */ > +#define TUN_BAT_MIN 6 > +#define TUN_BAT_MAX 9 > +#define TUN_BAT_OFF 256 > +#define TUN_BAT_NOP 0 > + > /* > * Default MTU of network over which tunnel data will pass by TCP/UDP. > */ > @@ -152,6 +160,10 @@ struct frame > * which defaults to 0 for tun and 32 > * (\c TAP_MTU_EXTRA_DEFAULT) for tap. > * */ > + > + int bulk_size; /**< Signal to the init frame function > + * to allow for bulk mode TCP transfers. > + * */ > }; > > /* Forward declarations, to prevent includes */ > @@ -171,6 +183,7 @@ struct options; > * larger than the headroom. > */ > #define BUF_SIZE(f) ((f)->buf.headroom + (f)->buf.payload_size + > (f)->buf.tailroom) > +#define BAT_SIZE(a, b, c) ((a * b) + c) > > /* > * Function prototypes. > diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c > index e1ce32ab..9e089703 100644 > --- a/src/openvpn/multi.c > +++ b/src/openvpn/multi.c > @@ -3414,6 +3414,7 @@ multi_process_incoming_link(struct multi_context *m, > struct multi_instance *inst > } > > process_incoming_link_part2(c, lsi, orig_buf); > + process_incoming_link_part3(c); > } > perf_pop(); > > @@ -3558,9 +3559,7 @@ multi_process_incoming_tun(struct multi_context *m, > const unsigned int mpp_flags > const int dev_type = TUNNEL_TYPE(m->top.c1.tuntap); > int16_t vid = 0; > > -#ifdef MULTI_DEBUG_EVENT_LOOP > - printf("TUN -> TCP/UDP [%d]\n", BLEN(&m->top.c2.buf)); > -#endif > + msg(D_MULTI_DEBUG, "TUN -> TCP/UDP [%d]", BLEN(&m->top.c2.buf)); > > if (m->pending) > { > @@ -3610,6 +3609,8 @@ multi_process_incoming_tun(struct multi_context *m, > const unsigned int mpp_flags > { > /* transfer packet pointer from top-level > context buffer to instance */ > c->c2.buf = m->top.c2.buf; > + /* todo determine if to call this > (multi_process_incoming_tun) for each bulk item read? */ > + xfer_io(c, &m->top); > } > else > { > diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h > index cd99cd40..21fa8967 100644 > --- a/src/openvpn/openvpn.h > +++ b/src/openvpn/openvpn.h > @@ -112,6 +112,14 @@ struct context_buffers > */ > struct buffer read_link_buf; > struct buffer read_tun_buf; > + > + struct buffer read_tun_bufs[TUN_BAT_MAX]; > + struct buffer read_tun_max; > + struct buffer send_tun_max; > + struct buffer to_tun_max; > + > + int bufs_indx; > + int flag_ciph; > }; > > /* > @@ -376,6 +384,8 @@ struct context_2 > struct buffer to_tun; > struct buffer to_link; > > + struct buffer bufs[TUN_BAT_MAX]; > + > /* should we print R|W|r|w to console on packet transfers? */ > bool log_rw; > > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index c54032d8..041d17d0 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -304,6 +304,7 @@ static const char usage_message[] = > " 'maybe' -- Use per-route hints\n" > " 'yes' -- Always DF (Don't Fragment)\n" > "--mtu-test : Empirically measure and report MTU.\n" > + "--bulk-mode : Use bulk TUN/TCP reads/writes.\n" > #ifdef ENABLE_FRAGMENT > "--fragment max : Enable internal datagram fragmentation so that no > UDP\n" > " datagrams are sent which are larger than max > bytes.\n" > @@ -3005,6 +3006,9 @@ options_postprocess_mutate_ce(struct options *o, > struct connection_entry *ce) > ce->tun_mtu_extra_defined = true; > ce->tun_mtu_extra = TAP_MTU_EXTRA_DEFAULT; > } > + if (ce->proto != PROTO_TCP && ce->proto != PROTO_TCP_SERVER && > ce->proto != PROTO_TCP_CLIENT) { > + ce->bulk_mode = false; > + } > } > > /* > @@ -9926,6 +9930,10 @@ add_option(struct options *options, char *p[], bool > is_inline, const char *file, > goto err; > } > } > + else if (streq(p[0], "bulk-mode")) > + { > + options->ce.bulk_mode = true; > + } > else > { > int i; > diff --git a/src/openvpn/options.h b/src/openvpn/options.h > index 38e67c8d..d1b0586d 100644 > --- a/src/openvpn/options.h > +++ b/src/openvpn/options.h > @@ -174,6 +174,9 @@ struct connection_entry > > /* Allow only client that support resending the wrapped client key */ > bool tls_crypt_v2_force_cookie; > + > + /* Bulk mode allows for multiple tun reads + larger tcp writes */ > + bool bulk_mode; > }; > > struct remote_entry > -- > 2.39.5 (Apple Git-154) > > |
From: Jon C. <ro...@fo...> - 2025-08-08 14:04:47
|
Hi Arne, You are correct, I didn't do a very good job of explaining the code in my blog post, I usually keep those short with more screen captures because I figure that not many people would actually take the time to read through it there. Also, I didn't really add many comments either but I did try to copy the present style of the code to try and make it match and be more consistent throughout, even if not perfect yet. I'm still testing out the change myself in my own home setup here to see if I run into any bad edge cases along the way. I can always try to explain the different code parts as I am indeed modifying the core parts of the read and write operations for tun and tcp so it's a big change to make to the code base. Basically this change is important to me in particular because of my setup and requirements in specific. I have WiFi LAN clients which all assume a 1500 byte MTU on their side and I have a router WAN client which enforces a 1500 byte MTU on the internet's side. In the middle of my core network is a VPN box and almost every VPN software will operate in UDP mode with a sub-1500 MTU in the middle of this network pipeline. This is not a good design to have in general as I don't want to waste cycles fragmenting and/or compressing the data into smaller sized UDP packets. With the code change I am presenting, I am able to specify a true 1500 byte VPN MTU interface with the exact matching 1500 byte read calls to the TUN interface itself (the code base had to be modified to allow for this because it was adding onto the payload_size used in the read call which I didn't want as I am operating on exact multiples of 1500 bytes in specific). With this change, my network pipeline is a true 1500 byte MTU which matches all the way from the client side to the vpn link to the internet side and to the server side (end to end to end to end). In addition, I also added the ability to batch together multiple 1500 byte read calls (specifically 6 x 1500 bytes into 9000 bytes) into one single encrypt sign call and one single TCP write call. This allows the encryption method to operate only once on a much larger payload size as well as allow the linux kernel to efficiently transfer the data with order and delivery guaranteed as fast as possible. The code base had to be modified to allow for all of this as well as it was preventing me from performing this much larger sized ssl sign and encrypt + tcp read and write (the code base assumes you are operating on only 1 tun read call worth of data at a time everywhere). This is exactly why I prefer using TCP to tunnel encrypted network data as my solution provided can properly set a full sized 1500 byte MTU as well as perform an exact matching read call of 1500 bytes to get the full amount of data from the interface and then bulk it together to efficiently encrypt it and then use the magic of TCP to transfer that data all at once as quickly as possible without any need for fragmentation or compression. I don't think any other VPN product on the market offers that kind of functionality as far as I am aware as most other VPN products use a smaller sized MTU as well as the packet size limitations of UDP. I believe that this could be a distinguishing feature for OpenVPN as well as automatically solve some of the issues that folks run into when inserting a VPN appliance into the middle of their network setups. I've been running this change on my own setup to at least make sure it works and it seems to be running pretty nicely so far. I haven't experienced any fragmentation or performance issues as any sized data that comes off the clients LAN side is fully taken care of now through the VPN side and onto the WAN and server side. If this is something you are not interested in I can understand that, I can stop posting here and the most I can do is at least submit a pull request in case anyone in the future is indeed interested in such work. It'd be nice to contribute to a good quality open source project that I have used for many many years and something which may help solve other community member's issues with regards to the small sized MTU + UDP problem which does exist in practice and really hampers connections along the way in a network design. I also don't mind explaining my code parts if you actually want, I just need to take time to write them out and describe what they are doing and why. As you can see, I am trying to achieve a very specific and exact design goal that the code base wasn't originally allowing for, so I had to make some modifications to be able to accomplish it. Thanks, Jon C On Fri, Aug 8, 2025 at 5:53 AM Arne Schwabe <ar...@rf...> wrote: > Am 07.08.25 um 20:29 schrieb Jon Chiappetta via Openvpn-devel: > > Thanks to Gert's help on this, I was able to finally configure and > > compile and run and test the bulk mode changes against the latest git > > source code to ensure everything still works correctly. > > > > I also fixed up some other issues like properly freeing the extra buffer > > allocations and removing the unneeded batched data prefixes and > > converting a remaining while loop to a max limited for loop and properly > > resetting the outgoing tun buffer pointer at the end of the write method > > when finished. > > > It would still good to explain what you are trying to achieve here and > what the idea behind the patch is to be able to review and understand > your patch. > > The patch itself basically has no comments at all, so it is very hard to > decipher for me from the patch what it is trying to to do. Eg there is a > variable flag_ciph that fiddles with encryption of packets. > > You are talking and describing this bulk mode as if it was obvious but > it is not. The description on your blog says: > > > [...] read 8192 bytes off of the client’s TCP sockets directly and > > proxy them in one write call over TCP directly to the VPN server > > without needing a tunnel interface with a small sized MTU which > > bottlenecks reads+writes to <1500 bytes per function call. > > It also not helping as you talking about TCP write/reads, where I can > see some improvement by cutting down the number of reads/writes. But the > second part then talks about not using a tunnel with a small sized MTU. > But if you use a larger sized TUN interface with a larger MTU, then you > already have larger reads/writes to the TCP socket. > > Also your speedtest showing 562 is meaningless without having any > comparison without your patch. > > Arne > |
From: flichtenheld (C. Review) <ge...@op...> - 2025-08-08 12:34:31
|
Attention is currently required from: plaisthos. Hello plaisthos, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/1142?usp=email to review the following change. Change subject: buffer: Clarify usage of char_class ...................................................................... buffer: Clarify usage of char_class Silence compiler warnings due to conversion from char to unsigned char. In this case we actually depend on this conversion. Change-Id: I73bc163d48b2d6a954cd231961826f33143fcd12 Signed-off-by: Frank Lichtenheld <fr...@li...> --- M src/openvpn/buffer.c 1 file changed, 11 insertions(+), 2 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/42/1142/1 diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c index 3dd8b31..ca2e3aa 100644 --- a/src/openvpn/buffer.c +++ b/src/openvpn/buffer.c @@ -560,7 +560,7 @@ { break; } - if (char_class(*last, CC_CRLF | CC_NULL)) + if (char_class((unsigned char)*last, CC_CRLF | CC_NULL)) { if (!buf_inc_len(buf, -1)) { @@ -872,6 +872,14 @@ * Classify and mutate strings based on character types. */ +/* Note 1: This functions depends on getting an unsigned + char. Both the is*() functions and our own checks expect it + this way. + Note 2: For CC_PRINT we just accept everything >= 32, so + if we ingest non-ASCII UTF-8 we will classify it as + printable since it will be >= 128. Other encodings are + not officially supported. +*/ bool char_class(const unsigned char c, const unsigned int flags) { @@ -1015,7 +1023,8 @@ static inline bool char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive) { - return char_class(c, inclusive) && !char_class(c, exclusive); + return char_class((unsigned char)c, inclusive) + && !char_class((unsigned char)c, exclusive); } bool -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1142?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: I73bc163d48b2d6a954cd231961826f33143fcd12 Gerrit-Change-Number: 1142 Gerrit-PatchSet: 1 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-MessageType: newchange |
From: flichtenheld (C. Review) <ge...@op...> - 2025-08-08 12:12:56
|
Attention is currently required from: plaisthos. Hello plaisthos, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/1141?usp=email to review the following change. Change subject: Review CMocka assertion usage ...................................................................... Review CMocka assertion usage Replace some assert_true calls with more specific assertions. This should improve reporting in case of problems and also just makes the code nicer. Change-Id: Ia2f374476c87855bba6c0f9d3e2f28a5fe62a152 Signed-off-by: Frank Lichtenheld <fr...@li...> --- M tests/unit_tests/openvpn/test_auth_token.c M tests/unit_tests/openvpn/test_packet_id.c M tests/unit_tests/openvpn/test_provider.c M tests/unit_tests/openvpn/test_tls_crypt.c 4 files changed, 27 insertions(+), 29 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/41/1141/1 diff --git a/tests/unit_tests/openvpn/test_auth_token.c b/tests/unit_tests/openvpn/test_auth_token.c index 0c5467e..e993409 100644 --- a/tests/unit_tests/openvpn/test_auth_token.c +++ b/tests/unit_tests/openvpn/test_auth_token.c @@ -286,9 +286,9 @@ strcpy(ctx->up.password, ctx->multi.auth_token); assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK); - assert_int_not_equal(0, memcmp(ctx->multi.auth_token_initial + strlen(SESSION_ID_PREFIX), - token_sessiona + strlen(SESSION_ID_PREFIX), - AUTH_TOKEN_SESSION_ID_BASE64_LEN)); + assert_memory_not_equal(ctx->multi.auth_token_initial + strlen(SESSION_ID_PREFIX), + token_sessiona + strlen(SESSION_ID_PREFIX), + AUTH_TOKEN_SESSION_ID_BASE64_LEN); /* The first token is valid but should trigger the invalid response since * the session id is not the same */ diff --git a/tests/unit_tests/openvpn/test_packet_id.c b/tests/unit_tests/openvpn/test_packet_id.c index d623c3d..85179cc 100644 --- a/tests/unit_tests/openvpn/test_packet_id.c +++ b/tests/unit_tests/openvpn/test_packet_id.c @@ -82,9 +82,9 @@ now = 5010; assert_true(packet_id_write(&data->pis, &data->test_buf, false, false)); - assert_true(data->pis.id == 1); - assert_true(data->test_buf_data.buf_id == htonl(1)); - assert_true(data->test_buf_data.buf_time == 0); + assert_int_equal(data->pis.id, 1); + assert_int_equal(data->test_buf_data.buf_id, htonl(1)); + assert_int_equal(data->test_buf_data.buf_time, 0); } static void @@ -96,8 +96,8 @@ assert_true(packet_id_write(&data->pis, &data->test_buf, true, false)); assert_int_equal(data->pis.id, 1); assert_int_equal(data->pis.time, now); - assert_true(data->test_buf_data.buf_id == htonl(1)); - assert_true(data->test_buf_data.buf_time == htonl((uint32_t)now)); + assert_int_equal(data->test_buf_data.buf_id, htonl(1)); + assert_int_equal(data->test_buf_data.buf_time, htonl((uint32_t)now)); } static void @@ -108,9 +108,9 @@ data->test_buf.offset = sizeof(packet_id_type); now = 5010; assert_true(packet_id_write(&data->pis, &data->test_buf, false, true)); - assert_true(data->pis.id == 1); - assert_true(data->test_buf_data.buf_id == htonl(1)); - assert_true(data->test_buf_data.buf_time == 0); + assert_int_equal(data->pis.id, 1); + assert_int_equal(data->test_buf_data.buf_id, htonl(1)); + assert_int_equal(data->test_buf_data.buf_time, 0); } static void @@ -123,8 +123,8 @@ assert_true(packet_id_write(&data->pis, &data->test_buf, true, true)); assert_int_equal(data->pis.id, 1); assert_int_equal(data->pis.time, now); - assert_true(data->test_buf_data.buf_id == htonl(1)); - assert_true(data->test_buf_data.buf_time == htonl((uint32_t)now)); + assert_int_equal(data->test_buf_data.buf_id, htonl(1)); + assert_int_equal(data->test_buf_data.buf_time, htonl((uint32_t)now)); } static void @@ -156,8 +156,8 @@ assert_int_equal(data->pis.id, 1); assert_int_equal(data->pis.time, now); - assert_true(data->test_buf_data.buf_id == htonl(1)); - assert_true(data->test_buf_data.buf_time == htonl((uint32_t)now)); + assert_int_equal(data->test_buf_data.buf_id, htonl(1)); + assert_int_equal(data->test_buf_data.buf_time, htonl((uint32_t)now)); } static void diff --git a/tests/unit_tests/openvpn/test_provider.c b/tests/unit_tests/openvpn/test_provider.c index 463b394..48adb96 100644 --- a/tests/unit_tests/openvpn/test_provider.c +++ b/tests/unit_tests/openvpn/test_provider.c @@ -287,9 +287,9 @@ for (size_t i = 0; i < _countof(pubkeys); i++) { pubkey = load_pubkey(pubkeys[i]); - assert_true(pubkey != NULL); + assert_non_null(pubkey); EVP_PKEY *privkey = xkey_load_management_key(NULL, pubkey); - assert_true(privkey != NULL); + assert_non_null(privkey); management->settings.flags = MF_EXTERNAL_KEY | MF_EXTERNAL_KEY_PSSPAD; @@ -384,11 +384,11 @@ for (size_t i = 0; i < _countof(pubkeys); i++) { pubkey = load_pubkey(pubkeys[i]); - assert_true(pubkey != NULL); + assert_non_null(pubkey); EVP_PKEY *privkey = xkey_load_generic_key(NULL, (void *)dummy, pubkey, xkey_sign, xkey_free); - assert_true(privkey != NULL); + assert_non_null(privkey); xkey_sign_called = 0; xkey_free_called = 0; diff --git a/tests/unit_tests/openvpn/test_tls_crypt.c b/tests/unit_tests/openvpn/test_tls_crypt.c index 596f0e0..6ae26fb 100644 --- a/tests/unit_tests/openvpn/test_tls_crypt.c +++ b/tests/unit_tests/openvpn/test_tls_crypt.c @@ -487,9 +487,8 @@ assert_true(tls_crypt_v2_unwrap_client_key(&unwrapped_client_key2, &unwrap_metadata, wrapped_client_key, &ctx->server_keys.decrypt)); - assert_true(0 - == memcmp(ctx->client_key2.keys, unwrapped_client_key2.keys, - sizeof(ctx->client_key2.keys))); + assert_memory_equal(ctx->client_key2.keys, unwrapped_client_key2.keys, + sizeof(ctx->client_key2.keys)); } /** @@ -511,9 +510,8 @@ assert_true(tls_crypt_v2_unwrap_client_key(&unwrapped_client_key2, &unwrap_metadata, ctx->wkc, &ctx->server_keys.decrypt)); - assert_true(0 - == memcmp(ctx->client_key2.keys, unwrapped_client_key2.keys, - sizeof(ctx->client_key2.keys))); + assert_memory_equal(ctx->client_key2.keys, unwrapped_client_key2.keys, + sizeof(ctx->client_key2.keys)); assert_true(buf_equal(&ctx->metadata, &unwrap_metadata)); struct tls_wrap_ctx wrap_ctx = { @@ -563,8 +561,8 @@ ctx->wkc, &ctx->server_keys.decrypt)); const struct key2 zero = { 0 }; - assert_true(0 == memcmp(&unwrapped_client_key2, &zero, sizeof(zero))); - assert_true(0 == BLEN(&ctx->unwrapped_metadata)); + assert_memory_equal(&unwrapped_client_key2, &zero, sizeof(zero)); + assert_int_equal(0, BLEN(&ctx->unwrapped_metadata)); } /** @@ -587,8 +585,8 @@ ctx->wkc, &ctx->server_keys.decrypt)); const struct key2 zero = { 0 }; - assert_true(0 == memcmp(&unwrapped_client_key2, &zero, sizeof(zero))); - assert_true(0 == BLEN(&ctx->unwrapped_metadata)); + assert_memory_equal(&unwrapped_client_key2, &zero, sizeof(zero)); + assert_int_equal(0, BLEN(&ctx->unwrapped_metadata)); } static void -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1141?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Ia2f374476c87855bba6c0f9d3e2f28a5fe62a152 Gerrit-Change-Number: 1141 Gerrit-PatchSet: 1 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-MessageType: newchange |
From: flichtenheld (C. Review) <ge...@op...> - 2025-08-08 12:12:44
|
Attention is currently required from: plaisthos. Hello plaisthos, I'd like you to reexamine a change. Please visit http://gerrit.openvpn.net/c/openvpn/+/1106?usp=email to look at the new patch set (#8). Change subject: options: Make sure option types are treated as unsigned ...................................................................... options: Make sure option types are treated as unsigned verify_permissions already expects them to be unsigned, make sure they are to avoid spurious conversion warnings. Change-Id: I1d27cb81d32058e40147e1d6dcd12df7f6cb2b30 Signed-off-by: Frank Lichtenheld <fr...@li...> --- M src/openvpn/options.h M src/openvpn/ssl_ncp.c M src/openvpn/ssl_ncp.h 3 files changed, 34 insertions(+), 34 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/06/1106/8 diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 5ee6e93..7dc2912 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -729,38 +729,38 @@ /* * Option classes. */ -#define OPT_P_GENERAL (1 << 0) -#define OPT_P_UP (1 << 1) -#define OPT_P_ROUTE (1 << 2) -#define OPT_P_DHCPDNS (1 << 3) /* includes ip windows options like */ -#define OPT_P_SCRIPT (1 << 4) -#define OPT_P_SETENV (1 << 5) -#define OPT_P_SHAPER (1 << 6) -#define OPT_P_TIMER (1 << 7) -#define OPT_P_PERSIST (1 << 8) -#define OPT_P_PERSIST_IP (1 << 9) -#define OPT_P_COMP (1 << 10) /* TODO */ -#define OPT_P_MESSAGES (1 << 11) -#define OPT_P_NCP (1 << 12) /**< Negotiable crypto parameters */ -#define OPT_P_TLS_PARMS (1 << 13) /* TODO */ -#define OPT_P_MTU (1 << 14) /* TODO */ -#define OPT_P_NICE (1 << 15) -#define OPT_P_PUSH (1 << 16) -#define OPT_P_INSTANCE (1 << 17) /**< allowed in ccd, client-connect etc*/ -#define OPT_P_CONFIG (1 << 18) -#define OPT_P_EXPLICIT_NOTIFY (1 << 19) -#define OPT_P_ECHO (1 << 20) -#define OPT_P_INHERIT (1 << 21) -#define OPT_P_ROUTE_EXTRAS (1 << 22) -#define OPT_P_PULL_MODE (1 << 23) -#define OPT_P_PLUGIN (1 << 24) -#define OPT_P_SOCKBUF (1 << 25) -#define OPT_P_SOCKFLAGS (1 << 26) -#define OPT_P_CONNECTION (1 << 27) -#define OPT_P_PEER_ID (1 << 28) -#define OPT_P_INLINE (1 << 29) -#define OPT_P_PUSH_MTU (1 << 30) -#define OPT_P_ROUTE_TABLE (1 << 31) +#define OPT_P_GENERAL (1u << 0) +#define OPT_P_UP (1u << 1) +#define OPT_P_ROUTE (1u << 2) +#define OPT_P_DHCPDNS (1u << 3) /* includes ip windows options like */ +#define OPT_P_SCRIPT (1u << 4) +#define OPT_P_SETENV (1u << 5) +#define OPT_P_SHAPER (1u << 6) +#define OPT_P_TIMER (1u << 7) +#define OPT_P_PERSIST (1u << 8) +#define OPT_P_PERSIST_IP (1u << 9) +#define OPT_P_COMP (1u << 10) /* TODO */ +#define OPT_P_MESSAGES (1u << 11) +#define OPT_P_NCP (1u << 12) /**< Negotiable crypto parameters */ +#define OPT_P_TLS_PARMS (1u << 13) /* TODO */ +#define OPT_P_MTU (1u << 14) /* TODO */ +#define OPT_P_NICE (1u << 15) +#define OPT_P_PUSH (1u << 16) +#define OPT_P_INSTANCE (1u << 17) /**< allowed in ccd, client-connect etc*/ +#define OPT_P_CONFIG (1u << 18) +#define OPT_P_EXPLICIT_NOTIFY (1u << 19) +#define OPT_P_ECHO (1u << 20) +#define OPT_P_INHERIT (1u << 21) +#define OPT_P_ROUTE_EXTRAS (1u << 22) +#define OPT_P_PULL_MODE (1u << 23) +#define OPT_P_PLUGIN (1u << 24) +#define OPT_P_SOCKBUF (1u << 25) +#define OPT_P_SOCKFLAGS (1u << 26) +#define OPT_P_CONNECTION (1u << 27) +#define OPT_P_PEER_ID (1u << 28) +#define OPT_P_INLINE (1u << 29) +#define OPT_P_PUSH_MTU (1u << 30) +#define OPT_P_ROUTE_TABLE (1u << 31) #define OPT_P_DEFAULT (~(OPT_P_INSTANCE | OPT_P_PULL_MODE)) diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c index 5e094a2..51f7f92 100644 --- a/src/openvpn/ssl_ncp.c +++ b/src/openvpn/ssl_ncp.c @@ -307,7 +307,7 @@ } bool -check_pull_client_ncp(struct context *c, const int found) +check_pull_client_ncp(struct context *c, const unsigned int found) { if (found & OPT_P_NCP) { diff --git a/src/openvpn/ssl_ncp.h b/src/openvpn/ssl_ncp.h index 2b2ff85..ed5d281 100644 --- a/src/openvpn/ssl_ncp.h +++ b/src/openvpn/ssl_ncp.h @@ -49,7 +49,7 @@ * * @return Wether the client NCP process suceeded or failed */ -bool check_pull_client_ncp(struct context *c, int found); +bool check_pull_client_ncp(struct context *c, unsigned int found); /** * Iterates through the ciphers in server_list and return the first -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1106?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: I1d27cb81d32058e40147e1d6dcd12df7f6cb2b30 Gerrit-Change-Number: 1106 Gerrit-PatchSet: 8 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-MessageType: newpatchset |
From: Arne S. <ar...@rf...> - 2025-08-08 09:53:41
|
Am 07.08.25 um 20:29 schrieb Jon Chiappetta via Openvpn-devel: > Thanks to Gert's help on this, I was able to finally configure and > compile and run and test the bulk mode changes against the latest git > source code to ensure everything still works correctly. > > I also fixed up some other issues like properly freeing the extra buffer > allocations and removing the unneeded batched data prefixes and > converting a remaining while loop to a max limited for loop and properly > resetting the outgoing tun buffer pointer at the end of the write method > when finished. It would still good to explain what you are trying to achieve here and what the idea behind the patch is to be able to review and understand your patch. The patch itself basically has no comments at all, so it is very hard to decipher for me from the patch what it is trying to to do. Eg there is a variable flag_ciph that fiddles with encryption of packets. You are talking and describing this bulk mode as if it was obvious but it is not. The description on your blog says: > [...] read 8192 bytes off of the client’s TCP sockets directly and > proxy them in one write call over TCP directly to the VPN server > without needing a tunnel interface with a small sized MTU which > bottlenecks reads+writes to <1500 bytes per function call. It also not helping as you talking about TCP write/reads, where I can see some improvement by cutting down the number of reads/writes. But the second part then talks about not using a tunnel with a small sized MTU. But if you use a larger sized TUN interface with a larger MTU, then you already have larger reads/writes to the TCP socket. Also your speedtest showing 562 is meaningless without having any comparison without your patch. Arne |
From: cron2 (C. Review) <ge...@op...> - 2025-08-08 05:58:42
|
cron2 has submitted this change. ( http://gerrit.openvpn.net/c/openvpn/+/1139?usp=email ) Change subject: t_client.sh: Do not wait 3 seconds for OpenVPN to come up ...................................................................... t_client.sh: Do not wait 3 seconds for OpenVPN to come up On most machines 1 second should be quite enough. Given that we run currently 23 tests on most t_client runs, this makes over 40 seconds difference. Not nothing. We keep the existing 30s maximum wait-time since sometimes we want to do tests with intentionally slow servers. Change-Id: Ice8c7ff4d8118a9e6465a4724207a355138360b8 Signed-off-by: Frank Lichtenheld <fr...@li...> Acked-by: Gert Doering <ge...@gr...> Message-Id: <202...@gr...> URL: https://www.mail-archive.com/ope...@li.../msg32585.html Signed-off-by: Gert Doering <ge...@gr...> --- M tests/t_client.sh.in 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/t_client.sh.in b/tests/t_client.sh.in index 7a271b6..a49de40 100755 --- a/tests/t_client.sh.in +++ b/tests/t_client.sh.in @@ -377,13 +377,13 @@ $RUN_SUDO "${openvpn}" $openvpn_conf >>$LOGDIR/$SUF:openvpn.log & sudopid=$! - # Check if OpenVPN has initialized before continuing. It will check every 3rd second up + # Check if OpenVPN has initialized before continuing. It will check every second up # to $ovpn_init_check times. - ovpn_init_check=10 + ovpn_init_check=30 ovpn_init_success=0 while [ $ovpn_init_check -gt 0 ]; do - sleep 3 # Wait for OpenVPN to initialize and have had time to write the pid file + sleep 1 # Wait for OpenVPN to initialize and have had time to write the pid file grep "Initialization Sequence Completed" $LOGDIR/$SUF:openvpn.log >/dev/null if [ $? -eq 0 ]; then ovpn_init_check=0 -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1139?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Ice8c7ff4d8118a9e6465a4724207a355138360b8 Gerrit-Change-Number: 1139 Gerrit-PatchSet: 3 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: cron2 <ge...@gr...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-MessageType: merged |
From: cron2 (C. Review) <ge...@op...> - 2025-08-08 05:58:41
|
cron2 has uploaded a new patch set (#3) to the change originally created by flichtenheld. ( http://gerrit.openvpn.net/c/openvpn/+/1139?usp=email ) The following approvals got outdated and were removed: Code-Review+2 by cron2 Change subject: t_client.sh: Do not wait 3 seconds for OpenVPN to come up ...................................................................... t_client.sh: Do not wait 3 seconds for OpenVPN to come up On most machines 1 second should be quite enough. Given that we run currently 23 tests on most t_client runs, this makes over 40 seconds difference. Not nothing. We keep the existing 30s maximum wait-time since sometimes we want to do tests with intentionally slow servers. Change-Id: Ice8c7ff4d8118a9e6465a4724207a355138360b8 Signed-off-by: Frank Lichtenheld <fr...@li...> Acked-by: Gert Doering <ge...@gr...> Message-Id: <202...@gr...> URL: https://www.mail-archive.com/ope...@li.../msg32585.html Signed-off-by: Gert Doering <ge...@gr...> --- M tests/t_client.sh.in 1 file changed, 3 insertions(+), 3 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/39/1139/3 diff --git a/tests/t_client.sh.in b/tests/t_client.sh.in index 7a271b6..a49de40 100755 --- a/tests/t_client.sh.in +++ b/tests/t_client.sh.in @@ -377,13 +377,13 @@ $RUN_SUDO "${openvpn}" $openvpn_conf >>$LOGDIR/$SUF:openvpn.log & sudopid=$! - # Check if OpenVPN has initialized before continuing. It will check every 3rd second up + # Check if OpenVPN has initialized before continuing. It will check every second up # to $ovpn_init_check times. - ovpn_init_check=10 + ovpn_init_check=30 ovpn_init_success=0 while [ $ovpn_init_check -gt 0 ]; do - sleep 3 # Wait for OpenVPN to initialize and have had time to write the pid file + sleep 1 # Wait for OpenVPN to initialize and have had time to write the pid file grep "Initialization Sequence Completed" $LOGDIR/$SUF:openvpn.log >/dev/null if [ $? -eq 0 ]; then ovpn_init_check=0 -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1139?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Ice8c7ff4d8118a9e6465a4724207a355138360b8 Gerrit-Change-Number: 1139 Gerrit-PatchSet: 3 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: cron2 <ge...@gr...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-MessageType: newpatchset |
From: Gert D. <ge...@gr...> - 2025-08-08 05:58:24
|
"Because it makes sense" :-) - lightly tested on Linux, heavily tested by the buildbots. Your patch has been applied to the master branch. commit db1fd1a80baa9e44df8ae82f0fd2b56c59195484 Author: Frank Lichtenheld Date: Thu Aug 7 22:37:35 2025 +0200 t_client.sh: Do not wait 3 seconds for OpenVPN to come up Signed-off-by: Frank Lichtenheld <fr...@li...> Acked-by: Gert Doering <ge...@gr...> Message-Id: <202...@gr...> URL: https://www.mail-archive.com/ope...@li.../msg32585.html Signed-off-by: Gert Doering <ge...@gr...> -- kind regards, Gert Doering |
From: cron2 (C. Review) <ge...@op...> - 2025-08-07 20:40:07
|
Attention is currently required from: flichtenheld, plaisthos. cron2 has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/1140?usp=email ) Change subject: Collect trivial conversion fixes ...................................................................... Patch Set 1: Code-Review+2 -- To view, visit http://gerrit.openvpn.net/c/openvpn/+/1140?usp=email To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings Gerrit-Project: openvpn Gerrit-Branch: master Gerrit-Change-Id: Id565ec17856444b580dd89edab92e9fe18d39b77 Gerrit-Change-Number: 1140 Gerrit-PatchSet: 1 Gerrit-Owner: flichtenheld <fr...@li...> Gerrit-Reviewer: cron2 <ge...@gr...> Gerrit-Reviewer: plaisthos <arn...@rf...> Gerrit-CC: openvpn-devel <ope...@li...> Gerrit-Attention: plaisthos <arn...@rf...> Gerrit-Attention: flichtenheld <fr...@li...> Gerrit-Comment-Date: Thu, 07 Aug 2025 20:39:52 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment |
From: Gert D. <ge...@gr...> - 2025-08-07 20:38:00
|
From: Frank Lichtenheld <fr...@li...> On most machines 1 second should be quite enough. Given that we run currently 23 tests on most t_client runs, this makes over 40 seconds difference. Not nothing. We keep the existing 30s maximum wait-time since sometimes we want to do tests with intentionally slow servers. Change-Id: Ice8c7ff4d8118a9e6465a4724207a355138360b8 Signed-off-by: Frank Lichtenheld <fr...@li...> Acked-by: Gert Doering <ge...@gr...> --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1139 This mail reflects revision 2 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering <ge...@gr...> diff --git a/tests/t_client.sh.in b/tests/t_client.sh.in index 7a271b6..a49de40 100755 --- a/tests/t_client.sh.in +++ b/tests/t_client.sh.in @@ -377,13 +377,13 @@ $RUN_SUDO "${openvpn}" $openvpn_conf >>$LOGDIR/$SUF:openvpn.log & sudopid=$! - # Check if OpenVPN has initialized before continuing. It will check every 3rd second up + # Check if OpenVPN has initialized before continuing. It will check every second up # to $ovpn_init_check times. - ovpn_init_check=10 + ovpn_init_check=30 ovpn_init_success=0 while [ $ovpn_init_check -gt 0 ]; do - sleep 3 # Wait for OpenVPN to initialize and have had time to write the pid file + sleep 1 # Wait for OpenVPN to initialize and have had time to write the pid file grep "Initialization Sequence Completed" $LOGDIR/$SUF:openvpn.log >/dev/null if [ $? -eq 0 ]; then ovpn_init_check=0 |