Pull request 2200: 6312-client-ipv6-zone

Updates #6312.

Squashed commit of the following:

commit bd9146ee161a67fa41763070f985e1e73b85823b
Merge: 58d2fd98d 856cc40cf
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 18:09:19 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit 58d2fd98d3e82c84638d58dd4d74d13a9a8fbca6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 18:00:56 2024 +0300

    client: imp naming

commit 922a14b036d829c2775feb7bb3e6beb6aa49692e
Merge: 6f4d58fe1 60f48e2d0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 14:29:00 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit 6f4d58fe1c42504e8345bff24dbb3f523e8c5f85
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 14:27:55 2024 +0300

    client: imp docs

commit fa292eee828cd6f27f62b782675aa1f998e44518
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 22 19:20:28 2024 +0300

    client: fix typo

commit 599414be0ccd3f9deb044e022a8ac0006c96b467
Merge: 502571756 762ef4a6d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 22 18:42:06 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit 502571756400a00445086b5ba412e03fca65e39f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 22 18:39:22 2024 +0300

    all: imp code; add tests

commit 155b2fef500a0d835f49957d9f30b0870712f6f2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 19:56:00 2024 +0300

    all: upd chlog; imp code

commit 7a4426c5d0a511cd3865884c00328b8c130746bf
Merge: e9c1cbb85 48c6242a7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 19:52:00 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit e9c1cbb85e4afa173969d5bedfaaaf92716b7fad
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 10 16:23:07 2024 +0300

    client: client ipv6 zone
This commit is contained in:
Stanislav Chzhen 2024-04-24 19:08:54 +03:00
parent 856cc40cf3
commit c1ee2c7e5e
5 changed files with 129 additions and 24 deletions

View file

