Skip to content

Commit b55dec5

Browse files
committed
Merge branch 'master' into ADG-12585-fix-dns64
2 parents 0cf542e + e17bc9d commit b55dec5

23 files changed

Lines changed: 622 additions & 134 deletions

internal/aghnet/dhcp_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
func checkOtherDHCP(
1313
_ context.Context,
1414
_ *slog.Logger,
15-
ifaceName string,
15+
_ string,
1616
) (ok4, ok6 bool, err4, err6 error) {
1717
return false,
1818
false,

internal/aghnet/hostscontainer_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ func TestNewHostsContainer(t *testing.T) {
6464
return nil
6565
}
6666

67-
var eventsCalledCounter uint32
67+
var eventsCalledCounter atomic.Uint32
6868
eventsCh := make(chan struct{})
6969
onEvents := func() (e <-chan struct{}) {
70-
assert.Equal(t, uint32(1), atomic.AddUint32(&eventsCalledCounter, 1))
70+
assert.Equal(t, uint32(1), eventsCalledCounter.Add(1))
7171

7272
return eventsCh
7373
}
@@ -95,7 +95,7 @@ func TestNewHostsContainer(t *testing.T) {
9595
assert.NotNil(t, <-hc.Upd())
9696

9797
eventsCh <- struct{}{}
98-
assert.Equal(t, uint32(1), atomic.LoadUint32(&eventsCalledCounter))
98+
assert.Equal(t, uint32(1), eventsCalledCounter.Load())
9999
})
100100
}
101101

