Skip to content

Commit 8a110d1

Browse files
committed
sockopts: support many more socket options, including strings
Add support for many more socket options, including those that take string inputs, such as `TCP_CONGESTION=bbr`. Options known to the code, but not present at build time will now emit an error that is distict from options unknown to the code (unknown vs not-available). This patch greatly eases running specific rsync configurations, without relying on LD_PRELOAD to modify socket behaviors. Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
1 parent 2f9b963 commit 8a110d1

File tree

11 files changed

+286
-57
lines changed

11 files changed

+286
-57
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ aclocal.m4
5858
/auto-build-save
5959
.deps
6060
/*.exe
61+
/sockopts.c

Makefile.in

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
4747
util1.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
4848
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
4949
usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
50-
OBJS3=progress.o pipe.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@
50+
OBJS3=progress.o pipe.o sockopts.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@
5151
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
5252
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
5353
popt/popthelp.o popt/poptparse.o
@@ -367,3 +367,7 @@ doxygen:
367367
doxygen-upload:
368368
rsync -avzv $(srcdir)/dox/html/ --delete \
369369
$${SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
370+
371+
# FreeBSD make does not support $<
372+
sockopts.c: sockopts.c.sh
373+
sh $* >$@.tmp && mv $@.tmp $@

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
- Changed the mapfrom & mapto perl scripts (in the support dir) into a single
5050
python script named idmap. Converted a couple more perl scripts into python.
5151

52+
- Recognize many more sockopt inputs, including string inputs.
53+
e.g. TCP_CONGESTION=bbr, TCP_FASTOPEN, TCP_FASTOPEN_CONNECT, IP_FREEBIND,
54+
SO_INCOMING_CPU, TCP_QUICKACK
55+
5256
### DEVELOPER RELATED:
5357

5458
- Updated config.guess (timestamp 2023-01-01) and config.sub (timestamp

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
1313
sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \
1414
popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netgroup.h \
1515
zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h \
16-
bsd/string.h)
16+
bsd/string.h netinet/ip6.h)
1717
AC_CHECK_HEADERS([netinet/ip.h], [], [], [[#include <netinet/in.h>]])
1818
AC_HEADER_MAJOR_FIXED
1919

rsync.1.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3071,6 +3071,18 @@ expand it.
30713071
able to set. By default no special socket options are set. This only
30723072
affects direct socket connections to a remote rsync daemon.
30733073

3074+
`OPTIONS` a space or comma seperated list of one or more `optname` strings
3075+
(e.g. `SO_KEEPALIVE`, `SO_SNDBUF=1234`, `SO_BINDTODEVICE=lo`, `IP_FREEBIND`,
3076+
`TCP_CONGESTION=reno`, `TCP_FASTOPEN`, `TCP_FASTOPEN_CONNECT`), or `optval`
3077+
strings (e.g. `IP_PMTUDISC_DO`, `IPTOS_THROUGHPUT`).
3078+
3079+
Unknown options are those not know to the source at build time.
3080+
Unsupported options are those known source code, but not present in the
3081+
build environment.
3082+
3083+
All errors are non-fatal, including unknown options, unsupported options,
3084+
missing required arguments or superflous arguments.
3085+
30743086
See also [the daemon version of the `--sockopts` option](#dopt--sockopts).
30753087

30763088
0. `--blocking-io`

rsync.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,24 @@ struct name_num_obj {
11711171
struct name_num_item *list;
11721172
};
11731173

1174+
enum SOCK_OPT_TYPES {
1175+
SOCK_OPT_BOOL,
1176+
SOCK_OPT_INT,
1177+
SOCK_OPT_ON,
1178+
SOCK_OPT_STR,
1179+
// error sentinal, hopefully never a valid setsockopt level
1180+
SOCK_OPT_ERR = 0xDEADCAFE
1181+
};
1182+
1183+
struct socket_option
1184+
{
1185+
char *name;
1186+
int level;
1187+
int option;
1188+
int value;
1189+
int opttype;
1190+
};
1191+
11741192
#ifdef EXTERNAL_ZLIB
11751193
#define read_buf read_buf_
11761194
#endif

socket.c

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extern char *sockopts;
4040
extern int default_af_hint;
4141
extern int connect_timeout;
4242
extern int pid_file_fd;
43+
extern struct socket_option socket_options[];
4344

4445
#ifdef HAVE_SIGACTION
4546
static struct sigaction sigact;
@@ -623,53 +624,6 @@ void start_accept_loop(int port, int (*fn)(int, int))
623624
}
624625
}
625626

626-
627-
enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
628-
629-
struct
630-
{
631-
char *name;
632-
int level;
633-
int option;
634-
int value;
635-
int opttype;
636-
} socket_options[] = {
637-
{"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
638-
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
639-
#ifdef SO_BROADCAST
640-
{"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
641-
#endif
642-
#ifdef TCP_NODELAY
643-
{"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
644-
#endif
645-
#ifdef IPTOS_LOWDELAY
646-
{"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
647-
#endif
648-
#ifdef IPTOS_THROUGHPUT
649-
{"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
650-
#endif
651-
#ifdef SO_SNDBUF
652-
{"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
653-
#endif
654-
#ifdef SO_RCVBUF
655-
{"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
656-
#endif
657-
#ifdef SO_SNDLOWAT
658-
{"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
659-
#endif
660-
#ifdef SO_RCVLOWAT
661-
{"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
662-
#endif
663-
#ifdef SO_SNDTIMEO
664-
{"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
665-
#endif
666-
#ifdef SO_RCVTIMEO
667-
{"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
668-
#endif
669-
{NULL,0,0,0,0}
670-
};
671-
672-
673627
/* Set user socket options. */
674628
void set_socket_options(int fd, char *options)
675629
{
@@ -682,13 +636,14 @@ void set_socket_options(int fd, char *options)
682636

683637
for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
684638
int ret=0,i;
685-
int value = 1;
686639
char *p;
640+
char *value;
641+
int intvalue = 1;
687642
int got_value = 0;
688643

689644
if ((p = strchr(tok,'='))) {
690645
*p = 0;
691-
value = atoi(p+1);
646+
value = p+1;
692647
got_value = 1;
693648
}
694649

@@ -701,16 +656,52 @@ void set_socket_options(int fd, char *options)
701656
rprintf(FERROR,"Unknown socket option %s\n",tok);
702657
continue;
703658
}
659+
if(socket_options[i].level == (int)(SOCK_OPT_ERR)) {
660+
// At compile-time, a potential socket option was NOT present on
661+
// the build system.
662+
rprintf(FERROR,"Unsupported socket option %s\n",tok);
663+
continue;
664+
}
704665

