diff --git a/readconf.c b/readconf.c
index 69d4553..fd6231d 100644
--- a/readconf.c
+++ b/readconf.c
@@ -157,6 +157,9 @@ typedef enum {
 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
+#ifdef TCP_STEALTH
+	oTCPStealthSecret,
+#endif
 	oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
 	oPubkeyAcceptedKeyTypes,
 	oIgnoredUnknownOption, oDeprecated, oUnsupported
@@ -281,6 +284,9 @@ static struct {
 	{ "hostbasedkeytypes", oHostbasedKeyTypes },
 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
 	{ "ignoreunknown", oIgnoreUnknown },
+#ifdef TCP_STEALTH
+	{ "tcpstealthsecret", oTCPStealthSecret },
+#endif
 
 	{ NULL, oBadOption }
 };
@@ -1543,6 +1549,23 @@ parse_keytypes:
 		multistate_ptr = multistate_yesnoaskconfirm;
 		goto parse_multistate;
 
+#ifdef TCP_STEALTH
+	case oTCPStealthSecret:
+		charptr = &options->tcp_stealth_secret;
+
+		arg = strdelim(&s);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing argument.",
+			    filename, linenum);
+
+		if (*activep && *charptr == NULL) {
+			*charptr = xmalloc(64 + 1);
+			memset(*charptr, 0x00, 64 + 1);
+			strncpy(*charptr, arg, 64);
+		}
+
+		break;
+#endif
 	case oDeprecated:
 		debug("%s line %d: Deprecated option \"%s\"",
 		    filename, linenum, keyword);
@@ -1720,6 +1743,9 @@ initialize_options(Options * options)
 	options->canonicalize_max_dots = -1;
 	options->canonicalize_fallback_local = -1;
 	options->canonicalize_hostname = -1;
+#ifdef TCP_STEALTH
+	options->tcp_stealth_secret = NULL;
+#endif
 	options->revoked_host_keys = NULL;
 	options->fingerprint_hash = -1;
 	options->update_hostkeys = -1;
diff --git a/readconf.h b/readconf.h
index c84d068..4c01418 100644
--- a/readconf.h
+++ b/readconf.h
@@ -159,6 +159,9 @@ typedef struct {
 	char   *pubkey_key_types;
 
 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
+#ifdef TCP_STEALTH
+	char	*tcp_stealth_secret;
+#endif
 }       Options;
 
 #define SSH_CANONICALISE_NO	0
diff --git a/servconf.c b/servconf.c
index b19d30e..526589d 100644
--- a/servconf.c
+++ b/servconf.c
@@ -168,6 +168,9 @@ initialize_server_options(ServerOptions *options)
 	options->ip_qos_interactive = -1;
 	options->ip_qos_bulk = -1;
 	options->version_addendum = NULL;
+#ifdef TCP_STEALTH
+	options->tcp_stealth_secret = NULL;
+#endif
 	options->fingerprint_hash = -1;
 }
 