internal/aghnet/interfaces_linux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
// listenPacketReusable announces on the local network address additionally
1313
// configuring the socket to have a reusable binding.
14-
func listenPacketReusable(ifaceName, network, address string) (c net.PacketConn, err error) {
14+
func listenPacketReusable(ifaceName, _, address string) (c net.PacketConn, err error) {
1515
var port uint16
1616
_, port, err = netutil.SplitHostPort(address)
1717
if err != nil {

internal/dhcpsvc/dhcpsvc_test.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -393,27 +393,19 @@ func (db *testDatabase) Store(ctx context.Context, leases []*dhcpsvc.Lease) (err
393393
return db.onStore(ctx, leases)
394394
}
395395

396-
// newTestDatabase creates a new *testDatabase for testing. If initial is not
397-
// nil, db.Load is set to return it. By default, db.Store panics on any call.
398-
func newTestDatabase(tb testing.TB, initial []*dhcpsvc.Lease) (db *testDatabase) {
396+
// newTestDatabase creates a new *testDatabase for testing. The Load method
397+
// returns [testLeases], and the Store method panics.
398+
func newTestDatabase(tb testing.TB) (db *testDatabase) {
399399
tb.Helper()
400400

401-
db = &testDatabase{
402-
onLoad: func(ctx context.Context) (_ []*dhcpsvc.Lease, _ error) {
403-
panic(testutil.UnexpectedCall(ctx))
401+
return &testDatabase{
402+
onLoad: func(_ context.Context) (leases []*dhcpsvc.Lease, err error) {
403+
return testLeases, nil
404404
},
405405
onStore: func(ctx context.Context, leases []*dhcpsvc.Lease) (_ error) {
406406
panic(testutil.UnexpectedCall(ctx, leases))
407407
},
408408
}
409-
410-
if initial != nil {
411-
db.onLoad = func(ctx context.Context) (leases []*dhcpsvc.Lease, err error) {
412-
return initial, nil
413-
}
414-
}
415-
416-
return db
417409
}
418410

419411
// newTestDHCPServer creates a new DHCPServer for testing. It uses the default

internal/dhcpsvc/handler4_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func TestDHCPServer_ServeEther4_discover(t *testing.T) {
8383

8484
ndMgr, inCh, outCh := newTestNetworkDeviceManager(t, testIfaceAddrV4)
8585
startTestDHCPServer(t, &dhcpsvc.Config{
86-
Database: newTestDatabase(t, testLeases),
86+
Database: newTestDatabase(t),
8787
Interfaces: testIPv4InterfacesConf,
8888
NetworkDeviceManager: ndMgr,
8989
Enabled: true,
@@ -105,7 +105,7 @@ func TestDHCPServer_ServeEther4_discoverExpired(t *testing.T) {
105105
ndMgr, inCh, outCh := newTestNetworkDeviceManager(t, testIfaceAddrV4)
106106

107107
startTestDHCPServer(t, &dhcpsvc.Config{
108-
Database: newTestDatabase(t, testLeases),
108+
Database: newTestDatabase(t),
109109
Interfaces: testIPv4InterfacesConf,
110110
NetworkDeviceManager: ndMgr,
111111
Enabled: true,
@@ -156,7 +156,7 @@ func TestDHCPServer_ServeEther4_release(t *testing.T) {
156156
t.Run(tc.name, func(t *testing.T) {
157157
t.Parallel()
158158

159-
db := newTestDatabase(t, testLeases)
159+
db := newTestDatabase(t)
160160

161161
onStore := func(ctx context.Context, leases []*dhcpsvc.Lease) (err error) {
162162
assert.NotContains(testutil.NewPanicT(t), leases, tc.want)
@@ -262,7 +262,7 @@ func TestDHCPServer_ServeEther4_requestSelecting(t *testing.T) {
262262
t.Run(tc.name, func(t *testing.T) {
263263
t.Parallel()
264264

265-
db := newTestDatabase(t, testLeases)
265+
db := newTestDatabase(t)
266266

267267
onStore := func(ctx context.Context, leases []*dhcpsvc.Lease) (err error) {
268268
assert.Contains(t, leases, tc.want)
@@ -311,7 +311,7 @@ func TestDHCPServer_ServeEther4_requestSelectingNoLease(t *testing.T) {
311311
flags: dhcpsvc.FlagsBroadcast,
312312
})
313313

314-
db := newTestDatabase(t, testLeases)
314+
db := newTestDatabase(t)
315315

316316
ndMgr, inCh, outCh := newTestNetworkDeviceManager(t, testIfaceAddrV4)
317317
startTestDHCPServer(t, &dhcpsvc.Config{
@@ -413,7 +413,7 @@ func TestDHCPServer_ServeEther4_requestInitReboot(t *testing.T) {
413413
t.Run(tc.name, func(t *testing.T) {
414414
t.Parallel()
415415

416-
db := newTestDatabase(t, testLeases)
416+
db := newTestDatabase(t)
417417

418418
onStore := func(ctx context.Context, leases []*dhcpsvc.Lease) (err error) {
419419
assert.Contains(t, leases, tc.want)
@@ -513,7 +513,7 @@ func TestDHCPServer_ServeEther4_requestRenewSuccess(t *testing.T) {
513513
t.Run(tc.name, func(t *testing.T) {
514514
t.Parallel()
515515

516-
db := newTestDatabase(t, testLeases)
516+
db := newTestDatabase(t)
517517

518518
onStore := func(ctx context.Context, leases []*dhcpsvc.Lease) (err error) {
519519
assert.Contains(t, leases, tc.want)
@@ -586,7 +586,7 @@ func TestDHCPServer_ServeEther4_requestRenewFail(t *testing.T) {
586586
}
587587

588588
startTestDHCPServer(t, &dhcpsvc.Config{
589-
Database: newTestDatabase(t, testLeases),
589+
Database: newTestDatabase(t),
590590
Interfaces: testIPv4InterfacesConf,
591591
NetworkDeviceManager: ndMgr,
592592
Enabled: true,
@@ -641,7 +641,7 @@ func TestDHCPServer_ServeEther4_decline(t *testing.T) {
641641
t.Run(tc.name, func(t *testing.T) {
642642
t.Parallel()
643643

644-
db := newTestDatabase(t, testLeases)
644+
db := newTestDatabase(t)
645645

646646
onStore := func(ctx context.Context, leases []*dhcpsvc.Lease) (err error) {
647647
assert.Contains(t, leases, wantLease)

internal/dhcpsvc/handler6.go

Lines changed: 91 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7+
"net/netip"
78

89
"github.com/AdguardTeam/golibs/errors"
910
"github.com/AdguardTeam/golibs/logutil/slogutil"
@@ -274,24 +275,27 @@ func (iface *dhcpInterfaceV6) handleRenew(
274275
// handler does.
275276
//
276277
// See RFC 9915 Section 18.3.4.
277-
resp.Options = iface.newRenewRespOpts(fd, req, cliID, layers.DHCPv6Option{})
278+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, layers.DHCPv6Option{})
278279

279280
return respond6(fd, resp)
280281
}
281282

282-
ianaOpt := iface.ianaForRenew(ctx, req, iana, fd.ether.SrcMAC)
283-
resp.Options = iface.newRenewRespOpts(fd, req, cliID, ianaOpt)
283+
ianaOpt := iface.ianaForUpdate(ctx, req, iana, fd.ether.SrcMAC)
284+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, ianaOpt)
284285

285286
return respond6(fd, resp)
286287
}
287288

288289
// handleRebind handles messages of type REBIND. req must not be nil and must
289-
// be a valid DHCPv6 message of type REBIND. fd must be valid.
290+
// be a valid DHCPv6 message of type REBIND, fd must be valid.
290291
//
291-
// TODO(e.burkov): Implement. This is a stub for now.
292+
// TODO(e.burkov): The current implementation rebinds only the first valid
293+
// IA_NA option. It does not verify that the addresses in the IA match the
294+
// stored lease, since clients are identified by MAC address rather than
295+
// DUID+IAID.
292296
func (iface *dhcpInterfaceV6) handleRebind(
293297
ctx context.Context,
294-
_ *frameData6,
298+
fd *frameData6,
295299
req *layers.DHCPv6,
296300
) (err error) {
297301
cliID, err := clientIDNoServer(req.Options)
@@ -302,31 +306,54 @@ func (iface *dhcpInterfaceV6) handleRebind(
302306
l := iface.common.logger
303307
l.DebugContext(ctx, "handling message", "type", req.MsgType, "cli_id", cliID)
304308

305-
return nil
309+
iface.common.indexMu.Lock()
310+
defer iface.common.indexMu.Unlock()
311+
312+
resp := &layers.DHCPv6{
313+
MsgType: layers.DHCPv6MsgTypeReply,
314+
TransactionID: req.TransactionID,
315+
}
316+
317+
iana, ok := iface.firstIANA(ctx, req)
318+
if !ok {
319+
// With no IA_NA options and no requested addresses there's nothing to
320+
// rebind. Respond with no IA options similarly to how the Renew
321+
// handler does.
322+
//
323+
// See RFC 9915 Section 18.3.5.
324+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, layers.DHCPv6Option{})
325+
326+
return respond6(fd, resp)
327+
}
328+
329+
ianaOpt := iface.ianaForUpdate(ctx, req, iana, fd.ether.SrcMAC)
330+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, ianaOpt)
331+
332+
return respond6(fd, resp)
306333
}
307334

308335
// handleInfo handles messages of type INFORMATION-REQUEST. req must not be nil
309336
// and must be a valid DHCPv6 message of type INFORMATION-REQUEST. fd must be
310337
// valid.
311338
//
312-
// TODO(e.burkov): Implement. This is a stub for now.
339+
// TODO(e.burkov): The current implementation does not handle relay-forwarded
340+
// INFORMATION-REQUEST messages.
313341
func (iface *dhcpInterfaceV6) handleInfo(
314342
ctx context.Context,
315343
fd *frameData6,
316344
req *layers.DHCPv6,
317345
) (err error) {
318-
if srvID, ok := findOption6(req.Options, layers.DHCPv6OptServerID); ok {
319-
if !bytes.Equal(srvID, fd.duidData) {
320-
return fmt.Errorf(
321-
"dhcpv6: server id: got %v, want %v: %w",
322-
srvID,
323-
fd.duidData,
324-
errors.ErrNotEqual,
325-
)
326-
}
346+
srvID, ok := findOption6(req.Options, layers.DHCPv6OptServerID)
347+
if ok && !bytes.Equal(srvID, fd.duidData) {
348+
return fmt.Errorf(
349+
"dhcpv6: server id: got %v, want %v: %w",
350+
srvID,
351+
fd.duidData,
352+
errors.ErrNotEqual,
353+
)
327354
}
328355

329-
_, ok := findOption6(req.Options, layers.DHCPv6OptIANA)
356+
_, ok = findOption6(req.Options, layers.DHCPv6OptIANA)
330357
if ok {
331358
return fmt.Errorf("dhcpv6: %s: ia option: %w", req.MsgType, errors.ErrUnexpectedValue)
332359
}
@@ -339,13 +366,20 @@ func (iface *dhcpInterfaceV6) handleInfo(
339366
l := iface.common.logger
340367
l.DebugContext(ctx, "handling message", "type", req.MsgType)
341368

342-
return nil
369+
resp := &layers.DHCPv6{
370+
MsgType: layers.DHCPv6MsgTypeReply,
371+
TransactionID: req.TransactionID,
372+
Options: iface.newInfoRespOpts(fd, req),
373+
}
374+
375+
return respond6(fd, resp)
343376
}
344377

345378
// handleRelease handles messages of type RELEASE. req must not be nil and must
346379
// be a valid DHCPv6 message of type RELEASE. fd must be valid.
347380
//
348-
// TODO(e.burkov): Implement. This is a stub for now.
381+
// TODO(e.burkov): Verify all IA_NA options instead of handling only the first
382+
// one.
349383
func (iface *dhcpInterfaceV6) handleRelease(
350384
ctx context.Context,
351385
fd *frameData6,
@@ -359,7 +393,43 @@ func (iface *dhcpInterfaceV6) handleRelease(
359393
l := iface.common.logger
360394
l.DebugContext(ctx, "handling message", "type", req.MsgType, "cli_id", cliID)
361395

362-
return nil
396+
resp := &layers.DHCPv6{
397+
MsgType: layers.DHCPv6MsgTypeReply,
398+
TransactionID: req.TransactionID,
399+
}
400+
401+
iaid, ip := iface.ipForRelease(ctx, req)
402+
if ip == (netip.Addr{}) {
403+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, layers.DHCPv6Option{})
404+
405+
return respond6(fd, resp)
406+
}
407+
408+
key := macToKey(fd.ether.SrcMAC)
409+
410+
iface.common.indexMu.Lock()
411+
defer iface.common.indexMu.Unlock()
412+
413+
lease, hasLease := iface.common.leases[key]
414+
if !hasLease || lease.IP != ip {
415+
respIANA := newIANAWithStatus(iaid, layers.DHCPv6StatusCodeNoBinding)
416+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, respIANA)
417+
418+
return respond6(fd, resp)
419+
}
420+
421+
err = iface.common.index.remove(ctx, lease, iface.common)
422+
if err != nil {
423+
l.ErrorContext(ctx, "removing lease", slogutil.KeyError, err)
424+
425+
respIANA := newIANAWithStatus(iaid, layers.DHCPv6StatusCodeNoBinding)
426+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, respIANA)
427+
} else {
428+
respIANA := &IANAOption{ID: iaid}
429+
resp.Options = iface.newUpdateRespOpts(fd, req, cliID, respIANA.Encode())
430+
}
431+
432+
return respond6(fd, resp)
363433
}
364434

365435
// handleDecline handles messages of type DECLINE. req must not be nil and must

0 commit comments

Comments
 (0)