xref: /openbsd/share/man/man4/divert.4 (revision fc61954a)
1.\"     $OpenBSD: divert.4,v 1.16 2015/09/10 17:55:21 schwarze 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 2015 $
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.
61.Xr pf 4
62reassembles TCP streams by default (if IP reassembly is not disabled)
63before sending them to the divert sockets.
64If there are no divert sockets listening, the packets are dropped.
65.Pp
66Packets can be read via
67.Xr read 2 ,
68.Xr recv 2 ,
69or
70.Xr recvfrom 2
71from the divert socket.
72The application that is processing the packets can then reinject them into the
73kernel.
74After being reinjected, inbound and outbound packets are treated differently.
75Inbound packets are added to the relevant input queue and a soft interrupt is
76scheduled to signal that a new packet is ready to be processed; outbound ones
77are processed directly by the relevant IPv4/IPv6 output function.
78Since the userspace application could have modified the packets, upon
79reinjection basic sanity checks are done to ensure that the packets are still
80valid.
81The packets' IPv4 and protocol checksums (TCP, UDP, ICMP, and ICMPv6) are also
82recalculated.
83.Pp
84Writing to a divert socket can be achieved using
85.Xr sendto 2
86and it will skip
87.Xr pf 4
88filters to avoid loops.
89A diverted packet that is not reinjected into the kernel stack is lost.
90.Pp
91Receive and send divert socket buffer space can be tuned through
92.Xr sysctl 8 .
93.Xr netstat 1
94shows information relevant to divert sockets.
95.Pp
96The IP_DIVERTFL socket option on the IPPROTO_IP level controls
97whether both inbound and outbound packets are diverted (the default)
98or only packets travelling in one direction.
99It cannot be reset once set.
100Valid values are
101.Dv IPPROTO_DIVERT_INIT
102for the direction of the initial packet of a flow, and
103.Dv IPPROTO_DIVERT_RESP
104for the direction of the response packets.
105.Sh EXAMPLES
106The following PF rule queues outbound IPv4 packets to TCP port 80,
107as well as the return traffic, on the em0 interface to divert port 700:
108.Bd -literal -offset indent
109pass out on em0 inet proto tcp to port 80 divert-packet port 700
110.Ed
111.Pp
112The following program reads packets on divert port 700 and reinjects them
113back into the kernel.
114This program does not perform any processing of the packets,
115apart from discarding invalid IP packets.
116.Bd -literal
117#include <sys/types.h>
118#include <sys/socket.h>
119#include <netinet/in.h>
120#include <netinet/ip.h>
121#include <netinet/tcp.h>
122#include <arpa/inet.h>
123#include <stdio.h>
124#include <string.h>
125#include <err.h>
126
127#define DIVERT_PORT 700
128
129int
130main(int argc, char *argv[])
131{
132	int fd, s;
133	struct sockaddr_in sin;
134	socklen_t sin_len;
135
136	fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
137	if (fd == -1)
138		err(1, "socket");
139
140	memset(&sin, 0, sizeof(sin));
141	sin.sin_family = AF_INET;
142	sin.sin_port = htons(DIVERT_PORT);
143	sin.sin_addr.s_addr = 0;
144
145	sin_len = sizeof(struct sockaddr_in);
146
147	s = bind(fd, (struct sockaddr *) &sin, sin_len);
148	if (s == -1)
149		err(1, "bind");
150
151	for (;;) {
152		ssize_t n;
153		char packet[IP_MAXPACKET];
154		struct ip *ip;
155		struct tcphdr *th;
156		int hlen;
157		char src[48], dst[48];
158
159		memset(packet, 0, sizeof(packet));
160		n = recvfrom(fd, packet, sizeof(packet), 0,
161		    (struct sockaddr *) &sin, &sin_len);
162		if (n == -1) {
163			warn("recvfrom");
164			continue;
165		}
166		if (n < sizeof(struct ip)) {
167			warnx("packet is too short");
168			continue;
169		}
170
171		ip = (struct ip *) packet;
172		hlen = ip->ip_hl << 2;
173		if (hlen < sizeof(struct ip) || ntohs(ip->ip_len) < hlen ||
174		    n < ntohs(ip->ip_len)) {
175			warnx("invalid IPv4 packet");
176			continue;
177		}
178
179		th = (struct tcphdr *) (packet + hlen);
180
181		if (inet_ntop(AF_INET, &ip->ip_src, src,
182		    sizeof(src)) == NULL)
183			(void)strlcpy(src, "?", sizeof(src));
184
185		if (inet_ntop(AF_INET, &ip->ip_dst, dst,
186		    sizeof(dst)) == NULL)
187			(void)strlcpy(dst, "?", sizeof(dst));
188
189		printf("%s:%u -> %s:%u\en",
190		    src,
191		    ntohs(th->th_sport),
192		    dst,
193		    ntohs(th->th_dport)
194		);
195
196		n = sendto(fd, packet, n, 0, (struct sockaddr *) &sin,
197		    sin_len);
198		if (n == -1)
199			warn("sendto");
200	}
201
202	return 0;
203}
204.Ed
205.Sh SEE ALSO
206.Xr setsockopt 2 ,
207.Xr socket 2 ,
208.Xr ip 4 ,
209.Xr pf.conf 5
210.Sh HISTORY
211The
212.Nm
213protocol first appeared in
214.Ox 4.7 .
215