1141381Smaxim/* $OpenBSD: tcpdrop.c,v 1.4 2004/05/22 23:55:22 deraadt Exp $ */ 2141381Smaxim 3141381Smaxim/*- 4193203Sjmallett * Copyright (c) 2009 Juli Mallett <jmallett@FreeBSD.org> 5141381Smaxim * Copyright (c) 2004 Markus Friedl <markus@openbsd.org> 6141381Smaxim * 7141381Smaxim * Permission to use, copy, modify, and distribute this software for any 8141381Smaxim * purpose with or without fee is hereby granted, provided that the above 9141381Smaxim * copyright notice and this permission notice appear in all copies. 10141381Smaxim * 11141381Smaxim * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12141381Smaxim * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13141381Smaxim * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14141381Smaxim * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15141381Smaxim * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16141381Smaxim * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17141381Smaxim * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18141381Smaxim */ 19141381Smaxim 20141381Smaxim#include <sys/cdefs.h> 21141381Smaxim__FBSDID("$FreeBSD$"); 22141381Smaxim 23222765Sbz#include <sys/param.h> 24141381Smaxim#include <sys/types.h> 25141381Smaxim#include <sys/socket.h> 26193203Sjmallett#include <sys/socketvar.h> 27141381Smaxim#include <sys/sysctl.h> 28222765Sbz 29141381Smaxim#include <netinet/in.h> 30193203Sjmallett#include <netinet/in_pcb.h> 31193203Sjmallett#define TCPSTATES 32193203Sjmallett#include <netinet/tcp_fsm.h> 33141381Smaxim#include <netinet/tcp_var.h> 34141381Smaxim 35141381Smaxim#include <err.h> 36141381Smaxim#include <netdb.h> 37193203Sjmallett#include <stdbool.h> 38141381Smaxim#include <stdio.h> 39141381Smaxim#include <stdlib.h> 40141381Smaxim#include <string.h> 41193203Sjmallett#include <unistd.h> 42141381Smaxim 43193203Sjmallett#define TCPDROP_FOREIGN 0 44193203Sjmallett#define TCPDROP_LOCAL 1 45193203Sjmallett 46193203Sjmallettstruct host_service { 47193203Sjmallett char hs_host[NI_MAXHOST]; 48193203Sjmallett char hs_service[NI_MAXSERV]; 49193203Sjmallett}; 50193203Sjmallett 51193203Sjmallettstatic bool tcpdrop_list_commands = false; 52193203Sjmallett 53246129Sjhbstatic char *findport(const char *); 54193203Sjmallettstatic struct xinpgen *getxpcblist(const char *); 55193203Sjmallettstatic void sockinfo(const struct sockaddr *, struct host_service *); 56193203Sjmallettstatic bool tcpdrop(const struct sockaddr *, const struct sockaddr *); 57193203Sjmallettstatic bool tcpdropall(void); 58193203Sjmallettstatic bool tcpdropbyname(const char *, const char *, const char *, 59193203Sjmallett const char *); 60193203Sjmallettstatic bool tcpdropconn(const struct in_conninfo *); 61193203Sjmallettstatic void usage(void); 62193203Sjmallett 63141381Smaxim/* 64141381Smaxim * Drop a tcp connection. 65141381Smaxim */ 66141381Smaximint 67141381Smaximmain(int argc, char *argv[]) 68141381Smaxim{ 69246129Sjhb char *lport, *fport; 70193203Sjmallett bool dropall; 71193203Sjmallett int ch; 72193203Sjmallett 73193203Sjmallett dropall = false; 74193203Sjmallett 75193203Sjmallett while ((ch = getopt(argc, argv, "al")) != -1) { 76193203Sjmallett switch (ch) { 77193203Sjmallett case 'a': 78193203Sjmallett dropall = true; 79193203Sjmallett break; 80193203Sjmallett case 'l': 81193203Sjmallett tcpdrop_list_commands = true; 82193203Sjmallett break; 83193203Sjmallett default: 84193203Sjmallett usage(); 85193203Sjmallett } 86193203Sjmallett } 87193203Sjmallett argc -= optind; 88193203Sjmallett argv += optind; 89193203Sjmallett 90193203Sjmallett if (dropall) { 91193203Sjmallett if (argc != 0) 92193203Sjmallett usage(); 93193203Sjmallett if (!tcpdropall()) 94193203Sjmallett exit(1); 95193203Sjmallett exit(0); 96193203Sjmallett } 97193203Sjmallett 98246129Sjhb if ((argc != 2 && argc != 4) || tcpdrop_list_commands) 99193203Sjmallett usage(); 100193203Sjmallett 101246129Sjhb if (argc == 2) { 102246129Sjhb lport = findport(argv[0]); 103246129Sjhb fport = findport(argv[1]); 104246129Sjhb if (lport == NULL || lport[1] == '\0' || fport == NULL || 105246129Sjhb fport[1] == '\0') 106246129Sjhb usage(); 107246129Sjhb *lport++ = '\0'; 108246129Sjhb *fport++ = '\0'; 109246129Sjhb if (!tcpdropbyname(argv[0], lport, argv[1], fport)) 110246129Sjhb exit(1); 111246129Sjhb } else if (!tcpdropbyname(argv[0], argv[1], argv[2], argv[3])) 112193203Sjmallett exit(1); 113193203Sjmallett 114193203Sjmallett exit(0); 115193203Sjmallett} 116193203Sjmallett 117246129Sjhbstatic char * 118246129Sjhbfindport(const char *arg) 119246129Sjhb{ 120246129Sjhb char *dot, *colon; 121246129Sjhb 122246129Sjhb /* A strrspn() or strrpbrk() would be nice. */ 123246129Sjhb dot = strrchr(arg, '.'); 124246129Sjhb colon = strrchr(arg, ':'); 125246129Sjhb if (dot == NULL) 126246129Sjhb return (colon); 127246129Sjhb if (colon == NULL) 128246129Sjhb return (dot); 129246129Sjhb if (dot < colon) 130246129Sjhb return (colon); 131246129Sjhb else 132246129Sjhb return (dot); 133246129Sjhb} 134246129Sjhb 135193203Sjmallettstatic struct xinpgen * 136193203Sjmallettgetxpcblist(const char *name) 137193203Sjmallett{ 138193203Sjmallett struct xinpgen *xinp; 139193203Sjmallett size_t len; 140193203Sjmallett int rv; 141193203Sjmallett 142193203Sjmallett len = 0; 143193203Sjmallett rv = sysctlbyname(name, NULL, &len, NULL, 0); 144193203Sjmallett if (rv == -1) 145193203Sjmallett err(1, "sysctlbyname %s", name); 146193203Sjmallett 147193203Sjmallett if (len == 0) 148193203Sjmallett errx(1, "%s is empty", name); 149193203Sjmallett 150193203Sjmallett xinp = malloc(len); 151193203Sjmallett if (xinp == NULL) 152193203Sjmallett errx(1, "malloc failed"); 153193203Sjmallett 154193203Sjmallett rv = sysctlbyname(name, xinp, &len, NULL, 0); 155193203Sjmallett if (rv == -1) 156193203Sjmallett err(1, "sysctlbyname %s", name); 157193203Sjmallett 158193203Sjmallett return (xinp); 159193203Sjmallett} 160193203Sjmallett 161193203Sjmallettstatic void 162193203Sjmallettsockinfo(const struct sockaddr *sa, struct host_service *hs) 163193203Sjmallett{ 164193203Sjmallett static const int flags = NI_NUMERICHOST | NI_NUMERICSERV; 165193203Sjmallett int rv; 166193203Sjmallett 167193203Sjmallett rv = getnameinfo(sa, sa->sa_len, hs->hs_host, sizeof hs->hs_host, 168193203Sjmallett hs->hs_service, sizeof hs->hs_service, flags); 169193203Sjmallett if (rv == -1) 170193203Sjmallett err(1, "getnameinfo"); 171193203Sjmallett} 172193203Sjmallett 173193203Sjmallettstatic bool 174193203Sjmalletttcpdrop(const struct sockaddr *lsa, const struct sockaddr *fsa) 175193203Sjmallett{ 176193203Sjmallett struct host_service local, foreign; 177141886Smaxim struct sockaddr_storage addrs[2]; 178193203Sjmallett int rv; 179141381Smaxim 180193203Sjmallett memcpy(&addrs[TCPDROP_FOREIGN], fsa, fsa->sa_len); 181193203Sjmallett memcpy(&addrs[TCPDROP_LOCAL], lsa, lsa->sa_len); 182193203Sjmallett 183193203Sjmallett sockinfo(lsa, &local); 184193203Sjmallett sockinfo(fsa, &foreign); 185193203Sjmallett 186193203Sjmallett if (tcpdrop_list_commands) { 187193203Sjmallett printf("tcpdrop %s %s %s %s\n", local.hs_host, local.hs_service, 188193203Sjmallett foreign.hs_host, foreign.hs_service); 189193203Sjmallett return (true); 190173223Sru } 191193203Sjmallett 192193203Sjmallett rv = sysctlbyname("net.inet.tcp.drop", NULL, NULL, &addrs, 193193203Sjmallett sizeof addrs); 194193203Sjmallett if (rv == -1) { 195193203Sjmallett warn("%s %s %s %s", local.hs_host, local.hs_service, 196193203Sjmallett foreign.hs_host, foreign.hs_service); 197193203Sjmallett return (false); 198141381Smaxim } 199193203Sjmallett printf("%s %s %s %s: dropped\n", local.hs_host, local.hs_service, 200193203Sjmallett foreign.hs_host, foreign.hs_service); 201193203Sjmallett return (true); 202193203Sjmallett} 203193203Sjmallett 204193203Sjmallettstatic bool 205193203Sjmalletttcpdropall(void) 206193203Sjmallett{ 207193203Sjmallett struct xinpgen *head, *xinp; 208193203Sjmallett struct xtcpcb *xpcb; 209193203Sjmallett struct tcpcb *tp; 210193203Sjmallett struct inpcb *inp; 211193203Sjmallett bool ok; 212193203Sjmallett 213193203Sjmallett ok = true; 214193203Sjmallett 215193203Sjmallett head = getxpcblist("net.inet.tcp.pcblist"); 216193203Sjmallett 217193203Sjmallett#define XINP_NEXT(xinp) \ 218193203Sjmallett ((struct xinpgen *)((uintptr_t)(xinp) + (xinp)->xig_len)) 219193203Sjmallett 220193203Sjmallett for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp; 221193203Sjmallett xinp = XINP_NEXT(xinp)) { 222193203Sjmallett xpcb = (struct xtcpcb *)xinp; 223193203Sjmallett tp = &xpcb->xt_tp; 224193203Sjmallett inp = &xpcb->xt_inp; 225193203Sjmallett 226193203Sjmallett /* 227193203Sjmallett * XXX 228193203Sjmallett * Check protocol, support just v4 or v6, etc. 229193203Sjmallett */ 230193203Sjmallett 231193203Sjmallett /* Ignore PCBs which were freed during copyout. */ 232193203Sjmallett if (inp->inp_gencnt > head->xig_gen) 233193203Sjmallett continue; 234193203Sjmallett 235193203Sjmallett /* Skip listening sockets. */ 236193203Sjmallett if (tp->t_state == TCPS_LISTEN) 237193203Sjmallett continue; 238193203Sjmallett 239193203Sjmallett if (!tcpdropconn(&inp->inp_inc)) 240193203Sjmallett ok = false; 241193203Sjmallett } 242193203Sjmallett free(head); 243193203Sjmallett 244193203Sjmallett return (ok); 245193203Sjmallett} 246193203Sjmallett 247193203Sjmallettstatic bool 248193203Sjmalletttcpdropbyname(const char *lhost, const char *lport, const char *fhost, 249193203Sjmallett const char *fport) 250193203Sjmallett{ 251193203Sjmallett static const struct addrinfo hints = { 252193203Sjmallett /* 253193203Sjmallett * Look for streams in all domains. 254193203Sjmallett */ 255193203Sjmallett .ai_family = AF_UNSPEC, 256193203Sjmallett .ai_socktype = SOCK_STREAM, 257193203Sjmallett }; 258193203Sjmallett struct addrinfo *ail, *local, *aif, *foreign; 259193203Sjmallett int error; 260193203Sjmallett bool ok, infamily; 261193203Sjmallett 262193203Sjmallett error = getaddrinfo(lhost, lport, &hints, &local); 263193203Sjmallett if (error != 0) 264193203Sjmallett errx(1, "getaddrinfo: %s port %s: %s", lhost, lport, 265193203Sjmallett gai_strerror(error)); 266193203Sjmallett 267193203Sjmallett error = getaddrinfo(fhost, fport, &hints, &foreign); 268193203Sjmallett if (error != 0) { 269193203Sjmallett freeaddrinfo(local); /* XXX gratuitous */ 270246129Sjhb errx(1, "getaddrinfo: %s port %s: %s", fhost, fport, 271193203Sjmallett gai_strerror(error)); 272193203Sjmallett } 273193203Sjmallett 274193203Sjmallett ok = true; 275193203Sjmallett infamily = false; 276193203Sjmallett 277193203Sjmallett /* 278193203Sjmallett * Try every combination of local and foreign address pairs. 279193203Sjmallett */ 280193203Sjmallett for (ail = local; ail != NULL; ail = ail->ai_next) { 281193203Sjmallett for (aif = foreign; aif != NULL; aif = aif->ai_next) { 282141381Smaxim if (ail->ai_family != aif->ai_family) 283141381Smaxim continue; 284193203Sjmallett infamily = true; 285193203Sjmallett if (!tcpdrop(ail->ai_addr, aif->ai_addr)) 286193203Sjmallett ok = false; 287141381Smaxim } 288141381Smaxim } 289193203Sjmallett 290193203Sjmallett if (!infamily) { 291193203Sjmallett warnx("%s %s %s %s: different address families", lhost, lport, 292193203Sjmallett fhost, fport); 293193203Sjmallett ok = false; 294193203Sjmallett } 295193203Sjmallett 296193203Sjmallett freeaddrinfo(local); 297193203Sjmallett freeaddrinfo(foreign); 298193203Sjmallett 299193203Sjmallett return (ok); 300141381Smaxim} 301193203Sjmallett 302193203Sjmallettstatic bool 303193203Sjmalletttcpdropconn(const struct in_conninfo *inc) 304193203Sjmallett{ 305193203Sjmallett struct sockaddr *local, *foreign; 306193203Sjmallett struct sockaddr_in6 sin6[2]; 307193203Sjmallett struct sockaddr_in sin4[2]; 308193203Sjmallett 309193203Sjmallett if ((inc->inc_flags & INC_ISIPV6) != 0) { 310193203Sjmallett memset(sin6, 0, sizeof sin6); 311193203Sjmallett 312193203Sjmallett sin6[TCPDROP_LOCAL].sin6_len = sizeof sin6[TCPDROP_LOCAL]; 313193203Sjmallett sin6[TCPDROP_LOCAL].sin6_family = AF_INET6; 314193203Sjmallett sin6[TCPDROP_LOCAL].sin6_port = inc->inc_lport; 315193203Sjmallett memcpy(&sin6[TCPDROP_LOCAL].sin6_addr, &inc->inc6_laddr, 316193203Sjmallett sizeof inc->inc6_laddr); 317193203Sjmallett local = (struct sockaddr *)&sin6[TCPDROP_LOCAL]; 318193203Sjmallett 319193203Sjmallett sin6[TCPDROP_FOREIGN].sin6_len = sizeof sin6[TCPDROP_FOREIGN]; 320193203Sjmallett sin6[TCPDROP_FOREIGN].sin6_family = AF_INET6; 321193203Sjmallett sin6[TCPDROP_FOREIGN].sin6_port = inc->inc_fport; 322193203Sjmallett memcpy(&sin6[TCPDROP_FOREIGN].sin6_addr, &inc->inc6_faddr, 323193203Sjmallett sizeof inc->inc6_faddr); 324193203Sjmallett foreign = (struct sockaddr *)&sin6[TCPDROP_FOREIGN]; 325193203Sjmallett } else { 326193203Sjmallett memset(&sin4[TCPDROP_LOCAL], 0, sizeof sin4[TCPDROP_LOCAL]); 327193203Sjmallett 328193203Sjmallett sin4[TCPDROP_LOCAL].sin_len = sizeof sin4[TCPDROP_LOCAL]; 329193203Sjmallett sin4[TCPDROP_LOCAL].sin_family = AF_INET; 330193203Sjmallett sin4[TCPDROP_LOCAL].sin_port = inc->inc_lport; 331193203Sjmallett memcpy(&sin4[TCPDROP_LOCAL].sin_addr, &inc->inc_laddr, 332193203Sjmallett sizeof inc->inc_laddr); 333193203Sjmallett local = (struct sockaddr *)&sin4[TCPDROP_LOCAL]; 334193203Sjmallett 335193203Sjmallett sin4[TCPDROP_FOREIGN].sin_len = sizeof sin4[TCPDROP_FOREIGN]; 336193203Sjmallett sin4[TCPDROP_FOREIGN].sin_family = AF_INET; 337193203Sjmallett sin4[TCPDROP_FOREIGN].sin_port = inc->inc_fport; 338193203Sjmallett memcpy(&sin4[TCPDROP_FOREIGN].sin_addr, &inc->inc_faddr, 339193203Sjmallett sizeof inc->inc_faddr); 340193203Sjmallett foreign = (struct sockaddr *)&sin4[TCPDROP_FOREIGN]; 341193203Sjmallett } 342193203Sjmallett 343193203Sjmallett return (tcpdrop(local, foreign)); 344193203Sjmallett} 345193203Sjmallett 346193203Sjmallettstatic void 347193203Sjmallettusage(void) 348193203Sjmallett{ 349193203Sjmallett fprintf(stderr, 350193203Sjmallett"usage: tcpdrop local-address local-port foreign-address foreign-port\n" 351246129Sjhb" tcpdrop local-address:local-port foreign-address:foreign-port\n" 352246129Sjhb" tcpdrop local-address.local-port foreign-address.foreign-port\n" 353193203Sjmallett" tcpdrop [-l] -a\n"); 354193203Sjmallett exit(1); 355193203Sjmallett} 356