diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 660dd5be4bc7..a2e3d3c0aae7 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1273,3 +1273,89 @@ tcp_usrclosed(tp) return (tp); } +static int +sysctl_drop(SYSCTL_HANDLER_ARGS) +{ + struct tcp_ident_mapping tir; + struct inpcb *inp; + struct tcpcb *tp; + struct sockaddr_in *fin, *lin; +#ifdef INET6 + struct sockaddr_in6 *fin6, *lin6; + struct in6_addr f6, l6; +#endif + int error; + + inp = NULL; + fin = lin = NULL; +#ifdef INET6 + fin6 = lin6 = NULL; +#endif + error = 0; + + if (req->oldptr != NULL || req->oldlen != 0) + return (EINVAL); + if (req->newptr == NULL) + return (EPERM); + if (req->newlen < sizeof(tir)) + return (ENOMEM); + if ((error = copyin(req->newptr, &tir, sizeof(tir))) != 0) + return (error); + + switch (tir.faddr.ss_family) { +#ifdef INET6 + case AF_INET6: + fin6 = (struct sockaddr_in6 *)&tir.faddr; + lin6 = (struct sockaddr_in6 *)&tir.laddr; + if (fin6->sin6_len != sizeof(struct sockaddr_in6) || + lin6->sin6_len != sizeof(struct sockaddr_in6)) + return (EINVAL); + error = in6_embedscope(&f6, fin6, NULL, NULL); + if (error) + return (EINVAL); + error = in6_embedscope(&l6, lin6, NULL, NULL); + if (error) + return (EINVAL); + break; +#endif + case AF_INET: + fin = (struct sockaddr_in *)&tir.faddr; + lin = (struct sockaddr_in *)&tir.laddr; + if (fin->sin_len != sizeof(struct sockaddr_in) || + lin->sin_len != sizeof(struct sockaddr_in)) + return (EINVAL); + break; + default: + return (EINVAL); + } + INP_INFO_WLOCK(&tcbinfo); + switch (tir.faddr.ss_family) { +#ifdef INET6 + case AF_INET6: + inp = in6_pcblookup_hash(&tcbinfo, &f6, fin6->sin6_port, + &l6, lin6->sin6_port, 0, NULL); + break; +#endif + case AF_INET: + inp = in_pcblookup_hash(&tcbinfo, fin->sin_addr, fin->sin_port, + lin->sin_addr, lin->sin_port, 0, NULL); + break; + } + if (inp != NULL) { + INP_LOCK(inp); + if ((tp = intotcpcb(inp)) && + ((inp->inp_socket->so_options & SO_ACCEPTCONN) == 0)) { + tp = tcp_drop(tp, ECONNABORTED); + if (tp != NULL) + INP_UNLOCK(inp); + } else + INP_UNLOCK(inp); + } else + error = ESRCH; + INP_INFO_WUNLOCK(&tcbinfo); + return (error); +} + +SYSCTL_PROC(_net_inet_tcp, TCPCTL_DROP, drop, + CTLTYPE_STRUCT|CTLFLAG_WR|CTLFLAG_SKIP, NULL, + 0, sysctl_drop, "", "Drop TCP connection"); diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 4f87ef4c5645..fa98508be59a 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -451,6 +451,11 @@ struct xtcpcb { }; #endif +struct tcp_ident_mapping { + struct sockaddr_storage faddr, laddr; + uid_t euid, ruid; +}; + /* * Names for TCP sysctl objects */ @@ -467,7 +472,8 @@ struct xtcpcb { #define TCPCTL_DELACKTIME 12 /* time before sending delayed ACK */ #define TCPCTL_V6MSSDFLT 13 /* MSS default for IPv6 */ #define TCPCTL_SACK 14 /* Selective Acknowledgement,rfc 2018 */ -#define TCPCTL_MAXID 15 +#define TCPCTL_DROP 15 /* drop tcp connection */ +#define TCPCTL_MAXID 16 #define TCPCTL_NAMES { \ { 0, 0 }, \ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index d8f78dc04265..55f7871b9334 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -159,6 +159,7 @@ SUBDIR= ac \ syslogd \ tcpdchk \ tcpdmatch \ + tcpdrop \ tcpdump \ timed \ traceroute \ diff --git a/usr.sbin/tcpdrop/Makefile b/usr.sbin/tcpdrop/Makefile new file mode 100644 index 000000000000..44766eb9a0a3 --- /dev/null +++ b/usr.sbin/tcpdrop/Makefile @@ -0,0 +1,8 @@ +# $OpenBSD: Makefile,v 1.1 2004/04/26 19:51:20 markus Exp $ +# $FreeBSD$ + +PROG= tcpdrop +MAN= tcpdrop.8 +WARNS?= 6 + +.include diff --git a/usr.sbin/tcpdrop/tcpdrop.8 b/usr.sbin/tcpdrop/tcpdrop.8 new file mode 100644 index 000000000000..a77c1d3f85e7 --- /dev/null +++ b/usr.sbin/tcpdrop/tcpdrop.8 @@ -0,0 +1,64 @@ +.\" $OpenBSD: tcpdrop.8,v 1.5 2004/05/24 13:57:31 jmc Exp $ +.\" +.\" Copyright (c) 2004 Markus Friedl +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 21, 2004 +.Dt TCPDROP 8 +.Os +.Sh NAME +.Nm tcpdrop +.Nd drop a TCP connection +.Sh SYNOPSIS +.Nm tcpdrop +.Ar laddr +.Ar lport +.Ar faddr +.Ar fport +.Sh DESCRIPTION +The +.Nm +command drops the TCP connection specified by the local address +.Ar laddr , +port +.Ar lport +and the foreign address +.Ar faddr , +port +.Ar fport . +Addresses and ports can be specified by name or numeric value. +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +If a connection to +.Xr httpd 8 +is causing congestion on a network link, one can drop the TCP session +in charge: +.Bd -literal -offset indent +# fstat | egrep 'httpd.*internet.*<--' +www httpd 21307 3* internet stream tcp \e + 0xd1007ca8 192.168.5.41:80 <-- 192.168.5.1:26747 +.Ed +.Pp +The following command will drop the connection: +.Bd -literal -offset indent +# tcpdrop 192.168.5.41 80 192.168.5.1 26747 +.Ed +.Sh SEE ALSO +.Xr fstat 1 , +.Xr netstat 1 +.Sh AUTHORS +.An Markus Friedl Aq markus@openbsd.org diff --git a/usr.sbin/tcpdrop/tcpdrop.c b/usr.sbin/tcpdrop/tcpdrop.c new file mode 100644 index 000000000000..cfa8c2adf367 --- /dev/null +++ b/usr.sbin/tcpdrop/tcpdrop.c @@ -0,0 +1,88 @@ +/* $OpenBSD: tcpdrop.c,v 1.4 2004/05/22 23:55:22 deraadt Exp $ */ + +/*- + * Copyright (c) 2004 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Drop a tcp connection. + */ +int +main(int argc, char *argv[]) +{ + struct addrinfo hints, *ail, *aif, *laddr, *faddr; + struct tcp_ident_mapping tir; + int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_DROP }; + int gaierr, rval = 0; + char fhbuf[NI_MAXHOST], fsbuf[NI_MAXSERV], lhbuf[NI_MAXHOST], + lsbuf[NI_MAXSERV]; + + if (argc != 5) + errx(1, "usage: tcpdrop laddr lport faddr fport\n"); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if ((gaierr = getaddrinfo(argv[1], argv[2], &hints, &laddr)) != 0) + errx(1, "%s port %s: %s", argv[1], argv[2], + gai_strerror(gaierr)); + if ((gaierr = getaddrinfo(argv[3], argv[4], &hints, &faddr)) != 0) { + freeaddrinfo(laddr); + errx(1, "%s port %s: %s", argv[3], argv[4], + gai_strerror(gaierr)); + } + for (ail = laddr; ail; ail = ail->ai_next) { + for (aif = faddr; aif; aif = aif->ai_next) { + if (ail->ai_family != aif->ai_family) + continue; + memcpy(&tir.faddr, aif->ai_addr, aif->ai_addrlen); + memcpy(&tir.laddr, ail->ai_addr, ail->ai_addrlen); + if (getnameinfo(aif->ai_addr, aif->ai_addrlen, + fhbuf, sizeof(fhbuf), + fsbuf, sizeof(fsbuf), + NI_NUMERICHOST | NI_NUMERICSERV) == -1) + err(1, "getnameinfo"); + if (getnameinfo(ail->ai_addr, ail->ai_addrlen, + lhbuf, sizeof(lhbuf), + lsbuf, sizeof(lsbuf), + NI_NUMERICHOST | NI_NUMERICSERV) == -1) + err(1, "getnameinfo"); + if (sysctl(mib, sizeof (mib) / sizeof (int), NULL, + NULL, &tir, sizeof(tir)) == -1) { + rval = 1; + warn("%s %s %s %s", lhbuf, lsbuf, fhbuf, fsbuf); + } else + printf("%s %s %s %s: dropped\n", + lhbuf, lsbuf, fhbuf, fsbuf); + } + } + freeaddrinfo(laddr); + freeaddrinfo(faddr); + exit(rval); +}