diff --git a/attribute.go b/attribute.go index 366da72b..986e191d 100644 --- a/attribute.go +++ b/attribute.go @@ -474,14 +474,14 @@ func IPv6Prefix(a Attribute) (*net.IPNet, error) { ip := make(net.IP, net.IPv6len) copy(ip, a[2:]) - bit := uint(prefixLength % 8) - for octet := prefixLength / 8; octet < len(ip); octet++ { - for ; bit < 8; bit++ { - if ip[octet]&(1<<(7-bit)) != 0 { - return nil, errors.New("invalid prefix data") - } - } - bit = 0 + // Clear any bits beyond the prefix length. RFC 3162 requires these bits to + // be zero, but some equipment leaves them set; tolerate that here instead + // of rejecting the attribute. + if bit := uint(prefixLength % 8); bit != 0 { + ip[prefixLength/8] &^= 0xff >> bit + } + for octet := (prefixLength + 7) / 8; octet < len(ip); octet++ { + ip[octet] = 0 } return &net.IPNet{ diff --git a/attribute_test.go b/attribute_test.go index 20655d0b..0811d207 100644 --- a/attribute_test.go +++ b/attribute_test.go @@ -143,6 +143,39 @@ func TestIPv6Prefix_issue118(t *testing.T) { } } +func TestIPv6Prefix_issue119(t *testing.T) { + tests := []struct { + Name string + Attr []byte + Expected string + }{ + { + // /64 prefix sent with the host bits (::1) still set. + Name: "host bits set", + Attr: []byte{0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + Expected: "2001:db8::/64", + }, + { + // /47 prefix with a non-zero bit beyond the prefix length. + Name: "non-octet boundary with trailing bit", + Attr: []byte{0x00, 0x2f, 0x20, 0x01, 0x15, 0x30, 0x10, 0x0f}, + Expected: "2001:1530:100e::/47", + }, + } + + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + ipNet, err := IPv6Prefix(tt.Attr) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if got := ipNet.String(); got != tt.Expected { + t.Fatalf("got %s, expected %s", got, tt.Expected) + } + }) + } +} + func ipNetEquals(a, b *net.IPNet) bool { if a == nil && b == nil { return true