View | Details | Raw Unified | Return to bug 215632
Collapse All | Expand All

(-)Makefile (-3 / +2 lines)
Lines 3-9 Link Here
3
3
4
PORTNAME=	openssh
4
PORTNAME=	openssh
5
DISTVERSION=	7.3p1
5
DISTVERSION=	7.3p1
6
PORTREVISION=	1
6
PORTREVISION=	2
7
PORTEPOCH=	1
7
PORTEPOCH=	1
8
CATEGORIES=	security ipv6
8
CATEGORIES=	security ipv6
9
MASTER_SITES=	OPENBSD/OpenSSH/portable
9
MASTER_SITES=	OPENBSD/OpenSSH/portable
Lines 66-74 Link Here
66
66
67
# See https://bugzilla.mindrot.org/show_bug.cgi?id=2016
67
# See https://bugzilla.mindrot.org/show_bug.cgi?id=2016
68
# and https://bugzilla.mindrot.org/show_bug.cgi?id=1604
68
# and https://bugzilla.mindrot.org/show_bug.cgi?id=1604
69
SCTP_PATCHFILES=	${PORTNAME}-7.2_p1-sctp.patch.gz:-p1
70
SCTP_CONFIGURE_WITH=	sctp
69
SCTP_CONFIGURE_WITH=	sctp
71
SCTP_BROKEN=		does not apply to 7.3+
70
EXTRA_PATCHES+=		${FILESDIR}/extra-patch-sctp:-p1
72
71
73
MIT_LIB_DEPENDS=		libkrb5.so.3:security/krb5
72
MIT_LIB_DEPENDS=		libkrb5.so.3:security/krb5
74
HEIMDAL_LIB_DEPENDS=		libkrb5.so.26:security/heimdal
73
HEIMDAL_LIB_DEPENDS=		libkrb5.so.26:security/heimdal
(-)files/extra-patch-sctp (+873 lines)
Line 0 Link Here
1
From 9ee55407a8a0fbaa0be5b5a70c6907f7a3fd061f Mon Sep 17 00:00:00 2001
2
From: rse <seggelmann@fh-muenster.de>
3
Date: Thu, 19 Mar 2015 20:08:09 -0400
4
Subject: [PATCH] add sctp support
5
6
https://bugzilla.mindrot.org/show_bug.cgi?id=1604
7
https://bugzilla.mindrot.org/show_bug.cgi?id=2016
8
9
People who have helped out:
10
Jan F. Chadima <jchadima@redhat.com>
11
rse <seggelmann@fh-muenster.de>
12
<openssh@ml.breakpoint.cc>
13
Joshua Kinard <kumba@gentoo.org>
14
Mike Frysinger <vapier@gentoo.org>
15
---
16
 configure.ac  |  14 ++++++
17
 misc.c        |  39 +++++++++++++---
18
 readconf.c    |  23 ++++++++++
19
 readconf.h    |   5 +++
20
 scp.1         |   5 ++-
21
 scp.c         |   7 +++
22
 servconf.c    | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++
23
 servconf.h    |   8 ++++
24
 ssh.1         |   5 ++-
25
 ssh.c         |  14 +++++-
26
 ssh_config.5  |   6 +++
27
 sshconnect.c  |  55 +++++++++++++++++++++++
28
 sshd.c        | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
29
 sshd_config.5 |  11 +++++
30
 14 files changed, 445 insertions(+), 10 deletions(-)
