skip to content

In a tagged RADIUS attribute such as Tunnel-Private-Group-ID (81), how does a receiver tell a Tag from the value?

level: seniorimportance: nice to knowfreq 26%

answer

  1. several tunnel groups in one packet
  2. the first octet is overloaded
  3. control characters do not start names
  4. 0x1F is the dividing line
  5. tagged integers lose their top octet

basics

~20 s

Tagged tunnel attributes reserve the value's first octet as a Tag grouping attributes that describe the same tunnel. In a text value an octet of 0x01 to 0x1F is read as the Tag; anything larger is the first character of the text.

solid answer

~40 s

RFC 2868's tunnel attributes may appear in several groups in one packet, so they reserve the first octet of the value as a `Tag` saying which group an attribute belongs to. For a text-valued attribute such as `Tunnel-Private-Group-ID` (81), the specification says an octet greater than 0x00 and not greater than 0x1F should be read as the `Tag`, and anything above 0x1F is the first octet of the text itself — printable characters do not fall in that low range, which is what makes the overload workable. For an integer-valued attribute such as `Tunnel-Type` (64) or `Tunnel-Medium-Type` (65), the `Tag` takes the most significant octet of the four-octet value, leaving three octets of number, and is zeroed when no tag is in use.

code

pseudocode · 12 lines
pseudocode
decode a tagged text attribute, e.g. Tunnel-Private-Group-ID (81):
  first = value[0]
  if first >= 0x01 and first <= 0x1F:
      tag  = first
      text = value[1 .. end]
  else:
      tag  = none                 # untagged
      text = value[0 .. end]

decode a tagged integer attribute, e.g. Tunnel-Type (64):
  tag    = value[0]               # 0x00 when no tag is in use
  number = value[1 .. 3]          # three octets remain, not four

go deeper

for a junior

It is enough to know that some RADIUS attributes carry a small group number in front of their value so that several sets of related attributes can travel in one packet without being confused.

for a middle

Explain the 0x1F boundary for text values and the most-significant-octet rule for integers, and why a tag had to be hidden inside the value rather than added to the attribute header.

for a senior

Demonstrate the diagnosis: a group identifier that is off by one character, or a tunnel number wrong by a huge factor, points straight at a decoder that strips the first octet unconditionally or never.

for a principal

The broader judgment is how much meaning to smuggle into a value field the base format never intended to structure, knowing every such rule must be agreed out of band by every implementation on the path.

## Why a tag exists at all The base attribute format has exactly three fields, and no room was left for a fourth. That became a problem the moment a reply needed to describe **more than one** of something. RFC 2868's tunnel attributes are the case that forced it: `Tunnel-Type` (64), `Tunnel-Medium-Type` (65) and `Tunnel-Private-Group-ID` (81) each describe one facet, and a reply that offers two alternative groups sends two of each. Without a marker, a receiver sees six attributes in a flat list and has no way to know which type belongs with which identifier. The answer was a **`Tag`**: a one-octet group number carried *inside the value*, so that attributes sharing a tag number describe the same thing. Putting it in the value rather than in a new header field was deliberate — a fourth header field would have changed the format every existing receiver already implemented. ## Reading a tagged text value Hiding a field inside a value creates an obvious ambiguity: is the first octet a tag, or the first character of the string? The specification resolves it by range, and states it as a *should* rather than an absolute: - An octet **greater than 0x00 and not greater than 0x1F** should be interpreted as a `Tag` indicating which tunnel the attribute pertains to. - An octet **greater than 0x1F** should be interpreted as the first octet of the `String` itself, with no tag present. The range works because 0x01 to 0x1F is the control-character range: identifiers people actually configure begin with letters or digits, which are all above 0x1F. So a `Tunnel-Private-Group-ID` (81) whose value begins with 0x03 carries tag 3 and a group name starting at the second octet, while one beginning with 0x41 — the character `A` — is an untagged value whose name starts at the first octet. ## Reading a tagged integer value Integers have no spare octet at the front and no equivalent of the control-character trick, so the encoding takes a different route. For a tagged integer attribute such as `Tunnel-Type` (64), the `Tag` occupies the **most significant octet** of the four-octet value, and the number itself lives in the remaining three. Where no tag is in use, that octet is zeroed. | | Tagged text attribute | Tagged integer attribute | |---|---|---| | Example | `Tunnel-Private-Group-ID` (81) | `Tunnel-Type` (64) | | Where the tag sits | First octet of the value, if in 0x01–0x1F | Most significant octet of the four-octet value | | How absence is shown | First octet above 0x1F, so no tag | That octet is zero | | Effect on the payload | Text starts one octet later | Three octets of number remain | ## What goes wrong The failures are all decoding failures, and they are quiet: 1. **Always stripping the first octet.** An implementation that assumes a tag is present mangles every untagged group identifier, silently deleting its first character. The value still arrives, still looks plausible, and names something that does not exist. 2. **Never stripping it.** The mirror image: a tagged value read whole produces a group name with a leading control character, which typically matches nothing and produces a session that is accepted and then behaves as though no group was named. 3. **Losing the grouping.** Tags that are dropped turn two coherent sets of tunnel attributes into six unrelated ones, and a receiver takes whichever instance it happened to keep. 4. **Assuming four octets of integer.** A tagged `Tunnel-Type` (64) has three octets of number, not four; code that reads the full word folds the tag into the high end of the value. ## The lesson beyond tunnels The tag is worth knowing less for tunnels than as the clearest example of what the attribute format costs. RADIUS fixed its encoding at `Type`, `Length`, `Value` and then had to express, inside that value, everything it later discovered it needed: grouping here, a vendor namespace under `Vendor-Specific` (26), a second level of type numbers and fragmentation under RFC 6929's extended formats. Every one of those is a field smuggled into the value because the header had no room, and every one has a decoding rule a receiver must be told about out of band. Naming the attribute and reading it correctly is this subject's job; what a group identifier is then *used* for on the network is a decision made elsewhere.

  • Why does the Tag live inside the value instead of being a new field in the attribute header?
    Because the header is fixed at a one-octet `Type` and a one-octet `Length`, and every receiver already walks the attribute list by stepping `Length` octets forward. Adding a fourth header field would change that walk for every attribute, so grouping was smuggled into the value where only attributes defined as tagged have to know about it.
  • What happens to the four-octet number in Tunnel-Type (64) when a Tag is present?
    It loses its most significant octet. The tag occupies that position, leaving three octets for the number itself, and where no tag is in use the octet is zeroed rather than omitted. Code that reads all four octets as the number folds the tag into the high end and produces a value wrong by a large multiple.

saying these in an interview costs you the question

  • Says every tagged attribute always begins with a Tag octet.
  • Thinks the Tag is a header field outside the value.
  • Believes a tagged integer still carries four octets of number.
  • Reads a leading 0x41 in a group identifier as a Tag.
  • Assumes the Tag numbers the attribute rather than its group.