DOMAIN(9)

πŸ“¦ version 15.1, in freebsd 14.5, freebsd 15.1

πŸ’… DOMAIN(9) β€” kernel socket vibes, explained

okay so this is the domain / protosw page from the Kernel Developer's Manual, and bestie, if you've ever wondered how the kernel actually makes socket(2) happen, this is the whole tea. grab a boba, we're going in 🧚✨


✨ SYNOPSIS ✨

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
#include <sys/domain.h>

void
domain_add(struct domain *dom);

void
domain_remove(struct domain *dom);

void
DOMAIN_SET(domain);

int
protosw_register(struct domain *dom, struct protosw *pr);

int
protosw_unregister(struct protosw *pr);

πŸ¦‹ DESCRIPTION πŸ¦‹

so the domain subsystem is literally the whole reason you can implement communication protocols that userland gets to touch via the socket(2) API. iconic behavior honestly.

when an app does a socket(domain, type, protocol) syscall, the kernel goes on a little scavenger hunt πŸ” β€” first it looks for a domain matching the domain argument, then within that domain it looks for a protocol matching type. and if the third argument, protocol, isn't 0, then that value has to match too, no exceptions. the structure it finds has to implement certain methods, so that the socket(2) API actually works for this particular kind of socket. slay.

a minimal domain structure implementing a domain shall be initialized with a sparse C99 initializer (yes, that's mandatory, not a suggestion πŸ’…) and has these public fields:

struct domain {
    /*
     * Mandatory fields.
     */
    int dom_family;     /* PF_xxx, first argument of socket(2) */
    char        *dom_name;      /* text name of the domain */
    u_int       dom_nprotosw;   /* length of dom_protosw[] */
    /*
     * Following methods are optional.
     */
    int (*dom_probe)(void);                     /* check for support */
    struct rib_head *(*dom_rtattach)(uint32_t); /* init route table */
    void (*dom_rtdetach)(struct rib_head *);    /* clean up table */
    void *(*dom_ifattach)(struct ifnet *);      /* interface attach */
    void (*dom_ifdetach)(struct ifnet *, void *);/* & detach callbacks */
    int (*dom_ifmtu)(struct ifnet *);           /* mtu change */
    /*
     * Mandatory variable size array of pointers to protosw structs.
     */
    struct  protosw *dom_protosw[];
};

every domain carries the dom_protosw array of protocol switch structures (struct protosw *) β€” one for each socket type it supports, bestie. the array may have NULL spacers for loadable protocols (iykyk 🫢). and yes, sparse C99 initializers shall be used to initialize protosw structures, that's the rule.

the protosw structure has a mandatory field pr_type and a mandatory pr_attach method. the rest of the methods are optional β€” but like, a meaningful protocol should implement some. no pressure, but also... some pressure ✨

struct protosw {
    short       pr_type;        /* second argument of socket(2) */
    short       pr_protocol;    /* third argument of socket(2) or 0 */
    short       pr_flags;       /* see protosw.h */
    pr_soreceive_t  *pr_soreceive;  /* recv(2) */
    pr_rcvd_t       *pr_rcvd;       /* soreceive_generic() if PR_WANTRCV */
    pr_sosend_t     *pr_sosend;     /* send(2) */
    pr_send_t       *pr_send;       /* send(2) via sosend_generic() */
    pr_ready_t      *pr_ready;      /* sendfile/ktls readyness */
    pr_sopoll_t     *pr_sopoll;     /* poll(2) */
    pr_attach_t     *pr_attach;     /* creation: socreate(), sonewconn() */
    pr_detach_t     *pr_detach;     /* destruction: sofree() */
    pr_connect_t    *pr_connect;    /* connect(2) */
    pr_disconnect_t *pr_disconnect; /* sodisconnect() */
    pr_close_t      *pr_close;      /* close(2) */
    pr_shutdown_t   *pr_shutdown;   /* shutdown(2) */
    pr_abort_t      *pr_abort;      /* abrupt tear down: soabort() */
    pr_aio_queue_t  *pr_aio_queue;  /* aio(9) */
    pr_bind_t       *pr_bind;       /* bind(2) */
    pr_bindat_t     *pr_bindat;     /* bindat(2) */
    pr_listen_t     *pr_listen;     /* listen(2) */
    pr_accept_t     *pr_accept;     /* accept(2) */
    pr_connectat_t  *pr_connectat;  /* connectat(2) */
    pr_connect2_t   *pr_connect2;   /* socketpair(2) */
    pr_control_t    *pr_control;    /* ioctl(2) */
    pr_rcvoob_t     *pr_rcvoob;     /* soreceive_rcvoob() */
    pr_ctloutput_t  *pr_ctloutput;  /* control output (from above) */
    pr_peeraddr_t   *pr_peeraddr;   /* getpeername(2) */
    pr_sockaddr_t   *pr_sockaddr;   /* getsockname(2) */
    pr_sense_t      *pr_sense;      /* stat(2) */
};

πŸŽ€ the registration functions πŸŽ€

these are the functions that handle registering new domains and protocols:

  • domain_add() β€” adds a new protocol domain to the system. in most cases you don't call domain_add() directly, bestie; instead you use DOMAIN_SET(), which is a wrapper around the SYSINIT() macro. if the new domain has defined a dom_probe routine, that gets called first inside domain_add() to figure out if the domain should even be supported on the current system. if the probe routine returns a non-0 value, the domain will not be added. periodt.
  • domain_remove() β€” ⚠️ experimental, ok? once a domain is added it cannot be completely unloaded. this is because there's no reference counting system in place to determine if there are any active references from sockets within that domain. so domain_remove() exists, but it's experimental β€” and unloadable domains may be supported in the future. no cap.
  • protosw_register() β€” dynamically adds a protocol to a domain, if the domain has an empty slot in its dom_protosw. a dynamically added protocol can later be unloaded with protosw_unregister().

πŸ’€ RETURN VALUES πŸ’€

domain_add() never fails β€” but ngl, it may just not add a domain if its dom_probe fails. it's giving "no thoughts, head empty, nothing happened."

the protosw_register() function may fail if:

  • [EEXIST] β€” a protocol with the same value of pr_type and pr_protocol already exists in the domain. (duplicate detected, not today 🚫)
  • [ENOMEM] β€” the domain doesn't have any NULL slots in its dom_protosw. (no room, the array is full, it's giving packed elevator 😭)

πŸ” SEE ALSO πŸ”

socket(2), SYSINIT(9)


🌈 HISTORY 🌈

the domain subsystem first appeared in 4.3BSD as part of the very first socket(2) API implementation β€” like, literally og main character energy πŸ‘‘

and the domain subsystem and this manual page were significantly rewritten in FreeBSD 14. glow-up era ✨


πŸ‘‘ AUTHORS πŸ‘‘

this manual page was written by Chad David <davidc@acns.ab.ca> and Gleb Smirnoff <glebius@FreeBSD.org>. absolute icons, we stan 🫢


k that's the whole page, bestie β€” you're now officially versed in kernel socket implementation. go forth and slay πŸ’…βœ¨

xoxo, this page came from the Debian edition of the Kernel Developer's Manual, dated September 14, 2022 πŸŽ€

πŸ“¦ where this page comes from
  • freebsd 14.5: base 14.5
  • freebsd 15.1: base 15.1
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms