chore: enable v6 support on the same port
Replace `SO_REUSEPORT` with `SO_REUSEPORT`. Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
This commit is contained in:
parent
83e0b0c19a
commit
fa2d34dd88
@ -6,7 +6,10 @@ package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/proxy"
|
||||
@ -24,6 +27,7 @@ import (
|
||||
// DNSResolveCacheController starts dns server on both udp and tcp ports based on finalized network configuration.
|
||||
type DNSResolveCacheController struct {
|
||||
Addr string
|
||||
AddrV6 string
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
@ -100,54 +104,70 @@ func (ctrl *DNSResolveCacheController) runServer(originCtx context.Context, r co
|
||||
defer handler.Stop()
|
||||
|
||||
cache := dns.NewCache(handler, ctrl.Logger)
|
||||
addr := ctrl.Addr
|
||||
ctx := originCtx
|
||||
|
||||
serverOpts := map[string]dns.ServerOptins{}
|
||||
|
||||
for _, opt := range []struct {
|
||||
net string
|
||||
addr string
|
||||
srvOpts dns.ServerOptins
|
||||
net string
|
||||
addr string
|
||||
}{
|
||||
{
|
||||
net: "udp",
|
||||
addr: addr,
|
||||
srvOpts: dns.ServerOptins{
|
||||
Handler: cache,
|
||||
},
|
||||
},
|
||||
{
|
||||
net: "tcp",
|
||||
addr: addr,
|
||||
srvOpts: dns.ServerOptins{
|
||||
{net: "udp", addr: ctrl.Addr},
|
||||
{net: "udp6", addr: ctrl.AddrV6},
|
||||
{net: "tcp", addr: ctrl.Addr},
|
||||
{net: "tcp6", addr: ctrl.AddrV6},
|
||||
} {
|
||||
l := ctrl.Logger.With(zap.String("net", opt.net), zap.String("addr", opt.addr))
|
||||
|
||||
switch opt.net {
|
||||
case "udp", "udp6":
|
||||
packetConn, err := dns.NewUDPPacketConn(opt.net, opt.addr)
|
||||
if err != nil {
|
||||
if opt.net == "udp6" {
|
||||
// If we can't bind to ipv6, we can continue with ipv4
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("error creating udp packet conn: %w", err)
|
||||
}
|
||||
|
||||
defer closeListener(packetConn, l)
|
||||
|
||||
serverOpts[opt.net] = dns.ServerOptins{
|
||||
PacketConn: packetConn,
|
||||
Handler: cache,
|
||||
}
|
||||
|
||||
case "tcp", "tcp6":
|
||||
listener, err := dns.NewTCPListener(opt.net, opt.addr)
|
||||
if err != nil {
|
||||
if opt.net == "tcp6" {
|
||||
// If we can't bind to ipv6, we can continue with ipv4
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("error creating tcp listener: %w", err)
|
||||
}
|
||||
|
||||
defer closeListener(listener, l)
|
||||
|
||||
serverOpts[opt.net] = dns.ServerOptins{
|
||||
Listener: listener,
|
||||
Handler: cache,
|
||||
ReadTimeout: 3 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
IdleTimeout: func() time.Duration { return 10 * time.Second },
|
||||
MaxTCPQueries: -1,
|
||||
},
|
||||
},
|
||||
} {
|
||||
l := ctrl.Logger.With(zap.String("net", opt.net))
|
||||
|
||||
if opt.net == "tcp" {
|
||||
listener, err := dns.NewTCPListener(opt.addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating tcp listener: %w", err)
|
||||
}
|
||||
|
||||
opt.srvOpts.Listener = listener
|
||||
} else if opt.net == "udp" {
|
||||
packetConn, err := dns.NewUDPPacketConn(opt.addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating udp packet conn: %w", err)
|
||||
}
|
||||
|
||||
opt.srvOpts.PacketConn = packetConn
|
||||
}
|
||||
}
|
||||
|
||||
runner := dns.NewRunner(dns.NewServer(opt.srvOpts), l)
|
||||
for netwk, opt := range serverOpts {
|
||||
l := ctrl.Logger.With(zap.String("net", netwk))
|
||||
|
||||
err := ctrl.writeDNSStatus(ctx, r, opt.net)
|
||||
runner := dns.NewRunner(dns.NewServer(opt), l)
|
||||
|
||||
err := ctrl.writeDNSStatus(ctx, r, netwk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -203,6 +223,12 @@ func (ctrl *DNSResolveCacheController) runServer(originCtx context.Context, r co
|
||||
}
|
||||
}
|
||||
|
||||
func closeListener(lis io.Closer, l *zap.Logger) {
|
||||
if err := lis.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
|
||||
l.Error("error closing listener", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func dropResolveResources(ctx context.Context, r controller.Runtime, nets ...resource.ID) error {
|
||||
for _, net := range nets {
|
||||
if err := r.Destroy(ctx, network.NewDNSResolveCache(net).Metadata()); err != nil {
|
||||
|
@ -148,7 +148,8 @@ func TestDNSServer(t *testing.T) {
|
||||
AfterSetup: func(suite *ctest.DefaultSuite) {
|
||||
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DNSUpstreamController{}))
|
||||
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DNSResolveCacheController{
|
||||
Addr: ":10700",
|
||||
Addr: "127.0.0.1:10700",
|
||||
AddrV6: "[::1]:10700",
|
||||
Logger: zaptest.NewLogger(t),
|
||||
}))
|
||||
},
|
||||
|
@ -190,6 +190,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
|
||||
&network.DeviceConfigController{},
|
||||
&network.DNSResolveCacheController{
|
||||
Addr: "127.0.0.1:53",
|
||||
AddrV6: "[::1]:53",
|
||||
Logger: dnsCacheLogger,
|
||||
},
|
||||
&network.DNSUpstreamController{},
|
||||
|
@ -229,39 +229,77 @@ func NewServer(opts ServerOptins) Server {
|
||||
type server struct{ *dns.Server }
|
||||
|
||||
// NewTCPListener creates a new TCP listener.
|
||||
func NewTCPListener(addr string) (net.Listener, error) {
|
||||
lc := net.ListenConfig{
|
||||
Control: makeControl(tcpOptions),
|
||||
func NewTCPListener(network, addr string) (net.Listener, error) {
|
||||
var opts []controlOptions
|
||||
|
||||
switch network {
|
||||
case "tcp", "tcp4":
|
||||
network = "tcp4"
|
||||
opts = tcpOptions
|
||||
|
||||
case "tcp6":
|
||||
opts = tcpOptionsV6
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported network: %s", network)
|
||||
}
|
||||
|
||||
return lc.Listen(context.Background(), "tcp", addr)
|
||||
lc := net.ListenConfig{Control: makeControl(opts)}
|
||||
|
||||
return lc.Listen(context.Background(), network, addr)
|
||||
}
|
||||
|
||||
// NewUDPPacketConn creates a new UDP packet connection.
|
||||
func NewUDPPacketConn(addr string) (net.PacketConn, error) {
|
||||
lc := net.ListenConfig{
|
||||
Control: makeControl(udpOptions),
|
||||
func NewUDPPacketConn(network, addr string) (net.PacketConn, error) {
|
||||
var opts []controlOptions
|
||||
|
||||
switch network {
|
||||
case "udp", "udp4":
|
||||
network = "udp4"
|
||||
opts = udpOptions
|
||||
|
||||
case "udp6":
|
||||
opts = udpOptionsV6
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported network: %s", network)
|
||||
}
|
||||
|
||||
return lc.ListenPacket(context.Background(), "udp", addr)
|
||||
lc := net.ListenConfig{
|
||||
Control: makeControl(opts),
|
||||
}
|
||||
|
||||
return lc.ListenPacket(context.Background(), network, addr)
|
||||
}
|
||||
|
||||
var (
|
||||
tcpOptions = []controlOptions{
|
||||
// this isn't really necessary, because currently if the process dies, OS dies with it
|
||||
{unix.SOL_SOCKET, unix.SO_REUSEADDR, 1, "failed to set SO_REUSEADDR"},
|
||||
{unix.SOL_SOCKET, unix.SO_REUSEPORT, 1, "failed to set SO_REUSEADDR"},
|
||||
{unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"},
|
||||
{unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd
|
||||
{unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd
|
||||
{unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"},
|
||||
}
|
||||
|
||||
tcpOptionsV6 = []controlOptions{
|
||||
{unix.SOL_SOCKET, unix.SO_REUSEPORT, 1, "failed to set SO_REUSEADDR"},
|
||||
{unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1, "failed to set IPV6_RECVHOPLIMIT"},
|
||||
{unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd
|
||||
{unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd
|
||||
{unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, 1, "failed to set IPV6_UNICAST_HOPS"},
|
||||
}
|
||||
|
||||
udpOptions = []controlOptions{
|
||||
// this isn't really necessary, because currently if the process dies, OS dies with it
|
||||
{unix.SOL_SOCKET, unix.SO_REUSEADDR, 1, "failed to set SO_REUSEADDR"},
|
||||
{unix.SOL_SOCKET, unix.SO_REUSEPORT, 1, "failed to set SO_REUSEADDR"},
|
||||
{unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"},
|
||||
{unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"},
|
||||
}
|
||||
|
||||
udpOptionsV6 = []controlOptions{
|
||||
{unix.SOL_SOCKET, unix.SO_REUSEPORT, 1, "failed to set SO_REUSEADDR"},
|
||||
{unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1, "failed to set IPV6_RECVHOPLIMIT"},
|
||||
{unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, 1, "failed to set IPV6_UNICAST_HOPS"},
|
||||
}
|
||||
)
|
||||
|
||||
type controlOptions struct {
|
||||
|
@ -111,16 +111,14 @@ func newServer(t *testing.T, nameservers ...string) (context.Context, func()) {
|
||||
p := proxy.NewProxy(ns, net.JoinHostPort(ns, "53"), "dns")
|
||||
p.Start(500 * time.Millisecond)
|
||||
|
||||
t.Cleanup(func() {
|
||||
p.Stop()
|
||||
})
|
||||
t.Cleanup(p.Stop)
|
||||
|
||||
return p
|
||||
})
|
||||
|
||||
handler.SetProxy(pxs)
|
||||
|
||||
pc, err := dns.NewUDPPacketConn(":10700")
|
||||
pc, err := dns.NewUDPPacketConn("udp", "127.0.0.1:10700")
|
||||
require.NoError(t, err)
|
||||
|
||||
runner := dns.NewRunner(dns.NewServer(dns.ServerOptins{
|
||||
|
Loading…
x
Reference in New Issue
Block a user