xref: /freebsd-src/usr.sbin/tcpsso/tcpsso.c (revision 6e25bccb989c3fdea550833484f262d47910a249)
1881631a2SMichael Tuexen /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3881631a2SMichael Tuexen  *
4881631a2SMichael Tuexen  * Copyright (c) 2022 Michael Tuexen <tuexen@FreeBSD.org>
5881631a2SMichael Tuexen  * Copyright (c) 2009 Juli Mallett <jmallett@FreeBSD.org>
6881631a2SMichael Tuexen  * Copyright (c) 2004 Markus Friedl <markus@openbsd.org>
7881631a2SMichael Tuexen  *
8881631a2SMichael Tuexen  * Redistribution and use in source and binary forms, with or without
9881631a2SMichael Tuexen  * modification, are permitted provided that the following conditions
10881631a2SMichael Tuexen  * are met:
11881631a2SMichael Tuexen  * 1. Redistributions of source code must retain the above copyright
12881631a2SMichael Tuexen  *    notice, this list of conditions and the following disclaimer.
13881631a2SMichael Tuexen  * 2. Redistributions in binary form must reproduce the above copyright
14881631a2SMichael Tuexen  *    notice, this list of conditions and the following disclaimer in the
15881631a2SMichael Tuexen  *    documentation and/or other materials provided with the distribution.
16881631a2SMichael Tuexen  *
17881631a2SMichael Tuexen  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18881631a2SMichael Tuexen  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19881631a2SMichael Tuexen  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20881631a2SMichael Tuexen  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21881631a2SMichael Tuexen  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22881631a2SMichael Tuexen  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23881631a2SMichael Tuexen  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24881631a2SMichael Tuexen  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25881631a2SMichael Tuexen  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26881631a2SMichael Tuexen  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27881631a2SMichael Tuexen  * SUCH DAMAGE.
28881631a2SMichael Tuexen  */
29881631a2SMichael Tuexen 
30881631a2SMichael Tuexen #include <sys/param.h>
31881631a2SMichael Tuexen #include <sys/types.h>
32881631a2SMichael Tuexen #include <sys/socket.h>
33881631a2SMichael Tuexen #include <sys/socketvar.h>
34881631a2SMichael Tuexen #include <sys/sysctl.h>
35881631a2SMichael Tuexen 
36881631a2SMichael Tuexen #include <netinet/in.h>
37881631a2SMichael Tuexen #include <netinet/in_pcb.h>
38881631a2SMichael Tuexen #define TCPSTATES
39881631a2SMichael Tuexen #include <netinet/tcp_fsm.h>
40881631a2SMichael Tuexen #include <netinet/tcp_var.h>
41881631a2SMichael Tuexen 
42881631a2SMichael Tuexen #include <err.h>
43881631a2SMichael Tuexen #include <errno.h>
44881631a2SMichael Tuexen #include <inttypes.h>
45881631a2SMichael Tuexen #include <stdbool.h>
46881631a2SMichael Tuexen #include <stdio.h>
47881631a2SMichael Tuexen #include <stdlib.h>
48881631a2SMichael Tuexen #include <string.h>
49881631a2SMichael Tuexen #include <unistd.h>
50881631a2SMichael Tuexen 
51881631a2SMichael Tuexen static struct xinpgen *
52881631a2SMichael Tuexen getxpcblist(const char *name)
53881631a2SMichael Tuexen {
54881631a2SMichael Tuexen 	struct xinpgen *xinp;
55881631a2SMichael Tuexen 	size_t len;
56881631a2SMichael Tuexen 	int rv;
57881631a2SMichael Tuexen 
58881631a2SMichael Tuexen 	len = 0;
59881631a2SMichael Tuexen 	rv = sysctlbyname(name, NULL, &len, NULL, 0);
60881631a2SMichael Tuexen 	if (rv == -1)
61881631a2SMichael Tuexen 		err(1, "sysctlbyname %s", name);
62881631a2SMichael Tuexen 
63881631a2SMichael Tuexen 	if (len == 0)
64881631a2SMichael Tuexen 		errx(1, "%s is empty", name);
65881631a2SMichael Tuexen 
66881631a2SMichael Tuexen 	xinp = malloc(len);
67881631a2SMichael Tuexen 	if (xinp == NULL)
68881631a2SMichael Tuexen 		errx(1, "malloc failed");
69881631a2SMichael Tuexen 
70881631a2SMichael Tuexen 	rv = sysctlbyname(name, xinp, &len, NULL, 0);
71881631a2SMichael Tuexen 	if (rv == -1)
72881631a2SMichael Tuexen 		err(1, "sysctlbyname %s", name);
73881631a2SMichael Tuexen 
74881631a2SMichael Tuexen 	return (xinp);
75881631a2SMichael Tuexen }
76881631a2SMichael Tuexen 
77881631a2SMichael Tuexen static bool
78881631a2SMichael Tuexen tcpsso(uint64_t id, struct sockopt_parameters *params, size_t optlen)
79881631a2SMichael Tuexen {
80881631a2SMichael Tuexen 	int rv;
81881631a2SMichael Tuexen 
82881631a2SMichael Tuexen 	params->sop_id = id;
83881631a2SMichael Tuexen 	rv = sysctlbyname("net.inet.tcp.setsockopt", NULL, NULL, params,
84881631a2SMichael Tuexen 	    sizeof(struct sockopt_parameters) + optlen);
85881631a2SMichael Tuexen 	if (rv == -1) {
86881631a2SMichael Tuexen 		warn("Failed for id %" PRIu64, params->sop_id);
87881631a2SMichael Tuexen 		return (false);
88881631a2SMichael Tuexen 	} else
89881631a2SMichael Tuexen 		return (true);
90881631a2SMichael Tuexen }
91881631a2SMichael Tuexen 
92881631a2SMichael Tuexen static bool
93881631a2SMichael Tuexen tcpssoall(const char *ca_name, const char *stack, int state,
94881631a2SMichael Tuexen     struct sockopt_parameters *params, size_t optlen)
95881631a2SMichael Tuexen {
96881631a2SMichael Tuexen 	struct xinpgen *head, *xinp;
97881631a2SMichael Tuexen 	struct xtcpcb *xtp;
98881631a2SMichael Tuexen 	struct xinpcb *xip;
99881631a2SMichael Tuexen 	bool ok;
100881631a2SMichael Tuexen 
101881631a2SMichael Tuexen 	ok = true;
102881631a2SMichael Tuexen 
103881631a2SMichael Tuexen 	head = getxpcblist("net.inet.tcp.pcblist");
104881631a2SMichael Tuexen 
105881631a2SMichael Tuexen #define	XINP_NEXT(xinp)							\
106881631a2SMichael Tuexen 	((struct xinpgen *)(uintptr_t)((uintptr_t)(xinp) + (xinp)->xig_len))
107881631a2SMichael Tuexen 
108881631a2SMichael Tuexen 	for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp;
109881631a2SMichael Tuexen 	    xinp = XINP_NEXT(xinp)) {
110881631a2SMichael Tuexen 		xtp = (struct xtcpcb *)xinp;
111881631a2SMichael Tuexen 		xip = &xtp->xt_inp;
112881631a2SMichael Tuexen 
113881631a2SMichael Tuexen 		/* Ignore PCBs which were freed during copyout. */
114881631a2SMichael Tuexen 		if (xip->inp_gencnt > head->xig_gen)
115881631a2SMichael Tuexen 			continue;
116881631a2SMichael Tuexen 
117881631a2SMichael Tuexen 
118881631a2SMichael Tuexen 		/* If requested, skip sockets not having the requested state. */
119881631a2SMichael Tuexen 		if ((state != -1) && (xtp->t_state != state))
120881631a2SMichael Tuexen 			continue;
121881631a2SMichael Tuexen 
122881631a2SMichael Tuexen 		/*
123881631a2SMichael Tuexen 		 * If requested, skip sockets not having the requested
124881631a2SMichael Tuexen 		 * congestion control algorithm.
125881631a2SMichael Tuexen 		 */
126881631a2SMichael Tuexen 		if (ca_name[0] != '\0' &&
127881631a2SMichael Tuexen 		    strncmp(xtp->xt_cc, ca_name, TCP_CA_NAME_MAX))
128881631a2SMichael Tuexen 			continue;
129881631a2SMichael Tuexen 
130881631a2SMichael Tuexen 		/* If requested, skip sockets not having the requested stack. */
131881631a2SMichael Tuexen 		if (stack[0] != '\0' &&
132881631a2SMichael Tuexen 		    strncmp(xtp->xt_stack, stack, TCP_FUNCTION_NAME_LEN_MAX))
133881631a2SMichael Tuexen 			continue;
134881631a2SMichael Tuexen 
135881631a2SMichael Tuexen 		params->sop_inc = xip->inp_inc;
136881631a2SMichael Tuexen 		if (!tcpsso(xip->inp_gencnt, params, optlen))
137881631a2SMichael Tuexen 			ok = false;
138881631a2SMichael Tuexen 	}
139881631a2SMichael Tuexen 	free(head);
140881631a2SMichael Tuexen 
141881631a2SMichael Tuexen 	return (ok);
142881631a2SMichael Tuexen }
143881631a2SMichael Tuexen 
144881631a2SMichael Tuexen struct so_level {
145881631a2SMichael Tuexen 	int level;
146881631a2SMichael Tuexen 	const char *name;
147881631a2SMichael Tuexen };
148881631a2SMichael Tuexen 
149881631a2SMichael Tuexen #define level_entry(level) { level, #level }
150881631a2SMichael Tuexen 
151881631a2SMichael Tuexen static struct so_level so_levels[] = {
152881631a2SMichael Tuexen 	level_entry(SOL_SOCKET),
153881631a2SMichael Tuexen 	level_entry(IPPROTO_IP),
154881631a2SMichael Tuexen 	level_entry(IPPROTO_IPV6),
155881631a2SMichael Tuexen 	level_entry(IPPROTO_TCP),
156881631a2SMichael Tuexen 	{ 0, NULL }
157881631a2SMichael Tuexen };
158881631a2SMichael Tuexen 
159881631a2SMichael Tuexen struct so_name {
160881631a2SMichael Tuexen 	int level;
161881631a2SMichael Tuexen 	int value;
162881631a2SMichael Tuexen 	const char *name;
163881631a2SMichael Tuexen };
164881631a2SMichael Tuexen 
165881631a2SMichael Tuexen #define sol_entry(name) { SOL_SOCKET, name, #name }
166881631a2SMichael Tuexen #define ip4_entry(name) { IPPROTO_IP, name, #name }
167881631a2SMichael Tuexen #define ip6_entry(name) { IPPROTO_IPV6, name, #name }
168881631a2SMichael Tuexen #define tcp_entry(name) { IPPROTO_TCP, name, #name }
169881631a2SMichael Tuexen 
170881631a2SMichael Tuexen static struct so_name so_names[] = {
171881631a2SMichael Tuexen 	/* SOL_SOCKET level socket options. */
172881631a2SMichael Tuexen 	sol_entry(SO_DEBUG),			/* int */
173881631a2SMichael Tuexen 	sol_entry(SO_RCVBUF),			/* int */
174881631a2SMichael Tuexen 	sol_entry(SO_SNDBUF),			/* int */
175881631a2SMichael Tuexen 	sol_entry(SO_RCVLOWAT),			/* int */
176881631a2SMichael Tuexen 	sol_entry(SO_SNDLOWAT),			/* int */
177881631a2SMichael Tuexen 	/* IPPROTO_IP level socket options. */
178881631a2SMichael Tuexen 	ip4_entry(IP_TTL),			/* int */
179881631a2SMichael Tuexen 	ip4_entry(IP_TOS),			/* int */
180881631a2SMichael Tuexen 	/* IPPROTO_IPV6 level socket options. */
181881631a2SMichael Tuexen 	ip6_entry(IPV6_UNICAST_HOPS),		/* int */
182881631a2SMichael Tuexen 	ip6_entry(IPV6_TCLASS),			/* int */
183881631a2SMichael Tuexen 	ip6_entry(IPV6_USE_MIN_MTU),		/* int */
184881631a2SMichael Tuexen 	/* IPPROTO_TCP level socket options. */
185881631a2SMichael Tuexen 	tcp_entry(TCP_NODELAY),			/* int */
186881631a2SMichael Tuexen 	tcp_entry(TCP_NOOPT),			/* int */
187881631a2SMichael Tuexen 	tcp_entry(TCP_NOPUSH),			/* int */
188881631a2SMichael Tuexen 	tcp_entry(TCP_REMOTE_UDP_ENCAPS_PORT),	/* int */
189881631a2SMichael Tuexen 	tcp_entry(TCP_MAXSEG),			/* int */
190881631a2SMichael Tuexen 	tcp_entry(TCP_TXTLS_MODE),		/* unsigned int */
191a779bb4dSMichael Tuexen 	tcp_entry(TCP_MAXUNACKTIME),		/* unsigned int */
192881631a2SMichael Tuexen 	tcp_entry(TCP_KEEPIDLE),		/* unsigned int */
193881631a2SMichael Tuexen 	tcp_entry(TCP_KEEPINTVL),		/* unsigned int */
194881631a2SMichael Tuexen 	tcp_entry(TCP_KEEPINIT),		/* unsigned int */
195881631a2SMichael Tuexen 	tcp_entry(TCP_KEEPCNT),			/* unsigned int */
196881631a2SMichael Tuexen 	tcp_entry(TCP_PCAP_OUT),		/* int */
197881631a2SMichael Tuexen 	tcp_entry(TCP_PCAP_IN),			/* int */
198881631a2SMichael Tuexen 	tcp_entry(TCP_LOG),			/* int */
199881631a2SMichael Tuexen 	tcp_entry(TCP_LOGID),			/* char * */
200881631a2SMichael Tuexen 	tcp_entry(TCP_LOGDUMP),			/* char * */
201881631a2SMichael Tuexen 	tcp_entry(TCP_LOGDUMPID),		/* char * */
202881631a2SMichael Tuexen 	tcp_entry(TCP_CONGESTION),		/* char * */
203881631a2SMichael Tuexen 	tcp_entry(TCP_FUNCTION_BLK),		/* char * */
204881631a2SMichael Tuexen 	tcp_entry(TCP_NO_PRR),			/* int */
205881631a2SMichael Tuexen 	tcp_entry(TCP_HDWR_RATE_CAP),		/* int */
206881631a2SMichael Tuexen #if notyet
207881631a2SMichael Tuexen 	tcp_entry(TCP_PACING_RATE_CAP),		/* uint64_t */
208881631a2SMichael Tuexen #endif
209881631a2SMichael Tuexen 	tcp_entry(TCP_HDWR_UP_ONLY),		/* int */
210881631a2SMichael Tuexen 	tcp_entry(TCP_DELACK),			/* int */
211881631a2SMichael Tuexen 	tcp_entry(TCP_REC_ABC_VAL),		/* int */
212881631a2SMichael Tuexen 	tcp_entry(TCP_USE_CMP_ACKS),		/* int */
213881631a2SMichael Tuexen 	tcp_entry(TCP_SHARED_CWND_TIME_LIMIT),	/* int */
214881631a2SMichael Tuexen 	tcp_entry(TCP_SHARED_CWND_ENABLE),	/* int */
215881631a2SMichael Tuexen 	tcp_entry(TCP_DATA_AFTER_CLOSE),	/* int */
216881631a2SMichael Tuexen 	tcp_entry(TCP_DEFER_OPTIONS),		/* int */
217881631a2SMichael Tuexen 	tcp_entry(TCP_TIMELY_DYN_ADJ),		/* int */
218881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_TLP_REDUCE),		/* int */
219881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACE_ALWAYS),	/* int */
220881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACE_MAX_SEG),	/* int */
221881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_FORCE_MSEG),		/* int */
222881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACE_RATE_CA),	/* int */
223881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACE_RATE_SS),	/* int */
224881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACE_RATE_REC),	/* int */
225881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_GP_INCREASE_CA),	/* int */
226881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_GP_INCREASE_SS),	/* int */
227881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_GP_INCREASE_REC),	/* int */
228881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_RR_CONF),		/* int */
229881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PRR_SENDALOT),	/* int */
230881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_MIN_TO),		/* int */
231881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_EARLY_SEG),		/* int */
232881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_REORD_THRESH),	/* int */
233881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_REORD_FADE),		/* int */
234881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_TLP_THRESH),		/* int */
235881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PKT_DELAY),		/* int */
236881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_TLP_USE),		/* int */
237881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_DO_DETECTION),	/* int */
238881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_NONRXT_CFG_RATE),	/* int */
239881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_MBUF_QUEUE),		/* int */
240881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_NO_PUSH_AT_MAX),	/* int */
241881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACE_TO_FILL),	/* int */
242881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PROFILE),		/* int */
243881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_ABC_VAL),		/* int */
244881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_MEASURE_CNT),	/* int */
245881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_DSACK_OPT),		/* int */
246881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACING_BETA),	/* int */
247881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_PACING_BETA_ECN),	/* int */
248881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_TIMER_SLOP),		/* int */
249881631a2SMichael Tuexen 	tcp_entry(TCP_RACK_ENABLE_HYSTART),	/* int */
250881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_RACK_RTT_USE),	/* int */
251881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_USE_RACK_RR),		/* int */
252881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_HDWR_PACE),		/* int */
253881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_RACK_INIT_RATE),	/* int */
254881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_IWINTSO),		/* int */
255881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_ALGORITHM),		/* int */
256881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_TSLIMITS),		/* int */
257881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_STARTUP_PG),		/* int */
258881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_DRAIN_PG),		/* int */
259881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PROBE_RTT_INT),	/* int */
260881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PROBE_RTT_GAIN),	/* int */
261881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PROBE_RTT_LEN),	/* int */
262881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_STARTUP_LOSS_EXIT),	/* int */
263881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_USEDEL_RATE),		/* int */
264881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_MIN_RTO),		/* int */
265881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_MAX_RTO),		/* int */
266881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PACE_PER_SEC),	/* int */
267881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PACE_DEL_TAR),	/* int */
268881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_SEND_IWND_IN_TSO),	/* int */
269881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_EXTRA_STATE),		/* int */
270881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_UTTER_MAX_TSO),	/* int */
271881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_MIN_TOPACEOUT),	/* int */
272881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_FLOOR_MIN_TSO),	/* int */
273881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_TSTMP_RAISES),	/* int */
274881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_USE_RACK_CHEAT),	/* int */
275881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PACE_SEG_MAX),	/* int */
276881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PACE_SEG_MIN),	/* int */
277881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PACE_CROSS),		/* int */
278881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_PACE_OH),		/* int */
279881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_TMR_PACE_OH),		/* int */
280881631a2SMichael Tuexen 	tcp_entry(TCP_BBR_RETRAN_WTSO),		/* int */
281881631a2SMichael Tuexen 	{0, 0, NULL}
282881631a2SMichael Tuexen };
283881631a2SMichael Tuexen 
284881631a2SMichael Tuexen static struct sockopt_parameters *
285881631a2SMichael Tuexen create_parameters(char *level_str, char *optname_str, char *optval_str,
286881631a2SMichael Tuexen     size_t *optlen)
287881631a2SMichael Tuexen {
288881631a2SMichael Tuexen 	long long arg;
289881631a2SMichael Tuexen 	int i, level, optname, optval_int;
290881631a2SMichael Tuexen 	struct sockopt_parameters *params;
291881631a2SMichael Tuexen 	char *end;
292881631a2SMichael Tuexen 	bool optval_is_int;
293881631a2SMichael Tuexen 
294881631a2SMichael Tuexen 	/* Determine level, use IPPROTO_TCP as default. */
295881631a2SMichael Tuexen 	if (level_str == NULL)
296881631a2SMichael Tuexen 		level = IPPROTO_TCP;
297881631a2SMichael Tuexen 	else {
298881631a2SMichael Tuexen 		arg = strtoll(level_str, &end, 0);
299881631a2SMichael Tuexen 		if (*end != '\0') {
300881631a2SMichael Tuexen 			for (i = 0; so_levels[i].name != NULL; i++)
301881631a2SMichael Tuexen 				if (strcmp(level_str, so_levels[i].name) == 0) {
302881631a2SMichael Tuexen 					level = so_levels[i].level;
303881631a2SMichael Tuexen 					break;
304881631a2SMichael Tuexen 				}
305881631a2SMichael Tuexen 			if (so_levels[i].name == NULL)
306881631a2SMichael Tuexen 				errx(1, "unsupported level %s", optname_str);
307881631a2SMichael Tuexen 		} else {
308881631a2SMichael Tuexen 			if (arg < 0)
309881631a2SMichael Tuexen 				errx(1, "level negative %s", optname_str);
310881631a2SMichael Tuexen 			else if (arg > INT_MAX)
311881631a2SMichael Tuexen 				errx(1, "level too large %s", optname_str);
312881631a2SMichael Tuexen 			else
313881631a2SMichael Tuexen 				level = (int)arg;
314881631a2SMichael Tuexen 		}
315881631a2SMichael Tuexen 	}
316881631a2SMichael Tuexen 	/* Determine option name. */
317881631a2SMichael Tuexen 	if (optname_str == NULL || *optname_str == '\0')
318881631a2SMichael Tuexen 		return (NULL);
319881631a2SMichael Tuexen 	arg = strtoll(optname_str, &end, 0);
320881631a2SMichael Tuexen 	if (*end != '\0') {
321881631a2SMichael Tuexen 		for (i = 0; so_names[i].name != NULL; i++)
322881631a2SMichael Tuexen 			if (strcmp(optname_str, so_names[i].name) == 0) {
323881631a2SMichael Tuexen 				level = so_names[i].level;
324881631a2SMichael Tuexen 				optname = so_names[i].value;
325881631a2SMichael Tuexen 				break;
326881631a2SMichael Tuexen 			}
327881631a2SMichael Tuexen 		if (so_names[i].name == NULL)
328881631a2SMichael Tuexen 			errx(1, "unsupported option name %s", optname_str);
329881631a2SMichael Tuexen 	} else {
330881631a2SMichael Tuexen 		if (arg < 0)
331881631a2SMichael Tuexen 			errx(1, "option name negative %s", optname_str);
332881631a2SMichael Tuexen 		else if (arg > INT_MAX)
333881631a2SMichael Tuexen 			errx(1, "option name too large %s", optname_str);
334881631a2SMichael Tuexen 		else
335881631a2SMichael Tuexen 			optname = (int)arg;
336881631a2SMichael Tuexen 	}
337881631a2SMichael Tuexen 	/*
338881631a2SMichael Tuexen 	 * Determine option value. Use int, if can be parsed as an int,
339881631a2SMichael Tuexen 	 * else use a char *.
340881631a2SMichael Tuexen 	 */
341881631a2SMichael Tuexen 	if (optval_str == NULL || *optval_str == '\0')
342881631a2SMichael Tuexen 		return (NULL);
343881631a2SMichael Tuexen 	arg = strtol(optval_str, &end, 0);
344881631a2SMichael Tuexen 	optval_is_int = (*end == '\0');
345881631a2SMichael Tuexen 	if (optval_is_int) {
346881631a2SMichael Tuexen 		if (arg < INT_MIN)
347881631a2SMichael Tuexen 			errx(1, "option value too small %s", optval_str);
348881631a2SMichael Tuexen 		else if (arg > INT_MAX)
349881631a2SMichael Tuexen 			errx(1, "option value too large %s", optval_str);
350881631a2SMichael Tuexen 		else
351881631a2SMichael Tuexen 			optval_int = (int)arg;
352881631a2SMichael Tuexen 	}
353881631a2SMichael Tuexen 	switch (optname) {
354881631a2SMichael Tuexen 	case TCP_FUNCTION_BLK:
355881631a2SMichael Tuexen 		*optlen = sizeof(struct tcp_function_set);
356881631a2SMichael Tuexen 		break;
357881631a2SMichael Tuexen 	default:
358881631a2SMichael Tuexen 		if (optval_is_int)
359881631a2SMichael Tuexen 			*optlen = sizeof(int);
360881631a2SMichael Tuexen 		else
361881631a2SMichael Tuexen 			*optlen = strlen(optval_str) + 1;
362881631a2SMichael Tuexen 		break;
363881631a2SMichael Tuexen 	}
364881631a2SMichael Tuexen 	/* Fill socket option parameters. */
365881631a2SMichael Tuexen 	params = malloc(sizeof(struct sockopt_parameters) + *optlen);
366881631a2SMichael Tuexen 	if (params == NULL)
367881631a2SMichael Tuexen 		return (NULL);
368881631a2SMichael Tuexen 	memset(params, 0, sizeof(struct sockopt_parameters) + *optlen);
369881631a2SMichael Tuexen 	params->sop_level = level;
370881631a2SMichael Tuexen 	params->sop_optname = optname;
371881631a2SMichael Tuexen 	switch (optname) {
372881631a2SMichael Tuexen 	case TCP_FUNCTION_BLK:
373881631a2SMichael Tuexen 		strlcpy(params->sop_optval, optval_str,
374881631a2SMichael Tuexen 		    TCP_FUNCTION_NAME_LEN_MAX);
375881631a2SMichael Tuexen 		break;
376881631a2SMichael Tuexen 	default:
377881631a2SMichael Tuexen 		if (optval_is_int)
378881631a2SMichael Tuexen 			memcpy(params->sop_optval, &optval_int, *optlen);
379881631a2SMichael Tuexen 		else
380881631a2SMichael Tuexen 			memcpy(params->sop_optval, optval_str, *optlen);
381881631a2SMichael Tuexen 	}
382881631a2SMichael Tuexen 	return (params);
383881631a2SMichael Tuexen }
384881631a2SMichael Tuexen 
385881631a2SMichael Tuexen static void
386881631a2SMichael Tuexen usage(void)
387881631a2SMichael Tuexen {
388881631a2SMichael Tuexen 	fprintf(stderr,
389881631a2SMichael Tuexen "usage: tcpsso -i id [level] opt-name opt-value\n"
390881631a2SMichael Tuexen "       tcpsso -a [level] opt-name opt-value\n"
391881631a2SMichael Tuexen "       tcpsso -C cc-algo [-S stack] [-s state] [level] opt-name opt-value\n"
392881631a2SMichael Tuexen "       tcpsso [-C cc-algo] -S stack [-s state] [level] opt-name opt-value\n"
393881631a2SMichael Tuexen "       tcpsso [-C cc-algo] [-S stack] -s state [level] opt-name opt-value\n");
394881631a2SMichael Tuexen 	exit(1);
395881631a2SMichael Tuexen }
396881631a2SMichael Tuexen 
397881631a2SMichael Tuexen int
398881631a2SMichael Tuexen main(int argc, char *argv[])
399881631a2SMichael Tuexen {
400881631a2SMichael Tuexen 	struct sockopt_parameters *params;
401881631a2SMichael Tuexen 	uint64_t id;
402881631a2SMichael Tuexen 	size_t optlen;
403881631a2SMichael Tuexen 	int ch, state;
404881631a2SMichael Tuexen 	char stack[TCP_FUNCTION_NAME_LEN_MAX];
405881631a2SMichael Tuexen 	char ca_name[TCP_CA_NAME_MAX];
406881631a2SMichael Tuexen 	bool ok, apply_all, apply_subset, apply_specific;
407881631a2SMichael Tuexen 
408881631a2SMichael Tuexen 	apply_all = false;
409881631a2SMichael Tuexen 	apply_subset = false;
410881631a2SMichael Tuexen 	apply_specific = false;
411881631a2SMichael Tuexen 	ca_name[0] = '\0';
412881631a2SMichael Tuexen 	stack[0] = '\0';
413881631a2SMichael Tuexen 	state = -1;
414881631a2SMichael Tuexen 	id = 0;
415881631a2SMichael Tuexen 
416881631a2SMichael Tuexen 	while ((ch = getopt(argc, argv, "aC:i:S:s:")) != -1) {
417881631a2SMichael Tuexen 		switch (ch) {
418881631a2SMichael Tuexen 		case 'a':
419881631a2SMichael Tuexen 			apply_all = true;
420881631a2SMichael Tuexen 			break;
421881631a2SMichael Tuexen 		case 'C':
422881631a2SMichael Tuexen 			apply_subset = true;
423881631a2SMichael Tuexen 			strlcpy(ca_name, optarg, sizeof(ca_name));
424881631a2SMichael Tuexen 			break;
425881631a2SMichael Tuexen 		case 'i':
426881631a2SMichael Tuexen 			apply_specific = true;
427881631a2SMichael Tuexen 			id = strtoull(optarg, NULL, 0);
428881631a2SMichael Tuexen 			break;
429881631a2SMichael Tuexen 		case 'S':
430881631a2SMichael Tuexen 			apply_subset = true;
431881631a2SMichael Tuexen 			strlcpy(stack, optarg, sizeof(stack));
432881631a2SMichael Tuexen 			break;
433881631a2SMichael Tuexen 		case 's':
434881631a2SMichael Tuexen 			apply_subset = true;
435881631a2SMichael Tuexen 			for (state = 0; state < TCP_NSTATES; state++) {
436881631a2SMichael Tuexen 				if (strcmp(tcpstates[state], optarg) == 0)
437881631a2SMichael Tuexen 					break;
438881631a2SMichael Tuexen 			}
439881631a2SMichael Tuexen 			break;
440881631a2SMichael Tuexen 		default:
441881631a2SMichael Tuexen 			usage();
442881631a2SMichael Tuexen 		}
443881631a2SMichael Tuexen 	}
444881631a2SMichael Tuexen 	argc -= optind;
445881631a2SMichael Tuexen 	argv += optind;
446881631a2SMichael Tuexen 	if ((state == TCP_NSTATES) ||
447881631a2SMichael Tuexen 	    (argc < 2) || (argc > 3) ||
448881631a2SMichael Tuexen 	    (apply_all && apply_subset) ||
449881631a2SMichael Tuexen 	    (apply_all && apply_specific) ||
450881631a2SMichael Tuexen 	    (apply_subset && apply_specific) ||
451881631a2SMichael Tuexen 	    !(apply_all || apply_subset || apply_specific))
452881631a2SMichael Tuexen 		usage();
453881631a2SMichael Tuexen 	if (argc == 2)
454881631a2SMichael Tuexen 		params = create_parameters(NULL, argv[0], argv[1], &optlen);
455881631a2SMichael Tuexen 	else
456881631a2SMichael Tuexen 		params = create_parameters(argv[0], argv[1], argv[2], &optlen);
457881631a2SMichael Tuexen 	if (params != NULL) {
458881631a2SMichael Tuexen 		if (apply_specific)
459881631a2SMichael Tuexen 			ok = tcpsso(id, params, optlen);
460881631a2SMichael Tuexen 		else
461881631a2SMichael Tuexen 			ok = tcpssoall(ca_name, stack, state, params, optlen);
462881631a2SMichael Tuexen 		free(params);
463881631a2SMichael Tuexen 	} else
464881631a2SMichael Tuexen 		ok = false;
465881631a2SMichael Tuexen 	return (ok ? 0 : 1);
466881631a2SMichael Tuexen }
467