Thread (1 message) 1 message, 1 author, 2025-08-28

Re: [PATCH iproute2] ss: Don't pad the last (enabled) column

From: Stefano Brivio <hidden>
Date: 2025-08-28 23:49:50

On Mon, 25 Aug 2025 11:51:20 +1000
Paul Wayper [off-list ref] wrote:
On 21/08/2025 20:35, Stefano Brivio wrote:
quoted
Hi Paul,

On Thu, 21 Aug 2025 15:45:47 +1000
Paul Wayper[off-list ref] wrote:
 
quoted
ss will emit spaces on the right hand side of a left-justified, enabled
column even if it's the last column.  In situations where one or more
lines are very long - e.g. because of a large PROCESS field value - this
causes a lot of excess output.  
I guess I understand the issue, but having an example would help,
because I'm not quite sure how to reproduce this.  
Hi Stefano,

To reproduce, do a `ss -tunap` and look at the output with something 
like `od -tx1c`:

0000000  4e  65  74  69  64  20  53  74 61  74  65  20  20  20  20  20
           N   e   t   i   d       S   t   a   t e
0000020  20  52  65  63  76  2d  51  20  53  65  6e  64  2d  51 20  20
               R   e   c   v   -   Q       S   e   n   d   - Q
0000040  20  20  20  20  20  20  20  20  20  20  20  20  20  20 20  20

0000060  20  20  20  20  20  20  20  20  20  20  20  4c  6f  63 61  6c
                                                       L   o c   a   l
0000100  20  41  64  64  72  65  73  73  3a  50  6f  72  74  20 20  20
               A   d   d   r   e   s   s   :   P   o   r t
0000120  20  20  20  20  20  20  20  50  65  65  72  20  41  64 64  72
                                       P   e   e   r       A d   d   r
0000140  65  73  73  3a  50  6f  72  74  50  72  6f  63  65  73 73  20
           e   s   s   :   P   o   r   t   P   r   o   c   e s   s
0000160  20  20  20  20  20  20  20  20  20  20  20  20  20  20 20  20

*
0000220  20  20  20  20  20  20  0a  75  64  70  20  20  20  55 4e  43
                                  \n   u   d   p U   N   C
0000240  4f  4e  4e  20  20  20  20  20  30  20  20  20  20  20 20  30
           O   N   N 0                           0
0000260  20  20  20  20  20  20  20  20  20  20  20  20  20  20 20  20

*
0000320  20  20  20  20  20  20  20  20  30  2e  30  2e  30  2e 30  3a
                                           0   .   0   .   0 .   0   :
0000340  34  36  37  36  35  20  20  20  20  20  20  20  20  20 20  20
           4   6   7   6 5
0000360  20  20  20  30  2e  30  2e  30  2e  30  3a  2a  20  20 20  20
                       0   .   0   .   0   .   0   : *
0000400  75  73  65  72  73  3a  28  28  22  77  73  64  64  22 2c  70
           u   s   e   r   s   :   (   (   "   w   s   d   d "   ,   p
0000420  69  64  3d  35  39  35  37  2c  66  64  3d  31  34  29 29  20
           i   d   =   5   9   5   7   ,   f   d   =   1   4 )   )
0000440  20  20  20  20  20  20  20  20  20  20  20  20  20  0a 75  64
\n   u   d

You can see the spaces after 'Process' and the process name (i.e. 
'users:(("wsdd",pid=5957,fd=14))'), just before the end-of-line.
Those spaces _should_ be harmless because they _should_ align with the
end of line anyway. We can drop them, but:
The problem we had was with the output when there was over 1000 
processes all listening on the same port.  That pushed the line length 
out to ~35,000 bytes, with almost all of that being space characters.  
Unfortunately on this server there was also ~21,000 connections in 
TIME-WAIT, which -a will show, so we had ~21,000 lines of ~35,000 bytes 
which added up to a very large output.
...I don't understand, from this, if your issue is that there was a
single process with a very long name (which won't be solved by just
dropping spaces), or the waste of bytes (which would be solved instead).

Having the exact line(s) causing you trouble would be helpful.
What's messing with my head right now though is that in the `ss -tunap` 
output, when the 'Process' column is on the end, the current standard 
'ss' on my Fedora 42 machine outputs the Process header right up against 
the 'Peer address:Port' header:

$ /bin/ss -tunap | head -4  # trimmed for email neatness...      problem 
is v- here
First off, note that if you pipe the output to head(1), isatty(3) in
render_screen_width() will return 0, and that affects the output of
course, because we can't fetch the number of columns.

What did you trim exactly here, and what do you mean by "problem is v-"?
Netid State      Recv-Q Send-Q Local Address:Port          Peer 
Address:PortProcess
udp   UNCONN     0      0            0.0.0.0:46765 0.0.0.0:*    
users:(("wsdd",pid=5957,fd=14))
udp   UNCONN     0      0            0.0.0.0:32772 0.0.0.0:*    
users:(("firefox",pid=8059,fd=279))
udp   UNCONN     0      0          127.0.0.1:53 0.0.0.0:*
I can see something like this as well if I pipe the output and I don't
set COLUMNS according to the current columns from my terminal. The
reason is that we try to pack as much output as possible into one line,
and we pack one byte too many, so the space between "Port" and
"Process" is elided.

This should be fixed, but it won't help with the issue you reported.

There seems to be another issue: render_screen_width() calls 
getenv("COLUMNS"), but, with $COLUMNS set to 211 for example, for
whatever reason, I get different outputs between 'ss -tunap' and
'COLUMNS=211 ss -tunap', as if environment variables were scrubbed or
something. I didn't debug that yet.
$ rpm -qf /bin/ss
iproute-6.12.0-3.fc42.x86_64

Is this something that I just haven't noticed until now?  Or is there a 
more fundamental bug in the way the table spacing is calculated?  I've 
tried changing the 'ldelim' to be a space, as it is in the non-first 
left-aligned columns (State, Recv-Q, Send-Q, Local address: and Peer 
address:), and that fixes the header but adds an extra space before the 
'users' process description.
quoted
There's a problem with this change though, which I didn't really
investigate. It turns something like this (105 columns):

$ ss -tunl
Netid   State    Recv-Q   Send-Q                           Local Address:Port        Peer Address:Port
udp     UNCONN   0        0                                 10.45.242.29:49047            0.0.0.0:*
udp     UNCONN   0        0                                192.168.122.1:53               0.0.0.0:*
udp     UNCONN   0        0                               0.0.0.0%virbr0:67               0.0.0.0:*
[snip]
tcp     LISTEN   0        1024                                         *:12865                  *:*
tcp     LISTEN   0        20                                       [::1]:25                  [::]:*

into this:

$ ./ss -tunl
Netid   State    Recv-Q   Send-Q                           Local Address:Port        Peer Address:Port
         udp      UNCONN   0                                        0     192.168.122.1:               530.0.0.0:
*                udp      UNCONN                                   0     0         0.0.0.0%virbr0:67
0.0.0.0: *                 udp                                      UNCONN0                  0     0.0.0.0:
[snip]
*                tcp      LISTEN                                   0     4096               [::1]:631
[::]:   *                 tcp                                      LISTEN0                  1024  *:
12865   *:       *                                                    tcpLISTEN             0     20
[::1]:  25       [::]:    *  
OK, that's weird.  I'm not seeing that:

$ misc/ss -tunl | head
The same note about piping the output applies here, but anyway:
Netid State  Recv-Q Send-Q                             Local 
Address:Port  Peer Address:Port
udp   UNCONN 0      0 0.0.0.0:46765      0.0.0.0:*
udp   UNCONN 0      0 127.0.0.1:53         0.0.0.0:*
udp   UNCONN 0      0 127.0.0.54:53         0.0.0.0:*
udp   UNCONN 0      0 127.0.0.53%lo:53         0.0.0.0:*
udp   UNCONN 0      0 127.0.0.1:323        0.0.0.0:*
udp   UNCONN 0      0 10.215.66.73:3702       0.0.0.0:*
udp   UNCONN 0      0 239.255.255.250:3702       0.0.0.0:*
udp   UNCONN 0      0 10.76.18.168:3702       0.0.0.0:*
udp   UNCONN 0      0 239.255.255.250:3702       0.0.0.0:*
...the alignment is completely broken even in your output. This is
with the second version of your patch, in my environment:

$ ./ss -tunl | head
Netid State  Recv-Q Send-Q                      Local Address:Port  Peer Address:Port
      udp    UNCONN 0                                   0     192.168.1.185:         564080.0.0.0:
*            udp    UNCONN                              0     0      192.168.122.1:53
0.0.0.0: *             udp                                 UNCONN0             0     0.0.0.0%virbr0:
67    0.0.0.0: *                                             udpUNCONN        0     0     
0.0.0.0: 111    0.0.0.0: *                                         udp          UNCONN0     
0      0.0.0.0: 33335  0.0.0.0:                                   *                udpUNCONN
0      0      10.45.242.21: 42711                             0.0.0.0:*                  udp
UNCONN 0      0      0.0.0.0:                                51540.0.0.0:             *
udp   UNCONN 0      0                             224.0.0.251:5353       0.0.0.0:*

...and without it:

$ ss -tunl | head
Netid State  Recv-Q Send-Q                      Local Address:Port  Peer Address:Port
udp   UNCONN 0      0                           192.168.122.1:53         0.0.0.0:*   
udp   UNCONN 0      0                          0.0.0.0%virbr0:67         0.0.0.0:*   
udp   UNCONN 0      0                                 0.0.0.0:111        0.0.0.0:*   
udp   UNCONN 0      0                                 0.0.0.0:33335      0.0.0.0:*   
udp   UNCONN 0      0                                 0.0.0.0:5154       0.0.0.0:*   
udp   UNCONN 0      0                             224.0.0.251:5353       0.0.0.0:*   
udp   UNCONN 0      0                             224.0.0.251:5353       0.0.0.0:*   
udp   UNCONN 0      0                             224.0.0.251:5353       0.0.0.0:*   
udp   UNCONN 0      0                             224.0.0.251:5353       0.0.0.0:*   
I'm not doubting you got that result, and at a rough guess it looks like 
the count of which field we're aligning is getting out of sync with the 
field we're actually printing.  That makes me think I must have 
interfered with the 'render' function's field output loop, but I didn't 
touch it.

I've tried changing the loops in the render_calc_width function to use 
LAST_COL rather than COL_MAX but it didn't change the output.

Stefano, are you using any CC options in your environment I should know 
about that might change the way your binary compiled compared to mine?
Not really, I'm just building with 'make'. The resulting command line
is:

gcc ss.o ssfilter_check.o ssfilter.tab.o  -lselinux  -ltirpc  -lelf  -L/usr/lib64 -lcap  ../lib/libutil.a ../lib/libnetlink.a -lselinux  -ltirpc  -lelf  -L/usr/lib64 -lcap  -o ss

and that's with gcc 13.3.0.
quoted
It's not referenced explicitly but it's definitely used, see also commits:  
Yeah, I should have just left it as is but marked it as explicitly 
disabled.  I've put it back.

I really appreciate your help here Stefano, I'll send through an updated 
patch today using git send-email for the right formatting.  I'm new to 
submitting patches to the Kernel so all advice is greatly appreciated.
Except for the missing 'PATCH v2' in the subject line, and the missing
description of the changes compared to the first version (check mailing
list archives for examples), your patch submissions look good to me.

It would be nice if you could drop the HTML attachment from your other
emails to the list (it just adds bytes) and the Red Hat logo as well,
by now we all know how it looks like. :)

I still have to go through your second version in detail. I'm not
really sure what exact problem it's meant to fix, though, and it would
be helpful to know that in advance.

-- 
Stefano
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help