NG_UNI(4)

πŸ’– NG_UNI β€” the netgraph UNI node type πŸ’–

(from the NG_UNI(4) page in the Kernel Interfaces Manual, bestie β€” buckle up, this one's a whole journey)


🌸 SYNOPSIS 🌸

#include <netnatm/msg/unistruct.h>
#include <netnatm/sig/unidef.h>
#include <netgraph/atm/ng_uni.h>

✨ DESCRIPTION ✨

okay so. The uni netgraph node type is the whole implementation of ATM Forum signalling 4.0. That's the vibe. πŸ’…

So like, the second you create the node, you don't actually have a UNI instance yet β€” not me forgetting a step, that's just how it works. You have to send an enable message to the node to actually create the UNI instance. Then once the node is enabled, you can pull the UNI parameters, change them, and then start the protocol. Order matters bestie 🫢

The node goes bye-bye either when you hit it with an NGM_SHUTDOWN message, or when every single hook gets disconnected. That's it, those are the two ways out. πŸ¦‹


πŸŽ€ HOOKS πŸŽ€

Every uni node has three hooks, and their names are fixed β€” no renaming, no nicknames, they are who they are:

  • lower β€” this is the glow-up line between the UNI protocol and the transport layer of the ATM control plane. At this hook the node expects the interface exported by ng_sscfu(4). No substitutions, periodt. ✨
  • upper β€” this is the "user" interface of the UNI protocol. Now, here's the thing: there's no standardized interface at this point, so this implementation just kinda follows the interface specified by the SDL diagrams in ITU-T recommendations Q.2931 and Q.2971. More or less. Usually you'd stack either a ng_ccatm(4) or a switch CAC at this interface. The message format at the upper hook is described below, keep scrolling πŸ“œ. And because netgraph(4) is functional, it sometimes makes sense to switch this hook into queueing mode from the peer node upon connection. Little trick, iykyk. 🧚

πŸ’œ the upper interface lore (yes there's lore)

The upper interface of the uni node is loosely modelled after the interface specified in the ITU-T signalling standards. But β€” and this is the important part β€” there's one derivation from it, so listen up bestie:

Normally there are four kinds of signals:

  1. requests
  2. responses
  3. indications
  4. confirmations

These get triggered either by external events (receiving a message) or internal events (a timer or another signal). And honestly? That scheme works fine for user APIs that are entirely asynchronous, and in cases where error handling is just... not taken into account. πŸ™ˆ

With synchronous APIs and error handling though? There's a problem. 😭

Let me walk you through it. Say the application issues a request to set up a connection β€” it might do that by sending a SETUP.request signal to the UNI. Normally the UNI stack will send a SETUP message and receive a message from the switch (a RELEASE, CONNECT, CALL PROCEEDING or ALERTING), or a timer inside the UNI stack will time out. In any of those cases, the UNI stack is supposed to report an event back to the application, and the application will unblock (that's the synchronous API part) and handle the event. Beautiful. Iconic. Working as intended.

Now the problem. Suppose there's no memory to send the SETUP message and to start the timer. In that case the application will block forever β€” because no received message and no timer will ever wake it up. πŸ’€ Forever, bestie. Just vibing in the void.

So for this reason, this implementation uses an additional message: for every signal sent from the application to the stack, the stack will respond with an error code.

  • If this code is zero β†’ the stack has accepted the signal, and the application may block. πŸŽ‰
  • If the code is non-zero β†’ the signal is effectively ignored, and the code describes what was wrong. πŸ”₯

This system makes it very easy to make a blocking interface out of the message-based netgraph interface. Slay. πŸ‘‘


🌈 the structure at the upper interface

The upper interface uses the following structure:

struct uni_arg {
        uint32_t        sig;
        uint32_t        cookie;
        u_char          data[];
};

The sig field contains the actual signal that is sent from the user to UNI or from UNI to the user. The cookie can be used by the user to correlate requests with events and responses. If an error response, a confirmation or an indication was triggered by a request or response, the cookie from that request or response is carried in the message from the stack to the user. And the cookie field is followed by the actual data for the signal. πŸ“


πŸ¦‹ the signals, in all their glory

The signal is one of the following:

enum uni_sig {
    UNIAPI_ERROR,                       /* UNI -> API */

    UNIAPI_CALL_CREATED,                /* UNI -> API */
    UNIAPI_CALL_DESTROYED,              /* UNI -> API */
    UNIAPI_PARTY_CREATED,               /* UNI -> API */
    UNIAPI_PARTY_DESTROYED,             /* UNI -> API */

    UNIAPI_LINK_ESTABLISH_request,      /* API -> UNI */
    UNIAPI_LINK_ESTABLISH_confirm,      /* UNI -> API */
    UNIAPI_LINK_RELEASE_request,        /* API -> UNI */
    UNIAPI_LINK_RELEASE_confirm,        /* UNI -> API */

    UNIAPI_RESET_request,               /* API -> UNI */
    UNIAPI_RESET_confirm,               /* UNI -> API */
    UNIAPI_RESET_indication,            /* UNI -> API */
    UNIAPI_RESET_ERROR_indication,      /* UNI -> API */
    UNIAPI_RESET_response,              /* API -> UNI */
    UNIAPI_RESET_ERROR_response,        /* API -> UNI */
    UNIAPI_RESET_STATUS_indication,     /* UNI -> API */

    UNIAPI_SETUP_request,               /* API -> UNI */
    UNIAPI_SETUP_indication,            /* UNI -> API */
    UNIAPI_SETUP_response,              /* API -> UNI */
    UNIAPI_SETUP_confirm,               /* UNI -> API */
    UNIAPI_SETUP_COMPLETE_indication,   /* UNI -> API */
    UNIAPI_ALERTING_request,            /* API -> UNI */
    UNIAPI_ALERTING_indication,         /* UNI -> API */
    UNIAPI_PROCEEDING_request,          /* API -> UNI */
    UNIAPI_PROCEEDING_indication,       /* UNI -> API */
    UNIAPI_RELEASE_request,             /* API -> UNI */
    UNIAPI_RELEASE_indication,          /* UNI -> API */
    UNIAPI_RELEASE_response,            /* API -> UNI */
    UNIAPI_RELEASE_confirm,             /* UNI -> API */
    UNIAPI_NOTIFY_request,              /* API -> UNI */
    UNIAPI_NOTIFY_indication,           /* UNI -> API */
    UNIAPI_STATUS_indication,           /* UNI -> API */
    UNIAPI_STATUS_ENQUIRY_request,      /* API -> UNI */

    UNIAPI_ADD_PARTY_request,           /* API -> UNI */
    UNIAPI_ADD_PARTY_indication,        /* UNI -> API */
    UNIAPI_PARTY_ALERTING_request,      /* API -> UNI */
    UNIAPI_PARTY_ALERTING_indication,   /* UNI -> API */
    UNIAPI_ADD_PARTY_ACK_request,       /* API -> UNI */
    UNIAPI_ADD_PARTY_ACK_indication,    /* UNI -> API */
    UNIAPI_ADD_PARTY_REJ_request,       /* API -> UNI */
    UNIAPI_ADD_PARTY_REJ_indication,    /* UNI -> API */
    UNIAPI_DROP_PARTY_request,          /* API -> UNI */
    UNIAPI_DROP_PARTY_indication,       /* UNI -> API */
    UNIAPI_DROP_PARTY_ACK_request,      /* API -> UNI */
    UNIAPI_DROP_PARTY_ACK_indication,   /* UNI -> API */

    UNIAPI_ABORT_CALL_request,          /* API -> UNI */

    UNIAPI_MAXSIG
};

The meaning of most of these signals can be deduced from the ITU-T SDLs. A number of signals, however, are unique to this implementation β€” and those deserve a lil spotlight moment ⭐:

  • UNIAPI_ERROR β€” this is the error response mentioned earlier. It carries an error code, or zero if the signal was accepted by the stack.
  • UNIAPI_CALL_CREATED β€” the UNI stack has created a call instance, either from an incoming SETUP or from the user requesting an outgoing SETUP. This may be used to synchronize the creation and destroying of call data between the UNI stack and the user. 🀝
  • UNIAPI_CALL_DESTROYED β€” a call instance has been destroyed and all resources have been freed. bye bestie πŸ‘‹
  • UNIAPI_PARTY_CREATED β€” a new party has been created for an existing point-to-multipoint call. This may be used to synchronize the creation and destroying of party data between the UNI stack and the user.
  • UNIAPI_PARTY_DESTROYED β€” a party has been destroyed and all resources have been freed.
  • UNIAPI_ABORT_CALL_request β€” this requests the stack to destroy the call instance and free all its resources, without sending any messages to the network. Just straight-up vanishing, no goodbye text. πŸ’€
  • UNIAPI_MAXSIG β€” this is not a signal, it's a definition to get the number of defined signals. Do not come for it, it's just here for the math. πŸ”’

Each of the signals is followed by a fixed size structure defined in <netnatm/sig/unidef.h>. ✨


πŸ’œ okay that's part one bestie, the rest of the page is coming right up β€” don't go anywhere 🫢

πŸŽ›οΈ CONTROL MESSAGES πŸŽ›οΈ

okay so the uni node already speaks the standard control messages, but it ALSO has these extra ones, and they are lowkey iconic:


NGM_UNI_SETDEBUG (setdebug) β€” set those debug levels ✨

Set debugging facility levels. The UNI stack defines a number of debugging facilities, each one associated with a debugging level. If the debugging level of a facility is non-zero, text output will be generated to the console. The message uses the following structure:

struct ngm_uni_debug {
        uint32_t        level[UNI_MAXFACILITY];
};

So like, each facility has a level, and if it's not zero you get console output. slay πŸ’…


NGM_UNI_GETDEBUG (getdebug) β€” read the debug vibes πŸ”

Get debugging facility levels. This returns an ngm_uni_debug structure.

yep, same struct, just reading the vibes. no cap.


NGM_UNI_GET_CONFIG (get_config) β€” show me the config, bestie πŸ’–

Retrieve the current configuration of the UNI instance. This message returns a uni_config structure:

struct uni_config {
        uint32_t proto;         /* which protocol */
        uint32_t popt;          /* protocol option */
        uint32_t option;        /* other options */
        uint32_t timer301;      /* T301 */
        uint32_t timer303;      /* T303 */
        uint32_t init303;       /* T303 retransmission count */
        uint32_t timer308;      /* T308 */
        uint32_t init308;       /* T308 retransmission count */
        uint32_t timer309;      /* T309 */
        uint32_t timer310;      /* T310 */
        uint32_t timer313;      /* T313 */
        uint32_t timer316;      /* T316 */
        uint32_t init316;       /* T316 retransmission count */
        uint32_t timer317;      /* T317 */
        uint32_t timer322;      /* T322 */
        uint32_t init322;       /* T322 retransmission count */
        uint32_t timer397;      /* T397 */
        uint32_t timer398;      /* T398 */
        uint32_t timer399;      /* T399 */
};

The field proto specifies one of the following protocols:

enum uni_proto {
        UNIPROTO_UNI40U,        /* UNI4.0 user side */
        UNIPROTO_UNI40N,        /* UNI4.0 network side */
        UNIPROTO_PNNI10,        /* PNNI1.0 */
};

Some protocols may have options which can be set in popt:

enum uni_popt {
        UNIPROTO_GFP,           /* enable GFP */
};

The option field controls parsing and checking of messages:

enum uni_option {
        UNIOPT_GIT_HARD,        /* harder check of GIT IE */
        UNIOPT_BEARER_HARD,     /* harder check of BEARER IE */
        UNIOPT_CAUSE_HARD,      /* harder check of CAUSE IE */
};

All timer values are given in milliseconds. Note, however, that the actual resolution of the timers depend on system configuration (see timeout(9)).

so don't come for me if the timing is a lil off, that's a system config thing, iykyk. periodt 🌸


NGM_UNI_SET_CONFIG (set_config) β€” change the config, no cap πŸŽ€

Change the UNI configuration. This takes a

struct ngm_uni_set_config {
        struct uni_config               config;
        struct ngm_uni_config_mask      mask;
};
struct ngm_uni_config_mask {
        uint32_t        mask;
        uint32_t        popt_mask;
        uint32_t        option_mask;
};

The fields of the ngm_uni_config_mask specify which configuration parameter to change. The mask field contains bit definitions for all timers, retransmission counters and the proto field, popt_mask selects which of the protocol options to change, and option_mask specifies which options should be changed. The following bits are defined:

enum uni_config_mask {
        UNICFG_PROTO,
        UNICFG_TIMER301,
        UNICFG_TIMER303,
        UNICFG_INIT303,
        UNICFG_TIMER308,
        UNICFG_INIT308,
        UNICFG_TIMER309,
        UNICFG_TIMER310,
        UNICFG_TIMER313,
        UNICFG_TIMER316,
        UNICFG_INIT316,
        UNICFG_TIMER317,
        UNICFG_TIMER322,
        UNICFG_INIT322,
        UNICFG_TIMER397,
        UNICFG_TIMER398,
        UNICFG_TIMER399,
};

For popt_mask and option_mask, the definitions from enum uni_popt and enum uni_option should be used.

like, use the right enum bestie, don't freestyle it. iykyk πŸ¦‹


NGM_UNI_ENABLE (enable) β€” turn it on, main character energy πŸ‘‘

Create the UNI instance and enable processing. Before the UNI is enabled parameters cannot be retrieved or set.

so like, turn it on first, bestie, or you're just yelling into the void. fr fr πŸ’›


NGM_UNI_DISABLE (disable) β€” bye-bye, clean up πŸ’€

Destroy the UNI instance and free all resources. Note, that connections are not released.

so it's giving cleanup but NOT hanging up your calls. periodt 😭


🌈 SEE ALSO 🌈

Bestie, go read netgraph(4), ng_atm(4), ng_sscfu(4), ng_sscop(4), and ngctl(8).


πŸ‘‘ AUTHORS πŸ‘‘

The uni netgraph node and this manual page were written by Harti Brandt <harti@FreeBSD.org>.


πŸ’€ BUGS πŸ’€

  • LIJ (leaf-initiated-join) is not implemented yet.
  • GFP (generic functional protocol, Q.2932.1) is not yet implemented.
  • More testing needed.
  • PNNI not yet implemented.
  • Need to implement connection modification and the Q.2931 amendments.

ngl bestie, it's a whole list of "not yet" but we move. πŸ’…


mwah πŸ’‹ this page is from Debian, dated October 6, 2003, and it's the NG_UNI(4) manual page. stay iconic. ✨🫢

πŸ“¦ where this page comes from