1 /*++
2 /* NAME
3 /*	vstream_tweak 3
4 /* SUMMARY
5 /*	performance tweaks
6 /* SYNOPSIS
7 /*	#include <vstream.h>
8 /*
9 /*	VSTREAM	*vstream_tweak_sock(stream)
10 /*	VSTREAM	*stream;
11 /*
12 /*	VSTREAM	*vstream_tweak_tcp(stream)
13 /*	VSTREAM	*stream;
14 /* DESCRIPTION
15 /*	vstream_tweak_sock() does a best effort to boost your
16 /*	network performance on the specified generic stream.
17 /*
18 /*	vstream_tweak_tcp() does a best effort to boost your
19 /*	Internet performance on the specified TCP stream.
20 /*
21 /*	Arguments:
22 /* .IP stream
23 /*	The stream being boosted.
24 /* DIAGNOSTICS
25 /*	Panics: interface violations.
26 /* LICENSE
27 /* .ad
28 /* .fi
29 /*	The Secure Mailer license must be distributed with this software.
30 /* AUTHOR(S)
31 /*	Wietse Venema
32 /*	IBM T.J. Watson Research
33 /*	P.O. Box 704
34 /*	Yorktown Heights, NY 10598, USA
35 /*
36 /*	Wietse Venema
37 /*	Google, Inc.
38 /*	111 8th Avenue
39 /*	New York, NY 10011, USA
40 /*--*/
41 
42 /* System library. */
43 
44 #include <sys_defs.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <netinet/tcp.h>
48 #include <errno.h>
49 
50 /* Utility library. */
51 
52 #include <msg.h>
53 #include <vstream.h>
54 
55 /* Application-specific. */
56 
57 #ifdef HAS_IPV6
58 #define SOCKADDR_STORAGE struct sockaddr_storage
59 #else
60 #define SOCKADDR_STORAGE struct sockaddr
61 #endif
62 
63 /* vstream_tweak_sock - boost your generic network performance */
64 
vstream_tweak_sock(VSTREAM * fp)65 int     vstream_tweak_sock(VSTREAM *fp)
66 {
67     SOCKADDR_STORAGE ss;
68     struct sockaddr *sa = (struct sockaddr *) &ss;
69     SOCKADDR_SIZE sa_length = sizeof(ss);
70     int     ret;
71 
72     /*
73      * If the caller doesn't know if this socket is AF_LOCAL, AF_INET, etc.,
74      * figure it out for them.
75      */
76     if ((ret = getsockname(vstream_fileno(fp), sa, &sa_length)) >= 0) {
77 	switch (sa->sa_family) {
78 #ifdef AF_INET6
79 	case AF_INET6:
80 #endif
81 	case AF_INET:
82 	    ret = vstream_tweak_tcp(fp);
83 	    break;
84 	}
85     }
86     return (ret);
87 }
88 
89 /* vstream_tweak_tcp - boost your TCP performance */
90 
vstream_tweak_tcp(VSTREAM * fp)91 int     vstream_tweak_tcp(VSTREAM *fp)
92 {
93     const char *myname = "vstream_tweak_tcp";
94     int     mss = 0;
95     SOCKOPT_SIZE mss_len = sizeof(mss);
96     int     err;
97 
98     /*
99      * Avoid Nagle delays when VSTREAM buffers are smaller than the MSS.
100      *
101      * Forcing TCP_NODELAY to be "always on" would hurt performance in the
102      * common case where VSTREAM buffers are larger than the MSS.
103      *
104      * Instead we ask the kernel what the current MSS is, and take appropriate
105      * action. Linux <= 2.2 getsockopt(TCP_MAXSEG) always returns zero (or
106      * whatever value was stored last with setsockopt()).
107      *
108      * Some ancient FreeBSD kernels don't report 'host unreachable' errors with
109      * getsockopt(SO_ERROR), and then treat getsockopt(TCP_MAXSEG) as a NOOP,
110      * leaving the mss parameter value unchanged. To work around these two
111      * getsockopt() bugs we set mss = 0, which is a harmless value.
112      */
113     if ((err = getsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_MAXSEG,
114 			  (void *) &mss, &mss_len)) < 0
115 	&& errno != ECONNRESET) {
116 	msg_warn("%s: getsockopt TCP_MAXSEG: %m", myname);
117 	return (err);
118     }
119     if (msg_verbose)
120 	msg_info("%s: TCP_MAXSEG %d", myname, mss);
121 
122     /*
123      * Fix for recent Postfix versions: increase the VSTREAM buffer size if
124      * it is smaller than the MSS. Note: the MSS may change when the route
125      * changes and IP path MTU discovery is turned on, so we choose a
126      * somewhat larger buffer.
127      *
128      * Note: as of 20120527, the CA_VSTREAM_CTL_BUFSIZE request can reduce the
129      * stream buffer size to less than VSTREAM_BUFSIZE, when the request is
130      * made before the first stream read or write operation. We don't want to
131      * reduce the buffer size.
132      *
133      * As of 20190820 we increase the mss size multiplier from 2x to 4x, because
134      * some LINUX loopback TCP stacks report an MSS of 21845 which is 3x
135      * smaller than the MTU of 65536. Even with a VSTREAM buffer 2x the
136      * reported MSS size, performance would suck due to Nagle or delayed ACK
137      * delays.
138      */
139 #define EFF_BUFFER_SIZE(fp) (vstream_req_bufsize(fp) ? \
140 		vstream_req_bufsize(fp) : VSTREAM_BUFSIZE)
141 
142 #ifdef CA_VSTREAM_CTL_BUFSIZE
143     if (mss > EFF_BUFFER_SIZE(fp) / 4) {
144 	if (mss < INT_MAX / 2)
145 	    mss *= 2;
146 	if (mss < INT_MAX / 2)
147 	    mss *= 2;
148 	vstream_control(fp,
149 			CA_VSTREAM_CTL_BUFSIZE(mss),
150 			CA_VSTREAM_CTL_END);
151     }
152 
153     /*
154      * Workaround for older Postfix versions: turn on TCP_NODELAY if the
155      * VSTREAM buffer size is smaller than the MSS.
156      */
157 #else
158     if (mss > VSTREAM_BUFSIZE) {
159 	int     nodelay = 1;
160 
161 	if ((err = setsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_NODELAY,
162 			      (void *) &nodelay, sizeof(nodelay))) < 0
163 	    && errno != ECONNRESET)
164 	    msg_warn("%s: setsockopt TCP_NODELAY: %m", myname);
165     }
166 #endif
167     return (err);
168 }
169