RTALLOC(9)

πŸ’– RTALLOC(9) πŸ’– β€” kernel dev manual, but make it make sense

okay so bestie, welcome to the kernel developer's manual page for RTALLOC(9) β€” yes that's the manual name, no I will not stop saying it. we're talking about the rtalloc family: looking up a route in the kernel routing table. it's giving networking internals. it's giving reference counting. buckle up πŸŽ€


✨ NAME ✨

rtalloc1_fib, rtalloc_ign_fib, rtalloc_fib β€” look up a route in the kernel routing table πŸ—ΊοΈπŸ’…


🌸 SYNOPSIS 🌸

#include <sys/types.h>
#include <sys/socket.h>
#include <net/route.h>

struct rtentry *
rtalloc1_fib(struct sockaddr *dst, int report, u_long flags,
    u_int fibnum);

void
rtalloc_fib(struct route *ro, u_int fibnum);

void
rtalloc_ign_fib(struct route *ro, u_long flags, u_int fibnum);

RTFREE_LOCKED(struct rt_entry *rt);

RTFREE(struct rt_entry *rt);

RT_LOCK(struct rt_entry *rt);

RT_UNLOCK(struct rt_entry *rt);

RT_ADDREF(struct rt_entry *rt);

RT_REMREF(struct rt_entry *rt);

RO_RTFREE(struct route *ro);

void
rtfree(struct rt_entry *rt);

struct rtentry *
rtalloc1(struct sockaddr *dst, int report, u_long flags);

void
rtalloc(struct route *ro);

void
rtalloc_ign(struct route *ro, u_long flags);

options RADIX_MPATH

πŸ¦‹ DESCRIPTION πŸ¦‹

so the kernel keeps a radix tree structure to manage routes for the networking subsystem. iconic of it. and if you compiled with options RADIX_MPATH, the kernel may maintain several independent forwarding information databases β€” aka FIBs. multiple personalities, but for packet forwarding πŸ’€

the rtalloc() family of routines is what protocols use to query these structures for a route that corresponds to a particular end-node address, and to make certain protocol- and interface-specific actions take place. it's the group chat where the kernel asks "hey where does this go" and the routing table answers. 🫢

πŸ’« the functions, one by one πŸ’«

rtalloc1_fib() β€” this one is the most general form of rtalloc(), and honestly every other form is just implemented as a call to it. nepo baby behavior but make it efficient. it takes a struct sockaddr * directly as the dst argument. the second argument, report, controls whether the routing sockets get notified when a lookup fails (so like, do we announce the L or keep it quiet). the third argument, flags, is a combination of the following values:

  • RTF_RNH_LOCKED β€” indicates that the radix tree lock is already held πŸ”’

the last argument fibnum specifies the number of the forwarding information database (FIB) on which the lookup should be performed. in case of success, rtalloc1_fib() returns a pointer to a locked struct rtentry with an additional reference. locked AND referenced. high maintenance but valid. πŸ‘‘

rtalloc_fib() β€” the most simple variant, the low-key one. its main argument is ro, a pointer to a struct route, which is defined as follows:

struct route {
        struct rtentry *ro_rt;
        struct llentry *ro_lle;
        struct sockaddr ro_dst;
};

so because of that, this function can only be used for address families which are smaller than the default struct sockaddr. yes that's a real constraint, no you can't ignore it. before calling rtalloc_fib() for the first time, callers should ensure that unused bits of the structure are set to zero. the second argument fibnum is the FIB number. in case of success of the rtalloc_fib(), the ro_rt points to a valid and unlocked rtentry(9), which has an additional reference put on it β€” and freeing that is the caller's responsibility, so don't come crying to me when you leak it 😭. on subsequent calls, rtalloc_fib() returns without performing a lookup if ro->ro_rt is non-null and the RTF_UP flag is set in the rtentry's rt_flags field. cached route, we love a shortcut ✨

rtalloc_ign_fib() β€” same as rtalloc_fib(), but there's an additional flags argument, which is the same as in rtalloc1_fib(). basically rtalloc_fib() but with opinions. πŸŽ€

πŸ”₯ the macros πŸ”₯

RTFREE_LOCKED() β€” used to unref and possibly free a locked routing entry that holds one of our references, for example one previously allocated by rtalloc1_fib(). locked girls, this one's for you.

RTFREE() β€” used to unref and possibly free an unlocked route entry with one of our references, for example one previously allocated by rtalloc_fib() or rtalloc_ign_fib().

both RTFREE_LOCKED() and RTFREE() decrement the reference count on the routing table entry, and proceed with the actual freeing if the reference count has reached zero. it's giving "last one out turns off the lights." πŸ’‘

RT_LOCK() β€” locks a routing table entry.

RT_UNLOCK() β€” unlocks a routing table entry. the yin to the previous yang.

RT_ADDREF() β€” increments the reference count on a previously locked route entry. it should be used whenever a reference to an rtentry(9) is going to be stored outside the routing table. if it's leaving the house, it needs its own reference. periodt. πŸ’…

RT_REMREF() β€” decrements the reference count on a previously locked route entry. its usage is contrary to RT_ADDREF(). they're opposites, they're enemies to lovers, they're refcount bookkeeping.

RO_RTFREE() β€” used to free the route entry that is referenced by a struct route. at certain circumstances the latter may not hold a reference on rtentry, and RO_RTFREE() treats such routes correctly. so it's the emotionally intelligent macro that handles the edge case for you. 🫢

rtfree() β€” does the actual free of the routing table entry, and it shouldn't be called directly by facilities that just perform routing table lookups. hands off, bestie, this is backstage. πŸ™ˆ


πŸ‘‘ LEGACY INTERFACE πŸ‘‘

back in the day, before multiple routing tables existed, these functions didn't require the u_int fibnum argument. the legacy rtalloc1(), rtalloc() and rtalloc_ign() functions are kept around for compatibility, and they're equivalent to calling the new interface with the fibnum argument equal to 0, which implies the default forwarding table. so it's the same vibe, just with the default settings. old money energy. πŸ’œ


🌈 RETURN VALUES 🌈

  • rtalloc1_fib() returns a pointer to a locked routing-table entry if it succeeds, otherwise a null pointer.
  • rtalloc_fib() and rtalloc_ign_fib() don't return a value at all β€” instead they fill in the *ro_rt member of the ro argument with a pointer to an unlocked routing-table entry if they succeed, otherwise a null pointer.
  • in a case of success, all of these functions put a reference on the routing-table entry, and freeing it is the caller's responsibility. again. it's always your responsibility. no cap.
  • lack of a route should in most cases be translated to the errno(2) value EHOSTUNREACH. so when the lookup ghosts you, that's the error you get back. πŸ’€

πŸŽ€ SEE ALSO πŸŽ€


⭐ HISTORY ⭐

  • the rtalloc facility first appeared in 4.2BSD, although with much different internals. glow up, honestly.
  • the rtalloc_ign() function and the flags argument to rtalloc1() first appeared in FreeBSD 2.0.
  • routing table locking was introduced in FreeBSD 5.2.
  • multiple routing tables were introduced in FreeBSD 8.0. era by era, we love a timeline. πŸ¦‹

πŸ’œ AUTHORS πŸ’œ

the original version of this manual page was written by Garrett Wollman. it was significantly updated by Gleb Smirnoff. legends only, we stan. πŸ‘‘


and that's the whole page bestie β€” this one ships out of Debian, dated July 4, 2012. go forth, lock your routes, free your references, and don't leak. mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from