skip to content
==> src/net.c <==
1/* ui.rs's "network" and "7 net" sections, ported. */
2#include "mesh/top_ui_net.h"
3
4#include <string.h>
5
6static const char *human(mt_frame *f, double x) {
7    char b[64];
8    mt_human(x, b, sizeof b);
9    return tui_arena_strdup(f->a, b);
10}
11
12static const char *sh(mt_frame *f, double x) {
13    char b[64];
14    mt_short(x, b, sizeof b);
15    return tui_arena_strdup(f->a, b);
16}
17
18static const char *spark(mt_frame *f, const mt_net_track *n, size_t w) {
19    char *o = tui_arena_alloc(f->a, 3 * w + 1);
20    mt_spark(n->hist, n->nhist, w, o);
21    return o;
22}
23
24static const char *share_text(mt_frame *f, const mt_net_track *n) {
25    double x;
26    return mt_net_mesh_share(n, &x) ? MT_FMT(f, "%s%%", mt_ff(f, x * 100.0, 0)) : "-";
27}
28
29static tui_span dim_if_zero(double x, const char *s) { return mt_fgs(s, x > 0.0 ? MT_FG : MT_DIM); }
30
31size_t mt_net_cells(mt_frame *f, const mt_machine *m, bool wide, tui_table_cell *out) {
32    const mt_net_track *n = &m->net;
33    double rx = mt_nic_rx(&n->total), tx = mt_nic_tx(&n->total);
34    double x;
35    tui_span share = mt_net_mesh_share(n, &x) ? mt_fgs(MT_FMT(f, "%s%%", mt_ff(f, x * 100.0, 0)), MT_ACCENT) : mt_fgs("-", MT_FAINT);
36    size_t k = 0;
37    uint64_t age;
38    if (!mt_net_nic_age_ms(n, &age)) {
39        out[k++] = mt_cell_span(f, mt_fgs("no sample", MT_DIM));
40        out[k++] = mt_cell_str(f, "");
41    } else if (wide) {
42        out[k++] = mt_cell_span(f, dim_if_zero(rx, human(f, rx)));
43        out[k++] = mt_cell_span(f, dim_if_zero(tx, human(f, tx)));
44    } else {
45        out[k++] = mt_cell_str(f, MT_FMT(f, "%s %s", sh(f, rx), sh(f, tx)));
46    }
47    out[k++] = mt_cell_span(f, share);
48    if (wide) out[k++] = mt_cell_span(f, mt_fgs(spark(f, n, MT_OV_SPARK), MT_ACCENT));
49    return k;
50}
51
52/* A member's short name for the network rows (its JOIN hostname). */
53static const char *peer_name(mt_frame *f, const mt_snapshot *s, uint32_t k, size_t w) {
54    mt_member mb = mt_snapshot_member(s, k);
55    const char *h = *mb.host ? mb.host : MT_FMT(f, "system %u", k);
56    return mt_take(f, h, w);
57}
58
59/* `2.0k/1.9k` (out/in frames/s), `-` when both are 0. */
60static const char *frames_pair(mt_frame *f, double out, double inn) {
61    if (out == 0.0 && inn == 0.0) return "-";
62    return MT_FMT(f, "%s/%s", sh(f, out), sh(f, inn));
63}
64
65/* The part of an address before its last ':' (the whole when none). */
66static const char *addr_of(mt_frame *f, const char *local) {
67    const char *c = strrchr(local, ':');
68    return c ? tui_arena_strndup(f->a, local, (size_t)(c - local)) : tui_arena_strdup(f->a, local);
69}
70
71static tui_style bad_style(double x) { return tui_fg(x > 0.0 ? MT_BAD : MT_DIM); }
72
73static tui_line nic_line(mt_frame *f, bool wide, const char *name, const mt_nic_track *t, const tui_span *extra, size_t nextra) {
74    double rx = mt_nic_rx(t), tx = mt_nic_tx(t), pr, pt;
75    mt_nic_packets(t, &pr, &pt);
76    double e = mt_nic_errors(t), d = mt_nic_drops(t);
77    tui_line v;
78    if (wide)
79        v = MT_LINE(f, mt_fgs(mt_pad(f, name, 50, '<'), MT_FG),
80                    mt_fgb(MT_FMT(f, "%s%s", mt_pad(f, human(f, rx), 12, '>'), mt_pad(f, human(f, tx), 12, '>')), MT_FG),
81                    mt_fgs(MT_FMT(f, "  %s", mt_pad(f, MT_FMT(f, "%s/%s", sh(f, pr), sh(f, pt)), 10, '>')), MT_DIM),
82                    mt_styled(MT_FMT(f, " pkt/s  err %s/s", sh(f, e)), bad_style(e)),
83                    mt_styled(MT_FMT(f, " drop %s/s ", sh(f, d)), bad_style(d)));
84    else
85        v = MT_LINE(f, mt_fgs(mt_pad(f, mt_take(f, name, 11), 12, '<'), MT_FG),
86                    mt_fgb(MT_FMT(f, "↓%s↑%s", mt_pad(f, sh(f, rx), 6, '<'), mt_pad(f, sh(f, tx), 6, '<')), MT_FG),
87                    mt_fgs(MT_FMT(f, "%s/%sp ", sh(f, pr), sh(f, pt)), MT_DIM), mt_styled(MT_FMT(f, "e%s ", sh(f, e)), bad_style(e)),
88                    mt_styled(MT_FMT(f, "d%s ", sh(f, d)), bad_style(d)));
89    for (size_t i = 0; i < nextra; i++) tui_line_push(f->a, &v, extra[i]);
90    return v;
91}
92
93void mt_net_lines(mt_frame *f, const mt_machine *m, const mt_snapshot *s, uint16_t width, bool compact, mt_lines *out) {
94    size_t w = width;
95    bool wide = w >= 100 && !compact;
96    const mt_net_track *n = &m->net;
97    uint32_t me;
98    bool has_me = mt_snapshot_system(s, &me);
99    if (wide && has_me) {
100        const char *h = MT_FMT(f, "%s%s%s%s  %s%s%s%s%s", mt_pad(f, "link", 20, '<'), mt_pad(f, "interface  local address", 30, '<'),
101                               mt_pad(f, "rx", 12, '>'), mt_pad(f, "tx", 12, '>'), mt_pad(f, "round o/i", 10, '>'),
102                               mt_pad(f, "place o/i", 10, '>'), mt_pad(f, "load", 8, '>'), mt_pad(f, "ctl", 8, '>'), mt_pad(f, "RTT", 8, '>'));
103        mt_lines_push(f, out, tui_line_span(f->a, mt_label(h)));
104    }
105    if (has_me) {
106        uint32_t ns = mt_snapshot_systems(s);
107        for (uint32_t k = 0; k < ns; k++) {
108            if (k == me) continue;
109            mt_member mb = mt_snapshot_member(s, k);
110            mt_link_stat l = mt_snapshot_link(s, k);
111            const mt_link_track *t = mt_net_link(n, k);
112            if (!t) continue;
113            double rx = mt_meter_per_s(&t->rx), tx = mt_meter_per_s(&t->tx);
114            double fr[MESH_NET_KINDS][2];
115            for (size_t i = 0; i < MESH_NET_KINDS; i++) {
116                double r, x;
117                mt_link_frames_per_s(t, i, &r, &x);
118                fr[i][0] = x;
119                fr[i][1] = r;
120            }
121            bool up = mb.state == MESH_MS_UP;
122            tui_style name_st = tui_fg_bold(up ? mt_sys_color(k) : MT_BAD);
123            const char *rtt = up && mb.rtt_us > 0.0f ? mt_fmt_us(f, mb.rtt_us) : "-";
124            const char *iface = *l.iface ? l.iface : "-";
125            const char *addr = addr_of(f, l.local);
126            const char *arrow = MT_FMT(f, "→%u ", k);
127            if (wide) {
128                mt_lines_push(f, out,
129                              MT_LINE(f, mt_styled(arrow, name_st), mt_fgs(mt_pad(f, peer_name(f, s, k, 16), 17, '<'), MT_FG),
130                                      mt_fgs(mt_pad(f, mt_take(f, MT_FMT(f, "%s %s", iface, addr), 29), 30, '<'), MT_DIM),
131                                      mt_fgb(MT_FMT(f, "%s%s", mt_pad(f, human(f, rx), 12, '>'), mt_pad(f, human(f, tx), 12, '>')), MT_FG),
132                                      mt_fgs(MT_FMT(f, "  %s%s", mt_pad(f, frames_pair(f, fr[0][0], fr[0][1]), 10, '>'),
133                                                    mt_pad(f, frames_pair(f, fr[1][0], fr[1][1]), 10, '>')),
134                                             MT_WARN),
135                                      mt_fgs(MT_FMT(f, "%s%s", mt_pad(f, frames_pair(f, fr[2][0], fr[2][1]), 8, '>'),
136                                                    mt_pad(f, frames_pair(f, fr[3][0], fr[3][1]), 8, '>')),
137                                             MT_DIM),
138                                      mt_fgs(mt_pad(f, rtt, 8, '>'), MT_ACCENT)));
139            } else if (compact) {
140                mt_lines_push(f, out,
141                              MT_LINE(f, mt_styled(arrow, name_st), mt_fgs(mt_pad(f, peer_name(f, s, k, 8), 9, '<'), MT_FG),
142                                      mt_fgb(MT_FMT(f, "↓%s↑%s", mt_pad(f, sh(f, rx), 6, '<'), mt_pad(f, sh(f, tx), 6, '<')), MT_FG),
143                                      mt_fgs(MT_FMT(f, "r%s", mt_pad(f, sh(f, fr[0][0]), 6, '<')), MT_WARN), mt_fgs(rtt, MT_ACCENT)));
144            } else {
145                mt_lines_push(f, out,
146                              MT_LINE(f, mt_styled(arrow, name_st), mt_fgs(mt_pad(f, peer_name(f, s, k, 15), 16, '<'), MT_FG),
147                                      mt_fgs(MT_FMT(f, "%s %s", iface, addr), MT_DIM)));
148                tui_line l2 = MT_LINE(f, tui_span_raw("   "),
149                                      mt_fgb(MT_FMT(f, "↓%s↑%s", mt_pad(f, sh(f, rx), 6, '<'), mt_pad(f, sh(f, tx), 6, '<')), MT_FG),
150                                      mt_fgs(MT_FMT(f, "r%s ", frames_pair(f, fr[0][0], fr[0][1])), MT_WARN));
151                static const char tag[4] = {0, 'p', 'l', 'c'};
152                for (size_t i = 1; i < 4; i++)
153                    tui_line_push(f->a, &l2, mt_fgs(MT_FMT(f, "%c%s ", tag[i], frames_pair(f, fr[i][0], fr[i][1])), MT_DIM));
154                tui_line_push(f->a, &l2, mt_fgs(rtt, MT_ACCENT));
155                mt_lines_push(f, out, l2);
156            }
157        }
158    }
159    /* The NICs that carry links, then the total. */
160    if (!compact) {
161        mt_nic nics[MESH_MAX_NICS];
162        size_t nn = mt_snapshot_nics(s, nics);
163        for (size_t i = 0; i < nn; i++) {
164            const mt_nic *x = &nics[i];
165            const char *name = wide ? MT_FMT(f, "%s (to %s)", x->name, mt_mask_list(f, x->links)) : tui_arena_strdup(f->a, x->name);
166            const mt_nic_track *t = mt_net_nic(n, x->name);
167            if (t && x->count > 0)
168                mt_lines_push(f, out, nic_line(f, wide, name, t, NULL, 0));
169            else
170                mt_lines_push(f, out, MT_LINE(f, mt_fgs(MT_FMT(f, "%s ", x->name), MT_FG), mt_fgs("gone", MT_BAD)));
171        }
172    }
173    mt_nic total = mt_snapshot_net_total(s);
174    const char *share = share_text(f, n);
175    uint64_t age;
176    if (!mt_net_nic_age_ms(n, &age)) {
177        mt_lines_push(f, out, tui_line_span(f->a, mt_fgs("NICs: no sample yet", MT_DIM)));
178    } else {
179        const char *name = wide ? MT_FMT(f, "total (%u interfaces)", total.count) : MT_FMT(f, "total(%u)", total.count);
180        /* Narrow: the mesh share is in the box's title. */
181        tui_span extra[2];
182        size_t ne = 0;
183        if (wide) extra[ne++] = mt_fgs(MT_FMT(f, "mesh %s", share), MT_ACCENT);
184        if (age > 3000) extra[ne++] = mt_fgb(MT_FMT(f, " sample %llus old", (unsigned long long)(age / 1000)), MT_BAD);
185        mt_lines_push(f, out, nic_line(f, wide, name, &n->total, extra, ne));
186    }
187    if (!compact) {
188        const mt_meter *c = n->ctl, *h = n->http;
189        mt_lines_push(f, out,
190                      MT_LINE(f, mt_label("ctl "),
191                              mt_fgs(MT_FMT(f, "↓%s ↑%s %s/s", sh(f, mt_meter_per_s(&c[0])), sh(f, mt_meter_per_s(&c[1])), sh(f, mt_meter_per_s(&c[2]))), MT_FG),
192                              mt_label(wide ? " requests   http relay " : "  http "),
193                              mt_fgs(MT_FMT(f, "↓%s ↑%s %s/s", sh(f, mt_meter_per_s(&h[0])), sh(f, mt_meter_per_s(&h[1])), sh(f, mt_meter_per_s(&h[2]))), MT_FG),
194                              mt_label(wide ? " connections" : "")));
195    }
196}
197
198void mt_draw_net_box(mt_frame *f, tui_rect area, const mt_machine *m, const mt_lines *lines) {
199    if (area.height == 0 || area.width == 0) return;
200    const mt_net_track *n = &m->net;
201    const char *share = share_text(f, n);
202    tui_line title = MT_LINE(f, mt_fgb(" Network ", MT_FG),
203                             mt_label(MT_FMT(f, "↓%s ↑%s B/s ", sh(f, mt_nic_rx(&n->total)), sh(f, mt_nic_tx(&n->total)))),
204                             mt_fgs(MT_FMT(f, "mesh %s ", share), MT_ACCENT));
205    if (area.width >= 60) tui_line_push(f->a, &title, mt_label("frames/s out/in "));
206    tui_block b = tui_block_bordered();
207    b.set = &TUI_BORDER_ROUNDED;
208    b.border_style = tui_fg(MT_FAINT);
209    tui_block_add_title(f->a, &b, title, TUI_TITLE_DEFAULT);
210    tui_rect inner = tui_block_inner(&b, area);
211    mt_block(f, &b, area);
212    mt_par(f, mt_text(f, lines), inner);
213}
214
215/* System k's hostname from its own snapshot (13 characters at most). */
216static const char *sys_host(mt_frame *f, const mt_machine *const *by_sys, uint32_t k) {
217    return by_sys[k] ? mt_take(f, mt_name(f, by_sys[k]), 13) : "";
218}
219
220typedef struct {
221    bool has_tx, has_rx;
222    double tx, rx;
223} dir_rates;
224
225/* Directed link k -> j: (sender's tx, receiver's rx), bytes/s. */
226static dir_rates dir(const mt_machine *const *by_sys, uint32_t k, uint32_t j) {
227    dir_rates d = {0};
228    const mt_link_track *t;
229    if (by_sys[k] && (t = mt_net_link(&by_sys[k]->net, j))) {
230        d.has_tx = true;
231        d.tx = mt_meter_per_s(&t->tx);
232    }
233    if (by_sys[j] && (t = mt_net_link(&by_sys[j]->net, k))) {
234        d.has_rx = true;
235        d.rx = mt_meter_per_s(&t->rx);
236    }
237    return d;
238}
239
240static void table_in(mt_frame *f, const mt_rows *rows, tui_table_row *hdr, const tui_constraint *w, size_t nw, tui_line title, tui_rect area) {
241    tui_block b = mt_panel(f, title, MT_FAINT);
242    tui_table t = tui_table_new(rows->rows, rows->n, w, nw);
243    t.header = hdr;
244    t.column_spacing = 1;
245    t.block = &b;
246    mt_table(f, &t, area);
247}
248
249void mt_draw_net(mt_frame *f, tui_rect area, const mt_app *app) {
250    uint32_t n = mt_app_cluster_size(app);
251    const mt_machine **by_sys = tui_arena_alloc(f->a, (n ? n : 1) * sizeof *by_sys);
252    for (uint32_t k = 0; k < n; k++)
253        for (size_t i = 0; i < app->nmachines; i++) {
254            uint32_t sk;
255            if (mt_machine_system(&app->machines[i], &sk) && sk == k) {
256                by_sys[k] = &app->machines[i];
257                break;
258            }
259        }
260    bool clustered = false;
261    for (size_t i = 0; i < app->nmachines; i++) {
262        uint32_t sk;
263        clustered |= mt_machine_system(&app->machines[i], &sk);
264    }
265    uint16_t links_h = clustered ? (uint16_t)(n * (n ? n - 1 : 0) + 3) : 0;
266    uint16_t matrix_h = clustered ? (uint16_t)(n + 4) : 0;
267    size_t nic_rows = 0;
268    for (size_t i = 0; i < app->nmachines; i++) {
269        const mt_machine *m = &app->machines[i];
270        if (m->has_snap) {
271            mt_nic tmp[MESH_MAX_NICS];
272            nic_rows += mt_snapshot_nics(&m->snap, tmp) + 2;
273        } else {
274            nic_rows += 1;
275        }
276    }
277    tui_rect r[3];
278    tui_constraint cons[3] = {TUI_LENGTH(matrix_h), TUI_LENGTH(links_h), TUI_MIN(nic_rows < 3 ? nic_rows : 3)};
279    tui_vsplit(area, 3, cons, r);
280    tui_rect mx = r[0], links = r[1], nics = r[2];
281    if (clustered && mx.height > 0) {
282        tui_table_row header = tui_table_row_new();
283        tui_table_row_push(f->a, &header, mt_cell_span(f, mt_label("from \\ to")));
284        for (uint32_t j = 0; j < n; j++)
285            tui_table_row_push(f->a, &header, mt_cell_span(f, mt_fgb(MT_FMT(f, "%u %s", j, sys_host(f, by_sys, j)), mt_sys_color(j))));
286        tui_table_row_push(f->a, &header, mt_cell_span(f, mt_label("sent, all links")));
287        mt_rows rows = {0};
288        for (uint32_t k = 0; k < n; k++) {
289            tui_table_row row = tui_table_row_new();
290            tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgb(MT_FMT(f, "%u %s", k, sys_host(f, by_sys, k)), mt_sys_color(k))));
291            double sum = 0.0;
292            for (uint32_t j = 0; j < n; j++) {
293                if (j == k) {
294                    tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs("·", MT_FAINT)));
295                    continue;
296                }
297                dir_rates d = dir(by_sys, k, j);
298                sum += d.has_tx ? d.tx : 0.0;
299                const char *t = d.has_tx ? sh(f, d.tx) : "?";
300                const char *rr = d.has_rx ? sh(f, d.rx) : "?";
301                tui_table_row_push(f->a, &row,
302                                   mt_cell_line(f, MT_LINE(f, mt_fgb(mt_pad(f, t, 6, '>'), MT_FG), mt_fgs(MT_FMT(f, " / %s", mt_pad(f, rr, 6, '<')), MT_DIM))));
303            }
304            tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(human(f, sum), MT_ACCENT)));
305            mt_rows_push(f, &rows, row);
306        }
307        tui_constraint *w = tui_arena_alloc(f->a, (n + 2) * sizeof *w);
308        w[0] = TUI_LENGTH(16);
309        for (uint32_t j = 0; j < n; j++) w[1 + j] = TUI_LENGTH(16);
310        w[n + 1] = TUI_FILL(1);
311        tui_line title = MT_LINE(f, mt_fgb(" link matrix ", MT_FG),
312                                 mt_label("bytes/s: sent (the sender's count) / received (the receiver's count) "));
313        table_in(f, &rows, &header, w, n + 2, title, mx);
314    }
315    if (clustered && links.height > 0) {
316        static const char *const header[9] = {"link", "via (sender)", "sent", "received", "round/s", "place/s", "load/s", "ctl/s", "RTT"};
317        mt_rows rows = {0};
318        for (uint32_t k = 0; k < n; k++)
319            for (uint32_t j = 0; j < n; j++) {
320                if (j == k) continue;
321                dir_rates d = dir(by_sys, k, j);
322                const mt_machine *sender = by_sys[k];
323                const mt_link_track *t = sender ? mt_net_link(&sender->net, j) : NULL;
324                const mt_snapshot *st = sender && sender->has_snap ? &sender->snap : NULL;
325                const char *via = "-", *rtt = "-";
326                if (st) {
327                    mt_link_stat l = mt_snapshot_link(st, j);
328                    via = MT_FMT(f, "%s %s", l.iface, addr_of(f, l.local));
329                    float r = mt_snapshot_member(st, j).rtt_us;
330                    if (r > 0.0f) rtt = mt_fmt_us(f, r);
331                }
332#define FR(i) (t ? sh(f, mt_meter_per_s(&t->tx_frames[i])) : "?")
333                tui_table_cell c[9] = {
334                    mt_cell_line(f, MT_LINE(f, mt_fgb(MT_FMT(f, "%u", k), mt_sys_color(k)), mt_label(" → "), mt_fgb(MT_FMT(f, "%u", j), mt_sys_color(j)))),
335                    mt_cell_span(f, mt_fgs(via, MT_DIM)),
336                    mt_cell_span(f, mt_fgb(d.has_tx ? human(f, d.tx) : "?", MT_FG)),
337                    mt_cell_span(f, mt_fgs(d.has_rx ? human(f, d.rx) : "?", MT_DIM)),
338                    mt_cell_span(f, mt_fgs(FR(MESH_KIND_ROUND), MT_WARN)),
339                    mt_cell_str(f, FR(MESH_KIND_PLACE)),
340                    mt_cell_str(f, FR(MESH_KIND_LOAD)),
341                    mt_cell_str(f, FR(MESH_KIND_CTL)),
342                    mt_cell_span(f, mt_fgs(rtt, MT_ACCENT)),
343                };
344#undef FR
345                mt_rows_push(f, &rows, mt_row(f, c, 9));
346            }
347        static const tui_constraint w[9] = {
348            {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 32, 0}, {TUI_C_LENGTH, 11, 0}, {TUI_C_LENGTH, 11, 0}, {TUI_C_LENGTH, 8, 0},
349            {TUI_C_LENGTH, 8, 0}, {TUI_C_LENGTH, 7, 0},  {TUI_C_LENGTH, 6, 0},  {TUI_C_FILL, 1, 0},
350        };
351        tui_table_row hdr = mt_header(f, header, 9);
352        table_in(f, &rows, &hdr, w, 9, MT_LINE(f, mt_fgb(" links ", MT_FG), mt_label("frames/s as sent ")), links);
353    }
354    if (nics.height == 0) return;
355    static const char *const header[10] = {"machine", "interface", "to sys", "rx", "tx", "pkt/s in/out", "err/s", "drop/s", "mesh", "rx+tx, 1 s/col"};
356    mt_rows rows = {0};
357    for (size_t i = 0; i < app->nmachines; i++) {
358        const mt_machine *m = &app->machines[i];
359        tui_span name = mt_fgb(mt_name(f, m), MT_MACHINE[i % 8]);
360        if (!m->has_snap) {
361            tui_table_cell c[2] = {mt_cell_span(f, name), mt_cell_span(f, mt_fgs(m->status, MT_BAD))};
362            mt_rows_push(f, &rows, mt_row(f, c, 2));
363            continue;
364        }
365        bool first = true;
366        mt_nic xs[MESH_MAX_NICS];
367        size_t nx = mt_snapshot_nics(&m->snap, xs);
368        for (size_t q = 0; q <= nx; q++) {
369            const char *iface, *lk;
370            const mt_nic_track *t;
371            if (q < nx) {
372                iface = tui_arena_strdup(f->a, xs[q].name);
373                lk = mt_mask_list(f, xs[q].links);
374                t = xs[q].count > 0 ? mt_net_nic(&m->net, xs[q].name) : NULL;
375            } else {
376                iface = MT_FMT(f, "total (%u)", mt_snapshot_net_total(&m->snap).count);
377                lk = "";
378                t = &m->net.total;
379            }
380            tui_table_row row = tui_table_row_new();
381            tui_table_row_push(f->a, &row, mt_cell_span(f, first ? name : tui_span_raw("")));
382            first = false;
383            tui_table_row_push(f->a, &row, mt_cell_str(f, iface));
384            tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(lk, MT_DIM)));
385            if (t) {
386                double pr, pt;
387                mt_nic_packets(t, &pr, &pt);
388                double e = mt_nic_errors(t), d = mt_nic_drops(t);
389                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgb(human(f, mt_nic_rx(t)), MT_FG)));
390                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgb(human(f, mt_nic_tx(t)), MT_FG)));
391                tui_table_row_push(f->a, &row, mt_cell_str(f, MT_FMT(f, "%s/%s", sh(f, pr), sh(f, pt))));
392                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_styled(sh(f, e), bad_style(e))));
393                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_styled(sh(f, d), bad_style(d))));
394            } else {
395                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs("gone", MT_BAD)));
396            }
397            if (q == nx) {
398                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(share_text(f, &m->net), MT_ACCENT)));
399                tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(spark(f, &m->net, 30), MT_ACCENT)));
400            }
401            mt_rows_push(f, &rows, row);
402        }
403        const mt_meter *c = m->net.ctl, *h = m->net.http;
404        tui_table_cell cc[6] = {
405            mt_cell_str(f, ""),
406            mt_cell_span(f, mt_label("ctl / http")),
407            mt_cell_str(f, ""),
408            mt_cell_span(f, mt_fgs(MT_FMT(f, "%s / %s", sh(f, mt_meter_per_s(&c[0])), sh(f, mt_meter_per_s(&h[0]))), MT_DIM)),
409            mt_cell_span(f, mt_fgs(MT_FMT(f, "%s / %s", sh(f, mt_meter_per_s(&c[1])), sh(f, mt_meter_per_s(&h[1]))), MT_DIM)),
410            mt_cell_span(f, mt_fgs(MT_FMT(f, "%s req, %s conn/s", sh(f, mt_meter_per_s(&c[2])), sh(f, mt_meter_per_s(&h[2]))), MT_DIM)),
411        };
412        mt_rows_push(f, &rows, mt_row(f, cc, 6));
413    }
414    static const tui_constraint w[10] = {
415        {TUI_C_LENGTH, 16, 0}, {TUI_C_LENGTH, 16, 0}, {TUI_C_LENGTH, 6, 0}, {TUI_C_LENGTH, 11, 0}, {TUI_C_LENGTH, 11, 0},
416        {TUI_C_LENGTH, 19, 0}, {TUI_C_LENGTH, 6, 0},  {TUI_C_LENGTH, 6, 0}, {TUI_C_LENGTH, 5, 0},  {TUI_C_FILL, 1, 0},
417    };
418    tui_table_row hdr = mt_header(f, header, 10);
419    tui_line title = MT_LINE(f, mt_fgb(" interfaces ", MT_FG),
420                             mt_label("the ones carrying peer links, and each machine's total (no loopback, container, VM or tunnel interfaces) "));
421    table_in(f, &rows, &hdr, w, 10, title, nics);
422}