netlink/nl/nl_linux_test.go
Flavio Crisciani ef2b2c42e6 Add test to cover recv on close leak
This test spawns a go routine that subscribe for some
events while the main thread will close the socket.
The go routine will returns after 5s when the timetout
on the recv fires and the fd is actually == -1

Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
2017-10-20 10:09:56 -07:00

100 lines
2.4 KiB
Go

package nl
import (
"bytes"
"crypto/rand"
"encoding/binary"
"reflect"
"syscall"
"testing"
"time"
)
type testSerializer interface {
serializeSafe() []byte
Serialize() []byte
}
func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) {
if !reflect.DeepEqual(safemsg, msg) {
t.Fatal("Deserialization failed.\n", safemsg, "\n", msg)
}
safe := msg.serializeSafe()
if !bytes.Equal(safe, orig) {
t.Fatal("Safe serialization failed.\n", safe, "\n", orig)
}
b := msg.Serialize()
if !bytes.Equal(b, safe) {
t.Fatal("Serialization failed.\n", b, "\n", safe)
}
}
func (msg *IfInfomsg) write(b []byte) {
native := NativeEndian()
b[0] = msg.Family
// pad byte is skipped because it is not exported on linux/s390x
native.PutUint16(b[2:4], msg.Type)
native.PutUint32(b[4:8], uint32(msg.Index))
native.PutUint32(b[8:12], msg.Flags)
native.PutUint32(b[12:16], msg.Change)
}
func (msg *IfInfomsg) serializeSafe() []byte {
length := syscall.SizeofIfInfomsg
b := make([]byte, length)
msg.write(b)
return b
}
func deserializeIfInfomsgSafe(b []byte) *IfInfomsg {
var msg = IfInfomsg{}
binary.Read(bytes.NewReader(b[0:syscall.SizeofIfInfomsg]), NativeEndian(), &msg)
return &msg
}
func TestIfInfomsgDeserializeSerialize(t *testing.T) {
var orig = make([]byte, syscall.SizeofIfInfomsg)
rand.Read(orig)
// zero out the pad byte
orig[1] = 0
safemsg := deserializeIfInfomsgSafe(orig)
msg := DeserializeIfInfomsg(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func TestIfSocketCloses(t *testing.T) {
nlSock, err := Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH)
if err != nil {
t.Fatalf("Error on creating the socket: %v", err)
}
nlSock.SetReceiveTimeout(&syscall.Timeval{Sec: 2, Usec: 0})
endCh := make(chan error)
go func(sk *NetlinkSocket, endCh chan error) {
endCh <- nil
for {
_, err := sk.Receive()
// Receive returned because of a timeout and the FD == -1 means that the socket got closed
if err == syscall.EAGAIN && nlSock.GetFd() == -1 {
endCh <- err
return
}
}
}(nlSock, endCh)
// first receive nil
if msg := <-endCh; msg != nil {
t.Fatalf("Expected nil instead got: %v", msg)
}
// this to guarantee that the receive is invoked before the close
time.Sleep(4 * time.Second)
// Close the socket
nlSock.Close()
// Expect to have an error
msg := <-endCh
if msg == nil {
t.Fatalf("Expected error instead received nil")
}
}