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