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 }