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