summaryrefslogtreecommitdiffstats
path: root/src/util/vstream_tweak.c
blob: 7100bc688d2c8fbbe4704de3bd5edba61a7d33b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*++
/* NAME
/*	vstream_tweak 3
/* SUMMARY
/*	performance tweaks
/* SYNOPSIS
/*	#include <vstream.h>
/*
/*	VSTREAM	*vstream_tweak_sock(stream)
/*	VSTREAM	*stream;
/*
/*	VSTREAM	*vstream_tweak_tcp(stream)
/*	VSTREAM	*stream;
/* DESCRIPTION
/*	vstream_tweak_sock() does a best effort to boost your
/*	network performance on the specified generic stream.
/*
/*	vstream_tweak_tcp() does a best effort to boost your
/*	Internet performance on the specified TCP stream.
/*
/*	Arguments:
/* .IP stream
/*	The stream being boosted.
/* DIAGNOSTICS
/*	Panics: interface violations.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>

/* Utility library. */

#include <msg.h>
#include <vstream.h>

/* Application-specific. */

#ifdef HAS_IPV6
#define SOCKADDR_STORAGE struct sockaddr_storage
#else
#define SOCKADDR_STORAGE struct sockaddr
#endif

/* vstream_tweak_sock - boost your generic network performance */

int     vstream_tweak_sock(VSTREAM *fp)
{
    SOCKADDR_STORAGE ss;
    struct sockaddr *sa = (struct sockaddr *) &ss;
    SOCKADDR_SIZE sa_length = sizeof(ss);
    int     ret;

    /*
     * If the caller doesn't know if this socket is AF_LOCAL, AF_INET, etc.,
     * figure it out for them.
     */
    if ((ret = getsockname(vstream_fileno(fp), sa, &sa_length)) >= 0) {
	switch (sa->sa_family) {
#ifdef AF_INET6
	case AF_INET6:
#endif
	case AF_INET:
	    ret = vstream_tweak_tcp(fp);
	    break;
	}
    }
    return (ret);
}

/* vstream_tweak_tcp - boost your TCP performance */

int     vstream_tweak_tcp(VSTREAM *fp)
{
    const char *myname = "vstream_tweak_tcp";
    int     mss = 0;
    SOCKOPT_SIZE mss_len = sizeof(mss);
    int     err;

    /*
     * Avoid Nagle delays when VSTREAM buffers are smaller than the MSS.
     * 
     * Forcing TCP_NODELAY to be "always on" would hurt performance in the
     * common case where VSTREAM buffers are larger than the MSS.
     * 
     * Instead we ask the kernel what the current MSS is, and take appropriate
     * action. Linux <= 2.2 getsockopt(TCP_MAXSEG) always returns zero (or
     * whatever value was stored last with setsockopt()).
     * 
     * Some ancient FreeBSD kernels don't report 'host unreachable' errors with
     * getsockopt(SO_ERROR), and then treat getsockopt(TCP_MAXSEG) as a NOOP,
     * leaving the mss parameter value unchanged. To work around these two
     * getsockopt() bugs we set mss = 0, which is a harmless value.
     */
    if ((err = getsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_MAXSEG,
			  (void *) &mss, &mss_len)) < 0
	&& errno != ECONNRESET) {
	msg_warn("%s: getsockopt TCP_MAXSEG: %m", myname);
	return (err);
    }
    if (msg_verbose)
	msg_info("%s: TCP_MAXSEG %d", myname, mss);

    /*
     * Fix for recent Postfix versions: increase the VSTREAM buffer size if
     * it is smaller than the MSS. Note: the MSS may change when the route
     * changes and IP path MTU discovery is turned on, so we choose a
     * somewhat larger buffer.
     * 
     * Note: as of 20120527, the CA_VSTREAM_CTL_BUFSIZE request can reduce the
     * stream buffer size to less than VSTREAM_BUFSIZE, when the request is
     * made before the first stream read or write operation. We don't want to
     * reduce the buffer size.
     * 
     * As of 20190820 we increase the mss size multiplier from 2x to 4x, because
     * some LINUX loopback TCP stacks report an MSS of 21845 which is 3x
     * smaller than the MTU of 65536. Even with a VSTREAM buffer 2x the
     * reported MSS size, performance would suck due to Nagle or delayed ACK
     * delays.
     */
#define EFF_BUFFER_SIZE(fp) (vstream_req_bufsize(fp) ? \
		vstream_req_bufsize(fp) : VSTREAM_BUFSIZE)

#ifdef CA_VSTREAM_CTL_BUFSIZE
    if (mss > EFF_BUFFER_SIZE(fp) / 4) {
	if (mss < INT_MAX / 2)
	    mss *= 2;
	if (mss < INT_MAX / 2)
	    mss *= 2;
	vstream_control(fp,
			CA_VSTREAM_CTL_BUFSIZE(mss),
			CA_VSTREAM_CTL_END);
    }

    /*
     * Workaround for older Postfix versions: turn on TCP_NODELAY if the
     * VSTREAM buffer size is smaller than the MSS.
     */
#else
    if (mss > VSTREAM_BUFSIZE) {
	int     nodelay = 1;

	if ((err = setsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_NODELAY,
			      (void *) &nodelay, sizeof(nodelay))) < 0
	    && errno != ECONNRESET)
	    msg_warn("%s: setsockopt TCP_NODELAY: %m", myname);
    }
#endif
    return (err);
}