Bugzilla – Attachment 3064 Details for
Bug 2784
Add native support for routing domains / VRF
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
patch v2
Add-support-for-SO_BINDTODEVICE-via-B-options.patch (text/plain), 25.09 KB, created by
Luca Boccassi
on 2017-10-07 03:05:39 AEDT
(
hide
)
Description:
patch v2
Filename:
MIME Type:
Creator:
Luca Boccassi
Created:
2017-10-07 03:05:39 AEDT
Size:
25.09 KB
patch
obsolete
>From 92bf0e62b869a5c3088a1d8fcff67995c4528786 Mon Sep 17 00:00:00 2001 >From: Luca Boccassi <luca.boccassi@gmail.com> >Date: Fri, 22 Sep 2017 17:09:05 +0100 >Subject: [PATCH] Add support for SO_BINDTODEVICE via -B options > >In the past couple of years the Linux kernel gained support for VRF. >Applications can bind to a specific VRF via the SO_BINDTODEVICE socket >option. >Add a new -B option that takes a string as a parameter to both ssh and >sshd, and use it to bind the socket. >Also extend the ListenAddress sshd options to take a domain keyword >followed by a value. This value has priority over the global -B value. >https://www.kernel.org/doc/Documentation/networking/vrf.txt > >Original mailing list thread asking for this feature: >https://lists.mindrot.org/pipermail/openssh-unix-dev/2015-November/034525.html >--- > readconf.c | 9 +++- > readconf.h | 2 + > servconf.c | 127 +++++++++++++++++++++++++++++--------------- > servconf.h | 15 +++++- > ssh.1 | 8 +++ > ssh.c | 7 ++- > ssh_config.5 | 4 ++ > sshconnect.c | 20 +++++++ > sshd.8 | 11 ++++ > sshd.c | 167 +++++++++++++++++++++++++++++++++++++--------------------- > sshd_config.5 | 19 +++++++ > 11 files changed, 283 insertions(+), 106 deletions(-) > >diff --git a/readconf.c b/readconf.c >index f63894f9..f7331b43 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -156,7 +156,7 @@ typedef enum { > oPubkeyAuthentication, > oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, > oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, >- oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, >+ oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, oBindDevice, > oClearAllForwardings, oNoHostAuthenticationForLocalhost, > oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, > oAddressFamily, oGssAuthentication, oGssDelegateCreds, >@@ -305,6 +305,7 @@ static struct { > { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, > { "ignoreunknown", oIgnoreUnknown }, > { "proxyjump", oProxyJump }, >+ { "binddevice", oBindDevice }, > > { NULL, oBadOption } > }; >@@ -1669,6 +1670,10 @@ parse_keytypes: > charptr = &options->identity_agent; > goto parse_string; > >+ case oBindDevice: >+ charptr = &options->bind_device; >+ goto parse_string; >+ > case oDeprecated: > debug("%s line %d: Deprecated option \"%s\"", > filename, linenum, keyword); >@@ -1869,6 +1874,7 @@ initialize_options(Options * options) > options->update_hostkeys = -1; > options->hostbased_key_types = NULL; > options->pubkey_key_types = NULL; >+ options->bind_device = NULL; > } > > /* >@@ -2544,6 +2550,7 @@ dump_client_config(Options *o, const char *host) > dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types); > dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); > dump_cfg_string(oXAuthLocation, o->xauth_location); >+ dump_cfg_string(oBindDevice, o->bind_device); > > /* Forwards */ > dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards); >diff --git a/readconf.h b/readconf.h >index 22fe5c18..a486ffa5 100644 >--- a/readconf.h >+++ b/readconf.h >@@ -163,6 +163,8 @@ typedef struct { > int jump_port; > char *jump_extra; > >+ char *bind_device; /* network device to bind to */ >+ > char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ > } Options; > >diff --git a/servconf.c b/servconf.c >index 95686295..13910a12 100644 >--- a/servconf.c >+++ b/servconf.c >@@ -58,8 +58,8 @@ > #include "myproposal.h" > #include "digest.h" > >-static void add_listen_addr(ServerOptions *, char *, int); >-static void add_one_listen_addr(ServerOptions *, char *, int); >+static void add_listen_addr(ServerOptions *, char *, int, char *); >+static void add_one_listen_addr(ServerOptions *, char *, int, char *); > > /* Use of privilege separation or not */ > extern int use_privsep; >@@ -79,8 +79,10 @@ initialize_server_options(ServerOptions *options) > options->num_ports = 0; > options->ports_from_cmdline = 0; > options->queued_listen_addrs = NULL; >+ options->queued_listen_domains = NULL; > options->num_queued_listens = 0; > options->listen_addrs = NULL; >+ options->num_listen_addrs = 0; > options->address_family = -1; > options->num_host_key_files = 0; > options->num_host_cert_files = 0; >@@ -165,6 +167,7 @@ initialize_server_options(ServerOptions *options) > options->fingerprint_hash = -1; > options->disable_forwarding = -1; > options->expose_userauth_info = -1; >+ options->bind_device = NULL; > } > > /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ >@@ -217,7 +220,7 @@ fill_default_server_options(ServerOptions *options) > if (options->address_family == -1) > options->address_family = AF_UNSPEC; > if (options->listen_addrs == NULL) >- add_listen_addr(options, NULL, 0); >+ add_listen_addr(options, NULL, 0, NULL); > if (options->pid_file == NULL) > options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); > if (options->login_grace_time == -1) >@@ -396,7 +399,7 @@ typedef enum { > sKerberosGetAFSToken, > sKerberosTgtPassing, sChallengeResponseAuthentication, > sPasswordAuthentication, sKbdInteractiveAuthentication, >- sListenAddress, sAddressFamily, >+ sListenAddress, sAddressFamily, sBindDevice, > sPrintMotd, sPrintLastLog, sIgnoreRhosts, > sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, > sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, >@@ -566,6 +569,7 @@ static struct { > { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, > { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, > { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, >+ { "binddevice", sBindDevice }, > { NULL, sBadOption, 0 } > }; > >@@ -619,21 +623,22 @@ derelativise_path(const char *path) > } > > static void >-add_listen_addr(ServerOptions *options, char *addr, int port) >+add_listen_addr(ServerOptions *options, char *addr, int port, char *domain) > { > u_int i; > > if (port == 0) > for (i = 0; i < options->num_ports; i++) >- add_one_listen_addr(options, addr, options->ports[i]); >+ add_one_listen_addr(options, addr, options->ports[i], >+ domain); > else >- add_one_listen_addr(options, addr, port); >+ add_one_listen_addr(options, addr, port, domain); > } > > static void >-add_one_listen_addr(ServerOptions *options, char *addr, int port) >+add_one_listen_addr(ServerOptions *options, char *addr, int port, char *domain) > { >- struct addrinfo hints, *ai, *aitop; >+ struct addrinfo hints, *aitop; > char strport[NI_MAXSERV]; > int gaierr; > >@@ -646,10 +651,14 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port) > fatal("bad addr or host: %s (%s)", > addr ? addr : "<NULL>", > ssh_gai_strerror(gaierr)); >- for (ai = aitop; ai->ai_next; ai = ai->ai_next) >- ; >- ai->ai_next = options->listen_addrs; >- options->listen_addrs = aitop; >+ >+ options->listen_addrs = xreallocarray( >+ options->listen_addrs, options->num_listen_addrs + 1, >+ sizeof(struct listen_address)); >+ options->listen_addrs[options->num_listen_addrs].addrs = aitop; >+ options->listen_addrs[options->num_listen_addrs].domain = >+ domain ? xstrdup(domain) : NULL; >+ options->num_listen_addrs++; > } > > /* >@@ -657,7 +666,7 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port) > * and AddressFamily options. > */ > static void >-queue_listen_addr(ServerOptions *options, char *addr, int port) >+queue_listen_addr(ServerOptions *options, char *addr, int port, char *domain) > { > options->queued_listen_addrs = xreallocarray( > options->queued_listen_addrs, options->num_queued_listens + 1, >@@ -665,9 +674,14 @@ queue_listen_addr(ServerOptions *options, char *addr, int port) > options->queued_listen_ports = xreallocarray( > options->queued_listen_ports, options->num_queued_listens + 1, > sizeof(port)); >+ options->queued_listen_domains = xreallocarray( >+ options->queued_listen_domains, options->num_queued_listens + 1, >+ sizeof(domain)); > options->queued_listen_addrs[options->num_queued_listens] = > xstrdup(addr); > options->queued_listen_ports[options->num_queued_listens] = port; >+ options->queued_listen_domains[options->num_queued_listens] = >+ domain ? xstrdup(domain) : NULL; > options->num_queued_listens++; > } > >@@ -686,14 +700,19 @@ process_queued_listen_addrs(ServerOptions *options) > > for (i = 0; i < options->num_queued_listens; i++) { > add_listen_addr(options, options->queued_listen_addrs[i], >- options->queued_listen_ports[i]); >+ options->queued_listen_ports[i], >+ options->queued_listen_domains[i]); > free(options->queued_listen_addrs[i]); > options->queued_listen_addrs[i] = NULL; >+ free(options->queued_listen_domains[i]); >+ options->queued_listen_domains[i] = NULL; > } > free(options->queued_listen_addrs); > options->queued_listen_addrs = NULL; > free(options->queued_listen_ports); > options->queued_listen_ports = NULL; >+ free(options->queued_listen_domains); >+ options->queued_listen_domains = NULL; > options->num_queued_listens = 0; > } > >@@ -992,7 +1011,7 @@ process_server_config_line(ServerOptions *options, char *line, > const char *filename, int linenum, int *activep, > struct connection_info *connectinfo) > { >- char *cp, **charptr, *arg, *arg2, *p; >+ char *cp, **charptr, *arg, *arg2, *p, *domain; > int cmdline = 0, *intptr, value, value2, n, port; > SyslogFacility *log_facility_ptr; > LogLevel *log_level_ptr; >@@ -1088,8 +1107,9 @@ process_server_config_line(ServerOptions *options, char *line, > /* check for bare IPv6 address: no "[]" and 2 or more ":" */ > if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL > && strchr(p+1, ':') != NULL) { >- queue_listen_addr(options, arg, 0); >- break; >+ port = 0; >+ p = arg; >+ goto parse_domain; > } > p = hpdelim(&arg); > if (p == NULL) >@@ -1101,7 +1121,16 @@ process_server_config_line(ServerOptions *options, char *line, > else if ((port = a2port(arg)) <= 0) > fatal("%s line %d: bad port number", filename, linenum); > >- queue_listen_addr(options, p, port); >+ parse_domain: >+ domain = NULL; >+ arg = strdelim(&cp); >+ if (arg && !strcmp(arg, "domain")) { >+ arg = strdelim(&cp); >+ if (arg != NULL && *arg != '\0') >+ domain = arg; >+ } >+ >+ queue_listen_addr(options, p, port, domain); > > break; > >@@ -1879,6 +1908,14 @@ process_server_config_line(ServerOptions *options, char *line, > options->fingerprint_hash = value; > break; > >+ case sBindDevice: >+ arg = strdelim(&cp); >+ if (!arg || *arg == '\0') >+ fatal("%s line %d: Missing argument.", filename, linenum); >+ if (options->bind_device == NULL) >+ options->bind_device = xstrdup(arg); >+ break; >+ > case sExposeAuthInfo: > intptr = &options->expose_userauth_info; > goto parse_flag; >@@ -2238,38 +2275,41 @@ dump_config(ServerOptions *o) > int ret; > struct addrinfo *ai; > char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; >- char *laddr1 = xstrdup(""), *laddr2 = NULL; >+ char *laddr = xstrdup(""); > > /* these are usually at the top of the config */ > for (i = 0; i < o->num_ports; i++) > printf("port %d\n", o->ports[i]); > dump_cfg_fmtint(sAddressFamily, o->address_family); > >- /* >- * ListenAddress must be after Port. add_one_listen_addr pushes >- * addresses onto a stack, so to maintain ordering we need to >- * print these in reverse order. >- */ >- for (ai = o->listen_addrs; ai; ai = ai->ai_next) { >- if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, >- sizeof(addr), port, sizeof(port), >- NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { >- error("getnameinfo failed: %.100s", >- (ret != EAI_SYSTEM) ? gai_strerror(ret) : >- strerror(errno)); >- } else { >- laddr2 = laddr1; >- if (ai->ai_family == AF_INET6) >- xasprintf(&laddr1, "listenaddress [%s]:%s\n%s", >- addr, port, laddr2); >- else >- xasprintf(&laddr1, "listenaddress %s:%s\n%s", >- addr, port, laddr2); >- free(laddr2); >+ /* ListenAddress must be after Port. */ >+ for (i = 0; i < o->num_listen_addrs; i++) { >+ for (ai = o->listen_addrs[i].addrs; ai; ai = ai->ai_next) { >+ if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, >+ sizeof(addr), port, sizeof(port), >+ NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { >+ error("getnameinfo failed: %.100s", >+ (ret != EAI_SYSTEM) ? gai_strerror(ret) : >+ strerror(errno)); >+ } else { >+ if (ai->ai_family == AF_INET6) >+ xasprintf(&laddr, >+ "%slistenaddress [%s]:%s", laddr, >+ addr, port); >+ else >+ xasprintf(&laddr, >+ "%slistenaddress %s:%s", laddr, >+ addr, port); >+ if (o->listen_addrs[i].domain) >+ xasprintf(&laddr, "%s domain %s\n", >+ laddr, o->listen_addrs[i].domain); >+ else >+ xasprintf(&laddr, "%s\n", laddr); >+ } > } > } >- printf("%s", laddr1); >- free(laddr1); >+ printf("%s", laddr); >+ free(laddr); > > /* integer arguments */ > #ifdef USE_PAM >@@ -2358,6 +2398,7 @@ dump_config(ServerOptions *o) > o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); > dump_cfg_string(sPubkeyAcceptedKeyTypes, o->pubkey_key_types ? > o->pubkey_key_types : KEX_DEFAULT_PK_ALG); >+ dump_cfg_string(sBindDevice, o->bind_device); > > /* string arguments requiring a lookup */ > dump_cfg_string(sLogLevel, log_level_name(o->log_level)); >diff --git a/servconf.h b/servconf.h >index 1dca702e..c263dcf0 100644 >--- a/servconf.h >+++ b/servconf.h >@@ -68,7 +68,9 @@ typedef struct { > u_int num_queued_listens; > char **queued_listen_addrs; > int *queued_listen_ports; >- struct addrinfo *listen_addrs; /* Addresses on which the server listens. */ >+ char **queued_listen_domains; >+ struct listen_address *listen_addrs; /* Addresses on which the server listens. */ >+ u_int num_listen_addrs; > int address_family; /* Address family used by the server. */ > char *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */ > int num_host_key_files; /* Number of files for host keys. */ >@@ -198,6 +200,8 @@ typedef struct { > > int fingerprint_hash; > int expose_userauth_info; >+ >+ char *bind_device; /* network device to bind to */ > } ServerOptions; > > /* Information about the incoming connection as used by Match */ >@@ -209,6 +213,15 @@ struct connection_info { > int lport; /* local port */ > }; > >+/* >+ * Each hostname might have multiple addresses, but they are all tied to >+ * the same domain (VRF). This struct helps keeping them associated. >+ */ >+struct listen_address { >+ struct addrinfo *addrs; >+ char *domain; >+}; >+ > > /* > * These are string config options that must be copied between the >diff --git a/ssh.1 b/ssh.1 >index 2ab1697f..f818ea3f 100644 >--- a/ssh.1 >+++ b/ssh.1 >@@ -45,6 +45,7 @@ > .Bk -words > .Op Fl 46AaCfGgKkMNnqsTtVvXxYy > .Op Fl b Ar bind_address >+.Op Fl B Ar bind_device > .Op Fl c Ar cipher_spec > .Op Fl D Oo Ar bind_address : Oc Ns Ar port > .Op Fl E Ar log_file >@@ -128,6 +129,13 @@ on the local machine as the source address > of the connection. > Only useful on systems with more than one address. > .Pp >+.It Fl B Ar bind_device >+Bind the connecting socket to >+.Ar bind_device >+on the local machine. >+Useful on systems that use >+.Cm VRF . >+.Pp > .It Fl C > Requests compression of all data (including stdin, stdout, stderr, and > data for forwarded X11, TCP and >diff --git a/ssh.c b/ssh.c >index ae37432b..b3305575 100644 >--- a/ssh.c >+++ b/ssh.c >@@ -197,7 +197,7 @@ static void > usage(void) > { > fprintf(stderr, >-"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n" >+"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-B bind_device] [-c cipher_spec]\n" > " [-D [bind_address:]port] [-E log_file] [-e escape_char]\n" > " [-F configfile] [-I pkcs11] [-i identity_file]\n" > " [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n" >@@ -612,7 +612,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:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { >+ "AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { > switch (opt) { > case '1': > fatal("SSH protocol v.1 is no longer supported"); >@@ -918,6 +918,9 @@ main(int ac, char **av) > case 'b': > options.bind_address = optarg; > break; >+ case 'B': >+ options.bind_device = optarg; >+ break; > case 'F': > config = optarg; > break; >diff --git a/ssh_config.5 b/ssh_config.5 >index eab8dd01..489a21dc 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -262,6 +262,10 @@ Note that this option does not work if > .Cm UsePrivilegedPort > is set to > .Cm yes . >+.It Cm BindDevice >+Bind the connecting socket to the specified device on the local machine. >+Useful on systems that use >+.Cm VRF . > .It Cm CanonicalDomains > When > .Cm CanonicalizeHostname >diff --git a/sshconnect.c b/sshconnect.c >index dc7a704d..8cb7108b 100644 >--- a/sshconnect.c >+++ b/sshconnect.c >@@ -286,6 +286,26 @@ ssh_create_socket(int privileged, struct addrinfo *ai) > } > fcntl(sock, F_SETFD, FD_CLOEXEC); > >+ if (options.bind_device != NULL) { >+#ifdef SO_BINDTODEVICE >+ r = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, >+ options.bind_device, strlen(options.bind_device)); >+ if (r != 0) { >+ error("Bind to: %s failed %s", options.bind_device, >+ strerror(errno)); >+ close(sock); >+ return -1; >+ } >+ debug("Bound to device: %s", options.bind_device); >+#else >+ error("No SO_BINDTODEVICE, unable to bind to: %s", >+ options.bind_device); >+ 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.8 b/sshd.8 >index a4201146..6c417325 100644 >--- a/sshd.8 >+++ b/sshd.8 >@@ -44,6 +44,7 @@ > .Nm sshd > .Bk -words > .Op Fl 46DdeiqTt >+.Op Fl B Ar bind_device > .Op Fl C Ar connection_spec > .Op Fl c Ar host_certificate_file > .Op Fl E Ar log_file >@@ -94,6 +95,16 @@ to use IPv4 addresses only. > Forces > .Nm > to use IPv6 addresses only. >+.It Fl B Ar bind_device >+Bind the listening sockets to >+.Ar bind_device >+on the local machine. >+Useful on systems that use >+.Cm VRF . >+.Pp >+Note that a domain specified on the >+.Cm ListenAddress >+line will have the priority. > .It Fl C Ar connection_spec > Specify the connection parameters to use for the > .Fl T >diff --git a/sshd.c b/sshd.c >index 51a1aaf6..ed62dd06 100644 >--- a/sshd.c >+++ b/sshd.c >@@ -908,7 +908,7 @@ usage(void) > #endif > ); > fprintf(stderr, >-"usage: sshd [-46DdeiqTt] [-C connection_spec] [-c host_cert_file]\n" >+"usage: sshd [-46DdeiqTt] [-B bind_device] [-C connection_spec] [-c host_cert_file]\n" > " [-E log_file] [-f config_file] [-g login_grace_time]\n" > " [-h host_key_file] [-o option] [-p port] [-u len]\n" > ); >@@ -1016,71 +1016,117 @@ server_accept_inetd(int *sock_in, int *sock_out) > static void > server_listen(void) > { >+ u_int i; > int ret, listen_sock, on = 1; > struct addrinfo *ai; >- char ntop[NI_MAXHOST], strport[NI_MAXSERV]; >+ char ntop[NI_MAXHOST], strport[NI_MAXSERV], *domain; > >- for (ai = options.listen_addrs; ai; ai = ai->ai_next) { >- if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) >- continue; >- if (num_listen_socks >= MAX_LISTEN_SOCKS) >- fatal("Too many listen sockets. " >- "Enlarge MAX_LISTEN_SOCKS"); >- if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, >- ntop, sizeof(ntop), strport, sizeof(strport), >- NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { >- error("getnameinfo failed: %.100s", >- ssh_gai_strerror(ret)); >- continue; >- } >- /* Create socket for listening. */ >- listen_sock = socket(ai->ai_family, ai->ai_socktype, >- ai->ai_protocol); >- if (listen_sock < 0) { >- /* kernel may not support ipv6 */ >- verbose("socket: %.100s", strerror(errno)); >- continue; >- } >- if (set_nonblock(listen_sock) == -1) { >- close(listen_sock); >- continue; >- } >- if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) { >- verbose("socket: CLOEXEC: %s", strerror(errno)); >- close(listen_sock); >- continue; >- } >- /* >- * Set socket options. >- * Allow local port reuse in TIME_WAIT. >- */ >- if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, >- &on, sizeof(on)) == -1) >- error("setsockopt SO_REUSEADDR: %s", strerror(errno)); >+ for (i = 0; i < options.num_listen_addrs; i++) { >+ for (ai = options.listen_addrs[i].addrs; ai; ai = ai->ai_next) { >+ if (ai->ai_family != AF_INET && >+ ai->ai_family != AF_INET6) >+ continue; >+ if (num_listen_socks >= MAX_LISTEN_SOCKS) >+ fatal("Too many listen sockets. " >+ "Enlarge MAX_LISTEN_SOCKS"); >+ if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, >+ ntop, sizeof(ntop), strport, sizeof(strport), >+ NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { >+ error("getnameinfo failed: %.100s", >+ ssh_gai_strerror(ret)); >+ continue; >+ } >+ /* Create socket for listening. */ >+ listen_sock = socket(ai->ai_family, ai->ai_socktype, >+ ai->ai_protocol); >+ if (listen_sock < 0) { >+ /* kernel may not support ipv6 */ >+ verbose("socket: %.100s", strerror(errno)); >+ continue; >+ } >+ if (set_nonblock(listen_sock) == -1) { >+ close(listen_sock); >+ continue; >+ } >+ if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) { >+ verbose("socket: CLOEXEC: %s", >+ strerror(errno)); >+ close(listen_sock); >+ continue; >+ } >+ /* >+ * Set socket options. >+ * Allow local port reuse in TIME_WAIT. >+ */ >+ if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, >+ &on, sizeof(on)) == -1) >+ error("setsockopt SO_REUSEADDR: %s", >+ strerror(errno)); > >- /* Only communicate in IPv6 over AF_INET6 sockets. */ >- if (ai->ai_family == AF_INET6) >- sock_set_v6only(listen_sock); >+ /* Only communicate in IPv6 over AF_INET6 sockets. */ >+ if (ai->ai_family == AF_INET6) >+ sock_set_v6only(listen_sock); > >- debug("Bind to port %s on %s.", strport, ntop); >+ /* Fallback to global domain if available. */ >+ if (options.listen_addrs[i].domain != NULL && >+ strlen(options.listen_addrs[i].domain) > 0) >+ domain = options.listen_addrs[i].domain; >+ else if (options.bind_device != NULL && >+ strlen(options.bind_device) > 0) >+ domain = options.bind_device; >+ else >+ domain = NULL; > >- /* Bind the socket to the desired port. */ >- if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { >- error("Bind to port %s on %s failed: %.200s.", >- strport, ntop, strerror(errno)); >- close(listen_sock); >- continue; >- } >- listen_socks[num_listen_socks] = listen_sock; >- num_listen_socks++; >+ if (domain) { >+#ifdef SO_BINDTODEVICE >+ ret = setsockopt(listen_sock, SOL_SOCKET, >+ SO_BINDTODEVICE, domain, strlen(domain)); >+ if (ret != 0) { >+ error("Bind to: %s failed: %s", >+ domain, strerror(errno)); >+ close(listen_sock); >+ continue; >+ } >+ debug("Bind to device %s", domain); >+#else >+ error("No SO_BINDTODEVICE, unable to bind to: %s", >+ domain); >+ close(listen_sock); >+ continue; >+#endif >+ } >+ >+ debug("Bind to port %s on %s.", strport, ntop); > >- /* Start listening on the port. */ >- if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) >- fatal("listen on [%s]:%s: %.100s", >- ntop, strport, strerror(errno)); >- logit("Server listening on %s port %s.", ntop, strport); >+ /* Bind the socket to the desired port. */ >+ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { >+ error("Bind to port %s on %s failed: %.200s.", >+ strport, ntop, strerror(errno)); >+ close(listen_sock); >+ continue; >+ } >+ listen_socks[num_listen_socks] = listen_sock; >+ num_listen_socks++; >+ >+ /* Start listening on the port. */ >+ if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) >+ fatal("listen on [%s]:%s: %.100s", >+ ntop, strport, strerror(errno)); >+ if (!domain) { >+ logit("Server listening on %s port %s.", ntop, >+ strport); >+ } else { >+ logit("Server listening on %s port %s domain %s.", >+ ntop, strport, domain); >+ } >+ } >+ freeaddrinfo(options.listen_addrs[i].addrs); >+ free(options.listen_addrs[i].domain); >+ options.listen_addrs[i].domain = NULL; > } >- freeaddrinfo(options.listen_addrs); >+ free(options.listen_addrs); >+ options.listen_addrs = NULL; >+ options.num_listen_addrs = 0; > > if (!num_listen_socks) > fatal("Cannot bind any address."); >@@ -1404,7 +1450,7 @@ 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) { >+ "C:E:b:c:B:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) { > switch (opt) { > case '4': > options.address_family = AF_INET; >@@ -1512,6 +1558,9 @@ main(int ac, char **av) > exit(1); > free(line); > break; >+ case 'B': >+ options.bind_device = optarg; >+ break; > case '?': > default: > usage(); >diff --git a/sshd_config.5 b/sshd_config.5 >index 251b7467..6acd6b93 100644 >--- a/sshd_config.5 >+++ b/sshd_config.5 >@@ -380,6 +380,14 @@ If the argument is > .Cm none > then no banner is displayed. > By default, no banner is displayed. >+.It Cm BindDevice >+Bind the listening sockets to the specified device on the local machine. >+Useful on systems that use >+.Cm VRF . >+.Pp >+Note that a domain specified on the >+.Cm ListenAddress >+line will have the priority. > .It Cm ChallengeResponseAuthentication > Specifies whether challenge-response authentication is allowed (e.g. via > PAM or through authentication styles supported in >@@ -921,6 +929,12 @@ The following forms may be used: > .Oo > .Ar host | Ar IPv6_addr Oc : Ar port > .Sm on >+.It >+.Cm ListenAddress >+.Sm off >+.Ar host | Ar IPv4_addr | Ar IPv6_addr >+.Sm on >+.Ar domain vrf-blue > .El > .Pp > If >@@ -929,6 +943,11 @@ is not specified, > sshd will listen on the address and all > .Cm Port > options specified. >+If >+.Ar domain >+is not specified, sshd will use >+.Cm BindDevice >+if present. > The default is to listen on all local addresses. > Multiple > .Cm ListenAddress >-- >2.11.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2784
:
3061
|
3064
|
3070
|
3071
|
3072
|
3075
|
3076
|
3077
|
3078
|
3079
|
3080
|
3081
|
3082