From 137e81b89c4e46778a8170457ffcf7d06c972480 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Thu, 19 Feb 2026 20:11:43 +0200 Subject: [PATCH] radius: support reading vendor-specific attributes handles paths with 2 elements the same way radAppendAttributes does e.g. *req.Microsoft.MS-CHAP2-Response Ref: #4962 --- agents/librad.go | 12 +++++++++--- agents/librad_test.go | 29 ++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/agents/librad.go b/agents/librad.go index b0e33600d..468464a87 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -72,7 +72,7 @@ func (pk *radiusDP) String() string { // FieldAsInterface is part of utils.DataProvider interface func (pk *radiusDP) FieldAsInterface(fldPath []string) (data any, err error) { - if len(fldPath) != 1 { + if len(fldPath) == 0 || len(fldPath) > 2 { return nil, utils.ErrNotFound } if data, err = pk.cache.FieldAsInterface(fldPath); err != nil { @@ -83,8 +83,14 @@ func (pk *radiusDP) FieldAsInterface(fldPath []string) (data any, err error) { } else { return // data found in cache } - if attrs := pk.req.AttributesWithName(fldPath[0], ""); len(attrs) != 0 { - data = attrs[0].GetStringValue() + var attrName, vendorName string + if len(fldPath) == 2 { + vendorName, attrName = fldPath[0], fldPath[1] + } else { + attrName = fldPath[0] + } + if avps := pk.req.AttributesWithName(attrName, vendorName); len(avps) != 0 { + data = avps[0].GetStringValue() } pk.cache.Set(fldPath, data) return diff --git a/agents/librad_test.go b/agents/librad_test.go index 25711e099..2be7e2b09 100644 --- a/agents/librad_test.go +++ b/agents/librad_test.go @@ -155,7 +155,6 @@ func TestRadiusDPFieldAsInterfaceCached(t *testing.T) { } dp := newRADataProvider(pkt) - if data, err := dp.FieldAsInterface([]string{"User-Name"}); err != nil { t.Error(err) } else if data != "cgr1" { @@ -180,8 +179,32 @@ func TestRadiusDPFieldAsInterfaceCached(t *testing.T) { t.Errorf("Expecting: nil, received: <%v>", data) } - if _, err := dp.FieldAsInterface([]string{"Field1", "Field2"}); err != utils.ErrNotFound { + if _, err := dp.FieldAsInterface([]string{"vendor", "attribute", "extra"}); err != utils.ErrNotFound { t.Errorf("Expecting: ErrNotFound, received: <%v>", err) } - +} + +func TestRadiusDPFieldAsInterfaceVSA(t *testing.T) { + pkt := radigo.NewPacket(radigo.AccountingRequest, 1, dictRad, coder, "CGRateS.org") + if err := pkt.AddAVPWithName("Cisco-NAS-Port", "CGR1", "Cisco"); err != nil { + t.Fatal(err) + } + dp := newRADataProvider(pkt) + if data, err := dp.FieldAsInterface([]string{"Cisco", "Cisco-NAS-Port"}); err != nil { + t.Error(err) + } else if data != "CGR1" { + t.Errorf("Expected CGR1, got: %v", data) + } +} + +func TestLibradFieldAsInterfaceWithInvalidPathLength(t *testing.T) { + pk := &radiusDP{} + fldPath := []string{"vendor", "attribute", "extra"} + data, err := pk.FieldAsInterface(fldPath) + if err != utils.ErrNotFound { + t.Errorf("Expected ErrNotFound error, got: %v", err) + } + if data != nil { + t.Errorf("Expected nil data, got: %v", data) + } }