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