Decoding ssh channel information


If you’re using ssh and hit the escape sequence ~# then it’ll tell you about the channels it has open. It looks like this:

The following connections are open:
  #0 client-session (t4 r0 i0/0 o0/0 fd 7/8 cc -1)

In this case I have just the main ssh connection open. If you’re using ssh’s port forwarding (and have active connections through it) you’ll get more output, like this:

The following connections are open:
  #0 client-session (t4 r0 i0/0 o0/0 fd 6/7 cc -1)
  #3 direct-tcpip: listening port 8080 for localhost port 8080, connect from ::1 port 64126 to ::1 port 8080 (t4 r2 i0/0 o0/0 fd 12/12 cc -1)

But what does that actually mean? The ssh man page doesn’t seem to be much help, so I went digging.

Here’s the code in channel.c which outputs the line:

snprintf(buf, sizeof buf,
    "  #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n",
    c->self, c->remote_name,
    c->type, c->remote_id,
    c->istate, buffer_len(&c->input),
    c->ostate, buffer_len(&c->output),
    c->rfd, c->wfd, c->ctl_chan);

The meanings of these fields are included in channel.h which defines the Channel struct (edited for just the fields above):

struct Channel {
    int     type;       /* channel type/state */
    int     self;       /* my own channel identifier */
    int     remote_id;  /* channel identifier for remote peer */

    u_int   istate;     /* input from channel (state of receive half) */
    u_int   ostate;     /* output to channel  (state of transmit half) */

    int     rfd;        /* read fd */
    int     wfd;        /* write fd */
    int     ctl_chan;   /* control channel (multiplexed connections) */

    Buffer  input;      /* data read from socket, to be sent over
                         * encrypted connection */
    Buffer  output;     /* data received over encrypted connection for
                         * send on socket */
...

So the first value is the local channel ID, followed by the name of the channel, which tends to be a description of what it’s for. Forwarding sets this in port_open_helper using the self-explanatory format seen above.

Then in parentheses, the t value is the channel type, the r is the remote end’s channel ID.

The i and o values are the state/buffer size for the input and output queues.

The two values after fd are the read and write file descriptor numbers, and finally the cc number is the control channel.

The channel type is one of these constants:

/* Definitions for channel types. */
#define SSH_CHANNEL_X11_LISTENER    1   /* Listening for inet X11 conn. */
#define SSH_CHANNEL_PORT_LISTENER   2   /* Listening on a port. */
#define SSH_CHANNEL_OPENING         3   /* waiting for confirmation */
#define SSH_CHANNEL_OPEN            4   /* normal open two-way channel */
#define SSH_CHANNEL_CLOSED          5   /* waiting for close confirmation */
#define SSH_CHANNEL_AUTH_SOCKET     6   /* authentication socket */
#define SSH_CHANNEL_X11_OPEN        7   /* reading first X11 packet */
#define SSH_CHANNEL_INPUT_DRAINING  8   /* sending remaining data to conn */
#define SSH_CHANNEL_OUTPUT_DRAINING 9   /* sending remaining data to app */
#define SSH_CHANNEL_LARVAL          10  /* larval session */
#define SSH_CHANNEL_RPORT_LISTENER  11  /* Listening to a R-style port  */
#define SSH_CHANNEL_CONNECTING      12
#define SSH_CHANNEL_DYNAMIC         13
#define SSH_CHANNEL_ZOMBIE          14  /* Almost dead. */
#define SSH_CHANNEL_MUX_LISTENER    15  /* Listener for mux conn. */
#define SSH_CHANNEL_MUX_CLIENT      16  /* Conn. to mux slave */
#define SSH_CHANNEL_ABANDONED       17  /* Abandoned session, eg mux */
#define SSH_CHANNEL_UNIX_LISTENER   18  /* Listening on a domain socket. */
#define SSH_CHANNEL_RUNIX_LISTENER  19  /* Listening to a R-style domain socket. */

The input and output state values can be decoded with the constants further down the file:

/* possible input states */
#define CHAN_INPUT_OPEN             0
#define CHAN_INPUT_WAIT_DRAIN       1
#define CHAN_INPUT_WAIT_OCLOSE      2
#define CHAN_INPUT_CLOSED           3

/* possible output states */
#define CHAN_OUTPUT_OPEN            0
#define CHAN_OUTPUT_WAIT_DRAIN      1
#define CHAN_OUTPUT_WAIT_IEOF       2
#define CHAN_OUTPUT_CLOSED          3

The control channel is statically set to -1, presumably set to other values if you’re using multiplexing.

Most of these values are of passing interest, it turns out – most of the time the information I need is in the 2nd field.