31
32
diff --git a/configure.ac b/configure.ac
33
index 7258cc0..2cb034b 100644
34
--- a/configure.ac
35
+++ b/configure.ac
36
@@ -4054,6 +4054,19 @@ AC_ARG_WITH([selinux],
37
 AC_SUBST([SSHLIBS])
38
 AC_SUBST([SSHDLIBS])
39
 
40
+#check whether user wants SCTP support
41
+SCTP_MSG="no"
42
+AC_ARG_WITH(sctp,
43
+	[  --with-sctp             Enable SCTP support],
44
+	[ if test "x$withval" != "xno" ; then
45
+		AC_DEFINE(SCTP,1,[Define if you want SCTP support.])
46
+		AC_CHECK_FUNCS(sctp_recvmsg, , AC_CHECK_LIB(sctp, sctp_recvmsg, ,
47
+			       [AC_MSG_ERROR([*** Can not use SCTP - maybe libsctp-dev is missing ***])]
48
+			       ))
49
+		SCTP_MSG="yes"
50
+	fi ]
51
+)
52
+
53
 # Check whether user wants Kerberos 5 support
54
 KRB5_MSG="no"
55
 AC_ARG_WITH([kerberos5],
56
@@ -4977,6 +4990,7 @@ echo "                       PAM support: $PAM_MSG"
57
 echo "                   OSF SIA support: $SIA_MSG"
58
 echo "                 KerberosV support: $KRB5_MSG"
59
 echo "                   SELinux support: $SELINUX_MSG"
60
+echo "                      SCTP support: $SCTP_MSG"
61
 echo "                 Smartcard support: $SCARD_MSG"
62
 echo "                     S/KEY support: $SKEY_MSG"
63
 echo "              MD5 password support: $MD5_MSG"
64
diff --git a/misc.c b/misc.c
65
index de7e1fa..17973d0 100644
66
--- a/misc.c
67
+++ b/misc.c
68
@@ -62,6 +62,10 @@
69
 #include "log.h"
70
 #include "ssh.h"
71
 
72
+#ifdef SCTP
73
+#include <netinet/sctp.h>
74
+#endif
75
+
76
 /* remove newline at end of string */
77
 char *
78
 chop(char *s)
79
@@ -140,21 +144,46 @@ void
80
 set_nodelay(int fd)
81
 {
82
 	int opt;
83
+	int is_tcp = 1;
84
+	int ret;
85
 	socklen_t optlen;
86
 
87
 	optlen = sizeof opt;
88
 	if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
89
-		debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
90
+#ifdef SCTP
91
+		/* TCP_NODELAY failed, try SCTP_NODELAY */
92
+		if (getsockopt(fd, IPPROTO_SCTP, SCTP_NODELAY, &opt, &optlen) == -1) {
93
+			debug("getsockopt TCP_NODELAY/SCTP_NODELAY: %.100s", strerror(errno));
94
+			return;
95
+		}
96
+		is_tcp = 0;
97
+#else
98
 		return;
99
+#endif
100
 	}
101
 	if (opt == 1) {
102
-		debug2("fd %d is TCP_NODELAY", fd);
103
+		debug2("fd %d is TCP_NODELAY/SCTP_NODELAY", fd);
104
 		return;
105
 	}
106
 	opt = 1;
107
-	debug2("fd %d setting TCP_NODELAY", fd);
108
-	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
109
-		error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
110
+	debug2("fd %d setting TCP_NODELAY/SCTP_NODELAY", fd);
111
+
112
+	if (is_tcp) {
113
+		ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt,
114
+				sizeof(opt));
115
+		if (ret < 0)
116
+			error("setsockopt TCP_NODELAY: %.100s",
117
+					strerror(errno));
118
+	}
119
+#ifdef SCTP
120
+	else {
121
+		ret = setsockopt(fd, IPPROTO_SCTP, SCTP_NODELAY, &opt,
122
+				sizeof(opt));
123
+		if (ret < 0)
124
+			error("setsockopt SCTP_NODELAY: %.100s",
125
+					strerror(errno));
126
+	}
127
+#endif
128
 }
129
 
130
 /* Characters considered whitespace in strsep calls. */
