=========================
Lunar RADIUS Dictionaries
=========================

This directory contains the RADIUS attribute dictionaries used by
Lunar. The dictionaries describe the RADIUS attributes and vendor
specific attributes (VSAs) that the server can encode and decode on the
wire, the data types of those attributes, and the named values
(enumerations) that they may take.

The format is a Lunar specific YAML format. The attribute names, codes
and data types are drawn from their authoritative sources: the IANA
RADIUS Attribute Types registry, the defining RFCs (2865, 2866, 2868,
2869, 3162, 5176, 6929, 8044 and friends), and each vendor's own
published VSA documentation.

The parser implementation lives in
``src/subscriber/radius/RadiusDictionary.cpp``.


File layout
===========

The master entry point is ``dictionary.yaml``. It only contains a list
of files to load::

    includes:
      - rfc2865.yaml
      - rfc2866.yaml
      - ...
      - cisco.yaml
      - mikrotik.yaml

Each included file describes either a set of standard (RFC) attributes
or a single vendor's VSAs. Include paths are resolved relative to the
directory of the file that does the include, and includes may be
nested.


Top level keys
==============

A dictionary file may contain any combination of the following top
level keys. All of them are optional.

``title``
    Free form human readable title for the file. Ignored by the
    parser, used for documentation only.

``includes``
    A list of additional YAML files to load. Paths are relative to the
    current file.

``vendor``
    Defines the vendor that owns the VSAs declared in this file. See
    `Vendor block`_ below.

``attributes``
    A mapping of attribute name to attribute definition. See
    `Attribute definitions`_ below.

``values``
    A mapping of attribute name to a mapping of named value to integer.
    Used to give symbolic names to enumerated attribute values. See
    `Value definitions`_ below.

Value parsing is *deferred* until after all attributes (across all
included files) have been loaded, so a ``values:`` block may reference
attributes defined in another file.


Vendor block
============

A ``vendor`` block declares a RADIUS vendor and its VSA header layout::

    vendor:
      id: 2544
      name: Adva
      format: 1,1

Fields:

``id``
    Integer SMI Network Management Private Enterprise Code (the
    vendor ID carried in attribute 26).

``name``
    Vendor name. Must be unique across all loaded dictionaries.

``format``
    VSA type/length header layout. Optional, defaults to ``1,1``.
    Supported values:

    ===========  =========  =============  ====================================
    Format       Hdr bytes  Continuation   Description
    ===========  =========  =============  ====================================
    ``1,1``      2          no             1 byte vendor-type, 1 byte length.
                                           Standard RFC 2865 layout.
    ``1,1,c``    3          yes            1 byte type, 1 byte length, 1 byte
                                           continuation flag (RFC 6929 long
                                           extended style). Used by WiMAX and
                                           Telrad.
    ``2,1``      3          no             2 byte vendor-type, 1 byte length.
                                           Used by Lucent and Alcatel ESAM.
    ``2,2``      4          no             2 byte vendor-type, 2 byte length.
    ``4,0``      4          no             4 byte vendor-type, no length field.
    ===========  =========  =============  ====================================

Only one ``vendor`` block is allowed per file. All attributes declared
in the same file are associated with that vendor.


Attribute definitions
=====================

Attributes are declared under the ``attributes`` key as a mapping from
the attribute name to its options::

    attributes:
      User-Name:
        code: 1
        type: text
      User-Password:
        code: 2
        type: text
        encrypt: 1
      Tunnel-Type:
        code: 64
        type: integer
        has_tag: true
      Adva-User-Level:
        code: 26.2544.100
        type: integer

Required fields
---------------

``code``
    The attribute code. A dotted path of integers identifying the
    attribute on the wire:

    * ``1`` -- a standard RADIUS attribute with type 1.
    * ``26.2544.100`` -- a VSA: attribute 26 (Vendor-Specific),
      vendor 2544 (Adva), vendor-type 100.
    * ``26.24757.1.1`` -- a TLV nested inside VSA 26.24757.1.
    * ``241.1`` -- an extended attribute (RFC 6929).

    The code must be unique within the loaded dictionary set.

``type``
    The attribute's data type. See `Supported data types`_.

Optional fields
---------------

