1 /* $OpenBSD: tcpdrop.c,v 1.4 2004/05/22 23:55:22 deraadt Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Juli Mallett <jmallett@FreeBSD.org> 5 * Copyright (c) 2004 Markus Friedl <markus@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 #include <sys/param.h> 22 #include <sys/types.h> 23 #include <sys/socket.h> 24 #include <sys/socketvar.h> 25 #include <sys/sysctl.h> 26 27 #include <netinet/in.h> 28 #include <netinet/in_pcb.h> 29 #define TCPSTATES 30 #include <netinet/tcp_fsm.h> 31 #include <netinet/tcp_var.h> 32 33 #include <err.h> 34 #include <netdb.h> 35 #include <stdbool.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #define TCPDROP_FOREIGN 0 42 #define TCPDROP_LOCAL 1 43 44 struct host_service { 45 char hs_host[NI_MAXHOST]; 46 char hs_service[NI_MAXSERV]; 47 }; 48 49 static bool tcpdrop_list_commands = false; 50 51 static char *findport(const char *); 52 static struct xinpgen *getxpcblist(const char *); 53 static void sockinfo(const struct sockaddr *, struct host_service *); 54 static bool tcpdrop(const struct sockaddr *, const struct sockaddr *); 55 static bool tcpdropall(const char *, const char *, int); 56 static bool tcpdropbyname(const char *, const char *, const char *, 57 const char *); 58 static bool tcpdropconn(const struct in_conninfo *); 59 static void usage(void) __dead2; 60 61 /* 62 * Drop a tcp connection. 63 */ 64 int 65 main(int argc, char *argv[]) 66 { 67 char stack[TCP_FUNCTION_NAME_LEN_MAX]; 68 char ca_name[TCP_CA_NAME_MAX]; 69 char *lport, *fport; 70 bool dropall, dropspecific; 71 int ch, state; 72 73 dropall = false; 74 dropspecific = false; 75 ca_name[0] = '\0'; 76 stack[0] = '\0'; 77 state = -1; 78 79 while ((ch = getopt(argc, argv, "aC:lS:s:")) != -1) { 80 switch (ch) { 81 case 'a': 82 dropall = true; 83 break; 84 case 'C': 85 dropspecific = true; 86 strlcpy(ca_name, optarg, sizeof(ca_name)); 87 break; 88 case 'l': 89 tcpdrop_list_commands = true; 90 break; 91 case 'S': 92 dropspecific = true; 93 strlcpy(stack, optarg, sizeof(stack)); 94 break; 95 case 's': 96 dropspecific = true; 97 for (state = 0; state < TCP_NSTATES; state++) { 98 if (strcmp(tcpstates[state], optarg) == 0) 99 break; 100 } 101 break; 102 default: 103 usage(); 104 } 105 } 106 argc -= optind; 107 argv += optind; 108 109 if (state == TCP_NSTATES || 110 state == TCPS_CLOSED || 111 state == TCPS_LISTEN) 112 usage(); 113 if (dropall && dropspecific) 114 usage(); 115 if (dropall || dropspecific) { 116 if (argc != 0) 117 usage(); 118 if (!tcpdropall(ca_name, stack, state)) 119 exit(1); 120 exit(0); 121 } 122 123 if ((argc != 2 && argc != 4) || tcpdrop_list_commands) 124 usage(); 125 126 if (argc == 2) { 127 lport = findport(argv[0]); 128 fport = findport(argv[1]); 129 if (lport == NULL || lport[1] == '\0' || fport == NULL || 130 fport[1] == '\0') 131 usage(); 132 *lport++ = '\0'; 133 *fport++ = '\0'; 134 if (!tcpdropbyname(argv[0], lport, argv[1], fport)) 135 exit(1); 136 } else if (!tcpdropbyname(argv[0], argv[1], argv[2], argv[3])) 137 exit(1); 138 139 exit(0); 140 } 141 142 static char * 143 findport(const char *arg) 144 { 145 char *dot, *colon; 146 147 /* A strrspn() or strrpbrk() would be nice. */ 148 dot = strrchr(arg, '.'); 149 colon = strrchr(arg, ':'); 150 if (dot == NULL) 151 return (colon); 152 if (colon == NULL) 153 return (dot); 154 if (dot < colon) 155 return (colon); 156 else 157 return (dot); 158 } 159 160 static struct xinpgen * 161 getxpcblist(const char *name) 162 { 163 struct xinpgen *xinp; 164 size_t len; 165 int rv; 166 167 len = 0; 168 rv = sysctlbyname(name, NULL, &len, NULL, 0); 169 if (rv == -1) 170 err(1, "sysctlbyname %s", name); 171 172 if (len == 0) 173 errx(1, "%s is empty", name); 174 175 xinp = malloc(len); 176 if (xinp == NULL) 177 errx(1, "malloc failed"); 178 179 rv = sysctlbyname(name, xinp, &len, NULL, 0); 180 if (rv == -1) 181 err(1, "sysctlbyname %s", name); 182 183 return (xinp); 184 } 185 186 static void 187 sockinfo(const struct sockaddr *sa, struct host_service *hs) 188 { 189 static const int flags = NI_NUMERICHOST | NI_NUMERICSERV; 190 int rv; 191 192 rv = getnameinfo(sa, sa->sa_len, hs->hs_host, sizeof hs->hs_host, 193 hs->hs_service, sizeof hs->hs_service, flags); 194 if (rv == -1) 195 err(1, "getnameinfo"); 196 } 197 198 static bool 199 tcpdrop(const struct sockaddr *lsa, const struct sockaddr *fsa) 200 { 201 struct host_service local, foreign; 202 struct sockaddr_storage addrs[2]; 203 int rv; 204 205 memcpy(&addrs[TCPDROP_FOREIGN], fsa, fsa->sa_len); 206 memcpy(&addrs[TCPDROP_LOCAL], lsa, lsa->sa_len); 207 208 sockinfo(lsa, &local); 209 sockinfo(fsa, &foreign); 210 211 if (tcpdrop_list_commands) { 212 printf("tcpdrop %s %s %s %s\n", local.hs_host, local.hs_service, 213 foreign.hs_host, foreign.hs_service); 214 return (true); 215 } 216 217 rv = sysctlbyname("net.inet.tcp.drop", NULL, NULL, &addrs, 218 sizeof addrs); 219 if (rv == -1) { 220 warn("%s %s %s %s", local.hs_host, local.hs_service, 221 foreign.hs_host, foreign.hs_service); 222 return (false); 223 } 224 printf("%s %s %s %s: dropped\n", local.hs_host, local.hs_service, 225 foreign.hs_host, foreign.hs_service); 226 return (true); 227 } 228 229 static bool 230 tcpdropall(const char *ca_name, const char *stack, int state) 231 { 232 struct xinpgen *head, *xinp; 233 struct xtcpcb *xtp; 234 struct xinpcb *xip; 235 bool ok; 236 237 ok = true; 238 239 head = getxpcblist("net.inet.tcp.pcblist"); 240 241 #define XINP_NEXT(xinp) \ 242 ((struct xinpgen *)(uintptr_t)((uintptr_t)(xinp) + (xinp)->xig_len)) 243 244 for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp; 245 xinp = XINP_NEXT(xinp)) { 246 xtp = (struct xtcpcb *)xinp; 247 xip = &xtp->xt_inp; 248 249 /* 250 * XXX 251 * Check protocol, support just v4 or v6, etc. 252 */ 253 254 /* Ignore PCBs which were freed during copyout. */ 255 if (xip->inp_gencnt > head->xig_gen) 256 continue; 257 258 /* Skip listening sockets. */ 259 if (xtp->t_state == TCPS_LISTEN) 260 continue; 261 262 /* If requested, skip sockets not having the requested state. */ 263 if ((state != -1) && (xtp->t_state != state)) 264 continue; 265 266 /* 267 * If requested, skip sockets not having the requested 268 * congestion control algorithm. 269 */ 270 if (ca_name[0] != '\0' && 271 strncmp(xtp->xt_cc, ca_name, TCP_CA_NAME_MAX)) 272 continue; 273 274 /* If requested, skip sockets not having the requested stack. */ 275 if (stack[0] != '\0' && 276 strncmp(xtp->xt_stack, stack, TCP_FUNCTION_NAME_LEN_MAX)) 277 continue; 278 279 if (!tcpdropconn(&xip->inp_inc)) 280 ok = false; 281 } 282 free(head); 283 284 return (ok); 285 } 286 287 static bool 288 tcpdropbyname(const char *lhost, const char *lport, const char *fhost, 289 const char *fport) 290 { 291 static const struct addrinfo hints = { 292 /* 293 * Look for TCP streams in all domains. 294 */ 295 .ai_family = AF_UNSPEC, 296 .ai_socktype = SOCK_STREAM, 297 .ai_protocol = IPPROTO_TCP, 298 }; 299 struct addrinfo *ail, *local, *aif, *foreign; 300 int error; 301 bool ok, infamily; 302 303 error = getaddrinfo(lhost, lport, &hints, &local); 304 if (error != 0) 305 errx(1, "getaddrinfo: %s port %s: %s", lhost, lport, 306 gai_strerror(error)); 307 308 error = getaddrinfo(fhost, fport, &hints, &foreign); 309 if (error != 0) { 310 freeaddrinfo(local); /* XXX gratuitous */ 311 errx(1, "getaddrinfo: %s port %s: %s", fhost, fport, 312 gai_strerror(error)); 313 } 314 315 ok = true; 316 infamily = false; 317 318 /* 319 * Try every combination of local and foreign address pairs. 320 */ 321 for (ail = local; ail != NULL; ail = ail->ai_next) { 322 for (aif = foreign; aif != NULL; aif = aif->ai_next) { 323 if (ail->ai_family != aif->ai_family) 324 continue; 325 infamily = true; 326 if (!tcpdrop(ail->ai_addr, aif->ai_addr)) 327 ok = false; 328 } 329 } 330 331 if (!infamily) { 332 warnx("%s %s %s %s: different address families", lhost, lport, 333 fhost, fport); 334 ok = false; 335 } 336 337 freeaddrinfo(local); 338 freeaddrinfo(foreign); 339 340 return (ok); 341 } 342 343 static bool 344 tcpdropconn(const struct in_conninfo *inc) 345 { 346 struct sockaddr *local, *foreign; 347 struct sockaddr_in6 sin6[2]; 348 struct sockaddr_in sin4[2]; 349 350 if ((inc->inc_flags & INC_ISIPV6) != 0) { 351 memset(sin6, 0, sizeof sin6); 352 353 sin6[TCPDROP_LOCAL].sin6_len = sizeof sin6[TCPDROP_LOCAL]; 354 sin6[TCPDROP_LOCAL].sin6_family = AF_INET6; 355 sin6[TCPDROP_LOCAL].sin6_port = inc->inc_lport; 356 memcpy(&sin6[TCPDROP_LOCAL].sin6_addr, &inc->inc6_laddr, 357 sizeof inc->inc6_laddr); 358 local = (struct sockaddr *)&sin6[TCPDROP_LOCAL]; 359 360 sin6[TCPDROP_FOREIGN].sin6_len = sizeof sin6[TCPDROP_FOREIGN]; 361 sin6[TCPDROP_FOREIGN].sin6_family = AF_INET6; 362 sin6[TCPDROP_FOREIGN].sin6_port = inc->inc_fport; 363 memcpy(&sin6[TCPDROP_FOREIGN].sin6_addr, &inc->inc6_faddr, 364 sizeof inc->inc6_faddr); 365 foreign = (struct sockaddr *)&sin6[TCPDROP_FOREIGN]; 366 } else { 367 memset(sin4, 0, sizeof sin4); 368 369 sin4[TCPDROP_LOCAL].sin_len = sizeof sin4[TCPDROP_LOCAL]; 370 sin4[TCPDROP_LOCAL].sin_family = AF_INET; 371 sin4[TCPDROP_LOCAL].sin_port = inc->inc_lport; 372 memcpy(&sin4[TCPDROP_LOCAL].sin_addr, &inc->inc_laddr, 373 sizeof inc->inc_laddr); 374 local = (struct sockaddr *)&sin4[TCPDROP_LOCAL]; 375 376 sin4[TCPDROP_FOREIGN].sin_len = sizeof sin4[TCPDROP_FOREIGN]; 377 sin4[TCPDROP_FOREIGN].sin_family = AF_INET; 378 sin4[TCPDROP_FOREIGN].sin_port = inc->inc_fport; 379 memcpy(&sin4[TCPDROP_FOREIGN].sin_addr, &inc->inc_faddr, 380 sizeof inc->inc_faddr); 381 foreign = (struct sockaddr *)&sin4[TCPDROP_FOREIGN]; 382 } 383 384 return (tcpdrop(local, foreign)); 385 } 386 387 static void 388 usage(void) 389 { 390 fprintf(stderr, 391 "usage: tcpdrop local-address local-port foreign-address foreign-port\n" 392 " tcpdrop local-address:local-port foreign-address:foreign-port\n" 393 " tcpdrop local-address.local-port foreign-address.foreign-port\n" 394 " tcpdrop [-l] -a\n" 395 " tcpdrop [-l] -C cc-algo [-S stack] [-s state]\n" 396 " tcpdrop [-l] [-C cc-algo] -S stack [-s state]\n" 397 " tcpdrop [-l] [-C cc-algo] [-S stack] -s state\n"); 398 exit(1); 399 } 400