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