Dictionary Reference
The dictionary is what pyrad2 reads to translate User-Name into "attribute code 1, type string" and back. This page documents every dictionary feature pyrad2 understands.
For a hands-on tour, run examples/dictionary_features.py - backed by examples/dictionary.extended - which exercises every feature below:
Goal
pyrad2 aims to load real-world FreeRADIUS dictionaries without modification. If a dictionary works in FreeRADIUS, it should work here too.
Data types
Use these in the third column of an ATTRIBUTE declaration:
| Type | Description |
|---|---|
string |
UTF-8 text |
octets |
Raw bytes |
integer |
32-bit unsigned integer |
signed |
32-bit signed integer |
short |
16-bit unsigned integer |
byte |
8-bit unsigned integer |
integer64 |
64-bit unsigned integer |
date |
Seconds since the Unix epoch |
ipaddr |
IPv4 address |
ipv6addr |
IPv6 address |
ipv6prefix |
IPv6 prefix |
ipv4prefix |
IPv4 prefix (RFC 5090) |
combo-ip |
Either IPv4 (4 bytes) or IPv6 (16 bytes); decided by wire length |
ifid |
8-byte Interface-Id (RFC 3162) |
ether |
MAC address (RFC 6911) |
abinary |
Ascend filter format |
tlv |
TLV container — arbitrarily deep nesting |
extended |
RFC 6929 wrapper (types 241–244) |
long-extended |
RFC 6929 wrapper (types 245–246) - transparent fragmentation |
vsa |
Bare Vendor-Specific attribute (RFC 2865 code 26) |
FreeRADIUS v4 uses modern type names — uint8, uint16, uint32, uint64, int32 — which pyrad2 accepts as aliases for byte, short, integer, integer64, and signed respectively. Mix and match freely.
Attribute options
Add comma-separated options after the type column:
| Option | Effect |
|---|---|
has_tag |
Attribute carries a one-byte tag prefix (RFC 2868) |
encrypt=1 |
User-Password obfuscation |
encrypt=2 |
Tunnel-Password / MS-MPPE-Key obfuscation |
encrypt=3 |
Ascend-Send / Ascend-Receive obfuscation |
concat |
Values > 253 bytes split across multiple AVPs on the wire (RFC 7268 §3.6) - typical for EAP-Message, CHAP-Challenge |
array |
Multiple fixed-length values packed into one AVP's value field (RFC 8044 §3.8) - typical for DHCP-Router-Address and other multi-address attributes |
virtual |
Server-internal attribute, never emitted on the wire. Used for things like Auth-Type, Client-Shortname |
secret |
Hint that the value is sensitive in logs. Accepted with no wire-format change |
Vendor-specific format
Per-vendor VSA wire format is honored end-to-end. Declare it on the VENDOR line:
The format=type_len,len_len[,c] directive controls:
type_len- bytes in the vendor-type field:1,2, or4len_len- bytes in the vendor-length field:0,1, or2c(optional) - opts the vendor into RFC 5904 long-packed VSAs: an extra continuation byte is inserted after the length header, with bit0x80flagging fragments. WiMAX (format=1,1,c) is the canonical example; Telrad uses the same shape. Pyrad2 handles fragmentation and reassembly transparently — callers see one logical value either way.
If format= is omitted, pyrad2 falls back to the RFC 2865 §5.26 default of 1,1.
Extended attributes (RFC 6929)
RFC 6929 reserves attribute types 241–246 as wrappers that hold sub-attributes. Declare the wrapper, then add sub-attributes using dotted-code notation:
ATTRIBUTE Extended-Attribute-1 241 extended
ATTRIBUTE Frag-Status 241.1 integer
ATTRIBUTE Auth-Lifetime 241.2 integer
ATTRIBUTE Extended-Attribute-5 245 long-extended
ATTRIBUTE WiMAX-Blob 245.1 octets
| Type | Use |
|---|---|
extended (241–244) |
Standard extended attribute |
long-extended (245–246) |
Extended attribute with fragmentation for values > 251 bytes |
Access on the packet
Sub-attributes are accessed by name; the parent returns a dict of sub-attribute values:
packet["Frag-Status"] = 5
packet["Auth-Lifetime"] = 3600
packet["Extended-Attribute-1"]
# -> {"Frag-Status": [5], "Auth-Lifetime": [3600]}
long-extended values larger than 251 bytes are fragmented on send and reassembled on receive. Callers see one logical value either way.
Extended-Vendor-Specific (EVS)
EVS (RFC 6929 §2.3) is how a vendor carries its own attributes inside an extended wrapper. The evs type marks the slot, and BEGIN-VENDOR <name> parent=<evs-attr> scopes the vendor's attributes underneath it:
ATTRIBUTE Extended-Attribute-1 241 extended
ATTRIBUTE Extended-Vendor-Specific-1 241.26 evs
VENDOR Example 12345
BEGIN-VENDOR Example parent=Extended-Vendor-Specific-1
ATTRIBUTE Example-Attr-1 1 string
ATTRIBUTE Example-Attr-2 2 integer
END-VENDOR Example
Access EVS attributes by name like any other attribute:
The wire encoding wraps the vendor id and vendor type into the extended payload. long-extended EVS values fragment and reassemble the same way as plain long-extended attributes.
Nested TLVs
TLV containers can hold further TLV containers to arbitrary depth. Sub-attributes use dotted-code notation at every level:
ATTRIBUTE Extended-Attribute-1 241 extended
ATTRIBUTE IP-Port-Limit-Info 241.5 tlv
ATTRIBUTE IP-Port-Type 241.5.1 integer
ATTRIBUTE IP-Port-Limit 241.5.2 integer
The packet API recurses through the nesting so each level surfaces as a {sub_name: [values]} map:
packet["IP-Port-Type"] = 7
packet["IP-Port-Limit"] = 100
packet["Extended-Attribute-1"]
# -> {"IP-Port-Limit-Info": {"IP-Port-Type": [7], "IP-Port-Limit": [100]}}
Wire encoding nests <type><length><value> triplets at every level inside the outer envelope. This covers the RFC 6929 / 7499 / 8045 dictionaries among others.
Real-world compatibility
pyrad2 is regression-tested against the upstream FreeRADIUS dictionary corpus and packet test vectors. See the FreeRADIUS Conformance page for the suite, its results, and the small number of known corner cases.
If you hit something that FreeRADIUS handles but pyrad2 doesn't, please open an issue.