@ -34,6 +34,8 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Fixed ### Fixed
- Support for link-local subnets, i.e. `fe80::/16`, as client identifiers
([#6312]).
- Issues with QUIC and HTTP/3 upstreams on older Linux kernel versions - Issues with QUIC and HTTP/3 upstreams on older Linux kernel versions
([#6422]). ([#6422]).
- YouTube restricted mode is not enforced by HTTPS queries on Firefox. - YouTube restricted mode is not enforced by HTTPS queries on Firefox.
@ -51,6 +53,7 @@ NOTE: Add new changes BELOW THIS COMMENT.
[#5345]: https://github.com/AdguardTeam/AdGuardHome/issues/5345 [#5345]: https://github.com/AdguardTeam/AdGuardHome/issues/5345
[#5812]: https://github.com/AdguardTeam/AdGuardHome/issues/5812 [#5812]: https://github.com/AdguardTeam/AdGuardHome/issues/5812
[#6192]: https://github.com/AdguardTeam/AdGuardHome/issues/6192 [#6192]: https://github.com/AdguardTeam/AdGuardHome/issues/6192
[#6312]: https://github.com/AdguardTeam/AdGuardHome/issues/6312
[#6422]: https://github.com/AdguardTeam/AdGuardHome/issues/6422 [#6422]: https://github.com/AdguardTeam/AdGuardHome/issues/6422
[#6854]: https://github.com/AdguardTeam/AdGuardHome/issues/6854 [#6854]: https://github.com/AdguardTeam/AdGuardHome/issues/6854
[#6875]: https://github.com/AdguardTeam/AdGuardHome/issues/6875 [#6875]: https://github.com/AdguardTeam/AdGuardHome/issues/6875

View file

@ -197,8 +197,10 @@ func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
return ci.uidToClient[uid], true return ci.uidToClient[uid], true
} }
ipWithoutZone := ip.WithZone("")
ci.subnetToUID.Range(func(pref netip.Prefix, id UID) (cont bool) { ci.subnetToUID.Range(func(pref netip.Prefix, id UID) (cont bool) {
if pref.Contains(ip) { // Remove zone before checking because prefixes strip zones.
if pref.Contains(ipWithoutZone) {
uid, found = id, true uid, found = id, true
return false return false
@ -214,6 +216,26 @@ func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
return nil, false return nil, false
} }
// FindByIPWithoutZone finds a persistent client by IP address without zone. It
// strips the IPv6 zone index from the stored IP addresses before comparing,
// because querylog entries don't have it. See TODO on [querylog.logEntry.IP].
//
// Note that multiple clients can have the same IP address with different zones.
// Therefore, the result of this method is indeterminate.
func (ci *Index) FindByIPWithoutZone(ip netip.Addr) (c *Persistent) {
if (ip == netip.Addr{}) {
return nil
}
for addr, uid := range ci.ipToUID {
if addr.WithZone("") == ip {
return ci.uidToClient[uid]
}
}
return nil
}
// find finds persistent client by MAC. // find finds persistent client by MAC.
func (ci *Index) findByMAC(mac net.HardwareAddr) (c *Persistent, found bool) { func (ci *Index) findByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
k := macToKey(mac) k := macToKey(mac)

View file

@ -35,27 +35,49 @@ func TestClientIndex(t *testing.T) {
cliID = "client-id" cliID = "client-id"
cliMAC = "11:11:11:11:11:11" cliMAC = "11:11:11:11:11:11"
linkLocalIP = "fe80::abcd:abcd:abcd:ab%eth0"
linkLocalSubnet = "fe80::/16"
) )
clients := []*Persistent{{ var (
clientWithBothFams = &Persistent{
Name: "client1", Name: "client1",
IPs: []netip.Addr{ IPs: []netip.Addr{
netip.MustParseAddr(cliIP1), netip.MustParseAddr(cliIP1),
netip.MustParseAddr(cliIPv6), netip.MustParseAddr(cliIPv6),
}, },
}, { }
clientWithSubnet = &Persistent{
Name: "client2", Name: "client2",
IPs: []netip.Addr{netip.MustParseAddr(cliIP2)}, IPs: []netip.Addr{netip.MustParseAddr(cliIP2)},
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)}, Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}, { }
clientWithMAC = &Persistent{
Name: "client_with_mac", Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)}, MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
}, { }
clientWithID = &Persistent{
Name: "client_with_id", Name: "client_with_id",
ClientIDs: []string{cliID}, ClientIDs: []string{cliID},
}} }
ci := newIDIndex(clients) clientLinkLocal = &Persistent{
Name: "client_link_local",
Subnets: []netip.Prefix{netip.MustParsePrefix(linkLocalSubnet)},
}
)
ci := newIDIndex([]*Persistent{
clientWithBothFams,
clientWithSubnet,
clientWithMAC,
clientWithID,
clientLinkLocal,
})
testCases := []struct { testCases := []struct {
want *Persistent want *Persistent
@ -64,19 +86,23 @@ func TestClientIndex(t *testing.T) {
}{{ }{{
name: "ipv4_ipv6", name: "ipv4_ipv6",
ids: []string{cliIP1, cliIPv6}, ids: []string{cliIP1, cliIPv6},
want: clients[0], want: clientWithBothFams,
}, { }, {
name: "ipv4_subnet", name: "ipv4_subnet",
ids: []string{cliIP2, cliSubnetIP}, ids: []string{cliIP2, cliSubnetIP},
want: clients[1], want: clientWithSubnet,
}, { }, {
name: "mac", name: "mac",
ids: []string{cliMAC}, ids: []string{cliMAC},
want: clients[2], want: clientWithMAC,
}, { }, {
name: "client_id", name: "client_id",
ids: []string{cliID}, ids: []string{cliID},
want: clients[3], want: clientWithID,
}, {
name: "client_link_local_subnet",
ids: []string{linkLocalIP},
want: clientLinkLocal,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
@ -221,3 +247,52 @@ func TestMACToKey(t *testing.T) {
_ = macToKey(mac) _ = macToKey(mac)
}) })
} }
func TestIndex_FindByIPWithoutZone(t *testing.T) {
var (
ip = netip.MustParseAddr("fe80::a098:7654:32ef:ff1")
ipWithZone = netip.MustParseAddr("fe80::1ff:fe23:4567:890a%eth2")
)
var (
clientNoZone = &Persistent{
Name: "client",
IPs: []netip.Addr{ip},
}
clientWithZone = &Persistent{
Name: "client_with_zone",
IPs: []netip.Addr{ipWithZone},
}
)
ci := newIDIndex([]*Persistent{
clientNoZone,
clientWithZone,
})
testCases := []struct {
ip netip.Addr
want *Persistent
name string
}{{
name: "without_zone",
ip: ip,
want: clientNoZone,
}, {
name: "with_zone",
ip: ipWithZone,
want: clientWithZone,
}, {
name: "zero_address",
ip: netip.Addr{},
want: nil,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := ci.FindByIPWithoutZone(tc.ip.WithZone(""))
require.Equal(t, tc.want, c)
})
}
}

View file

@ -412,7 +412,11 @@ func (clients *clientsContainer) clientOrArtificial(
}() }()
cli, ok := clients.find(id) cli, ok := clients.find(id)
if ok { if !ok {
cli = clients.clientIndex.FindByIPWithoutZone(ip)
}
if cli != nil {
return &querylog.Client{ return &querylog.Client{
Name: cli.Name, Name: cli.Name,
IgnoreQueryLog: cli.IgnoreQueryLog, IgnoreQueryLog: cli.IgnoreQueryLog,

View file

@ -31,6 +31,7 @@ type logEntry struct {
Answer []byte `json:",omitempty"` Answer []byte `json:",omitempty"`
OrigAnswer []byte `json:",omitempty"` OrigAnswer []byte `json:",omitempty"`
// TODO(s.chzhen): Use netip.Addr.
IP net.IP `json:"IP"` IP net.IP `json:"IP"`
Result filtering.Result Result filtering.Result