705666
switch (socket_options[i].opttype) {
706-
case OPT_BOOL:
707-
case OPT_INT:
708-
ret = setsockopt(fd,socket_options[i].level,
709-
socket_options[i].option,
710-
(char *)&value, sizeof (int));
667+
case SOCK_OPT_BOOL:
668+
if(got_value)
669+
intvalue = atoi(value);
670+
ret = setsockopt(fd,
671+
socket_options[i].level,
672+
socket_options[i].option,
673+
(char *)&intvalue,
674+
sizeof (int)
675+
);
676+
break;
677+
678+
case SOCK_OPT_INT:
679+
if(got_value) {
680+
intvalue = atoi(value);
681+
ret = setsockopt(fd,
682+
socket_options[i].level,
683+
socket_options[i].option,
684+
(char *)&intvalue,
685+
sizeof (int)
686+
);
687+
} else {
688+
rprintf(FERROR,"syntax error -- %s requires an integer value\n",tok);
689+
}
690+
break;
691+
692+
case SOCK_OPT_STR:
693+
if (got_value) {
694+
ret = setsockopt(fd,
695+
socket_options[i].level,
696+
socket_options[i].option,
697+
value,
698+
strlen(value));
699+
} else {
700+
rprintf(FERROR,"syntax error -- %s requires a string value\n",tok);
701+
}
711702
break;
712703

713-
case OPT_ON:
704+
case SOCK_OPT_ON:
714705
if (got_value)
715706
rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
716707

sockopts.c.sh

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/bin/sh
2+
# vim: noet
3+
# The "noet" is important because of the -EOF behavior.
4+
opt() {
5+
name=$1
6+
level=$2
7+
optname=$3
8+
optval=$4
9+
opttype=$5
10+
11+
check_optval=1
12+
case ${optval} in
13+
[A-Z_]*) ;;
14+
*) check_optval='' ;;
15+
esac
16+
# If we have configure --disable-ipv6, the options might be available in
17+
# the system headers, but the builder wants to NOT support them.
18+
optdef_extra=''
19+
case $name in
20+
IPV6*) optdef_extra=' && defined(INET6)' ;;
21+
esac
22+
23+
# If the values are NOT available at compile-time, we should recognize the
24+
# input and emit an error.
25+
cat <<-EOF
26+
#if defined(${level}) && defined(${optname}) ${check_optval:+&& defined(${optval})}${optdef_extra}
27+
{"${name}", ${level}, ${optname}, ${optval}, ${opttype}},
28+
#else
29+
{"${name}", SOCK_OPT_ERR, SOCK_OPT_ERR, SOCK_OPT_ERR, ${opttype}},
30+
#endif
31+
EOF
32+
}
33+
34+
opt_bool() {
35+
opt "$1" "$2" "${3:-$1}" 0 SOCK_OPT_BOOL
36+
}
37+
opt_int() {
38+
opt "$1" "$2" "${3:-$1}" 0 SOCK_OPT_INT
39+
}
40+
opt_val() {
41+
opt "$1" "$2" "$3" "$4" SOCK_OPT_ON
42+
}
43+
opt_str() {
44+
opt "$1" "$2" "${3:-$1}" 0 SOCK_OPT_STR
45+
}
46+
47+
cat <<-EOF
48+
#include "rsync.h"
49+
#ifdef HAVE_NETINET_IN_SYSTM_H
50+
#include <netinet/in_systm.h>
51+
#endif
52+
#ifdef HAVE_NETINET_IP_H
53+
#include <netinet/ip.h>
54+
#endif
55+
#ifdef HAVE_NETINET_IP6_H
56+
#include <netinet/ip6.h>
57+
#endif
58+
#include <netinet/tcp.h>
59+
struct socket_option socket_options[] = {
60+
EOF
61+
62+
# grouped by level, sorted by name.
63+
opt_str SO_BINDTODEVICE SOL_SOCKET
64+
opt_bool SO_BROADCAST SOL_SOCKET
65+
opt_int SO_BUSY_POLL SOL_SOCKET
66+
opt_bool SO_DEBUG SOL_SOCKET
67+
opt_bool SO_DONTROUTE SOL_SOCKET
68+
opt_int SO_INCOMING_CPU SOL_SOCKET
69+
opt_bool SO_KEEPALIVE SOL_SOCKET
70+
opt_int SO_MARK SOL_SOCKET
71+
opt_int SO_PRIORITY SOL_SOCKET
72+
opt_int SO_RCVBUF SOL_SOCKET
73+
opt_int SO_RCVLOWAT SOL_SOCKET
74+
opt_int SO_RCVTIMEO SOL_SOCKET
75+
opt_bool SO_REUSEADDR SOL_SOCKET
76+
opt_bool SO_REUSEPORT SOL_SOCKET
77+
opt_int SO_SNDBUF SOL_SOCKET
78+
opt_int SO_SNDLOWAT SOL_SOCKET
79+
opt_int SO_SNDTIMEO SOL_SOCKET
80+
81+
opt_bool IP_BIND_ADDRESS_NO_PORT IPPROTO_IP
82+
opt_int IP_CHECKSUM IPPROTO_IP
83+
opt_bool IP_FREEBIND IPPROTO_IP
84+
opt_int IP_LOCAL_PORT_RANGE IPPROTO_IP
85+
opt_int IP_MINTTL IPPROTO_IP
86+
opt_int IP_MTU IPPROTO_IP
87+
opt_val IP_PMTUDISC_DO IPPROTO_IP IP_MTU_DISCOVER IP_PMTUDISC_DO
88+
opt_val IP_PMTUDISC_DONT IPPROTO_IP IP_MTU_DISCOVER IP_PMTUDISC_DONT
89+
opt_val IP_PMTUDISC_INTERFACE IPPROTO_IP IP_MTU_DISCOVER IP_PMTUDISC_INTERFACE
90+
opt_val IP_PMTUDISC_OMIT IPPROTO_IP IP_MTU_DISCOVER IP_PMTUDISC_OMIT
91+
opt_val IP_PMTUDISC_PROBE IPPROTO_IP IP_MTU_DISCOVER IP_PMTUDISC_PROBE
92+
opt_val IP_PMTUDISC_WANT IPPROTO_IP IP_MTU_DISCOVER IP_PMTUDISC_WANT
93+
opt_bool IP_TRANSPARENT IPPROTO_IP
94+
95+
# sorting exception, to group the IPTOS together.
96+
opt_val IPTOS_LOWDELAY IPPROTO_IP IP_TOS IPTOS_LOWDELAY
97+
opt_val IPTOS_MINCOST IPPROTO_IP IP_TOS IPTOS_MINCOST
98+
opt_val IPTOS_RELIABILITY IPPROTO_IP IP_TOS IPTOS_RELIABILITY
99+
opt_val IPTOS_THROUGHPUT IPPROTO_IP IP_TOS IPTOS_THROUGHPUT
100+
101+
opt_bool IPV6_FREEBIND IPPROTO_IPV6
102+
opt_int IPV6_MTU IPPROTO_IPV6
103+
opt_val IPV6_PMTUDISC_DO IPPROTO_IPV6 IPV6_MTU_DISCOVER IPV6_PMTUDISC_DO
104+
opt_val IPV6_PMTUDISC_DONT IPPROTO_IPV6 IPV6_MTU_DISCOVER IPV6_PMTUDISC_DONT
105+
opt_val IPV6_PMTUDISC_INTERFACE IPPROTO_IPV6 IPV6_MTU_DISCOVER IPV6_PMTUDISC_INTERFACE
106+
opt_val IPV6_PMTUDISC_OMIT IPPROTO_IPV6 IPV6_MTU_DISCOVER IPV6_PMTUDISC_OMIT
107+
opt_val IPV6_PMTUDISC_PROBE IPPROTO_IPV6 IPV6_MTU_DISCOVER IPV6_PMTUDISC_PROBE
108+
opt_val IPV6_PMTUDISC_WANT IPPROTO_IPV6 IPV6_MTU_DISCOVER IPV6_PMTUDISC_WANT
109+
opt_bool IPV6_TRANSPARENT IPPROTO_IPV6
110+
opt_int IPV6_UNICAST_HOPS IPPROTO_IPV6
111+
112+
opt_str TCP_CONGESTION IPPROTO_TCP
113+
opt_bool TCP_CORK IPPROTO_TCP
114+
opt_int TCP_DEFER_ACCEPT IPPROTO_TCP
115+
opt_bool TCP_FASTOPEN_CONNECT IPPROTO_TCP
116+
opt_bool TCP_FASTOPEN IPPROTO_TCP
117+
opt_int TCP_KEEPCNT IPPROTO_TCP
118+
opt_int TCP_KEEPIDLE IPPROTO_TCP
119+
opt_int TCP_KEEPINTVL IPPROTO_TCP
120+
opt_int TCP_LINGER2 IPPROTO_TCP
121+
opt_int TCP_MAXSEG IPPROTO_TCP
122+
opt_bool TCP_NODELAY IPPROTO_TCP
123+
opt_bool TCP_QUICKACK IPPROTO_TCP
124+
opt_int TCP_SYNCNT IPPROTO_TCP
125+
opt_int TCP_USER_TIMEOUT IPPROTO_TCP
126+
opt_int TCP_WINDOW_CLAMP IPPROTO_TCP
127+
128+
cat <<EOF
129+
{NULL, 0, 0, 0, 0}
130+
};
131+
EOF

