xref: /openbsd-src/share/man/man4/divert.4 (revision 4cb74a97251101bfc8c54d71570124bf2c6d92ae)
1.\"     $OpenBSD: divert.4,v 1.20 2022/09/10 10:22:46 jsg Exp $
2.\"
3.\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
4.\" Copyright (c) 2012-2014 Lawrence Teo <lteo@openbsd.org>
5.\"
6.\" Permission to use, copy, modify, and distribute this software for any
7.\" purpose with or without fee is hereby granted, provided that the above
8.\" copyright notice and this permission notice appear in all copies.
9.\"
10.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17.\"
18.Dd $Mdocdate: September 10 2022 $
19.Dt DIVERT 4
20.Os
21.Sh NAME
22.Nm divert
23.Nd kernel packet diversion mechanism
24.Sh SYNOPSIS
25.In sys/types.h
26.In sys/socket.h
27.In netinet/in.h
28.Ft int
29.Fn socket AF_INET SOCK_RAW IPPROTO_DIVERT
30.Ft int
31.Fn socket AF_INET6 SOCK_RAW IPPROTO_DIVERT
32.Sh DESCRIPTION
33Divert sockets are part of a mechanism completely integrated with
34.Xr pf 4
35that queues raw packets from the kernel stack to userspace applications,
36and vice versa.
37.Pp
38A divert socket must be bound to a divert port through
39.Xr bind 2 ,
40which only the superuser can do.
41Divert ports have their own number space, completely separated from
42.Xr tcp 4
43and
44.Xr udp 4 .
45When
46.Xr pf 4
47processes a packet that matches a rule with the
48.Ar divert-packet
49parameter
50(see
51.Xr pf.conf 5
52for details), it is sent to the divert socket listening on the
53divert port specified in the rule.
54Note that
55.Ar divert-packet
56should not be confused with
57.Ar divert-to
58or
59.Ar divert-reply ,
60which do not use divert sockets.
61If there are no divert sockets listening, the packets are dropped.
62.Pp
63Packets can be read via
64.Xr read 2 ,
65.Xr recv 2 ,
66or
67.Xr recvfrom 2
68from the divert socket.
69The application that is processing the packets can then reinject them into the
70kernel.
71With
72.Xr recvfrom 2 ,
73an interface IP address is passed if it is an inbound packet.
74Outbound packets provide the unspecified address.
75When reinjecting, use this address as argument to
76.Xr sendto 2 .
77This allows the kernel to guess the original incoming interface and
78process it as an incoming packet.
79If no interface IP address is given, the reinjected packet is treated
80as an outgoing packet.
81Since the userspace application could have modified the packets, upon
82reinjection basic sanity checks are done to ensure that the packets are still
83valid.
84The packets' IPv4 and protocol checksums (TCP, UDP, ICMP, and ICMPv6) are also
85recalculated.
86.Pp
87Writing to a divert socket can be achieved using
88.Xr sendto 2
89and it will skip
90.Xr pf 4
91filters to avoid loops.
92Note that this means that a reinjected inbound packet will also not
93run through the pf out rules after being forwarded.
94A diverted packet that is not reinjected into the kernel stack is lost.
95.Pp
96Receive and send divert socket buffer space can be tuned through
97.Xr sysctl 8 .
98.Xr netstat 1
99shows information relevant to divert sockets.
100Note that the default is 64k and too short to handle full sized UDP
101packets.
102.Sh EXAMPLES
103The following PF rule queues outbound IPv4 packets to TCP port 80,
104as well as the return traffic, on the em0 interface to divert port 700:
105.Bd -literal -offset indent
106pass out on em0 inet proto tcp to port 80 divert-packet port 700
107.Ed
108.Pp
109The following program reads packets on divert port 700 and reinjects them
110back into the kernel.
111This program does not perform any processing of the packets,
112apart from discarding invalid IP packets.
113.Bd -literal
114#include <sys/types.h>
115#include <sys/socket.h>
116#include <netinet/in.h>
117#include <netinet/ip.h>
118#include <netinet/tcp.h>
119#include <arpa/inet.h>
120#include <stdio.h>
121#include <string.h>
122#include <err.h>
123
124#define DIVERT_PORT 700
125
126int
127main(int argc, char *argv[])
128{
129	int fd, s;
130	struct sockaddr_in sin;
131	socklen_t sin_len;
132
133	fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
134	if (fd == -1)
135		err(1, "socket");
136
137	memset(&sin, 0, sizeof(sin));
138	sin.sin_family = AF_INET;
139	sin.sin_port = htons(DIVERT_PORT);
140	sin.sin_addr.s_addr = 0;
141
142	sin_len = sizeof(struct sockaddr_in);
143
144	s = bind(fd, (struct sockaddr *) &sin, sin_len);
145	if (s == -1)
146		err(1, "bind");
147
148	for (;;) {
149		ssize_t n;
150		char packet[IP_MAXPACKET];
151		struct ip *ip;
152		struct tcphdr *th;
153		int hlen;
154		char src[48], dst[48];
155
156		memset(packet, 0, sizeof(packet));
157		n = recvfrom(fd, packet, sizeof(packet), 0,
158		    (struct sockaddr *) &sin, &sin_len);
159		if (n == -1) {
160			warn("recvfrom");
161			continue;
162		}
163		if (n < sizeof(struct ip)) {
164			warnx("packet is too short");
165			continue;
166		}
167
168		ip = (struct ip *) packet;
169		hlen = ip->ip_hl << 2;
170		if (hlen < sizeof(struct ip) || ntohs(ip->ip_len) < hlen ||
171		    n < ntohs(ip->ip_len)) {
172			warnx("invalid IPv4 packet");
173			continue;
174		}
175
176		th = (struct tcphdr *) (packet + hlen);
177
178		if (inet_ntop(AF_INET, &ip->ip_src, src,
179		    sizeof(src)) == NULL)
180			(void)strlcpy(src, "?", sizeof(src));
181
182		if (inet_ntop(AF_INET, &ip->ip_dst, dst,
183		    sizeof(dst)) == NULL)
184			(void)strlcpy(dst, "?", sizeof(dst));
185
186		printf("%s:%u -> %s:%u\en",
187		    src,
188		    ntohs(th->th_sport),
189		    dst,
190		    ntohs(th->th_dport)
191		);
192
193		n = sendto(fd, packet, n, 0, (struct sockaddr *) &sin,
194		    sin_len);
195		if (n == -1)
196			warn("sendto");
197	}
198
199	return 0;
200}
201.Ed
202.Sh SEE ALSO
203.Xr socket 2 ,
204.Xr ip 4 ,
205.Xr pf.conf 5
206.Sh HISTORY
207The
208.Nm
209protocol first appeared in
210.Ox 4.7 .
211