@@ -2,10 +2,15 @@ package netlink
22
33import (
44 "fmt"
5+ "io"
56 "log"
67 "os"
78 "strconv"
89 "strings"
10+ "syscall"
11+ "unsafe"
12+
13+ "github.com/mdlayher/netlink/nlenc"
914)
1015
1116// Arguments used to create a debugger.
@@ -23,8 +28,9 @@ func init() {
2328
2429// A debugger is used to provide debugging information about a netlink connection.
2530type debugger struct {
26- Log * log.Logger
27- Level int
31+ Log * log.Logger
32+ Level int
33+ Format string
2834}
2935
3036// newDebugger creates a debugger by parsing key=value arguments.
@@ -50,6 +56,8 @@ func newDebugger(args []string) *debugger {
5056 }
5157
5258 d .Level = level
59+ case "format" :
60+ d .Format = kv [1 ]
5361 }
5462 }
5563
@@ -60,10 +68,195 @@ func newDebugger(args []string) *debugger {
6068// high enough to print the message.
6169func (d * debugger ) debugf (level int , format string , v ... interface {}) {
6270 if d .Level >= level {
63- d .Log .Printf (format , v ... )
71+ if d .Format == "mnl" {
72+ for _ , iface := range v {
73+ if msg , ok := iface .(Message ); ok {
74+ nlmsgFprintf (d .Log .Writer (), msg )
75+ }
76+ }
77+ } else {
78+ d .Log .Printf (format , v ... )
79+ }
6480 }
6581}
6682
6783func panicf (format string , a ... interface {}) {
6884 panic (fmt .Sprintf (format , a ... ))
6985}
86+
87+ /*
88+ nlmsgFprintf - print netlink message to file
89+ - Based on https://git.netfilter.org/libmnl/tree/src/nlmsg.c
90+ - This function prints the netlink header to a file handle.
91+ - It may be useful for debugging purposes. One example of the output
92+ - is the following:
93+
94+ ---------------- ------------------
95+ | 0000000040 | | message length |
96+ | 00016 | R-A- | | type | flags |
97+ | 1289148991 | | sequence number|
98+ | 0000000000 | | port ID |
99+ ---------------- ------------------
100+ | 00 00 00 00 | | extra header |
101+ | 00 00 00 00 | | extra header |
102+ | 01 00 00 00 | | extra header |
103+ | 01 00 00 00 | | extra header |
104+ |00008|--|00003| |len |flags| type|
105+ | 65 74 68 30 | | data | e t h 0
106+ ---------------- ------------------
107+
108+ *
109+ * This example above shows the netlink message that is send to kernel-space
110+ * to set up the link interface eth0. The netlink and attribute header data
111+ * are displayed in base 10 whereas the extra header and the attribute payload
112+ * are expressed in base 16. The possible flags in the netlink header are:
113+ *
114+ * - R, that indicates that NLM_F_REQUEST is set.
115+ * - M, that indicates that NLM_F_MULTI is set.
116+ * - A, that indicates that NLM_F_ACK is set.
117+ * - E, that indicates that NLM_F_ECHO is set.
118+ *
119+ * The lack of one flag is displayed with '-'. On the other hand, the possible
120+ * attribute flags available are:
121+ *
122+ * - N, that indicates that NLA_F_NESTED is set.
123+ * - B, that indicates that NLA_F_NET_BYTEORDER is set.
124+ */
125+ func nlmsgFprintfHeader (fd io.Writer , nlh Header ) {
126+ fmt .Fprintf (fd , "----------------\t ------------------\n " )
127+ fmt .Fprintf (fd , "| %010d |\t | message length |\n " , nlh .Length )
128+ fmt .Fprintf (fd , "| %05d | %s%s%s%s |\t | type | flags |\n " ,
129+ nlh .Type ,
130+ ternary (nlh .Flags & Request != 0 , "R" , "-" ),
131+ ternary (nlh .Flags & Multi != 0 , "M" , "-" ),
132+ ternary (nlh .Flags & Acknowledge != 0 , "A" , "-" ),
133+ ternary (nlh .Flags & Echo != 0 , "E" , "-" ),
134+ )
135+ fmt .Fprintf (fd , "| %010d |\t | sequence number|\n " , nlh .Sequence )
136+ fmt .Fprintf (fd , "| %010d |\t | port ID |\n " , nlh .PID )
137+ fmt .Fprintf (fd , "----------------\t ------------------\n " )
138+ }
139+
140+ // nlmsgFprintf checks a single Message for netlink errors.
141+ func nlmsgFprintf (fd io.Writer , m Message ) {
142+ colorize := true
143+ var hasHeader bool
144+ nlmsgFprintfHeader (fd , m .Header )
145+ switch {
146+ case m .Header .Type == Error :
147+ hasHeader = true
148+ case m .Header .Type == Done && m .Header .Flags & Multi != 0 :
149+ if len (m .Data ) == 0 {
150+ return
151+ }
152+ default :
153+ // Neither, nothing to do.
154+ }
155+
156+ // Errno occupies 4 bytes.
157+ const endErrno = 4
158+ if len (m .Data ) < endErrno {
159+ return
160+ }
161+
162+ c := nlenc .Int32 (m .Data [:endErrno ])
163+ if c != 0 {
164+ b := m .Data [0 :4 ]
165+ fmt .Fprintf (fd , "| %.2x %.2x %.2x %.2x |\t " ,
166+ 0xff & b [0 ], 0xff & b [1 ],
167+ 0xff & b [2 ], 0xff & b [3 ])
168+ fmt .Fprintf (fd , "| extra header |\n " )
169+ }
170+
171+ // Flags indicate an extended acknowledgement. The type/flags combination
172+ // checked above determines the offset where the TLVs occur.
173+ var off int
174+ if hasHeader {
175+ // There is an nlmsghdr preceding the TLVs.
176+ if len (m .Data ) < endErrno + nlmsgHeaderLen {
177+ return
178+ }
179+
180+ // The TLVs should be at the offset indicated by the nlmsghdr.length,
181+ // plus the offset where the header began. But make sure the calculated
182+ // offset is still in-bounds.
183+ h := * (* Header )(unsafe .Pointer (& m .Data [endErrno : endErrno + nlmsgHeaderLen ][0 ]))
184+ off = endErrno + int (h .Length )
185+
186+ if len (m .Data ) < off {
187+ return
188+ }
189+ } else {
190+ // There is no nlmsghdr preceding the TLVs, parse them directly.
191+ off = endErrno
192+ }
193+
194+ data := m .Data [off :]
195+ for i := 0 ; i < len (data ); {
196+ // Make sure there's at least a header's worth
197+ // of data to read on each iteration.
198+ if len (data [i :]) < nlaHeaderLen {
199+ break
200+ }
201+
202+ // Extract the length of the attribute.
203+ l := int (nlenc .Uint16 (data [i : i + 2 ]))
204+ // extract the type
205+ t := nlenc .Uint16 (data [i + 2 : i + 4 ])
206+ // print attribute header
207+ if colorize {
208+ fmt .Fprintf (fd , "|\033 [1;31m%05d|\033 [1;32m%s%s|\033 [1;34m%05d\033 [0m|\t " ,
209+ l ,
210+ ternary (t & syscall .NLA_F_NESTED != 0 , "N" , "-" ),
211+ ternary (t & syscall .NLA_F_NET_BYTEORDER != 0 , "B" , "-" ),
212+ t & attrTypeMask )
213+ fmt .Fprintf (fd , "|len |flags| type|\n " )
214+ } else {
215+ fmt .Fprintf (fd , "|%05d|%s%s|%05d|\t " ,
216+ l ,
217+ ternary (t & syscall .NLA_F_NESTED != 0 , "N" , "-" ),
218+ ternary (t & syscall .NLA_F_NET_BYTEORDER != 0 , "B" , "-" ),
219+ t & attrTypeMask )
220+ fmt .Fprintf (fd , "|len |flags| type|\n " )
221+ }
222+
223+ nextAttr := i + nlaAlign (l )
224+
225+ // advance the pointer to the bytes after the header
226+ i += nlaHeaderLen
227+
228+ // Ignore zero-length attributes.
229+ if l == 0 {
230+ continue
231+ }
232+ // If nested check the next attribute
233+ if t & syscall .NLA_F_NESTED != 0 {
234+ continue
235+ }
236+
237+ // Print the remaining attributes bytes
238+ for ; i < nextAttr ; i += 4 {
239+ fmt .Fprintf (fd , "| %.2x %.2x %.2x %.2x |\t " ,
240+ 0xff & data [i ], 0xff & data [i + 1 ],
241+ 0xff & data [i + 2 ], 0xff & data [i + 3 ])
242+
243+ fmt .Fprintf (fd , "| data |" )
244+
245+ fmt .Fprintf (fd , "\t %s %s %s %s\n " ,
246+ ternary (strconv .IsPrint (rune (data [i ])), string (data [i ]), " " ),
247+ ternary (strconv .IsPrint (rune (data [i + 1 ])), string (data [i + 1 ]), " " ),
248+ ternary (strconv .IsPrint (rune (data [i + 2 ])), string (data [i + 2 ]), " " ),
249+ ternary (strconv .IsPrint (rune (data [i + 3 ])), string (data [i + 3 ]), " " ),
250+ )
251+ }
252+ }
253+ fmt .Fprintf (fd , "----------------\t ------------------\n " )
254+ }
255+
256+ func ternary (cond bool , iftrue string , iffalse string ) string {
257+ if cond {
258+ return iftrue
259+ } else {
260+ return iffalse
261+ }
262+ }
0 commit comments