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(void); 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 *lport, *fport; 70 bool dropall; 71 int ch; 72 73 dropall = false; 74 75 while ((ch = getopt(argc, argv, "al")) != -1) { 76 switch (ch) { 77 case 'a': 78 dropall = true; 79 break; 80 case 'l': 81 tcpdrop_list_commands = true; 82 break; 83 default: 84 usage(); 85 } 86 } 87 argc -= optind; 88 argv += optind; 89 90 if (dropall) { 91 if (argc != 0) 92 usage(); 93 if (!tcpdropall()) 94 exit(1); 95 exit(0); 96 } 97 98 if ((argc != 2 && argc != 4) || tcpdrop_list_commands) 99 usage(); 100 101 if (argc == 2) { 102 lport = findport(argv[0]); 103 fport = findport(argv[1]); 104 if (lport == NULL || lport[1] == '\0' || fport == NULL || 105 fport[1] == '\0') 106 usage(); 107 *lport++ = '\0'; 108 *fport++ = '\0'; 109 if (!tcpdropbyname(argv[0], lport, argv[1], fport)) 110 exit(1); 111 } else if (!tcpdropbyname(argv[0], argv[1], argv[2], argv[3])) 112 exit(1); 113 114 exit(0); 115 } 116 117 static char * 118 findport(const char *arg) 119 { 120 char *dot, *colon; 121 122 /* A strrspn() or strrpbrk() would be nice. */ 123 dot = strrchr(arg, '.'); 124 colon = strrchr(arg, ':'); 125 if (dot == NULL) 126 return (colon); 127 if (colon == NULL) 128 return (dot); 129 if (dot < colon) 130 return (colon); 131 else 132 return (dot); 133 } 134 135 static struct xinpgen * 136 getxpcblist(const char *name) 137 { 138 struct xinpgen *xinp; 139 size_t len; 140 int rv; 141 142 len = 0; 143 rv = sysctlbyname(name, NULL, &len, NULL, 0); 144 if (rv == -1) 145 err(1, "sysctlbyname %s", name); 146 147 if (len == 0) 148 errx(1, "%s is empty", name); 149 150 xinp = malloc(len); 151 if (xinp == NULL) 152 errx(1, "malloc failed"); 153 154 rv = sysctlbyname(name, xinp, &len, NULL, 0); 155 if (rv == -1) 156 err(1, "sysctlbyname %s", name); 157 158 return (xinp); 159 } 160 161 static void 162 sockinfo(const struct sockaddr *sa, struct host_service *hs) 163 { 164 static const int flags = NI_NUMERICHOST | NI_NUMERICSERV; 165 int rv; 166 167 rv = getnameinfo(sa, sa->sa_len, hs->hs_host, sizeof hs->hs_host, 168 hs->hs_service, sizeof hs->hs_service, flags); 169 if (rv == -1) 170 err(1, "getnameinfo"); 171 } 172 173 static bool 174 tcpdrop(const struct sockaddr *lsa, const struct sockaddr *fsa) 175 { 176 struct host_service local, foreign; 177 struct sockaddr_storage addrs[2]; 178 int rv; 179 180 memcpy(&addrs[TCPDROP_FOREIGN], fsa, fsa->sa_len); 181 memcpy(&addrs[TCPDROP_LOCAL], lsa, lsa->sa_len); 182 183 sockinfo(lsa, &local); 184 sockinfo(fsa, &foreign); 185 186 if (tcpdrop_list_commands) { 187 printf("tcpdrop %s %s %s %s\n", local.hs_host, local.hs_service, 188 foreign.hs_host, foreign.hs_service); 189 return (true); 190 } 191 192 rv = sysctlbyname("net.inet.tcp.drop", NULL, NULL, &addrs, 193 sizeof addrs); 194 if (rv == -1) { 195 warn("%s %s %s %s", local.hs_host, local.hs_service, 196 foreign.hs_host, foreign.hs_service); 197 return (false); 198 } 199 printf("%s %s %s %s: dropped\n", local.hs_host, local.hs_service, 200 foreign.hs_host, foreign.hs_service); 201 return (true); 202 } 203 204 static bool 205 tcpdropall(void) 206 { 207 struct xinpgen *head, *xinp; 208 struct xtcpcb *xtp; 209 struct xinpcb *xip; 210 bool ok; 211 212 ok = true; 213 214 head = getxpcblist("net.inet.tcp.pcblist"); 215 216 #define XINP_NEXT(xinp) \ 217 ((struct xinpgen *)((uintptr_t)(xinp) + (xinp)->xig_len)) 218 219 for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp; 220 xinp = XINP_NEXT(xinp)) { 221 xtp = (struct xtcpcb *)xinp; 222 xip = &xtp->xt_inp; 223 224 /* 225 * XXX 226 * Check protocol, support just v4 or v6, etc. 227 */ 228 229 /* Ignore PCBs which were freed during copyout. */ 230 if (xip->inp_gencnt > head->xig_gen) 231 continue; 232 233 /* Skip listening sockets. */ 234 if (xtp->t_state == TCPS_LISTEN) 235 continue; 236 237 if (!tcpdropconn(&xip->inp_inc)) 238 ok = false; 239 } 240 free(head); 241 242 return (ok); 243 } 244 245 static bool 246 tcpdropbyname(const char *lhost, const char *lport, const char *fhost, 247 const char *fport) 248 { 249 static const struct addrinfo hints = { 250 /* 251 * Look for streams in all domains. 252 */ 253 .ai_family = AF_UNSPEC, 254 .ai_socktype = SOCK_STREAM, 255 }; 256 struct addrinfo *ail, *local, *aif, *foreign; 257 int error; 258 bool ok, infamily; 259 260 error = getaddrinfo(lhost, lport, &hints, &local); 261 if (error != 0) 262 errx(1, "getaddrinfo: %s port %s: %s", lhost, lport, 263 gai_strerror(error)); 264 265 error = getaddrinfo(fhost, fport, &hints, &foreign); 266 if (error != 0) { 267 freeaddrinfo(local); /* XXX gratuitous */ 268 errx(1, "getaddrinfo: %s port %s: %s", fhost, fport, 269 gai_strerror(error)); 270 } 271 272 ok = true; 273 infamily = false; 274 275 /* 276 * Try every combination of local and foreign address pairs. 277 */ 278 for (ail = local; ail != NULL; ail = ail->ai_next) { 279 for (aif = foreign; aif != NULL; aif = aif->ai_next) { 280 if (ail->ai_family != aif->ai_family) 281 continue; 282 infamily = true; 283 if (!tcpdrop(ail->ai_addr, aif->ai_addr)) 284 ok = false; 285 } 286 } 287 288 if (!infamily) { 289 warnx("%s %s %s %s: different address families", lhost, lport, 290 fhost, fport); 291 ok = false; 292 } 293 294 freeaddrinfo(local); 295 freeaddrinfo(foreign); 296 297 return (ok); 298 } 299 300 static bool 301 tcpdropconn(const struct in_conninfo *inc) 302 { 303 struct sockaddr *local, *foreign; 304 struct sockaddr_in6 sin6[2]; 305 struct sockaddr_in sin4[2]; 306 307 if ((inc->inc_flags & INC_ISIPV6) != 0) { 308 memset(sin6, 0, sizeof sin6); 309 310 sin6[TCPDROP_LOCAL].sin6_len = sizeof sin6[TCPDROP_LOCAL]; 311 sin6[TCPDROP_LOCAL].sin6_family = AF_INET6; 312 sin6[TCPDROP_LOCAL].sin6_port = inc->inc_lport; 313 memcpy(&sin6[TCPDROP_LOCAL].sin6_addr, &inc->inc6_laddr, 314 sizeof inc->inc6_laddr); 315 local = (struct sockaddr *)&sin6[TCPDROP_LOCAL]; 316 317 sin6[TCPDROP_FOREIGN].sin6_len = sizeof sin6[TCPDROP_FOREIGN]; 318 sin6[TCPDROP_FOREIGN].sin6_family = AF_INET6; 319 sin6[TCPDROP_FOREIGN].sin6_port = inc->inc_fport; 320 memcpy(&sin6[TCPDROP_FOREIGN].sin6_addr, &inc->inc6_faddr, 321 sizeof inc->inc6_faddr); 322 foreign = (struct sockaddr *)&sin6[TCPDROP_FOREIGN]; 323 } else { 324 memset(sin4, 0, sizeof sin4); 325 326 sin4[TCPDROP_LOCAL].sin_len = sizeof sin4[TCPDROP_LOCAL]; 327 sin4[TCPDROP_LOCAL].sin_family = AF_INET; 328 sin4[TCPDROP_LOCAL].sin_port = inc->inc_lport; 329 memcpy(&sin4[TCPDROP_LOCAL].sin_addr, &inc->inc_laddr, 330 sizeof inc->inc_laddr); 331 local = (struct sockaddr *)&sin4[TCPDROP_LOCAL]; 332 333 sin4[TCPDROP_FOREIGN].sin_len = sizeof sin4[TCPDROP_FOREIGN]; 334 sin4[TCPDROP_FOREIGN].sin_family = AF_INET; 335 sin4[TCPDROP_FOREIGN].sin_port = inc->inc_fport; 336 memcpy(&sin4[TCPDROP_FOREIGN].sin_addr, &inc->inc_faddr, 337 sizeof inc->inc_faddr); 338 foreign = (struct sockaddr *)&sin4[TCPDROP_FOREIGN]; 339 } 340 341 return (tcpdrop(local, foreign)); 342 } 343 344 static void 345 usage(void) 346 { 347 fprintf(stderr, 348 "usage: tcpdrop local-address local-port foreign-address foreign-port\n" 349 " tcpdrop local-address:local-port foreign-address:foreign-port\n" 350 " tcpdrop local-address.local-port foreign-address.foreign-port\n" 351 " tcpdrop [-l] -a\n"); 352 exit(1); 353 } 354