@@ -430,6 +433,9 @@ typedef enum {
 	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
 	sAllowStreamLocalForwarding, sFingerprintHash,
+#ifdef TCP_STEALTH
+	sTCPStealthSecret,
+#endif
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
@@ -571,6 +577,9 @@ static struct {
 	{ "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
 	{ "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL },
 	{ "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
+#ifdef TCP_STEALTH
+	{ "tcpstealthsecret", sTCPStealthSecret },
+#endif
 	{ "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
 	{ NULL, sBadOption, 0 }
 };
@@ -1850,6 +1859,24 @@ process_server_config_line(ServerOptions *options, char *line,
 			options->fingerprint_hash = value;
 		break;
 
+#ifdef TCP_STEALTH
+	case sTCPStealthSecret:
+		charptr = &options->tcp_stealth_secret;
+
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: Missing argument.",
+			    filename, linenum);
+
+		if (*activep && *charptr == NULL) {
+			*charptr = xmalloc(64 + 1);
+			memset(*charptr, 0x00, 64 + 1);
+			strncpy(*charptr, arg, 64);
+		}
+
+		break;
+#endif
+
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
diff --git a/servconf.h b/servconf.h
index f4137af..79471d3 100644
--- a/servconf.h
+++ b/servconf.h
@@ -194,6 +194,9 @@ typedef struct {
 	u_int	num_auth_methods;
 	char   *auth_methods[MAX_AUTH_METHODS];
 
+#ifdef TCP_STEALTH
+	char    *tcp_stealth_secret;
+#endif
 	int	fingerprint_hash;
 }       ServerOptions;
 
@@ -216,6 +219,12 @@ struct connection_info {
  * NB. an option must appear in servconf.c:copy_set_server_options() or
  * COPY_MATCH_STRING_OPTS here but never both.
  */
+#ifdef TCP_STEALTH
+#define M_CP_STEALTHSCRT(X)	M_CP_STROPT(X);
+#else
+#define M_CP_STEALTHSCRT(X)	
+#endif
+
 #define COPY_MATCH_STRING_OPTS() do { \
 		M_CP_STROPT(banner); \
 		M_CP_STROPT(trusted_user_ca_keys); \
@@ -234,6 +243,7 @@ struct connection_info {
 		M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \
 		M_CP_STRARRAYOPT(accept_env, num_accept_env); \
 		M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
+		M_CP_STEALTHSCRT(tcp_stealth_secret); \
 	} while (0)
 
 struct connection_info *get_connection_info(int, int);
diff --git a/ssh.0 b/ssh.0
index 9aaf436..23d4b53 100644
--- a/ssh.0
+++ b/ssh.0
@@ -420,6 +420,20 @@ DESCRIPTION
      -y      Send log information using the syslog(3) system module.  By
              default this information is sent to stderr.
 
+     -z tcp_stealth_secret
+             Specifies the shared secret which is needed to connect to a stealth
+             SSH TCP server. Any string specified will be truncated to or padded
+             with zeroes to 64 bytes. This option needs kernel support and is
+             therefore only available if the required setsockopt() call is
+             available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
+             IMPORTANT: This option should only be used for the purpose of
+             testing as other users could easily read out the secret from the
+             command line arguments. The TCPStealthSecret configuration option
+             is the preferred way of specifying the TCP Stealth secret.
+
      ssh may additionally obtain configuration data from a per-user
      configuration file and a system-wide configuration file.  The file format
      and configuration options are described in ssh_config(5).
diff --git a/ssh.1 b/ssh.1
index cc53343..ede27b2 100644
--- a/ssh.1
+++ b/ssh.1
@@ -63,6 +63,7 @@
 .Op Fl S Ar ctl_path
 .Op Fl W Ar host : Ns Ar port
 .Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun
+.Op Fl z Ar tcp_stealth_secret
 .Oo Ar user Ns @ Oc Ns Ar hostname
 .Op Ar command
 .Ek
@@ -536,6 +537,7 @@ For full details of the options listed below, and their possible values, see
 .It StreamLocalBindUnlink
 .It StrictHostKeyChecking
 .It TCPKeepAlive
+.It TCPStealthSecret
 .It Tunnel
 .It TunnelDevice
 .It UpdateHostKeys
@@ -773,6 +775,21 @@ Send log information using the
 .Xr syslog 3
 system module.
 By default this information is sent to stderr.
+.It Fl z Ar tcp_stealth_secret
+Specifies the shared secret which is needed to connect to a stealth SSH TCP
+server. Any string specified will be truncated to or padded with zeroes to 64
+bytes. This option needs kernel support and is therefore only available if the
+required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
+.Pp
+.Cm IMPORTANT:
+This option should only be used for the purpose of testing as other users could
+easily read out the secret from the command line arguments. The
+.Cm TCPStealthSecret
+configuration option is the preferred way of specifying the TCP Stealth secret.
 .El
 .Pp
 .Nm
diff --git a/ssh.c b/ssh.c
index f9ff91f..49fb7e0 100644
--- a/ssh.c
+++ b/ssh.c
@@ -194,6 +194,14 @@ static int remote_forward_confirms_received = 0;
 extern int muxserver_sock;
 extern u_int muxclient_command;
 
+#ifdef TCP_STEALTH
+#define OPT_STEALTH	"[-z tcp_stealth_secret] "
+#define GETOPT_STEALTH	"z:"
+#else
+#define OPT_STEALTH	""
+#define GETOPT_STEALTH	""
+#endif
+
 /* Prints a help message to the user.  This function never returns. */
 
 static void
@@ -205,7 +213,7 @@ usage(void)
 "           [-F configfile] [-I pkcs11] [-i identity_file] [-L address]\n"
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
 "           [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n"
-"           [-w local_tun[:remote_tun]] [user@]hostname [command]\n"
+"           [-w local_tun[:remote_tun]] " OPT_STEALTH "[user@]hostname [command]\n"
 	);
 	exit(255);
 }
@@ -606,7 +614,7 @@ main(int ac, char **av)
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
-	    "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
+	    "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy" GETOPT_STEALTH)) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -920,6 +928,12 @@ main(int ac, char **av)
 		case 'F':
 			config = optarg;
 			break;
+#ifdef TCP_STEALTH
+		case 'z':
+			options.tcp_stealth_secret = xcalloc(64 + 1, sizeof(u_int8_t));
+			strncpy(options.tcp_stealth_secret, optarg, 64);
+			break;
+#endif
 		default:
 			usage();
 		}
diff --git a/ssh_config.0 b/ssh_config.0
index b823c02..c734fd8 100644
--- a/ssh_config.0
+++ b/ssh_config.0
@@ -927,6 +927,15 @@ DESCRIPTION
              To disable TCP keepalive messages, the value should be set to
              M-bM-^@M-^\noM-bM-^@M-^].
 
