infra-utils/tcp-ping/main.go

117 lines
2.6 KiB
Go

package main
import (
"errors"
"log"
"net"
"os"
"time"
"golang.org/x/sys/unix"
)
var addresses = []string{
"8.8.8.8:53", // will succeed
//"8.8.8.8:88", // will fail
}
const timeout time.Duration = 2000 * time.Millisecond
func parseSockAddr(addr string) (unix.Sockaddr, error) {
tAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
var addr4 [4]byte
if tAddr.IP != nil {
copy(addr4[:], tAddr.IP.To4()) // copy last 4 bytes of slice to array
}
return &unix.SockaddrInet4{Port: tAddr.Port, Addr: addr4}, nil
}
func Ping(address string, timeout time.Duration) error {
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
if err != nil {
return os.NewSyscallError("socket", err)
}
unix.CloseOnExec(fd)
log.Println("Socket FD:", fd)
err = unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 0)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
var zeroLinger = unix.Linger{Onoff: 1, Linger: 0}
err = unix.SetsockoptLinger(fd, unix.SOL_SOCKET, unix.SO_LINGER, &zeroLinger)
if err != nil {
return os.NewSyscallError("setsockoptlinger", err)
}
rAddr, err := parseSockAddr(address)
if err != nil {
return errors.New("failed to parse address")
}
err = unix.Connect(fd, rAddr)
if err != nil {
return os.NewSyscallError("connect", err)
}
epfd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)
if err != nil {
return os.NewSyscallError("epoll_create1", err)
}
log.Println("Epoll FD:", epfd)
var event unix.EpollEvent
event.Events = (unix.EPOLLOUT | unix.EPOLLIN | unix.EPOLLET)
event.Fd = int32(fd)
err = unix.EpollCtl(epfd, unix.EPOLL_CTL_ADD, fd, &event)
if err != nil {
return os.NewSyscallError("epoll_ctl", err)
}
var epollEvents [1]unix.EpollEvent
var nEvents int
nEvents, err = unix.EpollWait(epfd, epollEvents[:], int(timeout.Milliseconds()))
log.Println("EpollWait err:", err)
if err != nil {
return os.NewSyscallError("epoll_wait", err)
}
log.Println("EpollWait #:", nEvents)
if nEvents == 0 {
return errors.New("no events found")
}
errCode, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_ERROR)
if err != nil {
return os.NewSyscallError("getsockoptint", err)
}
log.Println("SO_ERROR:", errCode)
log.Println("EpollEvent:", epollEvents[0])
if epollEvents[0].Events&unix.EPOLLOUT != 0 {
return nil
}
return errors.New("UNKNOWN ")
}
func main() {
log.SetPrefix("TCP_PING:")
for i := 0; i < len(addresses); i++ {
log.Println("PINGING:", addresses[i])
err := Ping(addresses[i], timeout)
if err != nil {
log.Println("FAILURE:", err)
} else {
log.Println("SUCCESS")
}
}
}