Hi! I see my openvpn handling a lot of small packets (around 100 bytes) at a high rate. I have seen packing smaller packets into one bigger packet suggested but the idea always rejected for various reasons.
Anyways, I made a little patch to do that (works agains 2.1rc4), and did a couple of quick tests. I run 50 ping -i 0 -s 153 for 30 seconds, observing CPU usage and made note of bytes transfered over the link. In both cases, 18.7MB was pushed back and forth (pings, you know). I looked at CPU usage with top, so it is a tad subjective measure, but with multipacketing the cpu usage was mostly something like 10%-15%, but without it more like 20% (I used UDP transport with TLS layer). So it can probably give an advantage in some cases. I submit the patch to your scrutiny Siim Põder
diff -aur openvpn-2.1~rc2/forward.c openvpn-2.1~rc2-multipacket/forward.c --- openvpn-2.1~rc2/forward.c 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1~rc2-multipacket/forward.c 2007-06-18 17:12:36.000000000 +0300 @@ -867,6 +867,69 @@ } /* + * Pack multiple packets into the buffer + * Output: c->c2.buf + */ + +void +read_incoming_tun_mp(struct context *c) +{ + int readlen = 0, packets = 0; + BLEN (&c->c2.buf) = 0; + + ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame))); + ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame))); + + /* First check if there was a packet left out from last batch and use that one */ + if (BLEN (&c->c2.from_tun_of) != 0) + { + memcpy(BPTR (&c->c2.buf), BPTR (&c->c2.from_tun_of), BLEN (&c->c2.from_tun_of)); + BLEN (&c->c2.buf) = BLEN (&c->c2.from_tun_of); + BLEN (&c->c2.from_tun_of) = 0; + } + + /* Read more packets to fill the buffer */ + while (MAX_RW_SIZE_TUN (&c->c2.frame) - BLEN (&c->c2.buf) - 2 > 0) + { + readlen = read_tun (c->c1.tuntap, BPTR (&c->c2.buf) + BLEN (&c->c2.buf) + 2, MAX_RW_SIZE_TUN (&c->c2.frame)); + if (readlen > 0) + { + packets++; + *(uint16_t*) (BPTR (&c->c2.buf) + BLEN (&c->c2.buf)) = readlen; + BLEN (&c->c2.buf) += readlen + 2; + } + else + break; + } + + /* Last packet does not fit MTU, keep it post-buffer for retrieval next time around */ + if (BLEN (&c->c2.buf) > MAX_RW_SIZE_TUN (&c->c2.frame)) + { + BLEN (&c->c2.buf) -= readlen + 2; + memcpy(&c->c2.from_tun_of, &c->c2.buf, sizeof(struct buffer)); + c->c2.from_tun_of.offset += BLEN (&c->c2.buf); + BLEN (&c->c2.from_tun_of) = readlen + 2; +#ifdef PACKET_TRUNCATION_CHECK + ipv4_packet_size_verify (BPTR (&c->c2.from_tun_of) + c->c2.from_tun_of.offset + 2, + BLEN (&c->c2.from_tun_of) - 2, + TUNNEL_TYPE (c->c1.tuntap), + "READ_TUN", + &c->c2.n_trunc_tun_read); +#endif + } +#ifdef PACKET_TRUNCATION_CHECK + else if (packets == 1) + { + ipv4_packet_size_verify (BPTR (&c->c2.buf) + 2, + BLEN (&c->c2.buf) - 2, + TUNNEL_TYPE (c->c1.tuntap), + "READ_TUN", + &c->c2.n_trunc_tun_read); + } +#endif +} + +/* * Output: c->c2.buf */ @@ -881,21 +944,26 @@ perf_push (PERF_READ_IN_TUN); c->c2.buf = c->c2.buffers->read_tun_buf; + if (c->options.multipacket) + read_incoming_tun_mp (c); + else + { #ifdef TUN_PASS_BUFFER - read_tun_buffered (c->c1.tuntap, &c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame)); + read_tun_buffered (c->c1.tuntap, &c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame)); #else - ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame))); - ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame))); - c->c2.buf.len = read_tun (c->c1.tuntap, BPTR (&c->c2.buf), MAX_RW_SIZE_TUN (&c->c2.frame)); + ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame))); + ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame))); + c->c2.buf.len = read_tun (c->c1.tuntap, BPTR (&c->c2.buf), MAX_RW_SIZE_TUN (&c->c2.frame)); #endif #ifdef PACKET_TRUNCATION_CHECK - ipv4_packet_size_verify (BPTR (&c->c2.buf), - BLEN (&c->c2.buf), - TUNNEL_TYPE (c->c1.tuntap), - "READ_TUN", - &c->c2.n_trunc_tun_read); + ipv4_packet_size_verify (BPTR (&c->c2.buf), + BLEN (&c->c2.buf), + TUNNEL_TYPE (c->c1.tuntap), + "READ_TUN", + &c->c2.n_trunc_tun_read); #endif + } /* Was TUN/TAP interface stopped? */ if (tuntap_stop (c->c2.buf.len)) @@ -1160,6 +1228,25 @@ #endif dmsg (D_TUN_RW, "TUN WRITE [%d]", BLEN (&c->c2.to_tun)); + /* if multiple packets are packed into one frame */ + if (c->options.multipacket) + { + size = 0; + //fprintf(stderr, "Multipacketing\n"); + while (size < BLEN (&c->c2.to_tun)) + { +#ifdef PACKET_TRUNCATION_CHECK + ipv4_packet_size_verify (BPTR (&c->c2.to_tun) + size + 2, + *(uint16_t*) (BPTR (&c->c2.to_tun) + size), + TUNNEL_TYPE (c->c1.tuntap), + "WRITE_TUN", + &c->c2.n_trunc_tun_write); +#endif + size += write_tun (c->c1.tuntap, BPTR (&c->c2.to_tun) + size + 2, *(uint16_t*) (BPTR (&c->c2.to_tun) + size) ) + 2; + } + } + else /* not multipacket */ + { #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify (BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun), @@ -1173,7 +1260,6 @@ #else size = write_tun (c->c1.tuntap, BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun)); #endif - if (size > 0) c->c2.tun_write_bytes += size; check_status (size, "write to TUN/TAP", NULL, c->c1.tuntap); @@ -1192,6 +1278,7 @@ /* indicate activity regarding --inactive parameter */ register_activity (c, size); } + } } else { diff -aur openvpn-2.1~rc2/init.c openvpn-2.1~rc2-multipacket/init.c --- openvpn-2.1~rc2/init.c 2006-11-23 23:35:20.000000000 +0200 +++ openvpn-2.1~rc2-multipacket/init.c 2007-06-18 16:11:50.000000000 +0300 @@ -1816,7 +1816,7 @@ ALLOC_OBJ_CLEAR (b, struct context_buffers); b->read_link_buf = alloc_buf (BUF_SIZE (frame)); - b->read_tun_buf = alloc_buf (BUF_SIZE (frame)); + b->read_tun_buf = alloc_buf (BUF_SIZE (frame) * 2); b->aux_buf = alloc_buf (BUF_SIZE (frame)); @@ -1865,6 +1865,7 @@ { c->c2.buffers = init_context_buffers (&c->c2.frame); c->c2.buffers_owned = true; + c->c2.from_tun_of.len = 0; } #ifdef ENABLE_FRAGMENT Only in openvpn-2.1~rc2/install-win32: openvpn.nsi diff -aur openvpn-2.1~rc2/openvpn.h openvpn-2.1~rc2-multipacket/openvpn.h --- openvpn-2.1~rc2/openvpn.h 2006-11-23 23:34:40.000000000 +0200 +++ openvpn-2.1~rc2-multipacket/openvpn.h 2007-06-18 16:14:09.000000000 +0300 @@ -357,6 +357,7 @@ * struct context_buffers. */ struct buffer buf; + struct buffer from_tun_of; struct buffer to_tun; struct buffer to_link; Only in openvpn-2.1~rc2: openvpn.spec diff -aur openvpn-2.1~rc2/options.c openvpn-2.1~rc2-multipacket/options.c --- openvpn-2.1~rc2/options.c 2007-02-28 05:50:35.000000000 +0200 +++ openvpn-2.1~rc2-multipacket/options.c 2007-06-18 16:11:50.000000000 +0300 @@ -298,6 +298,7 @@ "--comp-noadapt : Don't use adaptive compression when --comp-lzo\n" " is specified.\n" #endif + "--multipacket : Pack consequtive small packets into one bigger packet.\n" #ifdef ENABLE_MANAGEMENT "--management ip port [pass] : Enable a TCP server on ip:port to handle\n" " management functions. pass is a password file\n" @@ -1177,6 +1178,8 @@ SHOW_INT (lzo); #endif + SHOW_BOOL (multipacket); + SHOW_STR (route_script); SHOW_STR (route_default_gateway); SHOW_INT (route_default_metric); @@ -2004,6 +2007,8 @@ * --comp-lzo * --fragment * + * --multipacket + * * Crypto Options: * * --cipher @@ -2081,6 +2086,9 @@ buf_printf (&out, ",comp-lzo"); #endif + if (o->multipacket) + buf_printf (&out, ",multipacket"); + #ifdef ENABLE_FRAGMENT if (o->fragment) buf_printf (&out, ",mtu-dynamic"); @@ -4651,6 +4659,11 @@ options->lzo &= ~LZO_ADAPTIVE; } #endif /* USE_LZO */ + else if (streq (p[0], "multipacket")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->multipacket = true; + } #ifdef USE_CRYPTO else if (streq (p[0], "show-ciphers")) { diff -aur openvpn-2.1~rc2/options.h openvpn-2.1~rc2-multipacket/options.h --- openvpn-2.1~rc2/options.h 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1~rc2-multipacket/options.h 2007-06-18 16:14:09.000000000 +0300 @@ -236,6 +236,9 @@ unsigned int lzo; #endif + /* pack consequtive small packets into one MTU-sized packet */ + bool multipacket; + /* buffer sizes */ int rcvbuf; int sndbuf;