131
diff --git a/readconf.c b/readconf.c
132
index 69d4553..83a2c06 100644
133
--- a/readconf.c
134
+++ b/readconf.c
135
@@ -136,6 +136,7 @@ typedef enum {
136
 	oChallengeResponseAuthentication, oXAuthLocation,
137
 	oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
138
 	oCertificateFile, oAddKeysToAgent, oIdentityAgent,
139
+	oTransport,
140
 	oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
141
 	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
142
 	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
143
@@ -208,6 +209,11 @@ static struct {
144
 	{ "hostname", oHostName },
145
 	{ "hostkeyalias", oHostKeyAlias },
146
 	{ "proxycommand", oProxyCommand },
147
+#ifdef SCTP
148
+	{ "transport", oTransport },
149
+#else
150
+	{ "transport", oUnsupported },
151
+#endif
152
 	{ "port", oPort },
153
 	{ "cipher", oCipher },
154
 	{ "ciphers", oCiphers },
155
@@ -1094,6 +1100,20 @@ parse_command:
156
 			*charptr = xstrdup(s + len);
157
 		return 0;
158
 
159
+	case oTransport:
160
+		arg = strdelim(&s);
161
+		if (!arg || *arg == '\0')
162
+			fatal("%s line %d: missing transport protocol specification",
163
+			    filename, linenum);
164
+		if (strcasecmp(arg, "tcp") == 0)
165
+			options->transport = TRANSPORT_TCP;
166
+		else if (strcasecmp(arg, "sctp") == 0)
167
+			options->transport = TRANSPORT_SCTP;
168
+		else
169
+			fatal("%s line %d: unknown transport protocol specified",
170
+			    filename, linenum);
171
+		break;
172
+
173
 	case oPort:
174
 		intptr = &options->port;
175
 parse_int:
176
@@ -1660,6 +1680,7 @@ initialize_options(Options * options)
177
 	options->compression = -1;
178
 	options->tcp_keep_alive = -1;
179
 	options->compression_level = -1;
180
+	options->transport = -1;
181
 	options->port = -1;
182
 	options->address_family = -1;
183
 	options->connection_attempts = -1;
184
@@ -1799,6 +1820,8 @@ fill_default_options(Options * options)
185
 		options->tcp_keep_alive = 1;
186
 	if (options->compression_level == -1)
187
 		options->compression_level = 6;
188
+	if (options->transport == -1)
189
+		options->transport = TRANSPORT_TCP;
190
 	if (options->port == -1)
191
 		options->port = 0;	/* Filled in ssh_connect. */
192
 	if (options->address_family == -1)
193
diff --git a/readconf.h b/readconf.h
194
index c84d068..28fa3ec 100644
195
--- a/readconf.h
196
+++ b/readconf.h
197
@@ -28,6 +28,10 @@ struct allowed_cname {
198
 	char *target_list;
199
 };
200
 
201
+/* Transport protocols */
202
+#define TRANSPORT_TCP  1
203
+#define TRANSPORT_SCTP 2
204
+
205
 typedef struct {
206
 	int     forward_agent;	/* Forward authentication agent. */
207
 	int     forward_x11;	/* Forward X11 display. */
208
@@ -61,6 +65,7 @@ typedef struct {
209
 	int	ip_qos_bulk;		/* IP ToS/DSCP/class for bulk traffic */
210
 	LogLevel log_level;	/* Level for logging. */
211
 
212
+	int     transport; /* Transport protocol used. */
213
 	int     port;		/* Port to connect. */
214
 	int     address_family;
215
 	int     connection_attempts;	/* Max attempts (seconds) before
216
diff --git a/scp.1 b/scp.1
217
index 54ea352..d12802e 100644
218
--- a/scp.1
219
+++ b/scp.1
220
@@ -19,7 +19,7 @@
221
 .Sh SYNOPSIS
222
 .Nm scp
223
 .Bk -words
224
-.Op Fl 12346BCpqrv
225
+.Op Fl 12346BCpqrvz
226
 .Op Fl c Ar cipher
227
 .Op Fl F Ar ssh_config
228
 .Op Fl i Ar identity_file
229
@@ -181,6 +181,7 @@ For full details of the options listed below, and their possible values, see
230
 .It ServerAliveCountMax
231
 .It StrictHostKeyChecking
232
 .It TCPKeepAlive
233
+.It Transport
234
 .It UpdateHostKeys
235
 .It UsePrivilegedPort
236
 .It User
237
@@ -222,6 +223,8 @@ and
238
 to print debugging messages about their progress.
239
 This is helpful in
240
 debugging connection, authentication, and configuration problems.
241
+.It Fl z
242
+Use the SCTP protocol for connection instead of TCP which is the default.
243
 .El
244
 .Sh EXIT STATUS
245
 .Ex -std scp
246
diff --git a/scp.c b/scp.c
247
index 0bdd7cb..8c456d4 100644
248
--- a/scp.c
249
+++ b/scp.c
250
@@ -396,7 +396,11 @@ main(int argc, char **argv)
251
 	addargs(&args, "-oClearAllForwardings=yes");
252
 
253
 	fflag = tflag = 0;
254
+#ifdef SCTP
255
+	while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:z")) != -1)
256
+#else
257
 	while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
258
+#endif
259
 		switch (ch) {
260
 		/* User-visible flags. */
261
 		case '1':
262
@@ -404,6 +408,9 @@ main(int argc, char **argv)
263
 		case '4':
264
 		case '6':
265
 		case 'C':
266
+#ifdef SCTP
267
+		case 'z':
268
+#endif
269
 			addargs(&args, "-%c", ch);
270
 			addargs(&remote_remote_args, "-%c", ch);
271
 			break;
272
diff --git a/servconf.c b/servconf.c
273
index b19d30e..14b0a0f 100644
274
--- a/servconf.c
275
+++ b/servconf.c
276
@@ -138,6 +138,7 @@ initialize_server_options(ServerOptions *options)
277
 	options->ciphers = NULL;
278
 	options->macs = NULL;
279
 	options->kex_algorithms = NULL;
280
+	options->transport = -1;
281
 	options->protocol = SSH_PROTO_UNKNOWN;
282
 	options->fwd_opts.gateway_ports = -1;
283
 	options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
284
@@ -315,6 +316,8 @@ fill_default_server_options(ServerOptions *options)
285
 		options->allow_streamlocal_forwarding = FORWARD_ALLOW;
286
 	if (options->allow_agent_forwarding == -1)
287
 		options->allow_agent_forwarding = 1;
288
+	if (options->transport == -1)
289
+		options->transport = TRANSPORT_TCP;
290
 	if (options->fwd_opts.gateway_ports == -1)
291
 		options->fwd_opts.gateway_ports = 0;
292
 	if (options->max_startups == -1)
293
@@ -406,6 +409,7 @@ typedef enum {
294
 	sKerberosTgtPassing, sChallengeResponseAuthentication,
295
 	sPasswordAuthentication, sKbdInteractiveAuthentication,
296
 	sListenAddress, sAddressFamily,
297
+	sTransport, sListenMultipleAddresses,
298
 	sPrintMotd, sPrintLastLog, sIgnoreRhosts,
299
 	sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
300
 	sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive,
301
@@ -504,6 +508,13 @@ static struct {
302
 	{ "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */
303
 	{ "checkmail", sDeprecated, SSHCFG_GLOBAL },
304
 	{ "listenaddress", sListenAddress, SSHCFG_GLOBAL },
305
+#ifdef SCTP
306
+	{ "listenmultipleaddresses", sListenMultipleAddresses, SSHCFG_GLOBAL },
307
+	{ "transport", sTransport, SSHCFG_GLOBAL },
308
+#else
309
+	{ "listenmultipleaddresses", sUnsupported, SSHCFG_GLOBAL },
310
+	{ "transport", sUnsupported, SSHCFG_GLOBAL },
311
+#endif
312
 	{ "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
313
 	{ "printmotd", sPrintMotd, SSHCFG_GLOBAL },
314
 #ifdef DISABLE_LASTLOG
315
@@ -717,6 +728,79 @@ get_connection_info(int populate, int use_dns)
316
 	return &ci;
317
 }
318
 
319
+#ifdef SCTP
320
+static void
321
+add_one_listen_multiple_addr(ServerOptions *options, char *addr, int port, int last)
322
+{
323
+	struct addrinfo hints, *ai, *aitop;
324
+	char strport[NI_MAXSERV];
325
+	int gaierr;
326
+
327
+	memset(&hints, 0, sizeof(hints));
328
+	hints.ai_family = options->address_family;
329
+	hints.ai_socktype = SOCK_STREAM;
330
+	hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0;
331
+	snprintf(strport, sizeof strport, "%d", port);
332
+	if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0)
333
+		fatal("bad addr or host: %s (%s)",
334
+				addr ? addr : "<NULL>",
335
+				ssh_gai_strerror(gaierr));
336
+	/* Mark addresses as multihomed */
337
+	for (ai = aitop; ai->ai_next; ai = ai->ai_next)
338
+		ai->ai_flags = IS_MULTIPLE_ADDR;
339
+	ai->ai_flags = IS_MULTIPLE_ADDR;
340
+	ai->ai_next = options->listen_addrs;
341
+	options->listen_addrs = aitop;
342
+
343
+	if (last) {
344
+		aitop->ai_flags = 0;
345
+	}
346
+}
347
+
348
+static void
349
+add_listen_multiple_addrs(ServerOptions *options, char *addrs, int port)
350
+{
351
+	u_int i, num_addrs;
352
+	char **addrsptr, *p;
353
+
354
+	if (options->num_ports == 0)
355
+		options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
356
+	if (options->address_family == -1)
357
+		options->address_family = AF_UNSPEC;
358
+
359
+	num_addrs = 1;
360
+	p = addrs;
361
+	while ((p = strchr(p, ',')) != NULL) {
362
+		num_addrs++;
363
+		p++;
364
+	}
365
+	debug("found %d addresses for multi-homing", num_addrs);
366
+
367
+	addrsptr = xmalloc(num_addrs * sizeof(char*));
368
+	p = addrs;
369
+	for (i = 0; i < num_addrs; i++) {
370
+		addrsptr[i] = p;
371
+		p = strchr(p+1, ',');
372
+		if (p != NULL)
373
+			*(p++) = '\0';
374
+	}
375
+
376
+	if (port == 0)
377
+		for (i = 0; i < options->num_ports; i++) {
378
+			while (--num_addrs)
379
+				add_one_listen_multiple_addr(options, addrsptr[num_addrs], options->ports[i], 0);
380
+			add_one_listen_multiple_addr(options, addrs, options->ports[i], 1);
381
+		}
382
+	else {
383
+		while (--num_addrs)
384
+			add_one_listen_multiple_addr(options, addrsptr[num_addrs], port, 0);
385
+		add_one_listen_multiple_addr(options, addrs, port, 1);
386
+	}
387
+
388
+	free(addrsptr);
389
+}
390
+#endif
391
+
392
 /*
393
  * The strategy for the Match blocks is that the config file is parsed twice.
394
  *
395
@@ -1061,6 +1145,25 @@ process_server_config_line(ServerOptions *options, char *line,
396
 		intptr = &options->key_regeneration_time;
397
 		goto parse_time;
398
 
399
+#ifdef SCTP
400
+	case sListenMultipleAddresses:
401
+		arg = strdelim(&cp);
402
+		if (arg == NULL || *arg == '\0')
403
+			fatal("%s line %d: missing addresses",
404
+				filename, linenum);
405
+
406
+		/* Check for appended port */
407
+		p = strchr(arg, ';');
408
+		if (p != NULL) {
409
+			if ((port = a2port(p + 1)) <= 0)
410
+				fatal("%s line %d: bad port number", filename, linenum);
411
+			*p = '\0';
412
+		} else
413
+			port = 0;
414
+		add_listen_multiple_addrs(options, arg, port);
415
+		break;
416
+#endif
417
+
418
 	case sListenAddress:
419
 		arg = strdelim(&cp);
420
 		if (arg == NULL || *arg == '\0')
421
@@ -1478,6 +1581,22 @@ process_server_config_line(ServerOptions *options, char *line,
422
 			options->kex_algorithms = xstrdup(arg);
423
 		break;
424
 
425
+	case sTransport:
426
+		arg = strdelim(&cp);
427
+		if (!arg || *arg == '\0')
428
+			fatal("%s line %d: missing transport protocol specification",
429
+			    filename, linenum);
430
+		if (strcasecmp(arg, "all") == 0)
431
+			options->transport = TRANSPORT_ALL;
432
+		else if (strcasecmp(arg, "tcp") == 0)
433
+			options->transport = TRANSPORT_TCP;
434
+		else if (strcasecmp(arg, "sctp") == 0)
435
+			options->transport = TRANSPORT_SCTP;
436
+		else
437
+			fatal("%s line %d: unknown transport protocol specified",
438
+			    filename, linenum);
439
+		break;
440
+
441
 	case sProtocol:
442
 		intptr = &options->protocol;
443
 		arg = strdelim(&cp);
444
@@ -1992,6 +2111,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
445
 	M_CP_INTOPT(allow_streamlocal_forwarding);
446
 	M_CP_INTOPT(allow_agent_forwarding);
447
 	M_CP_INTOPT(permit_tun);
448
+	M_CP_INTOPT(transport);
449
 	M_CP_INTOPT(fwd_opts.gateway_ports);
450
 	M_CP_INTOPT(x11_display_offset);
451
 	M_CP_INTOPT(x11_forwarding);
452
@@ -2286,6 +2406,9 @@ dump_config(ServerOptions *o)
453
 	dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env);
454
 	dump_cfg_fmtint(sUseLogin, o->use_login);
455
 	dump_cfg_fmtint(sCompression, o->compression);
456
+#ifdef SCTP
457
+	dump_cfg_fmtint(sTransport, o->transport);
458
+#endif
459
 	dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports);
460
 	dump_cfg_fmtint(sUseDNS, o->use_dns);
461
 	dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding);
462
diff --git a/servconf.h b/servconf.h
463
index f4137af..63a0637 100644
464
--- a/servconf.h
465
+++ b/servconf.h
466
@@ -54,6 +54,13 @@
467
 /* Magic name for internal sftp-server */
468
 #define INTERNAL_SFTP_NAME	"internal-sftp"
469
 
470
+/* Transport protocols */
471
+#define TRANSPORT_TCP  1
472
+#define TRANSPORT_SCTP 2
473
+#define TRANSPORT_ALL  (TRANSPORT_TCP | TRANSPORT_SCTP)
474
+
475
+#define IS_MULTIPLE_ADDR 0x1000
476
+
477
 typedef struct {
478
 	u_int	num_ports;
479
 	u_int	ports_from_cmdline;
480
@@ -93,6 +100,7 @@ typedef struct {
481
 	char   *ciphers;	/* Supported SSH2 ciphers. */
482
 	char   *macs;		/* Supported SSH2 macs. */
483
 	char   *kex_algorithms;	/* SSH2 kex methods in order of preference. */
484
+	int transport;	/* Transport protocol(s) used */
485
 	int	protocol;	/* Supported protocol versions. */
486
 	struct ForwardOptions fwd_opts;	/* forwarding options */
487
 	SyslogFacility log_facility;	/* Facility for system logging. */
488
diff --git a/ssh.1 b/ssh.1
489
index cc53343..b1a45e8 100644
490
--- a/ssh.1
491
+++ b/ssh.1
492
@@ -43,7 +43,7 @@
493
 .Sh SYNOPSIS
494
 .Nm ssh
495
 .Bk -words
496
-.Op Fl 1246AaCfGgKkMNnqsTtVvXxYy
497
+.Op Fl 1246AaCfGgKkMNnqsTtVvXxYyz
498
 .Op Fl b Ar bind_address
499
 .Op Fl c Ar cipher_spec
500
 .Op Fl D Oo Ar bind_address : Oc Ns Ar port
501
@@ -536,6 +536,7 @@ For full details of the options listed below, and their possible values, see
502
 .It StreamLocalBindUnlink
503
 .It StrictHostKeyChecking
504
 .It TCPKeepAlive
505
+.It Transport
506
 .It Tunnel
507
 .It TunnelDevice
508
 .It UpdateHostKeys
509
@@ -770,6 +771,8 @@ controls.
510
 .Pp
511
 .It Fl y
512
 Send log information using the
513
+.It Fl z
514
+Use the SCTP protocol for connection instead of TCP which is the default.
515
 .Xr syslog 3
516
 system module.
517
 By default this information is sent to stderr.
518
diff --git a/ssh.c b/ssh.c
519
index f9ff91f..d0d92ce 100644
520
--- a/ssh.c
521
+++ b/ssh.c
522
@@ -195,12 +195,17 @@ extern int muxserver_sock;
523
 extern u_int muxclient_command;
524
 
525
 /* Prints a help message to the user.  This function never returns. */
526
+#ifdef SCTP
527
+#define SCTP_OPT	"z"
528
+#else
529
+#define SCTP_OPT	""
530
+#endif
531
 
532
 static void
533
 usage(void)
534
 {
535
 	fprintf(stderr,
536
-"usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n"
537
+"usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy" SCTP_OPT "] [-b bind_address] [-c cipher_spec]\n"
538
 "           [-D [bind_address:]port] [-E log_file] [-e escape_char]\n"
539
 "           [-F configfile] [-I pkcs11] [-i identity_file] [-L address]\n"
540
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
541
@@ -605,7 +610,7 @@ main(int ac, char **av)
542
 	argv0 = av[0];
543
 
544
  again:
545
-	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
546
+	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" SCTP_OPT
547
 	    "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
548
 		switch (opt) {
549
 		case '1':
550
@@ -845,6 +850,11 @@ main(int ac, char **av)
551
 			else
552
 				options.control_master = SSHCTL_MASTER_YES;
553
 			break;
554
+#ifdef SCTP
555
+		case 'z':
556
+			options.transport = TRANSPORT_SCTP;
557
+			break;
558
+#endif
559
 		case 'p':
560
 			options.port = a2port(optarg);
561
 			if (options.port <= 0) {
562
diff --git a/ssh_config.5 b/ssh_config.5
563
index caf13a6..a088f30 100644
564
--- a/ssh_config.5
565
+++ b/ssh_config.5
566
@@ -1597,6 +1597,12 @@ This is important in scripts, and many users want it too.
567
 .Pp
568
 To disable TCP keepalive messages, the value should be set to
569
 .Dq no .
570
+.It Cm Transport
571
+Specifies the transport protocol while connecting. Valid values are
572
+.Dq TCP
573
+and
574
+.Dq SCTP .
575
+The default is TCP.
576
 .It Cm Tunnel
577
 Request
578
 .Xr tun 4
579
diff --git a/sshconnect.c b/sshconnect.c
580
index 356ec79..21b3f54 100644
581
--- a/sshconnect.c
582
+++ b/sshconnect.c
583
@@ -66,6 +66,10 @@
584
 #include "ssherr.h"
585
 #include "authfd.h"
586
 
587
+#ifdef SCTP
588
+#include <netinet/sctp.h>
589
+#endif
590
+
591
 char *client_version_string = NULL;
592
 char *server_version_string = NULL;
593
 Key *previous_host_key = NULL;
594
@@ -275,6 +279,9 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
595
 {
596
 	int sock, r, gaierr;
597
 	struct addrinfo hints, *res = NULL;
598
+#ifdef SCTP
599
+	char *more_addrs, *next_addr;
600
+#endif
601
 
602
 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
603
 	if (sock < 0) {
604
@@ -288,10 +295,21 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
605
 		return sock;
606
 
607
 	if (options.bind_address) {
608
+#ifdef SCTP
609
+		/* Check if multiple addresses have been specified */
610
+		if ((more_addrs = strchr(options.bind_address, ',')) != NULL) {
611
+			*(more_addrs++) = '\0';
612
+		}
613
+#endif
614
 		memset(&hints, 0, sizeof(hints));
615
 		hints.ai_family = ai->ai_family;
616
 		hints.ai_socktype = ai->ai_socktype;
617
+#ifndef SCTP
618
+		/* Only specify protocol if SCTP is not used, due
619
+		 * to the lack of SCTP support for getaddrinfo()
620
+		 */
621
 		hints.ai_protocol = ai->ai_protocol;
622
+#endif
623
 		hints.ai_flags = AI_PASSIVE;
624
 		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
625
 		if (gaierr) {
626
@@ -324,6 +342,34 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
627
 			return -1;
628
 		}
629
 	}
630
+#ifdef SCTP
631
+	/* If there are multiple addresses, bind them too */
632
+	if (more_addrs) {
633
+		do {
634
+			next_addr = strchr(more_addrs, ',');
635
+			if (next_addr != NULL) {
636
+				*(next_addr++) = '\0';
637
+			}
638
+
639
+			gaierr = getaddrinfo(more_addrs, NULL, &hints, &res);
640
+			if (gaierr) {
641
+				error("getaddrinfo: %s: %s", more_addrs,
642
+					  ssh_gai_strerror(gaierr));
643
+				close(sock);
644
+				return -1;
645
+			}
646
+			if (sctp_bindx(sock, (struct sockaddr *)res->ai_addr,
647
+						   1, SCTP_BINDX_ADD_ADDR) != 0) {
648
+				error("bind: %s: %s", options.bind_address, strerror(errno));
649
+				close(sock);
650
+				freeaddrinfo(res);
651
+				return -1;
652
+			}
653
+
654
+			more_addrs = next_addr;
655
+		} while (next_addr != NULL);
656
+	}
657
+#endif
658
 	if (res != NULL)
659
 		freeaddrinfo(res);
660
 	return sock;
661
@@ -437,6 +483,15 @@ ssh_connect_direct(const char *host, struct addrinfo *aitop,
662
 	memset(ntop, 0, sizeof(ntop));
663
 	memset(strport, 0, sizeof(strport));
664
 
665
+#ifdef SCTP
666
+	/* Use SCTP if requested */
667
+	if (options.transport == TRANSPORT_SCTP) {
668
+		for (ai = aitop; ai; ai = ai->ai_next) {
669
+			ai->ai_protocol = IPPROTO_SCTP;
670
+		}
671
+	}
672
+#endif
673
+
674
 	for (attempt = 0; attempt < connection_attempts; attempt++) {
675
 		if (attempt > 0) {
676
 			/* Sleep a moment before retrying. */
677
diff --git a/sshd.c b/sshd.c
678
index 430569c..4ca58ed 100644
679
--- a/sshd.c
680
+++ b/sshd.c
681
@@ -125,6 +125,10 @@
682
 #include "version.h"
683
 #include "ssherr.h"
684
 
685
+#ifdef SCTP
686
+#include <netinet/sctp.h>
687
+#endif
688
+
689
 #ifndef O_NOCTTY
690
 #define O_NOCTTY	0
691
 #endif
692
@@ -1164,6 +1168,12 @@ server_listen(void)
693
 	for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
694
 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
695
 			continue;
696
+#ifdef SCTP
697
+		/* Ignore multi-homing addresses for TCP */
698
+		if (ai->ai_flags & IS_MULTIPLE_ADDR ||
699
+		   (ai->ai_next != NULL && ai->ai_next->ai_flags & IS_MULTIPLE_ADDR))
700
+			continue;
701
+#endif
702
 		if (num_listen_socks >= MAX_LISTEN_SOCKS)
703
 			fatal("Too many listen sockets. "
704
 			    "Enlarge MAX_LISTEN_SOCKS");
705
@@ -1222,6 +1232,127 @@ server_listen(void)
706
 		fatal("Cannot bind any address.");
707
 }
708
 
709
+#ifdef SCTP
710
+/*
711
+ * Listen for SCTP connections
712
+ */
713
+static void
714
+server_listen_sctp(void)
715
+{
716
+	int ret, listen_sock, on = 1;
717
+	struct addrinfo *ai, *aiv6;
718
+	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
719
+
720
+	for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
721
+		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
722
+			continue;
723
+		/* Ignore multi-homing addresses at this point */
724
+		if (ai->ai_flags & IS_MULTIPLE_ADDR)
725
+			continue;
726
+		if (num_listen_socks >= MAX_LISTEN_SOCKS)
727
+			fatal("Too many listen sockets. "
728
+			    "Enlarge MAX_LISTEN_SOCKS");
729
+		if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
730
+		    ntop, sizeof(ntop), strport, sizeof(strport),
731
+		    NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
732
+			error("getnameinfo failed: %.100s",
733
+			    ssh_gai_strerror(ret));
734
+			continue;
735
+		}
736
+		/* Check for multi-homed IPv6 addresses if family is IPv4 */
737
+		if (ai->ai_family == AF_INET) {
738
+			aiv6 = ai->ai_next;
739
+			while (aiv6 != NULL && aiv6->ai_flags & IS_MULTIPLE_ADDR) {
740
+				if (aiv6->ai_family == AF_INET6) {
741
+					ai->ai_family = AF_INET6;
742
+					break;
743
+				}
744
+				aiv6 = aiv6->ai_next;
745
+			}
746
+		}
747
+
748
+		/* Create socket for listening. */
749
+		listen_sock = socket(ai->ai_family, ai->ai_socktype,
750
+		    IPPROTO_SCTP);
751
+		if (listen_sock < 0) {
752
+			/* kernel may not support ipv6 */
753
+			verbose("SCTP socket: %.100s", strerror(errno));
754
+			continue;
755
+		}
756
+		if (set_nonblock(listen_sock) == -1) {
757
+			close(listen_sock);
758
+			continue;
759
+		}
760
+		/*
761
+		 * Set socket options.
762
+		 * Allow local port reuse in TIME_WAIT.
763
+		 */
764
+		if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
765
+		    &on, sizeof(on)) == -1)
766
+			error("SCTP setsockopt SO_REUSEADDR: %s", strerror(errno));
767
+
768
+		/* Only communicate in IPv6 over AF_INET6 sockets if not multi-homed. */
769
+		if (ai->ai_family == AF_INET6 && (ai->ai_next == NULL ||
770
+		    (ai->ai_next != NULL && ai->ai_next->ai_flags == 0)))
771
+			sock_set_v6only(listen_sock);
772
+
773
+		if (ai->ai_next != NULL && ai->ai_next->ai_flags & IS_MULTIPLE_ADDR)
774
+			debug("Bind multi-homed to SCTP port %s on %s.", strport, ntop);
775
+		else
776
+			debug("Bind to SCTP port %s on %s.", strport, ntop);
777
+
778
+		/* Bind the socket to the desired port. */
779
+		if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
780
+			error("Bind to SCTP port %s on %s failed: %.200s.",
781
+			    strport, ntop, strerror(errno));
782
+			close(listen_sock);
783
+			continue;
784
+		}
785
+
786
+		/* Bind multi-homing addresses */
787
+		while (ai->ai_next != NULL &&
788
+		       ai->ai_next->ai_flags & IS_MULTIPLE_ADDR) {
789
+			ai = ai->ai_next;
790
+
791
+			if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
792
+				ntop, sizeof(ntop), strport, sizeof(strport),
793
+				NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
794
+				error("getnameinfo failed: %.100s",
795
+					ssh_gai_strerror(ret));
796
+				continue;
797
+			}
798
+
799
+			debug("Bind multi-homed to SCTP port %s on %s.", strport, ntop);
800
+
801
+			if (sctp_bindx(listen_sock, (struct sockaddr *)ai->ai_addr, 1, SCTP_BINDX_ADD_ADDR) != 0) {
802
+				error("Bind to SCTP port %s on %s failed: %.200s.",
803
+					strport, ntop, strerror(errno));
804
+				close(listen_sock);
805
+				continue;
806
+			}
807
+		}
808
+
809
+		listen_socks[num_listen_socks] = listen_sock;
810
+		num_listen_socks++;
811
+
812
+		/* Start listening on the port. */
813
+		if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0)
814
+			fatal("SCTP listen on [%s]:%s: %.100s",
815
+			    ntop, strport, strerror(errno));
816
+		if (ai->ai_flags & IS_MULTIPLE_ADDR)
817
+			logit("Server listening multi-homed with SCTP on port %s.", strport);
818
+		else
819
+			logit("Server listening with SCTP on %s port %s.", ntop, strport);
820
+	}
821
+	/* Only free addresses if SCTP is the only used protocol */
822
+	if (options.transport == TRANSPORT_SCTP)
823
+		freeaddrinfo(options.listen_addrs);
824
+
825
+	if (!num_listen_socks)
826
+		fatal("Cannot bind any address for SCTP.");
827
+}
828
+#endif
829
+
830
 /*
831
  * The main TCP accept loop. Note that, for the non-debug case, returns
832
  * from this function are in a forked subprocess.
833
@@ -2007,7 +2138,14 @@ main(int ac, char **av)
834
 		server_accept_inetd(&sock_in, &sock_out);
835
 	} else {
836
 		platform_pre_listen();
837
-		server_listen();
838
+
839
+#ifdef SCTP
840
+		if (options.transport & TRANSPORT_SCTP)
841
+			server_listen_sctp();
842
+
843
+		if (options.transport & TRANSPORT_TCP)
844
+#endif
845
+			server_listen();
846
 
847
 		if (options.protocol & SSH_PROTO_1)
848
 			generate_ephemeral_server_key();
849
diff --git a/sshd_config.5 b/sshd_config.5
850
index a37a3ac..24e3826 100644
851
--- a/sshd_config.5
852
+++ b/sshd_config.5
853
@@ -1508,6 +1508,17 @@ This avoids infinitely hanging sessions.
854
 .Pp
855
 To disable TCP keepalive messages, the value should be set to
856
 .Dq no .
857
+.It Cm Transport
858
+Specifies the transport protocol that should be used by
859
+.Xr sshd 8 .
860
+Valid values are
861
+.Dq TCP ,
862
+.Dq SCTP ,
863
+.Dq all.
864
+The value
865
+.Dq all
866
+means to listen on TCP and SCTP sockets. The default is to listen only on
867
+TCP sockets.
868
 .It Cm TrustedUserCAKeys
869
 Specifies a file containing public keys of certificate authorities that are
870
 trusted to sign user certificates for authentication, or
871
-- 
872
2.6.2
873

Return to bug 215632