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 *xpcb; 209 struct tcpcb *tp; 210 struct inpcb *inp; 211 bool ok; 212 213 ok = true; 214 215 head = getxpcblist("net.inet.tcp.pcblist"); 216 217 #define XINP_NEXT(xinp) \ 218 ((struct xinpgen *)((uintptr_t)(xinp) + (xinp)->xig_len)) 219 220 for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp; 221 xinp = XINP_NEXT(xinp)) { 222 xpcb = (struct xtcpcb *)xinp; 223 tp = &xpcb->xt_tp; 224 inp = &xpcb->xt_inp; 225 226 /* 227 * XXX 228 * Check protocol, support just v4 or v6, etc. 229 */ 230 231 /* Ignore PCBs which were freed during copyout. */ 232 if (inp->inp_gencnt > head->xig_gen) 233 continue; 234 235 /* Skip listening sockets. */ 236 if (tp->t_state == TCPS_LISTEN) 237 continue; 238 239 if (!tcpdropconn(&inp->inp_inc)) 240 ok = false; 241 } 242 free(head); 243 244 return (ok); 245 } 246 247 static bool 248 tcpdropbyname(const char *lhost, const char *lport, const char *fhost, 249 const char *fport) 250 { 251 static const struct addrinfo hints = { 252 /* 253 * Look for streams in all domains. 254 */ 255 .ai_family = AF_UNSPEC, 256 .ai_socktype = SOCK_STREAM, 257 }; 258 struct addrinfo *ail, *local, *aif, *foreign; 259 int error; 260 bool ok, infamily; 261 262 error = getaddrinfo(lhost, lport, &hints, &local); 263 if (error != 0) 264 errx(1, "getaddrinfo: %s port %s: %s", lhost, lport, 265 gai_strerror(error)); 266 267 error = getaddrinfo(fhost, fport, &hints, &foreign); 268 if (error != 0) { 269 freeaddrinfo(local); /* XXX gratuitous */ 270 errx(1, "getaddrinfo: %s port %s: %s", fhost, fport, 271 gai_strerror(error)); 272 } 273 274 ok = true; 275 infamily = false; 276 277 /* 278 * Try every combination of local and foreign address pairs. 279 */ 280 for (ail = local; ail != NULL; ail = ail->ai_next) { 281 for (aif = foreign; aif != NULL; aif = aif->ai_next) { 282 if (ail->ai_family != aif->ai_family) 283 continue; 284 infamily = true; 285 if (!tcpdrop(ail->ai_addr, aif->ai_addr)) 286 ok = false; 287 } 288 } 289 290 if (!infamily) { 291 warnx("%s %s %s %s: different address families", lhost, lport, 292 fhost, fport); 293 ok = false; 294 } 295 296 freeaddrinfo(local); 297 freeaddrinfo(foreign); 298 299 return (ok); 300 } 301 302 static bool 303 tcpdropconn(const struct in_conninfo *inc) 304 { 305 struct sockaddr *local, *foreign; 306 struct sockaddr_in6 sin6[2]; 307 struct sockaddr_in sin4[2]; 308 309 if ((inc->inc_flags & INC_ISIPV6) != 0) { 310 memset(sin6, 0, sizeof sin6); 311 312 sin6[TCPDROP_LOCAL].sin6_len = sizeof sin6[TCPDROP_LOCAL]; 313 sin6[TCPDROP_LOCAL].sin6_family = AF_INET6; 314 sin6[TCPDROP_LOCAL].sin6_port = inc->inc_lport; 315 memcpy(&sin6[TCPDROP_LOCAL].sin6_addr, &inc->inc6_laddr, 316 sizeof inc->inc6_laddr); 317 local = (struct sockaddr *)&sin6[TCPDROP_LOCAL]; 318 319 sin6[TCPDROP_FOREIGN].sin6_len = sizeof sin6[TCPDROP_FOREIGN]; 320 sin6[TCPDROP_FOREIGN].sin6_family = AF_INET6; 321 sin6[TCPDROP_FOREIGN].sin6_port = inc->inc_fport; 322 memcpy(&sin6[TCPDROP_FOREIGN].sin6_addr, &inc->inc6_faddr, 323 sizeof inc->inc6_faddr); 324 foreign = (struct sockaddr *)&sin6[TCPDROP_FOREIGN]; 325 } else { 326 memset(&sin4[TCPDROP_LOCAL], 0, sizeof sin4[TCPDROP_LOCAL]); 327 328 sin4[TCPDROP_LOCAL].sin_len = sizeof sin4[TCPDROP_LOCAL]; 329 sin4[TCPDROP_LOCAL].sin_family = AF_INET; 330 sin4[TCPDROP_LOCAL].sin_port = inc->inc_lport; 331 memcpy(&sin4[TCPDROP_LOCAL].sin_addr, &inc->inc_laddr, 332 sizeof inc->inc_laddr); 333 local = (struct sockaddr *)&sin4[TCPDROP_LOCAL]; 334 335 sin4[TCPDROP_FOREIGN].sin_len = sizeof sin4[TCPDROP_FOREIGN]; 336 sin4[TCPDROP_FOREIGN].sin_family = AF_INET; 337 sin4[TCPDROP_FOREIGN].sin_port = inc->inc_fport; 338 memcpy(&sin4[TCPDROP_FOREIGN].sin_addr, &inc->inc_faddr, 339 sizeof inc->inc_faddr); 340 foreign = (struct sockaddr *)&sin4[TCPDROP_FOREIGN]; 341 } 342 343 return (tcpdrop(local, foreign)); 344 } 345 346 static void 347 usage(void) 348 { 349 fprintf(stderr, 350 "usage: tcpdrop local-address local-port foreign-address foreign-port\n" 351 " tcpdrop local-address:local-port foreign-address:foreign-port\n" 352 " tcpdrop local-address.local-port foreign-address.foreign-port\n" 353 " tcpdrop [-l] -a\n"); 354 exit(1); 355 } 356