xref: /openbsd-src/share/man/man4/divert.4 (revision 4cb74a97251101bfc8c54d71570124bf2c6d92ae)
1*4cb74a97Sjsg.\"     $OpenBSD: divert.4,v 1.20 2022/09/10 10:22:46 jsg Exp $
2d85f7bc1Smichele.\"
3d85f7bc1Smichele.\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
41f2012eeSlteo.\" Copyright (c) 2012-2014 Lawrence Teo <lteo@openbsd.org>
5d85f7bc1Smichele.\"
6d85f7bc1Smichele.\" Permission to use, copy, modify, and distribute this software for any
7d85f7bc1Smichele.\" purpose with or without fee is hereby granted, provided that the above
8d85f7bc1Smichele.\" copyright notice and this permission notice appear in all copies.
9d85f7bc1Smichele.\"
10d85f7bc1Smichele.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d85f7bc1Smichele.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d85f7bc1Smichele.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d85f7bc1Smichele.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d85f7bc1Smichele.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d85f7bc1Smichele.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d85f7bc1Smichele.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d85f7bc1Smichele.\"
18*4cb74a97Sjsg.Dd $Mdocdate: September 10 2022 $
19d85f7bc1Smichele.Dt DIVERT 4
20d85f7bc1Smichele.Os
21d85f7bc1Smichele.Sh NAME
22d85f7bc1Smichele.Nm divert
23f7a18a8bSjmc.Nd kernel packet diversion mechanism
24d85f7bc1Smichele.Sh SYNOPSIS
25ec5cd23eSschwarze.In sys/types.h
26ec5cd23eSschwarze.In sys/socket.h
27ec5cd23eSschwarze.In netinet/in.h
28d85f7bc1Smichele.Ft int
29d85f7bc1Smichele.Fn socket AF_INET SOCK_RAW IPPROTO_DIVERT
3002aa720bSmichele.Ft int
3102aa720bSmichele.Fn socket AF_INET6 SOCK_RAW IPPROTO_DIVERT
32d85f7bc1Smichele.Sh DESCRIPTION
3302aa720bSmicheleDivert sockets are part of a mechanism completely integrated with
3402aa720bSmichele.Xr pf 4
3502aa720bSmichelethat queues raw packets from the kernel stack to userspace applications,
3602aa720bSmicheleand vice versa.
3702aa720bSmichele.Pp
3802aa720bSmicheleA divert socket must be bound to a divert port through
3902aa720bSmichele.Xr bind 2 ,
4002aa720bSmichelewhich only the superuser can do.
4102aa720bSmicheleDivert ports have their own number space, completely separated from
42d85f7bc1Smichele.Xr tcp 4
43d85f7bc1Smicheleand
4402aa720bSmichele.Xr udp 4 .
4502aa720bSmicheleWhen
46d85f7bc1Smichele.Xr pf 4
479844e2e0Slteoprocesses a packet that matches a rule with the
489844e2e0Slteo.Ar divert-packet
499844e2e0Slteoparameter
509844e2e0Slteo(see
5102aa720bSmichele.Xr pf.conf 5
5241ce3b17Snaddyfor details), it is sent to the divert socket listening on the
539844e2e0Slteodivert port specified in the rule.
549844e2e0SlteoNote that
559844e2e0Slteo.Ar divert-packet
569844e2e0Slteoshould not be confused with
57399f2a2fSbenno.Ar divert-to
58399f2a2fSbennoor
59399f2a2fSbenno.Ar divert-reply ,
60399f2a2fSbennowhich do not use divert sockets.
6102aa720bSmicheleIf there are no divert sockets listening, the packets are dropped.
62d85f7bc1Smichele.Pp
6302aa720bSmichelePackets can be read via
64d85f7bc1Smichele.Xr read 2 ,
65d85f7bc1Smichele.Xr recv 2 ,
66d85f7bc1Smicheleor
67d85f7bc1Smichele.Xr recvfrom 2
68d85f7bc1Smichelefrom the divert socket.
6902aa720bSmicheleThe application that is processing the packets can then reinject them into the
7002aa720bSmichelekernel.
71800182dcSbluhmWith
72800182dcSbluhm.Xr recvfrom 2 ,
73800182dcSbluhman interface IP address is passed if it is an inbound packet.
74800182dcSbluhmOutbound packets provide the unspecified address.
75800182dcSbluhmWhen reinjecting, use this address as argument to
76800182dcSbluhm.Xr sendto 2 .
77800182dcSbluhmThis allows the kernel to guess the original incoming interface and
78*4cb74a97Sjsgprocess it as an incoming packet.
79800182dcSbluhmIf no interface IP address is given, the reinjected packet is treated
80800182dcSbluhmas an outgoing packet.
819844e2e0SlteoSince the userspace application could have modified the packets, upon
829844e2e0Slteoreinjection basic sanity checks are done to ensure that the packets are still
839844e2e0Slteovalid.
849844e2e0SlteoThe packets' IPv4 and protocol checksums (TCP, UDP, ICMP, and ICMPv6) are also
859844e2e0Slteorecalculated.
8602aa720bSmichele.Pp
87d85f7bc1SmicheleWriting to a divert socket can be achieved using
88d85f7bc1Smichele.Xr sendto 2
89d85f7bc1Smicheleand it will skip
90d85f7bc1Smichele.Xr pf 4
91d85f7bc1Smichelefilters to avoid loops.
92800182dcSbluhmNote that this means that a reinjected inbound packet will also not
93800182dcSbluhmrun through the pf out rules after being forwarded.
9402aa720bSmicheleA diverted packet that is not reinjected into the kernel stack is lost.
95d85f7bc1Smichele.Pp
9602aa720bSmicheleReceive and send divert socket buffer space can be tuned through
9702aa720bSmichele.Xr sysctl 8 .
9802aa720bSmichele.Xr netstat 1
9902aa720bSmicheleshows information relevant to divert sockets.
100800182dcSbluhmNote that the default is 64k and too short to handle full sized UDP
101800182dcSbluhmpackets.
102f189bd67Sjmc.Sh EXAMPLES
103f189bd67SjmcThe following PF rule queues outbound IPv4 packets to TCP port 80,
104f189bd67Sjmcas well as the return traffic, on the em0 interface to divert port 700:
105f189bd67Sjmc.Bd -literal -offset indent
106f189bd67Sjmcpass out on em0 inet proto tcp to port 80 divert-packet port 700
107f189bd67Sjmc.Ed
108f189bd67Sjmc.Pp
109f189bd67SjmcThe following program reads packets on divert port 700 and reinjects them
110f189bd67Sjmcback into the kernel.
111f189bd67SjmcThis program does not perform any processing of the packets,
112f189bd67Sjmcapart from discarding invalid IP packets.
113f189bd67Sjmc.Bd -literal
114f189bd67Sjmc#include <sys/types.h>
115f189bd67Sjmc#include <sys/socket.h>
116f189bd67Sjmc#include <netinet/in.h>
117f189bd67Sjmc#include <netinet/ip.h>
118f189bd67Sjmc#include <netinet/tcp.h>
119f189bd67Sjmc#include <arpa/inet.h>
120f189bd67Sjmc#include <stdio.h>
121f189bd67Sjmc#include <string.h>
122f189bd67Sjmc#include <err.h>
123f189bd67Sjmc
124f189bd67Sjmc#define DIVERT_PORT 700
125f189bd67Sjmc
126f189bd67Sjmcint
127f189bd67Sjmcmain(int argc, char *argv[])
128f189bd67Sjmc{
129f189bd67Sjmc	int fd, s;
130f189bd67Sjmc	struct sockaddr_in sin;
131f189bd67Sjmc	socklen_t sin_len;
132f189bd67Sjmc
133f189bd67Sjmc	fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
134f189bd67Sjmc	if (fd == -1)
135f189bd67Sjmc		err(1, "socket");
136f189bd67Sjmc
1371f2012eeSlteo	memset(&sin, 0, sizeof(sin));
138f189bd67Sjmc	sin.sin_family = AF_INET;
139f189bd67Sjmc	sin.sin_port = htons(DIVERT_PORT);
140f189bd67Sjmc	sin.sin_addr.s_addr = 0;
141f189bd67Sjmc
142f189bd67Sjmc	sin_len = sizeof(struct sockaddr_in);
143f189bd67Sjmc
144f189bd67Sjmc	s = bind(fd, (struct sockaddr *) &sin, sin_len);
145f189bd67Sjmc	if (s == -1)
146f189bd67Sjmc		err(1, "bind");
147f189bd67Sjmc
148f189bd67Sjmc	for (;;) {
149f189bd67Sjmc		ssize_t n;
1501f2012eeSlteo		char packet[IP_MAXPACKET];
1511f2012eeSlteo		struct ip *ip;
1521f2012eeSlteo		struct tcphdr *th;
1531f2012eeSlteo		int hlen;
1541f2012eeSlteo		char src[48], dst[48];
155f189bd67Sjmc
1561f2012eeSlteo		memset(packet, 0, sizeof(packet));
1570e332954Sjmc		n = recvfrom(fd, packet, sizeof(packet), 0,
1580e332954Sjmc		    (struct sockaddr *) &sin, &sin_len);
1591f2012eeSlteo		if (n == -1) {
1601f2012eeSlteo			warn("recvfrom");
161f189bd67Sjmc			continue;
162f189bd67Sjmc		}
1631f2012eeSlteo		if (n < sizeof(struct ip)) {
1641f2012eeSlteo			warnx("packet is too short");
165f189bd67Sjmc			continue;
166f189bd67Sjmc		}
167f189bd67Sjmc
1681f2012eeSlteo		ip = (struct ip *) packet;
1691f2012eeSlteo		hlen = ip->ip_hl << 2;
1701f2012eeSlteo		if (hlen < sizeof(struct ip) || ntohs(ip->ip_len) < hlen ||
1711f2012eeSlteo		    n < ntohs(ip->ip_len)) {
1721f2012eeSlteo			warnx("invalid IPv4 packet");
1731f2012eeSlteo			continue;
1741f2012eeSlteo		}
1751f2012eeSlteo
1761f2012eeSlteo		th = (struct tcphdr *) (packet + hlen);
1771f2012eeSlteo
1781f2012eeSlteo		if (inet_ntop(AF_INET, &ip->ip_src, src,
1791f2012eeSlteo		    sizeof(src)) == NULL)
1801f2012eeSlteo			(void)strlcpy(src, "?", sizeof(src));
1811f2012eeSlteo
1821f2012eeSlteo		if (inet_ntop(AF_INET, &ip->ip_dst, dst,
1831f2012eeSlteo		    sizeof(dst)) == NULL)
1841f2012eeSlteo			(void)strlcpy(dst, "?", sizeof(dst));
1851f2012eeSlteo
186f189bd67Sjmc		printf("%s:%u -> %s:%u\en",
1871f2012eeSlteo		    src,
1881f2012eeSlteo		    ntohs(th->th_sport),
1891f2012eeSlteo		    dst,
1901f2012eeSlteo		    ntohs(th->th_dport)
191f189bd67Sjmc		);
192f189bd67Sjmc
1930e332954Sjmc		n = sendto(fd, packet, n, 0, (struct sockaddr *) &sin,
1940e332954Sjmc		    sin_len);
1951f2012eeSlteo		if (n == -1)
1961f2012eeSlteo			warn("sendto");
197f189bd67Sjmc	}
198f189bd67Sjmc
199f189bd67Sjmc	return 0;
200f189bd67Sjmc}
201f189bd67Sjmc.Ed
202d85f7bc1Smichele.Sh SEE ALSO
2035a430862Sjmc.Xr socket 2 ,
204d85f7bc1Smichele.Xr ip 4 ,
205f7a18a8bSjmc.Xr pf.conf 5
206d85f7bc1Smichele.Sh HISTORY
207d85f7bc1SmicheleThe
208d85f7bc1Smichele.Nm
209d85f7bc1Smicheleprotocol first appeared in
210d85f7bc1Smichele.Ox 4.7 .
211