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