SEND(4)
π SEND(4) π
okay bestie, buckle up β this is the send kernel interface page from the Kernel Interfaces Manual, and it's lowkey one of the coolest things in the whole networking stack. no cap. let's get into it π¦
β¨ NAME β¨
send β kernel side support for Secure Neighbor Discovery (SeND). that's it, that's the whole vibe π
β¨ SYNOPSIS β¨
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet6/send.h>
int
socket(PF_INET6, SOCK_RAW, IPPROTO_SEND);
and if you wanna load the driver as a module at boot time, you put this line in loader.conf(5):
send_load="YES"
periodt. that's the whole setup πΈ
β¨ DESCRIPTION β¨
so. IPv6 nodes use the Neighbor Discovery Protocol (NDP) to find other nodes on the link, to figure out their link-layer addresses, to find routers, and to keep track of reachability info about the paths to active members. very main character behavior. the problem? NDP is vulnerable to a whole bunch of attacks β see [RFC3756] for the receipts. Secure Neighbor Discovery is the set of extensions to NDP that counters those threats β that's [RFC3971], iykyk. π
so the kernel side support for SeND is basically a kernel module with hooks. those hooks divert the relevant packets β and when we say relevant we mean Neighbor Solicitations, Neighbor Advertisements, Router Solicitations, Router Advertisements and Redirects β away from the NDP stack, send them to user space on a dedicated socket, and then reinject them back for further processing. and the important bit: hooks only get triggered if the send module is loaded. no module, no hooks, no drama π
the native SeND socket is basically a raw IP socket but with its own internal pseudo-protocol (IPPROTO_SEND). the struct sockaddr_send lives in <netinet6/send.h>, and it defines the total length of the structure, the address family, the packet's incoming or outgoing direction from the interface's point of view, and the interface index. iconic.
struct sockaddr_send {
unsigned char send_len; /* total length */
sa_family_t send_family; /* address family */
int send_direction;
int send_ifidx;
char send_zero[8];
};
okay so breaking that down bestie:
- the address family is always
AF_INET6. no exceptions. send_directionis the direction of the packet from the interface's point of view, and it's either the valueSND_INorSND_OUT.send_ifidxis the interface index of the receiving or sending interface.send_zerois just padding and it must always be zero. do not play with it. π
and here's a lil mercy for you: in case no user space application is connected to the send socket, processing just continues normally, as if the module was never loaded. slay, graceful degradation π
β¨ INPUT HOOK β¨
the input hook is named after the input path of the incoming or outgoing NDP packets β the journey from the wire, through the nd6 stack, to user space. relevant packets get identified by adding an mbuf_tag (peep mbuf_tags(9)) to the mbuf(9), but only if the send module is loaded. after that it gets passed on to the kernel-userland interface, either for cryptographic protection or for validation by the SeND application. π¦
the hook takes an argument that describes the direction of the packet β and this applies to both incoming and outgoing packets:
SND_INβ the direction of the incoming packets, which are usually protected by the SeND options and then sent to user space for cryptographic validation.SND_OUTβ the outgoing direction. it covers both reply packets and locally originated outgoing packets, which get sent to user space so SeND options can be added. π«
β¨ INCOMING PACKETS β¨
the incoming ND packet from the wire:
kernelspace ( userspace
)
incoming SeND/ND packet (
| )
v ( SND_IN ) (
icmp6_input() -> send_input_hook ---> send socket ----+
: ) |
: # # ( |
normal : # # ) v
processing : # send.ko # ( SeND application
path : # # ) |
: # # ( |
v ) |
icmp6/nd6_??_input() <- protocol switch <--- send socket <---+
| structure (IPPPROTO_SEND) )
| ( SND_IN ) (
v )
continue normal ND processing (
β¨ OUTGOING PACKETS β¨
outgoing ND packet (reply or locally triggered):
kernelspace ( userspace
)
nd6_na_input() (
+PACKET_TAG_ND_OUTGOING )
| )
| outgoing packet (
| | )
| v (
| icmp6_redirect_output() )
| nd6_ns_output() (
| nd6_na_output() )
| +PACKET_TAG_ND_OUTGOING (
| | )
| +-----------<- rip6_output() <----------)----- rtsol/rtadvd/..
| | +PACKET_TAG_ND_OUTGOING (
| v )
| ip6_output() (
| | )
+-------->-+ (
| )
v ( SND_OUT ) (
nd6_output_lle() -> send_input_hook ---> send socket ----+
-PACKET_TAG_ND_OUTGOING ) |
: # # ( |
normal : # # ) v
processing : # send.ko # ( SeND application
path : # # ) |
: # # ( |
v ) |
(*ifp->if_output)() <- protocol switch <--- send socket <---+
| structure (IPPPROTO_SEND) )
| ( SND_OUT ) (
v )
continue with normal packet output (
β¨ ERRORS β¨
a socket operation may fail, and when it does, it hands you one of these:
[EEXIST]β another user space SeND application is already bound to the socket. one app at a time, bestie, it's giving exclusive π[ENOBUFS]β shortage of space to receive the incoming (SeND-protected) or outgoing (SeND-validated) packet from the SeND application. out of room, out of luck π[ENOSYS]β a packet received from user space and passed to the NDP stack for further processing is neither Neighbor Solicitation, Neighbor Advertisement, Router Solicitation, Router Advertisement nor Redirect. wrong packet, wrong party π«[ENOENT]β occurs if interface output routines fail to send the packet out of the interface. the packet did not make it out the door π
β¨ SEE ALSO β¨
recvfrom(2), sendto(2), socket(2), loader.conf(5)
go read those too, they're the supporting cast and they ate π
β¨ HISTORY β¨
the send module first appeared in FreeBSD 9.0. a legend was born β¨
β¨ AUTHORS β¨
Ana Kukec <anchie@FreeBSD.org>, University of Zagreb π
β¨ BUGS β¨
due to the lack of NDP locking, it is currently not possible to unload the send module. yeah. once it's in, it's in. no takebacks, no unload, it's committed to the relationship fr fr π
and that's the whole page bestie!! this one came from the Debian folks and it's dated September 19, 2010 β an absolute vintage classic, still serving looks in SEND(4) to this very day ππβ¨
π¦ where this page comes from
- debian bookworm: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- debian trixie: freebsd-manpages 12.2-2 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu jammy: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu noble: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu questing: freebsd-manpages 12.2-2
- ubuntu resolute: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu stonking: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- Copyright (c) 2010 Ana Kukec
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms