1141381Smaxim/* $OpenBSD: tcpdrop.c,v 1.4 2004/05/22 23:55:22 deraadt Exp $ */
2141381Smaxim
3141381Smaxim/*-
4193203Sjmallett * Copyright (c) 2009 Juli Mallett <jmallett@FreeBSD.org>
5141381Smaxim * Copyright (c) 2004 Markus Friedl <markus@openbsd.org>
6141381Smaxim *
7141381Smaxim * Permission to use, copy, modify, and distribute this software for any
8141381Smaxim * purpose with or without fee is hereby granted, provided that the above
9141381Smaxim * copyright notice and this permission notice appear in all copies.
10141381Smaxim *
11141381Smaxim * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12141381Smaxim * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13141381Smaxim * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14141381Smaxim * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15141381Smaxim * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16141381Smaxim * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17141381Smaxim * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18141381Smaxim */
19141381Smaxim
20141381Smaxim#include <sys/cdefs.h>
21141381Smaxim__FBSDID("$FreeBSD$");
22141381Smaxim
23222765Sbz#include <sys/param.h>
24141381Smaxim#include <sys/types.h>
25141381Smaxim#include <sys/socket.h>
26193203Sjmallett#include <sys/socketvar.h>
27141381Smaxim#include <sys/sysctl.h>
28222765Sbz
29141381Smaxim#include <netinet/in.h>
30193203Sjmallett#include <netinet/in_pcb.h>
31193203Sjmallett#define TCPSTATES
32193203Sjmallett#include <netinet/tcp_fsm.h>
33141381Smaxim#include <netinet/tcp_var.h>
34141381Smaxim
35141381Smaxim#include <err.h>
36141381Smaxim#include <netdb.h>
37193203Sjmallett#include <stdbool.h>
38141381Smaxim#include <stdio.h>
39141381Smaxim#include <stdlib.h>
40141381Smaxim#include <string.h>
41193203Sjmallett#include <unistd.h>
42141381Smaxim
43193203Sjmallett#define	TCPDROP_FOREIGN		0
44193203Sjmallett#define	TCPDROP_LOCAL		1
45193203Sjmallett
46193203Sjmallettstruct host_service {
47193203Sjmallett	char hs_host[NI_MAXHOST];
48193203Sjmallett	char hs_service[NI_MAXSERV];
49193203Sjmallett};
50193203Sjmallett
51193203Sjmallettstatic bool tcpdrop_list_commands = false;
52193203Sjmallett
53246129Sjhbstatic char *findport(const char *);
54193203Sjmallettstatic struct xinpgen *getxpcblist(const char *);
55193203Sjmallettstatic void sockinfo(const struct sockaddr *, struct host_service *);
56193203Sjmallettstatic bool tcpdrop(const struct sockaddr *, const struct sockaddr *);
57193203Sjmallettstatic bool tcpdropall(void);
58193203Sjmallettstatic bool tcpdropbyname(const char *, const char *, const char *,
59193203Sjmallett    const char *);
60193203Sjmallettstatic bool tcpdropconn(const struct in_conninfo *);
61193203Sjmallettstatic void usage(void);
62193203Sjmallett
63141381Smaxim/*
64141381Smaxim * Drop a tcp connection.
65141381Smaxim */
66141381Smaximint
67141381Smaximmain(int argc, char *argv[])
68141381Smaxim{
69246129Sjhb	char *lport, *fport;
70193203Sjmallett	bool dropall;
71193203Sjmallett	int ch;
72193203Sjmallett
73193203Sjmallett	dropall = false;
74193203Sjmallett
75193203Sjmallett	while ((ch = getopt(argc, argv, "al")) != -1) {
76193203Sjmallett		switch (ch) {
77193203Sjmallett		case 'a':
78193203Sjmallett			dropall = true;
79193203Sjmallett			break;
80193203Sjmallett		case 'l':
81193203Sjmallett			tcpdrop_list_commands = true;
82193203Sjmallett			break;
83193203Sjmallett		default:
84193203Sjmallett			usage();
85193203Sjmallett		}
86193203Sjmallett	}
87193203Sjmallett	argc -= optind;
88193203Sjmallett	argv += optind;
89193203Sjmallett
90193203Sjmallett	if (dropall) {
91193203Sjmallett		if (argc != 0)
92193203Sjmallett			usage();
93193203Sjmallett		if (!tcpdropall())
94193203Sjmallett			exit(1);
95193203Sjmallett		exit(0);
96193203Sjmallett	}
97193203Sjmallett
98246129Sjhb	if ((argc != 2 && argc != 4) || tcpdrop_list_commands)
99193203Sjmallett		usage();
100193203Sjmallett
101246129Sjhb	if (argc == 2) {
102246129Sjhb		lport = findport(argv[0]);
103246129Sjhb		fport = findport(argv[1]);
104246129Sjhb		if (lport == NULL || lport[1] == '\0' || fport == NULL ||
105246129Sjhb		    fport[1] == '\0')
106246129Sjhb			usage();
107246129Sjhb		*lport++ = '\0';
108246129Sjhb		*fport++ = '\0';
109246129Sjhb		if (!tcpdropbyname(argv[0], lport, argv[1], fport))
110246129Sjhb			exit(1);
111246129Sjhb	} else if (!tcpdropbyname(argv[0], argv[1], argv[2], argv[3]))
112193203Sjmallett		exit(1);
113193203Sjmallett
114193203Sjmallett	exit(0);
115193203Sjmallett}
116193203Sjmallett
117246129Sjhbstatic char *
118246129Sjhbfindport(const char *arg)
119246129Sjhb{
120246129Sjhb	char *dot, *colon;
121246129Sjhb
122246129Sjhb	/* A strrspn() or strrpbrk() would be nice. */
123246129Sjhb	dot = strrchr(arg, '.');
124246129Sjhb	colon = strrchr(arg, ':');
125246129Sjhb	if (dot == NULL)
126246129Sjhb		return (colon);
127246129Sjhb	if (colon == NULL)
128246129Sjhb		return (dot);
129246129Sjhb	if (dot < colon)
130246129Sjhb		return (colon);
131246129Sjhb	else
132246129Sjhb		return (dot);
133246129Sjhb}
134246129Sjhb
135193203Sjmallettstatic struct xinpgen *
136193203Sjmallettgetxpcblist(const char *name)
137193203Sjmallett{
138193203Sjmallett	struct xinpgen *xinp;
139193203Sjmallett	size_t len;
140193203Sjmallett	int rv;
141193203Sjmallett
142193203Sjmallett	len = 0;
143193203Sjmallett	rv = sysctlbyname(name, NULL, &len, NULL, 0);
144193203Sjmallett	if (rv == -1)
145193203Sjmallett		err(1, "sysctlbyname %s", name);
146193203Sjmallett
147193203Sjmallett	if (len == 0)
148193203Sjmallett		errx(1, "%s is empty", name);
149193203Sjmallett
150193203Sjmallett	xinp = malloc(len);
151193203Sjmallett	if (xinp == NULL)
152193203Sjmallett		errx(1, "malloc failed");
153193203Sjmallett
154193203Sjmallett	rv = sysctlbyname(name, xinp, &len, NULL, 0);
155193203Sjmallett	if (rv == -1)
156193203Sjmallett		err(1, "sysctlbyname %s", name);
157193203Sjmallett
158193203Sjmallett	return (xinp);
159193203Sjmallett}
160193203Sjmallett
161193203Sjmallettstatic void
162193203Sjmallettsockinfo(const struct sockaddr *sa, struct host_service *hs)
163193203Sjmallett{
164193203Sjmallett	static const int flags = NI_NUMERICHOST | NI_NUMERICSERV;
165193203Sjmallett	int rv;
166193203Sjmallett
167193203Sjmallett	rv = getnameinfo(sa, sa->sa_len, hs->hs_host, sizeof hs->hs_host,
168193203Sjmallett	    hs->hs_service, sizeof hs->hs_service, flags);
169193203Sjmallett	if (rv == -1)
170193203Sjmallett		err(1, "getnameinfo");
171193203Sjmallett}
172193203Sjmallett
173193203Sjmallettstatic bool
174193203Sjmalletttcpdrop(const struct sockaddr *lsa, const struct sockaddr *fsa)
175193203Sjmallett{
176193203Sjmallett	struct host_service local, foreign;
177141886Smaxim	struct sockaddr_storage addrs[2];
178193203Sjmallett	int rv;
179141381Smaxim
180193203Sjmallett	memcpy(&addrs[TCPDROP_FOREIGN], fsa, fsa->sa_len);
181193203Sjmallett	memcpy(&addrs[TCPDROP_LOCAL], lsa, lsa->sa_len);
182193203Sjmallett
183193203Sjmallett	sockinfo(lsa, &local);
184193203Sjmallett	sockinfo(fsa, &foreign);
185193203Sjmallett
186193203Sjmallett	if (tcpdrop_list_commands) {
187193203Sjmallett		printf("tcpdrop %s %s %s %s\n", local.hs_host, local.hs_service,
188193203Sjmallett		    foreign.hs_host, foreign.hs_service);
189193203Sjmallett		return (true);
190173223Sru	}
191193203Sjmallett
192193203Sjmallett	rv = sysctlbyname("net.inet.tcp.drop", NULL, NULL, &addrs,
193193203Sjmallett	    sizeof addrs);
194193203Sjmallett	if (rv == -1) {
195193203Sjmallett		warn("%s %s %s %s", local.hs_host, local.hs_service,
196193203Sjmallett		    foreign.hs_host, foreign.hs_service);
197193203Sjmallett		return (false);
198141381Smaxim	}
199193203Sjmallett	printf("%s %s %s %s: dropped\n", local.hs_host, local.hs_service,
200193203Sjmallett	    foreign.hs_host, foreign.hs_service);
201193203Sjmallett	return (true);
202193203Sjmallett}
203193203Sjmallett
204193203Sjmallettstatic bool
205193203Sjmalletttcpdropall(void)
206193203Sjmallett{
207193203Sjmallett	struct xinpgen *head, *xinp;
208193203Sjmallett	struct xtcpcb *xpcb;
209193203Sjmallett	struct tcpcb *tp;
210193203Sjmallett	struct inpcb *inp;
211193203Sjmallett	bool ok;
212193203Sjmallett
213193203Sjmallett	ok = true;
214193203Sjmallett
215193203Sjmallett	head = getxpcblist("net.inet.tcp.pcblist");
216193203Sjmallett
217193203Sjmallett#define	XINP_NEXT(xinp)							\
218193203Sjmallett	((struct xinpgen *)((uintptr_t)(xinp) + (xinp)->xig_len))
219193203Sjmallett
220193203Sjmallett	for (xinp = XINP_NEXT(head); xinp->xig_len > sizeof *xinp;
221193203Sjmallett	    xinp = XINP_NEXT(xinp)) {
222193203Sjmallett		xpcb = (struct xtcpcb *)xinp;
223193203Sjmallett		tp = &xpcb->xt_tp;
224193203Sjmallett		inp = &xpcb->xt_inp;
225193203Sjmallett
226193203Sjmallett		/*
227193203Sjmallett		 * XXX
228193203Sjmallett		 * Check protocol, support just v4 or v6, etc.
229193203Sjmallett		 */
230193203Sjmallett
231193203Sjmallett		/* Ignore PCBs which were freed during copyout.  */
232193203Sjmallett		if (inp->inp_gencnt > head->xig_gen)
233193203Sjmallett			continue;
234193203Sjmallett
235193203Sjmallett		/* Skip listening sockets.  */
236193203Sjmallett		if (tp->t_state == TCPS_LISTEN)
237193203Sjmallett			continue;
238193203Sjmallett
239193203Sjmallett		if (!tcpdropconn(&inp->inp_inc))
240193203Sjmallett			ok = false;
241193203Sjmallett	}
242193203Sjmallett	free(head);
243193203Sjmallett
244193203Sjmallett	return (ok);
245193203Sjmallett}
246193203Sjmallett
247193203Sjmallettstatic bool
248193203Sjmalletttcpdropbyname(const char *lhost, const char *lport, const char *fhost,
249193203Sjmallett    const char *fport)
250193203Sjmallett{
251193203Sjmallett	static const struct addrinfo hints = {
252193203Sjmallett		/*
253193203Sjmallett		 * Look for streams in all domains.
254193203Sjmallett		 */
255193203Sjmallett		.ai_family = AF_UNSPEC,
256193203Sjmallett		.ai_socktype = SOCK_STREAM,
257193203Sjmallett	};
258193203Sjmallett	struct addrinfo *ail, *local, *aif, *foreign;
259193203Sjmallett	int error;
260193203Sjmallett	bool ok, infamily;
261193203Sjmallett
262193203Sjmallett	error = getaddrinfo(lhost, lport, &hints, &local);
263193203Sjmallett	if (error != 0)
264193203Sjmallett		errx(1, "getaddrinfo: %s port %s: %s", lhost, lport,
265193203Sjmallett		    gai_strerror(error));
266193203Sjmallett
267193203Sjmallett	error = getaddrinfo(fhost, fport, &hints, &foreign);
268193203Sjmallett	if (error != 0) {
269193203Sjmallett		freeaddrinfo(local); /* XXX gratuitous */
270246129Sjhb		errx(1, "getaddrinfo: %s port %s: %s", fhost, fport,
271193203Sjmallett		    gai_strerror(error));
272193203Sjmallett	}
273193203Sjmallett
274193203Sjmallett	ok = true;
275193203Sjmallett	infamily = false;
276193203Sjmallett
277193203Sjmallett	/*
278193203Sjmallett	 * Try every combination of local and foreign address pairs.
279193203Sjmallett	 */
280193203Sjmallett	for (ail = local; ail != NULL; ail = ail->ai_next) {
281193203Sjmallett		for (aif = foreign; aif != NULL; aif = aif->ai_next) {
282141381Smaxim			if (ail->ai_family != aif->ai_family)
283141381Smaxim				continue;
284193203Sjmallett			infamily = true;
285193203Sjmallett			if (!tcpdrop(ail->ai_addr, aif->ai_addr))
286193203Sjmallett				ok = false;
287141381Smaxim		}
288141381Smaxim	}
289193203Sjmallett
290193203Sjmallett	if (!infamily) {
291193203Sjmallett		warnx("%s %s %s %s: different address families", lhost, lport,
292193203Sjmallett		    fhost, fport);
293193203Sjmallett		ok = false;
294193203Sjmallett	}
295193203Sjmallett
296193203Sjmallett	freeaddrinfo(local);
297193203Sjmallett	freeaddrinfo(foreign);
298193203Sjmallett
299193203Sjmallett	return (ok);
300141381Smaxim}
301193203Sjmallett
302193203Sjmallettstatic bool
303193203Sjmalletttcpdropconn(const struct in_conninfo *inc)
304193203Sjmallett{
305193203Sjmallett	struct sockaddr *local, *foreign;
306193203Sjmallett	struct sockaddr_in6 sin6[2];
307193203Sjmallett	struct sockaddr_in sin4[2];
308193203Sjmallett
309193203Sjmallett	if ((inc->inc_flags & INC_ISIPV6) != 0) {
310193203Sjmallett		memset(sin6, 0, sizeof sin6);
311193203Sjmallett
312193203Sjmallett		sin6[TCPDROP_LOCAL].sin6_len = sizeof sin6[TCPDROP_LOCAL];
313193203Sjmallett		sin6[TCPDROP_LOCAL].sin6_family = AF_INET6;
314193203Sjmallett		sin6[TCPDROP_LOCAL].sin6_port = inc->inc_lport;
315193203Sjmallett		memcpy(&sin6[TCPDROP_LOCAL].sin6_addr, &inc->inc6_laddr,
316193203Sjmallett		    sizeof inc->inc6_laddr);
317193203Sjmallett		local = (struct sockaddr *)&sin6[TCPDROP_LOCAL];
318193203Sjmallett
319193203Sjmallett		sin6[TCPDROP_FOREIGN].sin6_len = sizeof sin6[TCPDROP_FOREIGN];
320193203Sjmallett		sin6[TCPDROP_FOREIGN].sin6_family = AF_INET6;
321193203Sjmallett		sin6[TCPDROP_FOREIGN].sin6_port = inc->inc_fport;
322193203Sjmallett		memcpy(&sin6[TCPDROP_FOREIGN].sin6_addr, &inc->inc6_faddr,
323193203Sjmallett		    sizeof inc->inc6_faddr);
324193203Sjmallett		foreign = (struct sockaddr *)&sin6[TCPDROP_FOREIGN];
325193203Sjmallett	} else {
326193203Sjmallett		memset(&sin4[TCPDROP_LOCAL], 0, sizeof sin4[TCPDROP_LOCAL]);
327193203Sjmallett
328193203Sjmallett		sin4[TCPDROP_LOCAL].sin_len = sizeof sin4[TCPDROP_LOCAL];
329193203Sjmallett		sin4[TCPDROP_LOCAL].sin_family = AF_INET;
330193203Sjmallett		sin4[TCPDROP_LOCAL].sin_port = inc->inc_lport;
331193203Sjmallett		memcpy(&sin4[TCPDROP_LOCAL].sin_addr, &inc->inc_laddr,
332193203Sjmallett		    sizeof inc->inc_laddr);
333193203Sjmallett		local = (struct sockaddr *)&sin4[TCPDROP_LOCAL];
334193203Sjmallett
335193203Sjmallett		sin4[TCPDROP_FOREIGN].sin_len = sizeof sin4[TCPDROP_FOREIGN];
336193203Sjmallett		sin4[TCPDROP_FOREIGN].sin_family = AF_INET;
337193203Sjmallett		sin4[TCPDROP_FOREIGN].sin_port = inc->inc_fport;
338193203Sjmallett		memcpy(&sin4[TCPDROP_FOREIGN].sin_addr, &inc->inc_faddr,
339193203Sjmallett		    sizeof inc->inc_faddr);
340193203Sjmallett		foreign = (struct sockaddr *)&sin4[TCPDROP_FOREIGN];
341193203Sjmallett	}
342193203Sjmallett
343193203Sjmallett	return (tcpdrop(local, foreign));
344193203Sjmallett}
345193203Sjmallett
346193203Sjmallettstatic void
347193203Sjmallettusage(void)
348193203Sjmallett{
349193203Sjmallett	fprintf(stderr,
350193203Sjmallett"usage: tcpdrop local-address local-port foreign-address foreign-port\n"
351246129Sjhb"       tcpdrop local-address:local-port foreign-address:foreign-port\n"
352246129Sjhb"       tcpdrop local-address.local-port foreign-address.foreign-port\n"
353193203Sjmallett"       tcpdrop [-l] -a\n");
354193203Sjmallett	exit(1);
355193203Sjmallett}
356