+     TCPStealthSecret
+             Specifies the shared secret which is needed to connect to a stealth
+             SSH TCP Server. Any string specified will be truncated to or padded
+             with zeroes to 64 bytes. This option needs kernel support and is
+             therefore only available if the required setsockopt() call is
+             available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
      Tunnel  Request tun(4) device forwarding between the client and the
              server.  The argument must be M-bM-^@M-^\yesM-bM-^@M-^], M-bM-^@M-^\point-to-pointM-bM-^@M-^] (layer 3),
              M-bM-^@M-^\ethernetM-bM-^@M-^] (layer 2), or M-bM-^@M-^\noM-bM-^@M-^].  Specifying M-bM-^@M-^\yesM-bM-^@M-^] requests the
diff --git a/ssh_config.5 b/ssh_config.5
index caf13a6..51bc9cf 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1597,6 +1597,15 @@ This is important in scripts, and many users want it too.
 .Pp
 To disable TCP keepalive messages, the value should be set to
 .Dq no .
+.It Cm TCPStealthSecret
+Specifies the shared secret which is needed to connect to a stealth SSH TCP
+Server. Any string specified will be truncated to or padded with zeroes to 64
+bytes. This option needs kernel support and is therefore only available if the
+required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
 .It Cm Tunnel
 Request
 .Xr tun 4
diff --git a/sshconnect.c b/sshconnect.c
index 356ec79..807aa2e 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -283,6 +283,17 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
 	}
 	fcntl(sock, F_SETFD, FD_CLOEXEC);
 
+#ifdef TCP_STEALTH
+	if (options.tcp_stealth_secret) {
+		if (setsockopt(sock, IPPROTO_TCP, TCP_STEALTH,
+			       options.tcp_stealth_secret, 64) == -1) {
+			error("setsockopt TCP_STEALTH: %s", strerror(errno));
+			close(sock);
+			return -1;
+		}
+	}
+#endif
+
 	/* Bind the socket to an alternative local IP address */
 	if (options.bind_address == NULL && !privileged)
 		return sock;
diff --git a/sshd.0 b/sshd.0
index 7eb0531..75c8c1c 100644
--- a/sshd.0
+++ b/sshd.0
@@ -7,7 +7,7 @@ SYNOPSIS
      sshd [-46DdeiqTt] [-b bits] [-C connection_spec]
           [-c host_certificate_file] [-E log_file] [-f config_file]
           [-g login_grace_time] [-h host_key_file] [-k key_gen_time]
-          [-o option] [-p port] [-u len]
+          [-o option] [-p port] [-u len] [-z tcp_stealth_secret]
 
 DESCRIPTION
      sshd (OpenSSH Daemon) is the daemon program for ssh(1).  Together these
@@ -143,6 +143,21 @@ DESCRIPTION
              in a key file.  Configuration options that require DNS include
              using a USER@HOST pattern in AllowUsers or DenyUsers.
 
+     -z tcp_stealth_secret
+             Turns this SSH server into a Stealth SSH TCP Server. This option
+             specifies the shared secret which is needed by the clients in order
+             to be able to connect to the port the SSH server is listening on.
+             Any string specified will be truncated or padded with zeroes to 64
+             bytes. This option needs kernel support and is therefore only
+             available if the required setsockopt() call is available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
+             IMPORTANT: This option should only be used for the purpose of
+             testing as other users could easily read out the secret from the
+             command line arguments. The TCPStealthSecret configuration option
+             is the preferred way of specifying the TCP Stealth secret.
+
 AUTHENTICATION
      The OpenSSH SSH daemon supports SSH protocols 1 and 2.  The default is to
      use protocol 2 only, though this can be changed via the Protocol option
diff --git a/sshd.8 b/sshd.8
index 6c521f2..47247a3 100644
--- a/sshd.8
+++ b/sshd.8
@@ -55,6 +55,7 @@
 .Op Fl o Ar option
 .Op Fl p Ar port
 .Op Fl u Ar len
+.Op Fl z Ar tcp_stealth_secret
 .Ek
 .Sh DESCRIPTION
 .Nm
@@ -267,6 +268,24 @@ USER@HOST pattern in
 .Cm AllowUsers
 or
 .Cm DenyUsers .
