mirror of
https://github.com/openssh/openssh-portable.git
synced 2026-01-12 00:04:08 +08:00
upstream: Make ssh(1) and sshd(8) set IP QoS (aka IP_TOS, IPV6_TCLASS)
continually at runtime based on what sessions/channels are open. Previously, ssh(1) and sshd(8) would pick a QoS value when they were started and use it for the whole connection. This could produce suboptimal choices for the QoS value, e.g. for multiplexed sessions that started interactive but picked up a sftp client, or sessions that moved large amounts of data via port forwarding. Now the QoS value will change to the non-interactive IPQoS whenever a "non-interactive" channel is open; basically any channel that lacks a tty other than agent forwarding. This is important now that the default interactive IPQoS is EF (Expedited Forwarding), as many networks are configured to allow only relatively small amounts of traffic of this class and they will aggressively deprioritise the entire connection if this is exceeded. NB. because ssh(1) and sshd(8) now change IP_TOS/IPV6_TCLASS continually via setsockopt(), this commit requires a recent pledge(2) change that landed recently in the OpenBSD kernel. Please ensure you have updated to a kernel from within the last two weeks before updating OpenSSH. with job@ deraadt@ OpenBSD-Commit-ID: 325fc41717eecdf5e4b534bfa8d66817425b840f
This commit is contained in:
committed by
Damien Miller
parent
dc5147028f
commit
289239046b
73
packet.c
73
packet.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: packet.c,v 1.319 2025/08/06 23:44:09 djm Exp $ */
|
||||
/* $OpenBSD: packet.c,v 1.320 2025/08/18 03:43:01 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@@ -210,8 +210,8 @@ struct session_state {
|
||||
/* Used in ssh_packet_send_mux() */
|
||||
int mux;
|
||||
|
||||
/* Used in packet_set_interactive */
|
||||
int set_interactive_called;
|
||||
/* QoS handling */
|
||||
int qos_interactive, qos_other;
|
||||
|
||||
/* Used in packet_set_maxsize */
|
||||
int set_maxsize_called;
|
||||
@@ -225,6 +225,9 @@ struct session_state {
|
||||
*/
|
||||
int disconnecting;
|
||||
|
||||
/* Nagle disabled on socket */
|
||||
int nodelay_set;
|
||||
|
||||
/* Hook for fuzzing inbound packets */
|
||||
ssh_packet_hook_fn *hook_in;
|
||||
void *hook_in_ctx;
|
||||
@@ -253,6 +256,8 @@ ssh_alloc_session_state(void)
|
||||
state->connection_out = -1;
|
||||
state->max_packet_size = 32768;
|
||||
state->packet_timeout_ms = -1;
|
||||
state->interactive_mode = 1;
|
||||
state->qos_interactive = state->qos_other = -1;
|
||||
state->p_send.packets = state->p_read.packets = 0;
|
||||
state->initialized = 1;
|
||||
/*
|
||||
@@ -2212,37 +2217,44 @@ ssh_packet_interactive_data_to_write(struct ssh *ssh)
|
||||
sshbuf_len(ssh->state->output) < 256;
|
||||
}
|
||||
|
||||
void
|
||||
ssh_packet_set_tos(struct ssh *ssh, int tos)
|
||||
static void
|
||||
apply_qos(struct ssh *ssh)
|
||||
{
|
||||
if (!ssh_packet_connection_is_on_socket(ssh) || tos == INT_MAX)
|
||||
struct session_state *state = ssh->state;
|
||||
int qos = state->interactive_mode ?
|
||||
state->qos_interactive : state->qos_other;
|
||||
|
||||
if (!ssh_packet_connection_is_on_socket(ssh))
|
||||
return;
|
||||
set_sock_tos(ssh->state->connection_in, tos);
|
||||
if (!state->nodelay_set) {
|
||||
set_nodelay(state->connection_in);
|
||||
state->nodelay_set = 1;
|
||||
}
|
||||
set_sock_tos(ssh->state->connection_in, qos);
|
||||
}
|
||||
|
||||
/* Informs that the current session is interactive. Sets IP flags for that. */
|
||||
|
||||
/* Informs that the current session is interactive. */
|
||||
void
|
||||
ssh_packet_set_interactive(struct ssh *ssh, int interactive, int qos_interactive, int qos_bulk)
|
||||
ssh_packet_set_interactive(struct ssh *ssh, int interactive)
|
||||
{
|
||||
struct session_state *state = ssh->state;
|
||||
|
||||
if (state->set_interactive_called)
|
||||
return;
|
||||
state->set_interactive_called = 1;
|
||||
|
||||
/* Record that we are in interactive mode. */
|
||||
state->interactive_mode = interactive;
|
||||
apply_qos(ssh);
|
||||
}
|
||||
|
||||
/* Only set socket options if using a socket. */
|
||||
if (!ssh_packet_connection_is_on_socket(ssh))
|
||||
return;
|
||||
set_nodelay(state->connection_in);
|
||||
ssh_packet_set_tos(ssh, interactive ? qos_interactive : qos_bulk);
|
||||
/* Set QoS flags to be used for interactive and non-interactive sessions */
|
||||
void
|
||||
ssh_packet_set_qos(struct ssh *ssh, int qos_interactive, int qos_other)
|
||||
{
|
||||
struct session_state *state = ssh->state;
|
||||
|
||||
state->qos_interactive = qos_interactive;
|
||||
state->qos_other = qos_other;
|
||||
apply_qos(ssh);
|
||||
}
|
||||
|
||||
/* Returns true if the current connection is interactive. */
|
||||
|
||||
int
|
||||
ssh_packet_is_interactive(struct ssh *ssh)
|
||||
{
|
||||
@@ -2421,6 +2433,7 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m)
|
||||
struct session_state *state = ssh->state;
|
||||
int r;
|
||||
|
||||
#define ENCODE_INT(v) (((v) < 0) ? 0xFFFFFFFF : (u_int)v)
|
||||
if ((r = kex_to_blob(m, ssh->kex)) != 0 ||
|
||||
(r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 ||
|
||||
(r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 ||
|
||||
@@ -2435,9 +2448,12 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m)
|
||||
(r = sshbuf_put_u32(m, state->p_read.packets)) != 0 ||
|
||||
(r = sshbuf_put_u64(m, state->p_read.bytes)) != 0 ||
|
||||
(r = sshbuf_put_stringb(m, state->input)) != 0 ||
|
||||
(r = sshbuf_put_stringb(m, state->output)) != 0)
|
||||
(r = sshbuf_put_stringb(m, state->output)) != 0 ||
|
||||
(r = sshbuf_put_u32(m, ENCODE_INT(state->interactive_mode))) != 0 ||
|
||||
(r = sshbuf_put_u32(m, ENCODE_INT(state->qos_interactive))) != 0 ||
|
||||
(r = sshbuf_put_u32(m, ENCODE_INT(state->qos_other))) != 0)
|
||||
return r;
|
||||
|
||||
#undef ENCODE_INT
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2556,6 +2572,7 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m)
|
||||
const u_char *input, *output;
|
||||
size_t ilen, olen;
|
||||
int r;
|
||||
u_int interactive, qos_interactive, qos_other;
|
||||
|
||||
if ((r = kex_from_blob(m, &ssh->kex)) != 0 ||
|
||||
(r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 ||
|
||||
@@ -2592,6 +2609,16 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m)
|
||||
(r = sshbuf_put(state->output, output, olen)) != 0)
|
||||
return r;
|
||||
|
||||
if ((r = sshbuf_get_u32(m, &interactive)) != 0 ||
|
||||
(r = sshbuf_get_u32(m, &qos_interactive)) != 0 ||
|
||||
(r = sshbuf_get_u32(m, &qos_other)) != 0)
|
||||
return r;
|
||||
#define DECODE_INT(v) ((v) > INT_MAX ? -1 : (v))
|
||||
state->interactive_mode = DECODE_INT(interactive);
|
||||
state->qos_interactive = DECODE_INT(qos_interactive);
|
||||
state->qos_other = DECODE_INT(qos_other);
|
||||
#undef DECODE_INT
|
||||
|
||||
if (sshbuf_len(m))
|
||||
return SSH_ERR_INVALID_FORMAT;
|
||||
debug3_f("done");
|
||||
|
||||
Reference in New Issue
Block a user