testsuite/daemon-sockopts.test

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/sh
2+
3+
# The objective here is to test both --address and --sockopt
4+
5+
. "$suitedir/rsync.fns"
6+
7+
# This is a kitchen sink test.
8+
sockopts='IP_PMTUDISC_DO,TCP_CORK,TCP_DEFER_ACCEPT=1,IP_FREEBIND,IP_LOCAL_PORT_RANGE=1234,TCP_CONGESTION=bbr,TCP_QUICKACK,TCP_NODELAY,TCP_DEFER_ACCEPT=1,TCP_FASTOPEN,TCP_FASTOPEN_CONNECT'
9+
10+
extra_config="
11+
socket options = ${sockopts}
12+
"
13+
build_rsyncd_conf
14+
15+
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon --sockopts=${sockopts}"
16+
export RSYNC_CONNECT_PROG
17+
18+
hands_setup
19+
20+
# Build chkdir with a normal rsync and an --exclude.
21+
$RSYNC -av --exclude=foobar.baz "$fromdir/" "$chkdir/"
22+
23+
checkit "'$ignore23' $RSYNC -avvvvzz --sockopts=${sockopts} '$fromdir/' localhost::test-to/" "$chkdir" "$todir"
24+
25+
# The script would have aborted on error, so getting here means we've won.
26+
exit 0

testsuite/rsync.fns

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ exclude = ? foobar.baz
387387
max verbosity = 4
388388
$uid_setting
389389
$gid_setting
390+
$extra_config
390391
391392
[test-from]
392393
path = $fromdir

0 commit comments

Comments
 (0)