+.It Fl z Ar tcp_stealth_secret
+Turns this SSH server into a stealth SSH TCP server. This option specifies the
+shared secret which is needed by the clients in order to be able to connect to
+the port the SSH server is listening on.  Any string specified will be truncated
+or padded with zeroes to 64 bytes. This option needs kernel support and is
+therefore only available if the required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
+
+.Cm IMPORTANT:
+This option should only be used for the purpose of
+testing as other users could easily read out the secret from the
+command line arguments. The
+.Cm TCPStealthSecret
+configuration option
+is the preferred way of specifying the TCP Stealth secret.
 .El
 .Sh AUTHENTICATION
 The OpenSSH SSH daemon supports SSH protocols 1 and 2.
diff --git a/sshd.c b/sshd.c
index 430569c..d2dc98d 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1000,6 +1000,14 @@ drop_connection(int startups)
 	return (r < p) ? 1 : 0;
 }
 
+#ifdef TCP_STEALTH
+#define OPT_STEALTH	" [-z tcp_stealth_secret]"
+#define GETOPT_STEALTH	"z:"
+#else
+#define OPT_STEALTH	""
+#define GETOPT_STEALTH	""
+#endif
+
 static void
 usage(void)
 {
@@ -1015,7 +1023,7 @@ usage(void)
 "usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
 "            [-E log_file] [-f config_file] [-g login_grace_time]\n"
 "            [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n"
-"            [-u len]\n"
+"            [-u len]" OPT_STEALTH "\n"
 	);
 	exit(1);
 }
@@ -1193,6 +1201,14 @@ server_listen(void)
 		if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
 		    &on, sizeof(on)) == -1)
 			error("setsockopt SO_REUSEADDR: %s", strerror(errno));
+#ifdef TCP_STEALTH
+		if (options.tcp_stealth_secret != NULL) {
+			if (setsockopt(listen_sock, IPPROTO_TCP, TCP_STEALTH,
+			    options.tcp_stealth_secret, 64) == -1)
+				error("setsockopt TCP_STEALTH: %s",
+				      strerror(errno));
+		}
+#endif
 
 		/* Only communicate in IPv6 over AF_INET6 sockets. */
 		if (ai->ai_family == AF_INET6)
@@ -1508,7 +1524,8 @@ main(int ac, char **av)
 
 	/* Parse command-line arguments. */
 	while ((opt = getopt(ac, av,
-	    "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) {
+	    "z:C:E:b:c:f:g:h:k:o:p:u:" \
+	    "46DQRTdeiqrt" GETOPT_STEALTH)) != -1) {
 		switch (opt) {
 		case '4':
 			options.address_family = AF_INET;
@@ -1620,6 +1637,12 @@ main(int ac, char **av)
 				exit(1);
 			free(line);
 			break;
+#ifdef TCP_STEALTH
+		case 'z':
+			options.tcp_stealth_secret = xcalloc(64 + 1, sizeof(u_int8_t));
+			strncpy(options.tcp_stealth_secret, optarg, 64);
+			break;
+#endif
 		case '?':
 		default:
 			usage();
diff --git a/sshd_config.0 b/sshd_config.0
index 8bda6a3..3b38822 100644
--- a/sshd_config.0
+++ b/sshd_config.0
@@ -909,6 +909,19 @@ DESCRIPTION
              To disable TCP keepalive messages, the value should be set to
              M-bM-^@M-^\noM-bM-^@M-^].
 
+     TCPStealthSecret
+             Turns this SSH server into a stealth SSH TCP server. This
+             configuration option specifies the shared secret needed by the
+             clients in order to be able to connect to the port the SSH server
+             is listening on. This means that port scanners will receive a
+             TCP RST and thus will not recognize this TCP port being open.
+
+             Any string specified will be truncated or padded with zeroes to 64
+             bytes. This option needs kernel support and is therefore only
+             available if the required setsockopt() call is available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
      TrustedUserCAKeys
              Specifies a file containing public keys of certificate
              authorities that are trusted to sign user certificates for
diff --git a/sshd_config.5 b/sshd_config.5
index a37a3ac..05a6483 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -1508,6 +1508,18 @@ This avoids infinitely hanging sessions.
 .Pp
 To disable TCP keepalive messages, the value should be set to
 .Dq no .
+.It Cm TCPStealthSecret
+Turns this SSH server into a stealth SSH TCP server. This configuration option
+specifies the shared secret needed by the clients in order to be able to connect
+to the port the SSH server is listening on. This means that port scanners will
+receive a TCP RST and thus will not recognize this TCP port being open.  Any
+string specified will be truncated or padded with zeroes to 64 bytes. This
+option needs kernel support and is therefore only available if the required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
+
 .It Cm TrustedUserCAKeys
 Specifies a file containing public keys of certificate authorities that are
 trusted to sign user certificates for authentication, or
