Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNStap collector: add option to increase SO_RCVBUF #231

Merged
merged 5 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions collectors/dnstap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package collectors
import (
"bufio"
"crypto/tls"
"io"
"net"
"os"
"strconv"
Expand All @@ -13,6 +14,25 @@ import (
"github.com/dmachard/go-logger"
)

// thanks to https://stackoverflow.com/questions/28967701/golang-tcp-socket-cant-close-after-get-file,
// call conn.CloseRead() before calling conn.Close()
func Close(conn io.Closer) error {
type ReadCloser interface {
CloseRead() error
}
var errs []error
if closer, ok := conn.(ReadCloser); ok {
errs = append(errs, closer.CloseRead())
}
errs = append(errs, conn.Close())
for _, err := range errs {
if err != nil {
return err
}
}
return nil
}

type Dnstap struct {
done chan bool
listen net.Listener
Expand All @@ -22,6 +42,7 @@ type Dnstap struct {
config *dnsutils.Config
logger *logger.Logger
name string
connMode string
}

func NewDnstap(loggers []dnsutils.Worker, config *dnsutils.Config, logger *logger.Logger, name string) *Dnstap {
Expand Down Expand Up @@ -57,6 +78,14 @@ func (c *Dnstap) ReadConfig() {
}

c.sockPath = c.config.Collectors.Dnstap.SockPath

if len(c.config.Collectors.Dnstap.SockPath) > 0 {
c.connMode = "unix"
} else if c.config.Collectors.Dnstap.TlsSupport {
c.connMode = "tls"
} else {
c.connMode = "tcp"
}
}

func (c *Dnstap) LogInfo(msg string, v ...interface{}) {
Expand Down Expand Up @@ -114,7 +143,7 @@ func (c *Dnstap) Stop() {
for _, conn := range c.conns {
peer := conn.RemoteAddr().String()
c.LogInfo("%s - closing connection...", peer)
conn.Close()
Close(conn)
}
// Finally close the listener to unblock accept
c.LogInfo("stop listening...")
Expand Down Expand Up @@ -145,7 +174,6 @@ func (c *Dnstap) Listen() error {
c.logger.Fatal("loading certificate failed:", err)
}

// prepare tls configuration
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cer},
MinVersion: tls.VersionTLS12,
Expand All @@ -159,7 +187,9 @@ func (c *Dnstap) Listen() error {
} else {
listener, err = tls.Listen(dnsutils.SOCKET_TCP, addrlisten, tlsConfig)
}

} else {

// basic listening
if len(c.sockPath) > 0 {
listener, err = net.Listen(dnsutils.SOCKET_UNIX, c.sockPath)
Expand All @@ -172,7 +202,7 @@ func (c *Dnstap) Listen() error {
if err != nil {
return err
}
c.LogInfo("is listening on %s", listener.Addr())
c.LogInfo("is listening on %s://%s", c.connMode, listener.Addr())
c.listen = listener
return nil
}
Expand All @@ -191,6 +221,21 @@ func (c *Dnstap) Run() {
break
}

if (c.connMode == "tls" || c.connMode == "tcp") && c.config.Collectors.Dnstap.RcvBufSize > 0 {

var is_tls bool
if c.config.Collectors.Dnstap.TlsSupport {
is_tls = true
}

before, actual, err := SetSock_RCVBUF(conn, c.config.Collectors.Dnstap.RcvBufSize, is_tls)
if err != nil {
c.logger.Fatal("Unable to set SO_RCVBUF: ", err)
}
c.LogInfo("set SO_RCVBUF option, value before: %d, desired: %d, actual: %d", before,
c.config.Collectors.Dnstap.RcvBufSize, actual)
}

c.conns = append(c.conns, conn)
go c.HandleConn(conn)
}
Expand Down
3 changes: 1 addition & 2 deletions collectors/dnstap_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,9 @@ func (d *DnstapProcessor) Run(sendTo []chan dnsutils.DnsMessage) {
dm.DnsTap.Identity = string(identity)
}
version := dt.GetVersion()
if len(identity) > 0 {
if len(version) > 0 {
dm.DnsTap.Version = string(version)
}

dm.DnsTap.Operation = dt.GetMessage().GetType().String()
dm.NetworkInfo.Family = dt.GetMessage().GetSocketFamily().String()
dm.NetworkInfo.Protocol = dt.GetMessage().GetSocketProtocol().String()
Expand Down
43 changes: 43 additions & 0 deletions collectors/setsock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//go:build linux || darwin
// +build linux darwin

package collectors

import (
"crypto/tls"
"net"
"os"
"syscall"
)

// Configure SO_RCVBUF, thanks to https://github.com/dmachard/go-dns-collector/issues/61#issuecomment-1201199895
func SetSock_RCVBUF(conn net.Conn, desired int, is_tls bool) (int, int, error) {
var file *os.File
var err error
if is_tls {
tlsConn := conn.(*tls.Conn).NetConn()
file, err = tlsConn.(*net.TCPConn).File()
if err != nil {
return 0, 0, err
}
} else {
file, err = conn.(*net.TCPConn).File()
if err != nil {
return 0, 0, err
}
}

// get the before value
before, err := syscall.GetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
if err != nil {
return 0, 0, err
}

// set the new one and check the new actual value
syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_RCVBUF, desired)
actual, err := syscall.GetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
if err != nil {
return 0, 0, err
}
return before, actual, nil
}
44 changes: 44 additions & 0 deletions collectors/setsock_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//go:build windows
// +build windows

package collectors

import (
"crypto/tls"
"net"
"os"

"golang.org/x/sys/windows"
)

// Configure SO_RCVBUF, thanks to https://github.com/dmachard/go-dns-collector/issues/61#issuecomment-1201199895
func SetSock_RCVBUF(conn net.Conn, desired int, is_tls bool) (int, int, error) {
var file *os.File
var err error
if is_tls {
tlsConn := conn.(*tls.Conn).NetConn()
file, err = tlsConn.(*net.TCPConn).File()
if err != nil {
return 0, 0, err
}
} else {
file, err = conn.(*net.TCPConn).File()
if err != nil {
return 0, 0, err
}
}

// get the before value
before, err := windows.GetsockoptInt(windows.Handle(file.Fd()), windows.SOL_SOCKET, windows.SO_RCVBUF)
if err != nil {
return 0, 0, err
}

// set the new one and check the new actual value
windows.SetsockoptInt(windows.Handle(file.Fd()), windows.SOL_SOCKET, windows.SO_RCVBUF, desired)
actual, err := windows.GetsockoptInt(windows.Handle(file.Fd()), windows.SOL_SOCKET, windows.SO_RCVBUF)
if err != nil {
return 0, 0, err
}
return before, actual, nil
}
2 changes: 2 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ multiplexer:
# cache-support: false
# # Ttl in second, max time to keep the query record in memory cache
# query-timeout: 5
# # Sets the socket receive buffer in bytes SO_RCVBUF, set to zero to use the default system value
# sock-rcvbuf: 0

# # dnstap proxifier with no protobuf decoding.
# dnstap-proxifier:
Expand Down
2 changes: 2 additions & 0 deletions dnsutils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ type Config struct {
KeyFile string `yaml:"key-file"`
CacheSupport bool `yaml:"cache-support"`
QueryTimeout int `yaml:"query-timeout"`
RcvBufSize int `yaml:"sock-rcvbuf"`
} `yaml:"dnstap"`
DnstapProxifier struct {
Enable bool `yaml:"enable"`
Expand Down Expand Up @@ -386,6 +387,7 @@ func (c *Config) SetDefault() {
c.Collectors.Dnstap.KeyFile = ""
c.Collectors.Dnstap.QueryTimeout = 5
c.Collectors.Dnstap.CacheSupport = false
c.Collectors.Dnstap.RcvBufSize = 0

c.Collectors.DnstapProxifier.Enable = false
c.Collectors.DnstapProxifier.ListenIP = ANY_IP
Expand Down
2 changes: 2 additions & 0 deletions doc/collectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Options:
- `key-file`: (string) private key server file
- `cache-support`: (boolean) disable or enable the cache dns, this feature can be enabled if your dns server doesn't add the latency
- `query-timeout`: (integer) in second, max time to keep the query record in memory
- `sock-rcvbuf`: (integer) sets the socket receive buffer in bytes SO_RCVBUF, set to zero to use the default system value

Default values:

Expand All @@ -39,6 +40,7 @@ dnstap:
key-file: ""
cache-support: false
query-timeout: 5.0
sock-rcvbuf: 0
```

### DNS tap Proxifier
Expand Down