]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/tcpsso/tcpsso.c
genl: add a monitor subcommand
[FreeBSD/FreeBSD.git] / usr.sbin / tcpsso / tcpsso.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Michael Tuexen <tuexen@FreeBSD.org>
5  * Copyright (c) 2009 Juli Mallett <jmallett@FreeBSD.org>
6  * Copyright (c) 2004 Markus Friedl <markus@openbsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/socketvar.h>
34 #include <sys/sysctl.h>
35
36 #include <netinet/in.h>
37 #include <netinet/in_pcb.h>
38 #define TCPSTATES
39 #include <netinet/tcp_fsm.h>
40 #include <netinet/tcp_var.h>
41
42 #include <err.h>
43 #include <errno.h>
44 #include <inttypes.h>
45 #include <stdbool.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50
51 static struct xinpgen *
52 getxpcblist(const char *name)
53 {
54         struct xinpgen *xinp;
55         size_t len;
56         int rv;
57
58         len = 0;
59         rv = sysctlbyname(name, NULL, &len, NULL, 0);
60         if (rv == -1)
61                 err(1, "sysctlbyname %s", name);
62
63         if (len == 0)
64                 errx(1, "%s is empty", name);
65
66         xinp = malloc(len);
67         if (xinp == NULL)
68                 errx(1, "malloc failed");
69
70         rv = sysctlbyname(name, xinp, &len, NULL, 0);
71         if (rv == -1)
72                 err(1, "sysctlbyname %s", name);
73
74         return (xinp);
75 }
76
77 static bool
78 tcpsso(uint64_t id, struct sockopt_parameters *params, size_t optlen)
79 {
80         int rv;
81
82         params->sop_id = id;
83         rv = sysctlbyname("net.inet.tcp.setsockopt", NULL, NULL, params,
84             sizeof(struct sockopt_parameters) + optlen);
85         if (rv == -1) {
86                 warn("Failed for id %" PRIu64, params->sop_id);
87                 return (false);
88         } else
89                 return (true);
90 }
91
92 static bool
93 tcpssoall(const char *ca_name, const char *stack, int state,
94     struct sockopt_parameters *params, size_t optlen)
95 {
96         struct xinpgen *head, *xinp;
97         struct xtcpcb *xtp;
98         struct xinpcb *xip;
99         bool ok;
100
101         ok = true;
102
103         head = getxpcblist("net.inet.tcp.pcblist");
104
105 #define XINP_NEXT(xinp)                                                 \
106         ((struct xinpgen *)(uintptr_t)((uintptr_t)(xinp) + (xinp)->xig_len))
107
108         for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp;
109             xinp = XINP_NEXT(xinp)) {
110                 xtp = (struct xtcpcb *)xinp;
111                 xip = &xtp->xt_inp;
112
113                 /* Ignore PCBs which were freed during copyout. */
114                 if (xip->inp_gencnt > head->xig_gen)
115                         continue;
116
117
118                 /* Skip endpoints in TIME WAIT. */
119                 if (xtp->t_state == TCPS_TIME_WAIT)
120                         continue;
121
122                 /* If requested, skip sockets not having the requested state. */
123                 if ((state != -1) && (xtp->t_state != state))
124                         continue;
125
126                 /*
127                  * If requested, skip sockets not having the requested
128                  * congestion control algorithm.
129                  */
130                 if (ca_name[0] != '\0' &&
131                     strncmp(xtp->xt_cc, ca_name, TCP_CA_NAME_MAX))
132                         continue;
133
134                 /* If requested, skip sockets not having the requested stack. */
135                 if (stack[0] != '\0' &&
136                     strncmp(xtp->xt_stack, stack, TCP_FUNCTION_NAME_LEN_MAX))
137                         continue;
138
139                 params->sop_inc = xip->inp_inc;
140                 if (!tcpsso(xip->inp_gencnt, params, optlen))
141                         ok = false;
142         }
143         free(head);
144
145         return (ok);
146 }
147
148 struct so_level {
149         int level;
150         const char *name;
151 };
152
153 #define level_entry(level) { level, #level }
154
155 static struct so_level so_levels[] = {
156         level_entry(SOL_SOCKET),
157         level_entry(IPPROTO_IP),
158         level_entry(IPPROTO_IPV6),
159         level_entry(IPPROTO_TCP),
160         { 0, NULL }
161 };
162
163 struct so_name {
164         int level;
165         int value;
166         const char *name;
167 };
168
169 #define sol_entry(name) { SOL_SOCKET, name, #name }
170 #define ip4_entry(name) { IPPROTO_IP, name, #name }
171 #define ip6_entry(name) { IPPROTO_IPV6, name, #name }
172 #define tcp_entry(name) { IPPROTO_TCP, name, #name }
173
174 static struct so_name so_names[] = {
175         /* SOL_SOCKET level socket options. */
176         sol_entry(SO_DEBUG),                    /* int */
177         sol_entry(SO_RCVBUF),                   /* int */
178         sol_entry(SO_SNDBUF),                   /* int */
179         sol_entry(SO_RCVLOWAT),                 /* int */
180         sol_entry(SO_SNDLOWAT),                 /* int */
181         /* IPPROTO_IP level socket options. */
182         ip4_entry(IP_TTL),                      /* int */
183         ip4_entry(IP_TOS),                      /* int */
184         /* IPPROTO_IPV6 level socket options. */
185         ip6_entry(IPV6_UNICAST_HOPS),           /* int */
186         ip6_entry(IPV6_TCLASS),                 /* int */
187         ip6_entry(IPV6_USE_MIN_MTU),            /* int */
188         /* IPPROTO_TCP level socket options. */
189         tcp_entry(TCP_NODELAY),                 /* int */
190         tcp_entry(TCP_NOOPT),                   /* int */
191         tcp_entry(TCP_NOPUSH),                  /* int */
192         tcp_entry(TCP_REMOTE_UDP_ENCAPS_PORT),  /* int */
193         tcp_entry(TCP_MAXSEG),                  /* int */
194         tcp_entry(TCP_TXTLS_MODE),              /* unsigned int */
195         tcp_entry(TCP_MAXUNACKTIME),            /* unsigned int */
196         tcp_entry(TCP_KEEPIDLE),                /* unsigned int */
197         tcp_entry(TCP_KEEPINTVL),               /* unsigned int */
198         tcp_entry(TCP_KEEPINIT),                /* unsigned int */
199         tcp_entry(TCP_KEEPCNT),                 /* unsigned int */
200         tcp_entry(TCP_PCAP_OUT),                /* int */
201         tcp_entry(TCP_PCAP_IN),                 /* int */
202         tcp_entry(TCP_LOG),                     /* int */
203         tcp_entry(TCP_LOGID),                   /* char * */
204         tcp_entry(TCP_LOGDUMP),                 /* char * */
205         tcp_entry(TCP_LOGDUMPID),               /* char * */
206         tcp_entry(TCP_CONGESTION),              /* char * */
207         tcp_entry(TCP_FUNCTION_BLK),            /* char * */
208         tcp_entry(TCP_NO_PRR),                  /* int */
209         tcp_entry(TCP_HDWR_RATE_CAP),           /* int */
210 #if notyet
211         tcp_entry(TCP_PACING_RATE_CAP),         /* uint64_t */
212 #endif
213         tcp_entry(TCP_HDWR_UP_ONLY),            /* int */
214         tcp_entry(TCP_FAST_RSM_HACK),           /* int */
215         tcp_entry(TCP_DELACK),                  /* int */
216         tcp_entry(TCP_REC_ABC_VAL),             /* int */
217         tcp_entry(TCP_USE_CMP_ACKS),            /* int */
218         tcp_entry(TCP_SHARED_CWND_TIME_LIMIT),  /* int */
219         tcp_entry(TCP_SHARED_CWND_ENABLE),      /* int */
220         tcp_entry(TCP_DATA_AFTER_CLOSE),        /* int */
221         tcp_entry(TCP_DEFER_OPTIONS),           /* int */
222         tcp_entry(TCP_MAXPEAKRATE),             /* int */
223         tcp_entry(TCP_TIMELY_DYN_ADJ),          /* int */
224         tcp_entry(TCP_RACK_TLP_REDUCE),         /* int */
225         tcp_entry(TCP_RACK_PACE_ALWAYS),        /* int */
226         tcp_entry(TCP_RACK_PACE_MAX_SEG),       /* int */
227         tcp_entry(TCP_RACK_FORCE_MSEG),         /* int */
228         tcp_entry(TCP_RACK_PACE_RATE_CA),       /* int */
229         tcp_entry(TCP_RACK_PACE_RATE_SS),       /* int */
230         tcp_entry(TCP_RACK_PACE_RATE_REC),      /* int */
231         tcp_entry(TCP_RACK_GP_INCREASE_CA),     /* int */
232         tcp_entry(TCP_RACK_GP_INCREASE_SS),     /* int */
233         tcp_entry(TCP_RACK_GP_INCREASE_REC),    /* int */
234         tcp_entry(TCP_RACK_RR_CONF),            /* int */
235         tcp_entry(TCP_RACK_PRR_SENDALOT),       /* int */
236         tcp_entry(TCP_RACK_MIN_TO),             /* int */
237         tcp_entry(TCP_RACK_EARLY_SEG),          /* int */
238         tcp_entry(TCP_RACK_REORD_THRESH),       /* int */
239         tcp_entry(TCP_RACK_REORD_FADE),         /* int */
240         tcp_entry(TCP_RACK_TLP_THRESH),         /* int */
241         tcp_entry(TCP_RACK_PKT_DELAY),          /* int */
242         tcp_entry(TCP_RACK_TLP_USE),            /* int */
243         tcp_entry(TCP_RACK_DO_DETECTION),       /* int */
244         tcp_entry(TCP_RACK_NONRXT_CFG_RATE),    /* int */
245         tcp_entry(TCP_RACK_MBUF_QUEUE),         /* int */
246         tcp_entry(TCP_RACK_NO_PUSH_AT_MAX),     /* int */
247         tcp_entry(TCP_RACK_PACE_TO_FILL),       /* int */
248         tcp_entry(TCP_RACK_PROFILE),            /* int */
249         tcp_entry(TCP_RACK_ABC_VAL),            /* int */
250         tcp_entry(TCP_RACK_MEASURE_CNT),        /* int */
251         tcp_entry(TCP_RACK_DSACK_OPT),          /* int */
252         tcp_entry(TCP_RACK_PACING_BETA),        /* int */
253         tcp_entry(TCP_RACK_PACING_BETA_ECN),    /* int */
254         tcp_entry(TCP_RACK_TIMER_SLOP),         /* int */
255         tcp_entry(TCP_RACK_ENABLE_HYSTART),     /* int */
256         tcp_entry(TCP_BBR_RACK_RTT_USE),        /* int */
257         tcp_entry(TCP_BBR_USE_RACK_RR),         /* int */
258         tcp_entry(TCP_BBR_HDWR_PACE),           /* int */
259         tcp_entry(TCP_BBR_RACK_INIT_RATE),      /* int */
260         tcp_entry(TCP_BBR_IWINTSO),             /* int */
261         tcp_entry(TCP_BBR_ALGORITHM),           /* int */
262         tcp_entry(TCP_BBR_TSLIMITS),            /* int */
263         tcp_entry(TCP_BBR_RECFORCE),            /* int */
264         tcp_entry(TCP_BBR_STARTUP_PG),          /* int */
265         tcp_entry(TCP_BBR_DRAIN_PG),            /* int */
266         tcp_entry(TCP_BBR_RWND_IS_APP),         /* int */
267         tcp_entry(TCP_BBR_PROBE_RTT_INT),       /* int */
268         tcp_entry(TCP_BBR_PROBE_RTT_GAIN),      /* int */
269         tcp_entry(TCP_BBR_PROBE_RTT_LEN),       /* int */
270         tcp_entry(TCP_BBR_STARTUP_LOSS_EXIT),   /* int */
271         tcp_entry(TCP_BBR_USEDEL_RATE),         /* int */
272         tcp_entry(TCP_BBR_MIN_RTO),             /* int */
273         tcp_entry(TCP_BBR_MAX_RTO),             /* int */
274         tcp_entry(TCP_BBR_PACE_PER_SEC),        /* int */
275         tcp_entry(TCP_BBR_PACE_DEL_TAR),        /* int */
276         tcp_entry(TCP_BBR_SEND_IWND_IN_TSO),    /* int */
277         tcp_entry(TCP_BBR_EXTRA_STATE),         /* int */
278         tcp_entry(TCP_BBR_UTTER_MAX_TSO),       /* int */
279         tcp_entry(TCP_BBR_MIN_TOPACEOUT),       /* int */
280         tcp_entry(TCP_BBR_FLOOR_MIN_TSO),       /* int */
281         tcp_entry(TCP_BBR_TSTMP_RAISES),        /* int */
282         tcp_entry(TCP_BBR_POLICER_DETECT),      /* int */
283         tcp_entry(TCP_BBR_USE_RACK_CHEAT),      /* int */
284         tcp_entry(TCP_BBR_PACE_SEG_MAX),        /* int */
285         tcp_entry(TCP_BBR_PACE_SEG_MIN),        /* int */
286         tcp_entry(TCP_BBR_PACE_CROSS),          /* int */
287         tcp_entry(TCP_BBR_PACE_OH),             /* int */
288         tcp_entry(TCP_BBR_TMR_PACE_OH),         /* int */
289         tcp_entry(TCP_BBR_RETRAN_WTSO),         /* int */
290         {0, 0, NULL}
291 };
292
293 static struct sockopt_parameters *
294 create_parameters(char *level_str, char *optname_str, char *optval_str,
295     size_t *optlen)
296 {
297         long long arg;
298         int i, level, optname, optval_int;
299         struct sockopt_parameters *params;
300         char *end;
301         bool optval_is_int;
302
303         /* Determine level, use IPPROTO_TCP as default. */
304         if (level_str == NULL)
305                 level = IPPROTO_TCP;
306         else {
307                 arg = strtoll(level_str, &end, 0);
308                 if (*end != '\0') {
309                         for (i = 0; so_levels[i].name != NULL; i++)
310                                 if (strcmp(level_str, so_levels[i].name) == 0) {
311                                         level = so_levels[i].level;
312                                         break;
313                                 }
314                         if (so_levels[i].name == NULL)
315                                 errx(1, "unsupported level %s", optname_str);
316                 } else {
317                         if (arg < 0)
318                                 errx(1, "level negative %s", optname_str);
319                         else if (arg > INT_MAX)
320                                 errx(1, "level too large %s", optname_str);
321                         else
322                                 level = (int)arg;
323                 }
324         }
325         /* Determine option name. */
326         if (optname_str == NULL || *optname_str == '\0')
327                 return (NULL);
328         arg = strtoll(optname_str, &end, 0);
329         if (*end != '\0') {
330                 for (i = 0; so_names[i].name != NULL; i++)
331                         if (strcmp(optname_str, so_names[i].name) == 0) {
332                                 level = so_names[i].level;
333                                 optname = so_names[i].value;
334                                 break;
335                         }
336                 if (so_names[i].name == NULL)
337                         errx(1, "unsupported option name %s", optname_str);
338         } else {
339                 if (arg < 0)
340                         errx(1, "option name negative %s", optname_str);
341                 else if (arg > INT_MAX)
342                         errx(1, "option name too large %s", optname_str);
343                 else
344                         optname = (int)arg;
345         }
346         /*
347          * Determine option value. Use int, if can be parsed as an int,
348          * else use a char *.
349          */
350         if (optval_str == NULL || *optval_str == '\0')
351                 return (NULL);
352         arg = strtol(optval_str, &end, 0);
353         optval_is_int = (*end == '\0');
354         if (optval_is_int) {
355                 if (arg < INT_MIN)
356                         errx(1, "option value too small %s", optval_str);
357                 else if (arg > INT_MAX)
358                         errx(1, "option value too large %s", optval_str);
359                 else
360                         optval_int = (int)arg;
361         }
362         switch (optname) {
363         case TCP_FUNCTION_BLK:
364                 *optlen = sizeof(struct tcp_function_set);
365                 break;
366         default:
367                 if (optval_is_int)
368                         *optlen = sizeof(int);
369                 else
370                         *optlen = strlen(optval_str) + 1;
371                 break;
372         }
373         /* Fill socket option parameters. */
374         params = malloc(sizeof(struct sockopt_parameters) + *optlen);
375         if (params == NULL)
376                 return (NULL);
377         memset(params, 0, sizeof(struct sockopt_parameters) + *optlen);
378         params->sop_level = level;
379         params->sop_optname = optname;
380         switch (optname) {
381         case TCP_FUNCTION_BLK:
382                 strlcpy(params->sop_optval, optval_str,
383                     TCP_FUNCTION_NAME_LEN_MAX);
384                 break;
385         default:
386                 if (optval_is_int)
387                         memcpy(params->sop_optval, &optval_int, *optlen);
388                 else
389                         memcpy(params->sop_optval, optval_str, *optlen);
390         }
391         return (params);
392 }
393
394 static void
395 usage(void)
396 {
397         fprintf(stderr,
398 "usage: tcpsso -i id [level] opt-name opt-value\n"
399 "       tcpsso -a [level] opt-name opt-value\n"
400 "       tcpsso -C cc-algo [-S stack] [-s state] [level] opt-name opt-value\n"
401 "       tcpsso [-C cc-algo] -S stack [-s state] [level] opt-name opt-value\n"
402 "       tcpsso [-C cc-algo] [-S stack] -s state [level] opt-name opt-value\n");
403         exit(1);
404 }
405
406 int
407 main(int argc, char *argv[])
408 {
409         struct sockopt_parameters *params;
410         uint64_t id;
411         size_t optlen;
412         int ch, state;
413         char stack[TCP_FUNCTION_NAME_LEN_MAX];
414         char ca_name[TCP_CA_NAME_MAX];
415         bool ok, apply_all, apply_subset, apply_specific;
416
417         apply_all = false;
418         apply_subset = false;
419         apply_specific = false;
420         ca_name[0] = '\0';
421         stack[0] = '\0';
422         state = -1;
423         id = 0;
424
425         while ((ch = getopt(argc, argv, "aC:i:S:s:")) != -1) {
426                 switch (ch) {
427                 case 'a':
428                         apply_all = true;
429                         break;
430                 case 'C':
431                         apply_subset = true;
432                         strlcpy(ca_name, optarg, sizeof(ca_name));
433                         break;
434                 case 'i':
435                         apply_specific = true;
436                         id = strtoull(optarg, NULL, 0);
437                         break;
438                 case 'S':
439                         apply_subset = true;
440                         strlcpy(stack, optarg, sizeof(stack));
441                         break;
442                 case 's':
443                         apply_subset = true;
444                         for (state = 0; state < TCP_NSTATES; state++) {
445                                 if (strcmp(tcpstates[state], optarg) == 0)
446                                         break;
447                         }
448                         break;
449                 default:
450                         usage();
451                 }
452         }
453         argc -= optind;
454         argv += optind;
455         if ((state == TCP_NSTATES) ||
456             (state == TCPS_TIME_WAIT) ||
457             (argc < 2) || (argc > 3) ||
458             (apply_all && apply_subset) ||
459             (apply_all && apply_specific) ||
460             (apply_subset && apply_specific) ||
461             !(apply_all || apply_subset || apply_specific))
462                 usage();
463         if (argc == 2)
464                 params = create_parameters(NULL, argv[0], argv[1], &optlen);
465         else
466                 params = create_parameters(argv[0], argv[1], argv[2], &optlen);
467         if (params != NULL) {
468                 if (apply_specific)
469                         ok = tcpsso(id, params, optlen);
470                 else
471                         ok = tcpssoall(ca_name, stack, state, params, optlen);
472                 free(params);
473         } else
474                 ok = false;
475         return (ok ? 0 : 1);
476 }