From 68c78c586b207100705e3865d4c3a6b9f8403657 Mon Sep 17 00:00:00 2001 From: Jon Chiappetta Date: Wed, 6 Aug 2025 16:33:18 -0400 Subject: [PATCH] bulk mode --- src/openvpn/forward.c | 288 ++++++++++++++++++++++++++++++++++------- src/openvpn/forward.h | 7 + src/openvpn/init.c | 64 +++++++++ src/openvpn/mtu.c | 11 +- src/openvpn/mtu.h | 15 +++ src/openvpn/mudp.c | 10 +- src/openvpn/multi.c | 199 ++++++++++++++++++++++++---- src/openvpn/multi.h | 22 +++- src/openvpn/multi_io.c | 88 ++++++------- src/openvpn/multi_io.h | 1 + src/openvpn/openvpn.h | 11 ++ src/openvpn/options.c | 10 ++ src/openvpn/options.h | 3 + 13 files changed, 596 insertions(+), 133 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 492e6671665..6317acef15e 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -45,6 +45,9 @@ #include "memdbg.h" +#include +#include + counter_type link_read_bytes_global; /* GLOBAL */ counter_type link_write_bytes_global; /* GLOBAL */ @@ -612,6 +615,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] = (uint8_t)((*size >> 8) & 0xff); + buff[1] = (uint8_t)((*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 @@ -889,7 +907,7 @@ socks_postprocess_incoming_link(struct context *c, struct link_socket *sock) { if (sock->socks_proxy && sock->info.proto == PROTO_UDP) { - socks_process_incoming_udp(&c->c2.buf, &c->c2.from); + socks_process_incoming_udp(&c->c2.buf2, &c->c2.from); } } @@ -919,7 +937,7 @@ link_socket_write_post_size_adjust(int *size, int size_delta, struct buffer *buf } /* - * Output: c->c2.buf + * Output: c->c2.buf2 */ void @@ -933,10 +951,10 @@ read_incoming_link(struct context *c, struct link_socket *sock) /*ASSERT (!c->c2.to_tun.len);*/ - c->c2.buf = c->c2.buffers->read_link_buf; - ASSERT(buf_init(&c->c2.buf, c->c2.frame.buf.headroom)); + c->c2.buf2 = c->c2.buffers->read_link_buf; + ASSERT(buf_init(&c->c2.buf2, c->c2.frame.buf.headroom)); - status = link_socket_read(sock, &c->c2.buf, &c->c2.from); + status = link_socket_read(sock, &c->c2.buf2, &c->c2.from); if (socket_connection_reset(sock, status)) { @@ -989,11 +1007,11 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo struct gc_arena gc = gc_new(); bool decrypt_status = false; - if (c->c2.buf.len > 0) + if (c->c2.buf2.len > 0) { - c->c2.link_read_bytes += c->c2.buf.len; - link_read_bytes_global += c->c2.buf.len; - c->c2.original_recv_size = c->c2.buf.len; + c->c2.link_read_bytes += c->c2.buf2.len; + link_read_bytes_global += c->c2.buf2.len; + c->c2.original_recv_size = c->c2.buf2.len; } else { @@ -1006,21 +1024,22 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo { if (!ask_gremlin(c->options.gremlin)) { - c->c2.buf.len = 0; + c->c2.buf2.len = 0; } - corrupt_gremlin(&c->c2.buf, c->options.gremlin); + corrupt_gremlin(&c->c2.buf2, c->options.gremlin); } #endif /* log incoming packet */ #ifdef LOG_RW - if (c->c2.log_rw && c->c2.buf.len > 0) + if (c->c2.log_rw && c->c2.buf2.len > 0) { 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)); + BLEN(&c->c2.buf2), print_link_socket_actual(&c->c2.from, &gc), PROTO_DUMP(&c->c2.buf2, &gc)); /* * Good, non-zero length packet received. @@ -1029,18 +1048,18 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo * If any stage fails, it sets buf.len to 0 or -1, * telling downstream stages to ignore the packet. */ - if (c->c2.buf.len > 0) + if (c->c2.buf2.len > 0) { struct crypto_options *co = NULL; const uint8_t *ad_start = NULL; - if (!link_socket_verify_incoming_addr(&c->c2.buf, lsi, &c->c2.from)) + if (!link_socket_verify_incoming_addr(&c->c2.buf2, lsi, &c->c2.from)) { - link_socket_bad_incoming_addr(&c->c2.buf, lsi, &c->c2.from); + link_socket_bad_incoming_addr(&c->c2.buf2, lsi, &c->c2.from); } if (c->c2.tls_multi) { - uint8_t opcode = *BPTR(&c->c2.buf) >> P_OPCODE_SHIFT; + uint8_t opcode = *BPTR(&c->c2.buf2) >> P_OPCODE_SHIFT; /* * If DCO is enabled, the kernel drivers require that the @@ -1054,7 +1073,7 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo { msg(D_LINK_ERRORS, "Data Channel Offload doesn't support DATA_V1 packets. " "Upgrade your server to 2.4.5 or newer."); - c->c2.buf.len = 0; + c->c2.buf2.len = 0; } /* @@ -1067,7 +1086,7 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo * will load crypto_options with the correct encryption key * and return false. */ - if (tls_pre_decrypt(c->c2.tls_multi, &c->c2.from, &c->c2.buf, &co, floated, &ad_start)) + if (tls_pre_decrypt(c->c2.tls_multi, &c->c2.from, &c->c2.buf2, &co, floated, &ad_start)) { interval_action(&c->c2.tmp_int); @@ -1090,12 +1109,12 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo */ if (c->c2.tls_multi && c->c2.tls_multi->multi_state < CAS_CONNECT_DONE) { - c->c2.buf.len = 0; + c->c2.buf2.len = 0; } /* authenticate and decrypt the incoming packet */ decrypt_status = - openvpn_decrypt(&c->c2.buf, c->c2.buffers->decrypt_buf, co, &c->c2.frame, ad_start); + openvpn_decrypt(&c->c2.buf2, c->c2.buffers->decrypt_buf, co, &c->c2.frame, ad_start); if (!decrypt_status /* on the instance context we have only one socket, so just check the first one */ @@ -1120,12 +1139,12 @@ void process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf) { - if (c->c2.buf.len > 0) + if (c->c2.buf2.len > 0) { #ifdef ENABLE_FRAGMENT if (c->c2.fragment) { - fragment_incoming(c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment); + fragment_incoming(c->c2.fragment, &c->c2.buf2, &c->c2.frame_fragment); } #endif @@ -1133,14 +1152,14 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, /* decompress the incoming packet */ if (c->c2.comp_context) { - (*c->c2.comp_context->alg.decompress)(&c->c2.buf, c->c2.buffers->decompress_buf, + (*c->c2.comp_context->alg.decompress)(&c->c2.buf2, c->c2.buffers->decompress_buf, c->c2.comp_context, &c->c2.frame); } #endif #ifdef PACKET_TRUNCATION_CHECK - /* if (c->c2.buf.len > 1) --c->c2.buf.len; */ - ipv4_packet_size_verify(BPTR(&c->c2.buf), BLEN(&c->c2.buf), TUNNEL_TYPE(c->c1.tuntap), + /* if (c->c2.buf2.len > 1) --c->c2.buf2.len; */ + ipv4_packet_size_verify(BPTR(&c->c2.buf2), BLEN(&c->c2.buf2), TUNNEL_TYPE(c->c1.tuntap), "POST_DECRYPT", &c->c2.n_trunc_post_decrypt); #endif @@ -1153,39 +1172,39 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, * * Also, update the persisted version of our packet-id. */ - if (!TLS_MODE(c) && c->c2.buf.len > 0) + if (!TLS_MODE(c) && c->c2.buf2.len > 0) { link_socket_set_outgoing_addr(lsi, &c->c2.from, NULL, c->c2.es); } /* reset packet received timer */ - if (c->options.ping_rec_timeout && c->c2.buf.len > 0) + if (c->options.ping_rec_timeout && c->c2.buf2.len > 0) { event_timeout_reset(&c->c2.ping_rec_interval); } /* increment authenticated receive byte count */ - if (c->c2.buf.len > 0) + if (c->c2.buf2.len > 0) { - c->c2.link_read_bytes_auth += c->c2.buf.len; + c->c2.link_read_bytes_auth += c->c2.buf2.len; c->c2.max_recv_size_local = max_int(c->c2.original_recv_size, c->c2.max_recv_size_local); } /* Did we just receive an openvpn ping packet? */ - if (is_ping_msg(&c->c2.buf)) + if (is_ping_msg(&c->c2.buf2)) { dmsg(D_PING, "RECEIVED PING PACKET"); - c->c2.buf.len = 0; /* drop packet */ + c->c2.buf2.len = 0; /* drop packet */ } /* Did we just receive an OCC packet? */ - if (is_occ_msg(&c->c2.buf)) + if (is_occ_msg(&c->c2.buf2)) { process_received_occ_msg(c); } - buffer_turnover(orig_buf, &c->c2.to_tun, &c->c2.buf, &c->c2.buffers->read_link_buf); + buffer_turnover(orig_buf, &c->c2.to_tun, &c->c2.buf2, &c->c2.buffers->read_link_buf); /* to_tun defined + unopened tuntap can cause deadlock */ if (!tuntap_defined(c->c1.tuntap)) @@ -1199,14 +1218,31 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, } } +void process_incoming_link_part3(struct context *c) +{ + if (BULK_MODE(c)) + { + if (c->c2.buf2.len > 0) + { + c->c2.to_tun.offset += 2; + c->c2.buf2.offset += 2; + } + else + { + buf_reset(&c->c2.to_tun); + } + } +} + static void process_incoming_link(struct context *c, struct link_socket *sock) { struct link_socket_info *lsi = &sock->info; - const uint8_t *orig_buf = c->c2.buf.data; + const uint8_t *orig_buf = c->c2.buf2.data; process_incoming_link_part1(c, lsi, false); process_incoming_link_part2(c, lsi, orig_buf); + process_incoming_link_part3(c); } void @@ -1297,7 +1333,7 @@ process_incoming_dco(dco_context_t *dco) */ void -read_incoming_tun(struct context *c) +read_incoming_tun_part2(struct context *c) { /* * Setup for read() call on TUN/TAP device. @@ -1352,10 +1388,62 @@ read_incoming_tun(struct context *c) check_status(c->c2.buf.len, "read from TUN/TAP", NULL, c->c1.tuntap); } -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif +void read_incoming_tun_part3(struct context *c) +{ + fd_set rfds; + struct timeval timo; + int plen = 0, pidx = -1; + int fdno = c->c1.tuntap->fd; + for (int x = 0; x < TUN_BAT_MIN; ++x) + { + int leng = plen, indx = (pidx + 1); + 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->bulk_indx = 0; + c->c2.buffers->bulk_leng = (pidx + 1); +} + +void read_incoming_tun(struct context *c) +{ + if (!BULK_MODE(c)) + { + read_incoming_tun_part2(c); + } + else + { + read_incoming_tun_part3(c); + } +} /** * Drops UDP packets which OS decided to route via tun. @@ -1481,7 +1569,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(); @@ -1498,7 +1586,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) { @@ -1522,7 +1610,10 @@ process_incoming_tun(struct context *c, struct link_socket *out_sock) } if (c->c2.buf.len > 0) { - encrypt_sign(c, true); + if (!BULK_MODE(c)) + { + encrypt_sign(c, true); + } } else { @@ -1531,6 +1622,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 (BULK_DATA(c->c2.buffers)) + { + 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 leng = c->c2.buffers->bulk_leng; + int plen = 0, maxl = 0; + for (int x = 0; x < leng; ++x) + { + c->c2.buf = c->c2.bufs[x]; + process_incoming_tun_part2(c, out_sock); + plen = BLEN(&c->c2.buf); + if (plen > 0) + { + temp = buff_prepsize(temp, &plen); + bcopy(BPTR(&c->c2.buf), temp, plen); + temp += plen; maxl += (plen + 2); + } + c->c2.bufs[x].len = 0; + } + 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); + } + else + { + buf_reset(&c->c2.to_link); + } + } + else + { + buf_reset(&c->c2.to_link); + } + c->c2.buffers->bulk_indx = -1; + c->c2.buffers->bulk_leng = -1; +} + +void process_incoming_tun(struct context *c, struct link_socket *out_sock) +{ + if (!BULK_MODE(c)) + { + process_incoming_tun_part2(c, out_sock); + } + else + { + 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 @@ -1759,7 +1904,7 @@ process_outgoing_link(struct context *c, struct link_socket *sock) struct gc_arena gc = gc_new(); int error_code = 0; - 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 @@ -1804,6 +1949,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)); @@ -1888,7 +2034,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 @@ -1919,7 +2065,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), @@ -1973,6 +2120,49 @@ process_outgoing_tun(struct context *c, struct link_socket *in_sock) buf_reset(&c->c2.to_tun); } +void process_outgoing_tun_part3(struct context *c, struct link_socket *in_sock) +{ + if ((c->c2.to_tun.len > 0) && (c->c2.to_tun.offset > 1)) + { + c->c2.to_tun.offset -= 2; + buf_init(&c->c2.buffers->send_tun_max, TUN_BAT_OFF); + buf_copy(&c->c2.buffers->send_tun_max, &c->c2.to_tun); + 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_MIN; ++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 (!BULK_MODE(c)) + { + process_outgoing_tun_part2(c, in_sock); + } + else + { + process_outgoing_tun_part3(c, in_sock); + } +} + void pre_select(struct context *c) { @@ -2224,7 +2414,7 @@ io_wait(struct context *c, const unsigned int flags) if (!c->sig->signal_received) { - if (!(flags & IOW_CHECK_RESIDUAL) || !sockets_read_residual(c)) + if (true) { int status; @@ -2276,9 +2466,9 @@ io_wait(struct context *c, const unsigned int flags) c->c2.event_set_status = ES_TIMEOUT; } } - else + if (sockets_read_residual(c)) { - c->c2.event_set_status = SOCKET_READ; + c->c2.event_set_status |= (SOCKET_READ << SOCKET_SHIFT); } } diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index 7f6f666f4de..7cdb7b15b13 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -34,6 +34,11 @@ * file */ +#define BULK_MODE(c) (c && c->c2.frame.bulk_size > 0) +#define BULK_DATA(b) (b && (b->bulk_leng > 0) && (b->bulk_indx < b->bulk_leng)) +#define INST_LENG(a) (a && (a->inst_leng > 0) && (a->inst_indx < a->inst_leng)) +#define LINK_LEFT(i) (i && sockets_read_residual(i)) + #define TUN_OUT(c) (BLEN(&(c)->c2.to_tun) > 0) #define LINK_OUT(c) (BLEN(&(c)->c2.to_link) > 0) #define ANY_OUT(c) (TUN_OUT(c) || LINK_OUT(c)) @@ -193,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 ee198cea563..2d173463d2e 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2952,6 +2952,11 @@ frame_finalize_options(struct context *c, const struct options *o) tailroom += COMP_EXTRA_BUFFER(payload_size); #endif + if (frame->bulk_size > 0) + { + payload_size = BAT_SIZE(TUN_BAT_ONE, frame->tun_mtu, TUN_BAT_OFF); + } + frame->buf.payload_size = payload_size; frame->buf.headroom = headroom; frame->buf.tailroom = tailroom; @@ -3457,6 +3462,10 @@ 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"); @@ -3524,6 +3533,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. @@ -3664,9 +3681,45 @@ 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, "BULK bufs [%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) + { + size_t off_size = (frame->buf.headroom + TUN_BAT_OFF + frame->buf.tailroom); + size_t one_size = BAT_SIZE(TUN_BAT_ONE, frame->tun_mtu, off_size); + + for (int x = 0; x < TUN_BAT_MAX; ++x) + { + b->read_tun_bufs[x] = alloc_buf(one_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->bulk_indx = -1; + b->bulk_leng = -1; + b->aux_buf = alloc_buf(buf_size); b->encrypt_buf = alloc_buf(buf_size); @@ -3689,6 +3742,17 @@ 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 e080ea9eb09..6b3d521fb46 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -41,9 +41,16 @@ void alloc_buf_sock_tun(struct buffer *buf, const struct frame *frame) { /* allocate buffer for overlapped I/O */ - *buf = alloc_buf(BUF_SIZE(frame)); + int alen = BUF_SIZE(frame); + int 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 b6901491e14..92f621ba6d2 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -58,6 +58,15 @@ */ #define TUN_MTU_MIN 100 +/* + * Bulk mode static define values. + */ +#define TUN_BAT_MIN 6 +#define TUN_BAT_MAX 9 +#define TUN_BAT_OFF 250 +#define TUN_BAT_NOP 0 +#define TUN_BAT_ONE 1 + /* * Default MTU of network over which tunnel data will pass by TCP/UDP. */ @@ -157,6 +166,11 @@ struct frame * which defaults to 0 for tun and 32 * (\c TAP_MTU_EXTRA_DEFAULT) for tap. * */ + + int bulk_size; /**< Configure and setup in the init library + * frame function to signal and inform the various + * related function calls to process bulk mode data transfers. + * */ }; /* Forward declarations, to prevent includes */ @@ -176,6 +190,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/mudp.c b/src/openvpn/mudp.c index 92d4dda028d..73e1c850b3a 100644 --- a/src/openvpn/mudp.c +++ b/src/openvpn/mudp.c @@ -367,15 +367,15 @@ unsigned int p2mp_iow_flags(const struct multi_context *m) { unsigned int flags = IOW_WAIT_SIGNAL; - if (m->pending) + if (m->pending || m->pending2) { - if (TUN_OUT(&m->pending->context)) + if (m->pending && LINK_OUT(&m->pending->context)) { - flags |= IOW_TO_TUN; + flags |= IOW_TO_LINK; } - if (LINK_OUT(&m->pending->context)) + if (m->pending2 && TUN_OUT(&m->pending2->context)) { - flags |= IOW_TO_LINK; + flags |= IOW_TO_TUN; } } else if (mbuf_defined(m->mbuf)) diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 329d0a3c3b9..27beba39d6e 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -420,6 +420,10 @@ multi_init(struct context *t) } m->deferred_shutdown_signal.signal_received = 0; + + m->inst_indx = -1; + m->inst_leng = -1; + m->inst_list = calloc(TUN_BAT_MAX, sizeof(struct multi_instance *)); } const char * @@ -580,6 +584,10 @@ multi_close_instance(struct multi_context *m, struct multi_instance *mi, bool sh { multi_set_pending(m, NULL); } + if (m->pending2 == mi) + { + multi_set_pending2(m, NULL); + } if (m->earliest_wakeup == mi) { m->earliest_wakeup = NULL; @@ -701,6 +709,10 @@ multi_uninit(struct multi_context *m) multi_reap_free(m->reaper); mroute_helper_free(m->route_helper); multi_io_free(m->multi_io); + + m->inst_indx = -1; + m->inst_leng = -1; + free(m->inst_list); } } @@ -3010,8 +3022,7 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns { bool ret = true; - if (!IS_SIG(&mi->context) - && ((flags & MPP_PRE_SELECT))) + if (!IS_SIG(&mi->context) && (flags & MPP_PRE_SELECT) && !LINK_OUT(&mi->context)) { #if defined(ENABLE_ASYNC_PUSH) bool was_unauthenticated = true; @@ -3078,10 +3089,11 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns else { /* continue to pend on output? */ - multi_set_pending(m, ANY_OUT(&mi->context) ? mi : NULL); + multi_set_pending(m, LINK_OUT(&mi->context) ? mi : NULL); + multi_set_pending2(m, TUN_OUT(&mi->context) ? mi : NULL); #ifdef MULTI_DEBUG_EVENT_LOOP - printf("POST %s[%d] to=%d lo=%d/%d w=%" PRIi64 "/%ld\n", id(mi), (int)(mi == m->pending), + printf("POST %s[%d][%d] to=%d lo=%d/%d w=%" PRIi64 "/%ld\n", id(mi), (int)(mi == m->pending), (int)(mi == m->pending2), mi ? mi->context.c2.to_tun.len : -1, mi ? mi->context.c2.to_link.len : -1, (mi && mi->context.c2.fragment) ? mi->context.c2.fragment->outgoing.len : -1, (int64_t)mi->context.c2.timeval.tv_sec, (long)mi->context.c2.timeval.tv_usec); @@ -3140,7 +3152,7 @@ multi_process_float(struct multi_context *m, struct multi_instance *mi, struct l msg(D_MULTI_LOW, "Disallow float to an address taken by another client %s", multi_instance_string(ex_mi, false, &gc)); - mi->context.c2.buf.len = 0; + mi->context.c2.buf2.len = 0; goto done; } @@ -3345,7 +3357,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst bool ret = true; bool floated = false; - if (m->pending) + if (m->pending2) { return true; } @@ -3353,26 +3365,26 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (!instance) { #ifdef MULTI_DEBUG_EVENT_LOOP - printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf)); + printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf2)); #endif - multi_set_pending(m, multi_get_create_instance_udp(m, &floated, sock)); + multi_set_pending2(m, multi_get_create_instance_udp(m, &floated, sock)); } else { - multi_set_pending(m, instance); + multi_set_pending2(m, instance); } - if (m->pending) + if (m->pending2) { - set_prefix(m->pending); + set_prefix(m->pending2); /* get instance context */ - c = &m->pending->context; + c = &m->pending2->context; if (!instance) { /* transfer packet pointer from top-level context buffer to instance */ - c->c2.buf = m->top.c2.buf; + c->c2.buf2 = m->top.c2.buf2; /* transfer from-addr from top-level context buffer to instance */ if (!floated) @@ -3381,7 +3393,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } } - if (BLEN(&c->c2.buf) > 0) + if (BLEN(&c->c2.buf2) > 0) { struct link_socket_info *lsi; const uint8_t *orig_buf; @@ -3389,16 +3401,17 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst /* decrypt in instance context */ lsi = &sock->info; - orig_buf = c->c2.buf.data; + orig_buf = c->c2.buf2.data; if (process_incoming_link_part1(c, lsi, floated)) { /* nonzero length means that we have a valid, decrypted packed */ - if (floated && c->c2.buf.len > 0) + if (floated && c->c2.buf2.len > 0) { - multi_process_float(m, m->pending, sock); + multi_process_float(m, m->pending2, sock); } process_incoming_link_part2(c, lsi, orig_buf); + process_incoming_link_part3(c); } if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN) @@ -3413,7 +3426,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst c->c2.to_tun.len = 0; } /* make sure that source address is associated with this client */ - else if (multi_get_instance_by_virtual_addr(m, &src, true) != m->pending) + else if (multi_get_instance_by_virtual_addr(m, &src, true) != m->pending2) { /* IPv6 link-local address (fe80::xxx)? */ if ((src.type & MR_ADDR_MASK) == MR_ADDR_IPV6 @@ -3436,7 +3449,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (mroute_flags & MROUTE_EXTRACT_MCAST) { /* for now, treat multicast as broadcast */ - multi_bcast(m, &c->c2.to_tun, m->pending, 0); + multi_bcast(m, &c->c2.to_tun, m->pending2, 0); } else /* possible client to client routing */ { @@ -3478,14 +3491,14 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED) { - if (multi_learn_addr(m, m->pending, &src, 0) == m->pending) + if (multi_learn_addr(m, m->pending2, &src, 0) == m->pending2) { /* check for broadcast */ if (m->enable_c2c) { if (mroute_flags & (MROUTE_EXTRACT_BCAST | MROUTE_EXTRACT_MCAST)) { - multi_bcast(m, &c->c2.to_tun, m->pending, vid); + multi_bcast(m, &c->c2.to_tun, m->pending2, vid); } else /* try client-to-client routing */ { @@ -3517,7 +3530,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } /* postprocess and set wakeup */ - ret = multi_process_post(m, m->pending, mpp_flags); + ret = multi_process_post(m, m->pending2, mpp_flags); clear_prefix(); } @@ -3526,12 +3539,19 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst return ret; } +int min_max(int a, int b, int c) +{ + if (a > c) { return c; } + if (a < b) { return b; } + return a; +} + /* * Process packets in the TUN/TAP interface -> TCP/UDP socket direction, * i.e. server -> client direction. */ bool -multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags) +multi_process_incoming_tun_part2(struct multi_context *m, const unsigned int mpp_flags) { bool ret = true; @@ -3577,10 +3597,30 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags /* for now, treat multicast as broadcast */ multi_bcast(m, &m->top.c2.buf, NULL, vid); } + else if (m->inst_indx == -9) + { + struct multi_instance *inst = multi_get_instance_by_virtual_addr(m, &dest, dev_type == DEV_TYPE_TUN); + if (inst) + { + int leng = m->inst_leng; + for (int x = 0; x < leng; ++x) + { + if (m->inst_list[x] == inst) + { + m->inst_indx = x; + return true; + } + } + leng = min_max(leng, 0, TUN_BAT_MIN - 1); + m->inst_list[leng] = inst; + m->inst_indx = leng; + m->inst_leng = (leng + 1); + } + return true; + } else { - multi_set_pending( - m, multi_get_instance_by_virtual_addr(m, &dest, dev_type == DEV_TYPE_TUN)); + multi_set_pending(m, multi_get_instance_by_virtual_addr(m, &dest, dev_type == DEV_TYPE_TUN)); if (m->pending) { @@ -3618,6 +3658,113 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags return ret; } +bool multi_process_inp_tun_post(struct multi_context *m, const unsigned int mpp_flags) +{ + if (!INST_LENG(m)) + { + return false; + } + if (m->pending) + { + return false; + } + while (m->inst_indx < m->inst_leng) + { + struct multi_instance *i = m->inst_list[m->inst_indx]; + if (i) + { + if (!(multi_output_queue_ready(m, i))) + { + return false; + } + multi_set_pending(m, i); + set_prefix(m->pending); + multi_process_post(m, m->pending, mpp_flags); + clear_prefix(); + m->inst_list[m->inst_indx] = NULL; + } + m->inst_indx += 1; + break; + } + return true; +} + +bool multi_process_incoming_tun_part3(struct multi_context *m, const unsigned int mpp_flags) +{ + struct context *c, *b = &(m->top); + struct multi_instance *i; + int leng = b->c2.buffers->bulk_leng; + m->inst_indx = -1; + m->inst_leng = -1; + for (int x = 0; x < leng; ++x) + { + m->inst_indx = -9; + b->c2.buf = b->c2.bufs[x]; + multi_process_incoming_tun_part2(m, mpp_flags); + if (m->inst_indx > -1) + { + i = m->inst_list[m->inst_indx]; + c = &(i->context); + int y = min_max(c->c2.buffers->bulk_leng, 0, TUN_BAT_MIN - 1); + c->c2.buffers->read_tun_bufs[y].offset = TUN_BAT_OFF; + c->c2.buffers->read_tun_bufs[y].len = BLEN(&b->c2.bufs[x]); + bcopy(BPTR(&b->c2.bufs[x]), BPTR(&c->c2.buffers->read_tun_bufs[y]), BLEN(&b->c2.bufs[x])); + c->c2.bufs[y] = c->c2.buffers->read_tun_bufs[y]; + c->c2.buffers->bulk_indx = 0; + c->c2.buffers->bulk_leng = (y + 1); + } + } + leng = m->inst_leng; + b->c2.buffers->bulk_indx = -1; + b->c2.buffers->bulk_leng = -1; + for (int x = 0; x < leng; ++x) + { + i = m->inst_list[x]; + c = &(i->context); + c->c2.buf = c->c2.bufs[0]; + process_incoming_tun(c, c->c2.link_sockets[0]); + } + m->inst_indx = 0; + b->c2.buf.len = 0; + buf_reset(&b->c2.to_link); + return true; +} + +bool multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags) +{ + if (!(m->top.options.ce.bulk_mode)) + { + return multi_process_incoming_tun_part2(m, mpp_flags); + } + else + { + return multi_process_incoming_tun_part3(m, mpp_flags); + } +} + +bool multi_in_tun(struct multi_context *m, const unsigned int mpp_flags) +{ + if (INST_LENG(m)) + { + multi_process_inp_tun_post(m, mpp_flags); + } + else + { + struct context *c = &(m->top); + read_incoming_tun(c); + if (!IS_SIG(c)) + { + multi_process_incoming_tun(m, mpp_flags); + } + if (!IS_SIG(c)) + { + multi_process_inp_tun_post(m, mpp_flags); + } + return true; + } + return false; +} + /* * Process a possible client-to-client/bcast/mcast message in the * queue. @@ -3693,7 +3840,7 @@ multi_process_timeout(struct multi_context *m, const unsigned int mpp_flags) void multi_process_drop_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags) { - struct multi_instance *mi = m->pending; + struct multi_instance *mi = m->pending2; ASSERT(mi); diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index 1209dfb928f..3880a03d5c5 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -194,6 +194,7 @@ struct multi_context #endif struct multi_instance *pending; + struct multi_instance *pending2; struct multi_instance *earliest_wakeup; struct multi_instance **mpp_touched; struct context_buffers *context_buffers; @@ -217,6 +218,10 @@ struct multi_context #endif struct deferred_signal_schedule_entry deferred_shutdown_signal; + + int inst_indx; + int inst_leng; + struct multi_instance **inst_list; }; /** @@ -256,6 +261,7 @@ struct multi_route */ void tunnel_server(struct context *top); +int min_max(int a, int b, int c); const char *multi_instance_string(const struct multi_instance *mi, bool null, struct gc_arena *gc); @@ -353,6 +359,9 @@ bool multi_process_incoming_link(struct multi_context *m, struct multi_instance */ bool multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags); +bool multi_process_inp_tun_post(struct multi_context *m, const unsigned int mpp_flags); + +bool multi_in_tun(struct multi_context *m, const unsigned int mpp_flags); void multi_process_drop_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags); @@ -405,7 +414,6 @@ static inline struct multi_instance * multi_process_outgoing_link_pre(struct multi_context *m) { struct multi_instance *mi = NULL; - if (m->pending) { mi = m->pending; @@ -630,7 +638,7 @@ multi_get_timeout_instance(struct multi_context *m, struct timeval *dest) static inline bool multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags) { - struct multi_instance *mi = m->pending; + struct multi_instance *mi = m->pending2; bool ret = true; ASSERT(mi); @@ -650,8 +658,7 @@ multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags | OPT_P_COMP | OPT_P_SOCKFLAGS) static inline bool -multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instance *mi, - const unsigned int mpp_flags) +multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags) { bool ret = true; set_prefix(mi); @@ -698,6 +705,13 @@ multi_set_pending(struct multi_context *m, struct multi_instance *mi) { m->pending = mi; } + +static inline void +multi_set_pending2(struct multi_context *m, struct multi_instance *mi) +{ + m->pending2 = mi; +} + /** * Assigns a peer-id to a a client and adds the instance to the * the instances array of the \c multi_context structure. diff --git a/src/openvpn/multi_io.c b/src/openvpn/multi_io.c index 997951ec05f..f5c5039fc9c 100644 --- a/src/openvpn/multi_io.c +++ b/src/openvpn/multi_io.c @@ -292,12 +292,9 @@ multi_io_dispatch(struct multi_context *m, struct multi_instance *mi, const int switch (action) { + case TA_INST_LENG: case TA_TUN_READ: - read_incoming_tun(&m->top); - if (!IS_SIG(&m->top)) - { - multi_process_incoming_tun(m, mpp_flags); - } + multi_in_tun(m, mpp_flags); break; case TA_SOCKET_READ: @@ -363,52 +360,34 @@ multi_io_post(struct multi_context *m, struct multi_instance *mi, const int acti struct context *c = multi_get_context(m, mi); int newaction = TA_UNDEF; -#define MTP_NONE 0 -#define MTP_TUN_OUT (1 << 0) -#define MTP_LINK_OUT (1 << 1) - unsigned int flags = MTP_NONE; + if (LINK_OUT(c)) + { + newaction = TA_SOCKET_WRITE; + goto last; + } + else if (INST_LENG(m)) + { + newaction = TA_INST_LENG; + goto last; + } if (TUN_OUT(c)) { - flags |= MTP_TUN_OUT; + newaction = TA_TUN_WRITE; + goto last; } - if (LINK_OUT(c)) + else if (LINK_LEFT(c)) { - flags |= MTP_LINK_OUT; + newaction = TA_SOCKET_READ_RESIDUAL; + goto last; } - switch (flags) + if (mi) { - case MTP_TUN_OUT | MTP_LINK_OUT: - case MTP_TUN_OUT: - newaction = TA_TUN_WRITE; - break; - - case MTP_LINK_OUT: - newaction = TA_SOCKET_WRITE; - break; - - case MTP_NONE: - if (mi && sockets_read_residual(c)) - { - newaction = TA_SOCKET_READ_RESIDUAL; - } - else - { - multi_io_set_global_rw_flags(m, mi); - } - break; - - default: - { - struct gc_arena gc = gc_new(); - msg(M_FATAL, "MULTI IO: multi_io_post bad state, mi=%s flags=%d", - multi_instance_string(mi, false, &gc), flags); - gc_free(&gc); - break; - } + multi_io_set_global_rw_flags(m, mi); } +last: dmsg(D_MULTI_DEBUG, "MULTI IO: multi_io_post %s -> %s", pract(action), pract(newaction)); return newaction; @@ -462,19 +441,17 @@ multi_io_process_io(struct multi_context *m) { socket_reset_listen_persistent(ev_arg->u.sock); mi = multi_create_instance_tcp(m, ev_arg->u.sock); + if (mi) { multi_io_action(m, mi, TA_INITIAL, false); } } else { multi_process_io_udp(m, ev_arg->u.sock); - mi = m->pending; + if (m->pending) { multi_io_action(m, m->pending, TA_INITIAL, false); } + if (m->pending2) { multi_io_action(m, m->pending2, TA_INITIAL, false); } } /* monitor and/or handle events that are * triggered in succession by the first one * before returning to the main loop. */ - if (mi) - { - multi_io_action(m, mi, TA_INITIAL, false); - } break; } } @@ -559,7 +536,7 @@ multi_io_action(struct multi_context *m, struct multi_instance *mi, int action, * On our first pass, poll will be false because we already know * that input is available, and to call io_wait would be redundant. */ - if (poll && action != TA_SOCKET_READ_RESIDUAL) + if (poll && action != TA_SOCKET_READ_RESIDUAL && action != TA_INST_LENG) { const int orig_action = action; action = multi_io_wait_lite(m, mi, action, &tun_input_pending); @@ -594,10 +571,22 @@ multi_io_action(struct multi_context *m, struct multi_instance *mi, int action, * for a particular instance, point to * that instance. */ + int retry_undef = 0; if (m->pending) { mi = m->pending; } + if (m->pending2) + { + if (!m->pending) + { + mi = m->pending2; + } + else + { + retry_undef = 1; + } + } /* * Based on the effects of the action, @@ -605,6 +594,11 @@ multi_io_action(struct multi_context *m, struct multi_instance *mi, int action, * possibly transition to a new action state. */ action = multi_io_post(m, mi, action); + if ((action == TA_UNDEF) && (retry_undef == 1)) + { + mi = m->pending2; + action = multi_io_post(m, mi, action); + } /* * If we are finished processing the original action, diff --git a/src/openvpn/multi_io.h b/src/openvpn/multi_io.h index 4a3c60d236d..daf43b0e0f6 100644 --- a/src/openvpn/multi_io.h +++ b/src/openvpn/multi_io.h @@ -44,6 +44,7 @@ #define TA_INITIAL 8 #define TA_TIMEOUT 9 #define TA_TUN_WRITE_TIMEOUT 10 +#define TA_INST_LENG 11 /* * I/O state and events tracker diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index 9325e21fd2f..80991ddcd76 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 bulk_indx; + int bulk_leng; }; /* @@ -373,9 +381,12 @@ struct context_2 * struct context_buffers. */ struct buffer buf; + struct buffer buf2; 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 34af0d31d44..5b65631d897 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -306,6 +306,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" @@ -3292,6 +3293,11 @@ options_postprocess_mutate_invariant(struct options *options) options->pkcs11_providers[0] = DEFAULT_PKCS11_MODULE; } #endif + + if ((options->ce.proto != PROTO_TCP) && (options->ce.proto != PROTO_TCP_SERVER) && (options->ce.proto != PROTO_TCP_CLIENT)) + { + options->ce.bulk_mode = false; + } } static void @@ -9271,6 +9277,10 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file, } options->vlan_pvid = (uint16_t)vlan_pvid; } + 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 0561c2563d8..655bd4dbc29 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -177,6 +177,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