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