github.com/vishvananda/netlink@v1.3.1/nl/nl_linux_test.go (about)

     1  package nl
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"encoding/binary"
     7  	"reflect"
     8  	"testing"
     9  	"time"
    10  
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  type testSerializer interface {
    15  	serializeSafe() []byte
    16  	Serialize() []byte
    17  }
    18  
    19  func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) {
    20  	if !reflect.DeepEqual(safemsg, msg) {
    21  		t.Fatal("Deserialization failed.\n", safemsg, "\n", msg)
    22  	}
    23  	safe := msg.serializeSafe()
    24  	if !bytes.Equal(safe, orig) {
    25  		t.Fatal("Safe serialization failed.\n", safe, "\n", orig)
    26  	}
    27  	b := msg.Serialize()
    28  	if !bytes.Equal(b, safe) {
    29  		t.Fatal("Serialization failed.\n", b, "\n", safe)
    30  	}
    31  }
    32  
    33  func (msg *IfInfomsg) write(b []byte) {
    34  	native := NativeEndian()
    35  	b[0] = msg.Family
    36  	// pad byte is skipped because it is not exported on linux/s390x
    37  	native.PutUint16(b[2:4], msg.Type)
    38  	native.PutUint32(b[4:8], uint32(msg.Index))
    39  	native.PutUint32(b[8:12], msg.Flags)
    40  	native.PutUint32(b[12:16], msg.Change)
    41  }
    42  
    43  func (msg *IfInfomsg) serializeSafe() []byte {
    44  	length := unix.SizeofIfInfomsg
    45  	b := make([]byte, length)
    46  	msg.write(b)
    47  	return b
    48  }
    49  
    50  func deserializeIfInfomsgSafe(b []byte) *IfInfomsg {
    51  	var msg = IfInfomsg{}
    52  	binary.Read(bytes.NewReader(b[0:unix.SizeofIfInfomsg]), NativeEndian(), &msg)
    53  	return &msg
    54  }
    55  
    56  func TestIfInfomsgDeserializeSerialize(t *testing.T) {
    57  	var orig = make([]byte, unix.SizeofIfInfomsg)
    58  	rand.Read(orig)
    59  	// zero out the pad byte
    60  	orig[1] = 0
    61  	safemsg := deserializeIfInfomsgSafe(orig)
    62  	msg := DeserializeIfInfomsg(orig)
    63  	testDeserializeSerialize(t, orig, safemsg, msg)
    64  }
    65  
    66  func TestIfSocketCloses(t *testing.T) {
    67  	nlSock, err := Subscribe(unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
    68  	if err != nil {
    69  		t.Fatalf("Error on creating the socket: %v", err)
    70  	}
    71  	endCh := make(chan error)
    72  	go func(sk *NetlinkSocket, endCh chan error) {
    73  		endCh <- nil
    74  		for {
    75  			_, _, err := sk.Receive()
    76  			if err == unix.EAGAIN {
    77  				endCh <- err
    78  				return
    79  			}
    80  		}
    81  	}(nlSock, endCh)
    82  
    83  	// first receive nil
    84  	if msg := <-endCh; msg != nil {
    85  		t.Fatalf("Expected nil instead got: %v", msg)
    86  	}
    87  	// this to guarantee that the receive is invoked before the close
    88  	time.Sleep(4 * time.Second)
    89  
    90  	// Close the socket
    91  	nlSock.Close()
    92  
    93  	// Expect to have an error
    94  	msg := <-endCh
    95  	if msg == nil {
    96  		t.Fatalf("Expected error instead received nil")
    97  	}
    98  }
    99  
   100  func TestReceiveTimeout(t *testing.T) {
   101  	nlSock, err := getNetlinkSocket(unix.NETLINK_ROUTE)
   102  	if err != nil {
   103  		t.Fatalf("Error creating the socket: %v", err)
   104  	}
   105  	// Even if the test fails because the timeout doesn't work, closing the
   106  	// socket at the end of the test should result in an EAGAIN (as long as
   107  	// TestIfSocketCloses completed, otherwise this test will leak the
   108  	// goroutines running the Receive).
   109  	defer nlSock.Close()
   110  	const failAfter = time.Second
   111  
   112  	tests := []struct {
   113  		name    string
   114  		timeout time.Duration
   115  	}{
   116  		{
   117  			name:    "1us timeout", // The smallest value accepted by Handle.SetSocketTimeout
   118  			timeout: time.Microsecond,
   119  		},
   120  		{
   121  			name:    "100ms timeout",
   122  			timeout: 100 * time.Millisecond,
   123  		},
   124  		{
   125  			name:    "500ms timeout",
   126  			timeout: 500 * time.Millisecond,
   127  		},
   128  	}
   129  	for _, tc := range tests {
   130  		tc := tc
   131  		t.Run(tc.name, func(t *testing.T) {
   132  			timeout := unix.NsecToTimeval(int64(tc.timeout))
   133  			nlSock.SetReceiveTimeout(&timeout)
   134  
   135  			doneC := make(chan time.Duration)
   136  			errC := make(chan error)
   137  			go func() {
   138  				start := time.Now()
   139  				_, _, err := nlSock.Receive()
   140  				dur := time.Since(start)
   141  				if err != unix.EAGAIN {
   142  					errC <- err
   143  					return
   144  				}
   145  				doneC <- dur
   146  			}()
   147  
   148  			failTimerC := time.After(failAfter)
   149  			select {
   150  			case dur := <-doneC:
   151  				if dur < tc.timeout || dur > (tc.timeout+(100*time.Millisecond)) {
   152  					t.Fatalf("Expected timeout %v got %v", tc.timeout, dur)
   153  				}
   154  			case err := <-errC:
   155  				t.Fatalf("Expected EAGAIN, but got: %v", err)
   156  			case <-failTimerC:
   157  				t.Fatalf("No timeout received")
   158  			}
   159  		})
   160  	}
   161  }
   162  
   163  func (msg *CnMsgOp) write(b []byte) {
   164  	native := NativeEndian()
   165  	native.PutUint32(b[0:4], msg.ID.Idx)
   166  	native.PutUint32(b[4:8], msg.ID.Val)
   167  	native.PutUint32(b[8:12], msg.Seq)
   168  	native.PutUint32(b[12:16], msg.Ack)
   169  	native.PutUint16(b[16:18], msg.Length)
   170  	native.PutUint16(b[18:20], msg.Flags)
   171  	native.PutUint32(b[20:24], msg.Op)
   172  }
   173  
   174  func (msg *CnMsgOp) serializeSafe() []byte {
   175  	length := msg.Len()
   176  	b := make([]byte, length)
   177  	msg.write(b)
   178  	return b
   179  }
   180  
   181  func deserializeCnMsgOpSafe(b []byte) *CnMsgOp {
   182  	var msg = CnMsgOp{}
   183  	binary.Read(bytes.NewReader(b[0:SizeofCnMsgOp]), NativeEndian(), &msg)
   184  	return &msg
   185  }
   186  
   187  func TestCnMsgOpDeserializeSerialize(t *testing.T) {
   188  	var orig = make([]byte, SizeofCnMsgOp)
   189  	rand.Read(orig)
   190  	safemsg := deserializeCnMsgOpSafe(orig)
   191  	msg := DeserializeCnMsgOp(orig)
   192  	testDeserializeSerialize(t, orig, safemsg, msg)
   193  }
   194  
   195  func TestParseRouteAttrAsMap(t *testing.T) {
   196  	attr1 := NewRtAttr(0x1, ZeroTerminated("foo"))
   197  	attr2 := NewRtAttr(0x2, ZeroTerminated("bar"))
   198  	raw := make([]byte, 0)
   199  	raw = append(raw, attr1.Serialize()...)
   200  	raw = append(raw, attr2.Serialize()...)
   201  	attrs, err := ParseRouteAttrAsMap(raw)
   202  	if err != nil {
   203  		t.Errorf("failed to parse route attributes %s", err)
   204  	}
   205  
   206  	attr, ok := attrs[0x1]
   207  	if !ok || BytesToString(attr.Value) != "foo" {
   208  		t.Error("missing/incorrect \"foo\" attribute")
   209  	}
   210  
   211  	attr, ok = attrs[0x2]
   212  	if !ok || BytesToString(attr.Value) != "bar" {
   213  		t.Error("missing/incorrect \"bar\" attribute")
   214  	}
   215  }