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