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