``encrypt``
    Integer indicating that the attribute is obfuscated on the wire.

    * ``0`` (default) -- no encryption.
    * ``1`` -- RFC 2865 User-Password style obfuscation.
    * ``2`` -- Tunnel-Password style obfuscation (RFC 2868).

    Any other value (for example ``3`` as used by some Ascend / Lucent
    attributes) is not supported; the parser will emit a warning and
    skip the attribute.

``has_tag``
    Boolean. When ``true`` the attribute carries an RFC 2868 tag byte
    used to group related attributes (most commonly the ``Tunnel-*``
    family).


Value definitions
=================

The ``values`` section assigns symbolic names to integer attribute
values. It is a mapping from attribute name to a mapping of
``name: value``::

    values:
      Adva-User-Level:
        Super: 5
        Admin: 4
        Provision: 3
        Operate_Control: 2
        Reserved: 1
        Retrieve: 0

The attribute being referenced must exist (in any loaded file). The
values are passed through the attribute's encoder, so the value must
be compatible with the declared ``type``. Individual values that raise
a ``TypeError`` from the encoder are skipped with a warning; any other
encoding failure aborts parsing of the whole ``values`` block for that
attribute and is reported as a parse error.

Duplicate value names for the same attribute are allowed -- the last
one wins.


Supported data types
====================

The following values are valid for the ``type`` field. They follow the
naming used by RFC 8044, section 3.

==================  ==========================================================
Type                Description
==================  ==========================================================
``text``            UTF-8 text string (RFC 8044 ``text``).
``string``          Opaque binary octets. Also used as the fallback for any
                    unknown type.
``integer``         Unsigned 32 bit integer.
``enum``            Unsigned 32 bit integer; semantically an enumeration. The
                    symbolic names are supplied via the ``values`` block.
``integer64``       Unsigned 64 bit integer.
``signed``          Signed 32 bit integer.
``short``           Unsigned 16 bit integer.
``byte``            Unsigned 8 bit integer.
``time``            32 bit seconds since the UNIX epoch.
``ipv4addr``        IPv4 address (4 octets).
``ipv4prefix``      IPv4 prefix (RFC 6572) -- reserved byte, prefix length,
                    address octets.
``ipv6addr``        IPv6 address (16 octets).
``ipv6prefix``      IPv6 prefix (RFC 3162) -- reserved byte, prefix length,
                    address octets.
``combo-ip``        IPv4 or IPv6 address; the form is inferred from the value
                    length.
``ifid``            8 octet IPv6 interface identifier.
``ether``           6 octet Ethernet MAC address.
``tlv``             Container holding nested attributes encoded as RFC 6929
                    TLVs. The container itself is opaque to the encoder.
``vsa``             Vendor-Specific Attribute container (attribute 26).
``extended``        RFC 6929 extended attribute container.
``long-extended``   RFC 6929 long extended attribute container.
``evs``             RFC 6929 Extended-Vendor-Specific container.
==================  ==========================================================

Any ``type`` value not listed above is accepted, but the attribute is
encoded and decoded as raw ``string`` octets.


Parser behaviour and validation
===============================

* Files are loaded with yaml-cpp.
* Includes are resolved recursively, relative to the including file.
  A file that has already been loaded is skipped, so circular includes
  terminate.
* When two files define the same attribute *name* or the same *code*,
  the definition loaded last wins (see the include-order comment in
  ``dictionary.yaml``).
* A missing or malformed file raises an exception; the server refuses
  to start the subscriber service without a loadable dictionary.


Adding a new vendor dictionary
==============================

1. Create a new file ``<vendor>.yaml`` in this directory.
2. Add a ``title``, a ``vendor`` block with the vendor's IANA
   enterprise number and chosen ``format``, an ``attributes`` block,
   and (optionally) a ``values`` block.
3. Register the file by adding its name to the ``includes:`` list in
   ``dictionary.yaml``.
4. Restart lunar so the new definitions are picked up.

Minimal example::

    title: Example Networks
    vendor:
      id: 65535
      name: Example
      format: 1,1
    attributes:
      Example-User-Role:
        code: 26.65535.1
        type: integer
      Example-Account-Id:
        code: 26.65535.2
        type: text
    values:
      Example-User-Role:
        Admin: 1
        Operator: 2
        ReadOnly: 3
