2018-01-29 19:44:18 +00:00
|
|
|
// Copyright (c) 2017 Arista Networks, Inc.
|
|
|
|
// Use of this source code is governed by the Apache License 2.0
|
|
|
|
// that can be found in the COPYING file.
|
|
|
|
|
|
|
|
package dscp
|
|
|
|
|
|
|
|
import (
|
2018-09-05 15:36:14 +00:00
|
|
|
"fmt"
|
2018-01-29 19:44:18 +00:00
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"reflect"
|
2018-09-05 15:36:14 +00:00
|
|
|
"syscall"
|
2018-01-29 19:44:18 +00:00
|
|
|
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
|
|
|
|
|
|
|
// This works for the UNIX implementation of netFD, i.e. not on Windows and Plan9.
|
2018-09-05 15:36:14 +00:00
|
|
|
// conn must either implement syscall.Conn or be a TCPListener.
|
|
|
|
func setTOS(ip net.IP, conn interface{}, tos byte) error {
|
2018-01-29 19:44:18 +00:00
|
|
|
var proto, optname int
|
|
|
|
if ip.To4() != nil {
|
|
|
|
proto = unix.IPPROTO_IP
|
|
|
|
optname = unix.IP_TOS
|
|
|
|
} else {
|
|
|
|
proto = unix.IPPROTO_IPV6
|
|
|
|
optname = unix.IPV6_TCLASS
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
|
|
|
|
switch c := conn.(type) {
|
|
|
|
case syscall.Conn:
|
|
|
|
return setTOSWithSyscallConn(proto, optname, c, tos)
|
|
|
|
case *net.TCPListener:
|
|
|
|
// This code is needed to support go1.9. In go1.10
|
|
|
|
// *net.TCPListener implements syscall.Conn.
|
|
|
|
return setTOSWithTCPListener(proto, optname, c, tos)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("unsupported connection type: %T", conn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func setTOSWithTCPListener(proto, optname int, conn *net.TCPListener, tos byte) error {
|
|
|
|
// A kludge for pre-go1.10 to get the fd of a net.TCPListener
|
|
|
|
value := reflect.ValueOf(conn)
|
|
|
|
netFD := value.Elem().FieldByName("fd").Elem()
|
|
|
|
fd := int(netFD.FieldByName("pfd").FieldByName("Sysfd").Int())
|
2018-01-29 19:44:18 +00:00
|
|
|
if err := unix.SetsockoptInt(fd, proto, optname, int(tos)); err != nil {
|
|
|
|
return os.NewSyscallError("setsockopt", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
|
|
|
|
func setTOSWithSyscallConn(proto, optname int, conn syscall.Conn, tos byte) error {
|
|
|
|
syscallConn, err := conn.SyscallConn()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var setsockoptErr error
|
|
|
|
err = syscallConn.Control(func(fd uintptr) {
|
|
|
|
if err := unix.SetsockoptInt(int(fd), proto, optname, int(tos)); err != nil {
|
|
|
|
setsockoptErr = os.NewSyscallError("setsockopt", err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if setsockoptErr != nil {
|
|
|
|
return setsockoptErr
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|