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