github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/dscp/dscp_unix.go (about)

     1  // Copyright (c) 2019 Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  //go:build linux || darwin
     6  // +build linux darwin
     7  
     8  package dscp
     9  
    10  import (
    11  	"context"
    12  	"net"
    13  	"os"
    14  	"strings"
    15  	"syscall"
    16  
    17  	"github.com/aristanetworks/goarista/logger"
    18  	"golang.org/x/sys/unix"
    19  )
    20  
    21  // ListenTCPWithTOS is similar to net.ListenTCP but with the socket configured
    22  // to the use the given ToS (Type of Service), to specify DSCP / ECN / class
    23  // of service flags to use for incoming connections.
    24  func ListenTCPWithTOS(address *net.TCPAddr, tos byte) (*net.TCPListener, error) {
    25  	return ListenTCPWithTOSLogger(address, tos, logger.Std)
    26  }
    27  
    28  // ListenTCPWithTOSLogger is similar to net.ListenTCP but with the
    29  // socket configured to the use the given ToS (Type of Service), to
    30  // specify DSCP / ECN / class of service flags to use for incoming
    31  // connections. Allows passing in a Logger.
    32  func ListenTCPWithTOSLogger(address *net.TCPAddr, tos byte, l logger.Logger) (*net.TCPListener,
    33  	error) {
    34  	cfg := net.ListenConfig{
    35  		Control: func(network, address string, c syscall.RawConn) error {
    36  			return SetTOSLogger(network, c, tos, l)
    37  		},
    38  	}
    39  
    40  	lsnr, err := cfg.Listen(context.Background(), "tcp", address.String())
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	return lsnr.(*net.TCPListener), err
    46  }
    47  
    48  // SetTOS will set the TOS byte on a unix system. It's intended to be
    49  // used in a net.Dialer's Control function.
    50  func SetTOS(network string, c syscall.RawConn, tos byte) error {
    51  	return SetTOSLogger(network, c, tos, logger.Std)
    52  }
    53  
    54  // SetTOSLogger will set the TOS byte on a unix system. It's intended
    55  // to be used in a net.Dialer's Control function. Allows passing in a
    56  // Logger.
    57  func SetTOSLogger(network string, c syscall.RawConn, tos byte, l logger.Logger) error {
    58  	return c.Control(func(fd uintptr) {
    59  		// Configure ipv4 TOS for both IPv4 and IPv6 networks because
    60  		// v4 connections can still come over v6 networks.
    61  		err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, int(tos))
    62  		if err != nil {
    63  			l.Errorf("failed to configure IP_TOS: %v", os.NewSyscallError("setsockopt", err))
    64  		}
    65  		if strings.HasSuffix(network, "4") {
    66  			// Skip configuring IPv6 when we know we are using an IPv4
    67  			// network to avoid error.
    68  			return
    69  		}
    70  		err6 := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, int(tos))
    71  		if err6 != nil {
    72  			l.Errorf(
    73  				"failed to configure IPV6_TCLASS, traffic may not use the configured DSCP: %v",
    74  				os.NewSyscallError("setsockopt", err6))
    75  		}
    76  
    77  	})
    78  }