@@ -5,8 +5,10 @@ import (
55 "errors"
66 "fmt"
77 "net"
8+ "syscall"
89
910 "github.com/vishvananda/netlink/nl"
11+ "github.com/vishvananda/netns"
1012 "golang.org/x/sys/unix"
1113)
1214
@@ -227,73 +229,11 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
227229
228230 var res = make ([]Rule , 0 )
229231 for i := range msgs {
230- msg := nl .DeserializeRtMsg (msgs [i ])
231- attrs , err := nl .ParseRouteAttr (msgs [i ][msg .Len ():])
232+ rule , err := deserializeRule (msgs [i ])
232233 if err != nil {
233234 return nil , err
234235 }
235236
236- rule := NewRule ()
237- rule .Priority = 0 // The default priority from kernel
238-
239- rule .Invert = msg .Flags & FibRuleInvert > 0
240- rule .Family = int (msg .Family )
241- rule .Tos = uint (msg .Tos )
242-
243- for j := range attrs {
244- switch attrs [j ].Attr .Type {
245- case unix .RTA_TABLE :
246- rule .Table = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
247- case nl .FRA_SRC :
248- rule .Src = & net.IPNet {
249- IP : attrs [j ].Value ,
250- Mask : net .CIDRMask (int (msg .Src_len ), 8 * len (attrs [j ].Value )),
251- }
252- case nl .FRA_DST :
253- rule .Dst = & net.IPNet {
254- IP : attrs [j ].Value ,
255- Mask : net .CIDRMask (int (msg .Dst_len ), 8 * len (attrs [j ].Value )),
256- }
257- case nl .FRA_FWMARK :
258- rule .Mark = native .Uint32 (attrs [j ].Value [0 :4 ])
259- case nl .FRA_FWMASK :
260- mask := native .Uint32 (attrs [j ].Value [0 :4 ])
261- rule .Mask = & mask
262- case nl .FRA_TUN_ID :
263- rule .TunID = uint (native .Uint64 (attrs [j ].Value [0 :8 ]))
264- case nl .FRA_IIFNAME :
265- rule .IifName = string (attrs [j ].Value [:len (attrs [j ].Value )- 1 ])
266- case nl .FRA_OIFNAME :
267- rule .OifName = string (attrs [j ].Value [:len (attrs [j ].Value )- 1 ])
268- case nl .FRA_SUPPRESS_PREFIXLEN :
269- i := native .Uint32 (attrs [j ].Value [0 :4 ])
270- if i != 0xffffffff {
271- rule .SuppressPrefixlen = int (i )
272- }
273- case nl .FRA_SUPPRESS_IFGROUP :
274- i := native .Uint32 (attrs [j ].Value [0 :4 ])
275- if i != 0xffffffff {
276- rule .SuppressIfgroup = int (i )
277- }
278- case nl .FRA_FLOW :
279- rule .Flow = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
280- case nl .FRA_GOTO :
281- rule .Goto = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
282- case nl .FRA_PRIORITY :
283- rule .Priority = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
284- case nl .FRA_IP_PROTO :
285- rule .IPProto = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
286- case nl .FRA_DPORT_RANGE :
287- rule .Dport = NewRulePortRange (native .Uint16 (attrs [j ].Value [0 :2 ]), native .Uint16 (attrs [j ].Value [2 :4 ]))
288- case nl .FRA_SPORT_RANGE :
289- rule .Sport = NewRulePortRange (native .Uint16 (attrs [j ].Value [0 :2 ]), native .Uint16 (attrs [j ].Value [2 :4 ]))
290- case nl .FRA_UID_RANGE :
291- rule .UIDRange = NewRuleUIDRange (native .Uint32 (attrs [j ].Value [0 :4 ]), native .Uint32 (attrs [j ].Value [4 :8 ]))
292- case nl .FRA_PROTOCOL :
293- rule .Protocol = uint8 (attrs [j ].Value [0 ])
294- }
295- }
296-
297237 if filter != nil {
298238 switch {
299239 case filterMask & RT_FILTER_SRC != 0 &&
@@ -316,12 +256,210 @@ func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) (
316256 }
317257 }
318258
319- res = append (res , * rule )
259+ res = append (res , rule )
320260 }
321261
322262 return res , executeErr
323263}
324264
265+ func deserializeRule (m []byte ) (Rule , error ) {
266+ msg := nl .DeserializeRtMsg (m )
267+ attrs , err := nl .ParseRouteAttr (m [msg .Len ():])
268+ if err != nil {
269+ return Rule {}, err
270+ }
271+
272+ rule := NewRule ()
273+
274+ rule .Invert = msg .Flags & FibRuleInvert > 0
275+ rule .Tos = uint (msg .Tos )
276+ // The kernel returns the table id in rtmsg.table for values < 256 and as
277+ // the RTA_TABLE attribute for larger values. Start with the header value.
278+ rule .Table = int (msg .Table )
279+
280+ for j := range attrs {
281+ switch attrs [j ].Attr .Type {
282+ case unix .RTA_TABLE :
283+ rule .Table = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
284+ case nl .FRA_SRC :
285+ rule .Src = & net.IPNet {
286+ IP : attrs [j ].Value ,
287+ Mask : net .CIDRMask (int (msg .Src_len ), 8 * len (attrs [j ].Value )),
288+ }
289+ case nl .FRA_DST :
290+ rule .Dst = & net.IPNet {
291+ IP : attrs [j ].Value ,
292+ Mask : net .CIDRMask (int (msg .Dst_len ), 8 * len (attrs [j ].Value )),
293+ }
294+ case nl .FRA_FWMARK :
295+ rule .Mark = native .Uint32 (attrs [j ].Value [0 :4 ])
296+ case nl .FRA_FWMASK :
297+ mask := native .Uint32 (attrs [j ].Value [0 :4 ])
298+ rule .Mask = & mask
299+ case nl .FRA_TUN_ID :
300+ rule .TunID = uint (native .Uint64 (attrs [j ].Value [0 :8 ]))
301+ case nl .FRA_IIFNAME :
302+ rule .IifName = string (attrs [j ].Value [:len (attrs [j ].Value )- 1 ])
303+ case nl .FRA_OIFNAME :
304+ rule .OifName = string (attrs [j ].Value [:len (attrs [j ].Value )- 1 ])
305+ case nl .FRA_SUPPRESS_PREFIXLEN :
306+ i := native .Uint32 (attrs [j ].Value [0 :4 ])
307+ if i != 0xffffffff {
308+ rule .SuppressPrefixlen = int (i )
309+ }
310+ case nl .FRA_SUPPRESS_IFGROUP :
311+ i := native .Uint32 (attrs [j ].Value [0 :4 ])
312+ if i != 0xffffffff {
313+ rule .SuppressIfgroup = int (i )
314+ }
315+ case nl .FRA_FLOW :
316+ rule .Flow = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
317+ case nl .FRA_GOTO :
318+ rule .Goto = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
319+ case nl .FRA_PRIORITY :
320+ rule .Priority = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
321+ case nl .FRA_IP_PROTO :
322+ rule .IPProto = int (native .Uint32 (attrs [j ].Value [0 :4 ]))
323+ case nl .FRA_DPORT_RANGE :
324+ rule .Dport = NewRulePortRange (native .Uint16 (attrs [j ].Value [0 :2 ]), native .Uint16 (attrs [j ].Value [2 :4 ]))
325+ case nl .FRA_SPORT_RANGE :
326+ rule .Sport = NewRulePortRange (native .Uint16 (attrs [j ].Value [0 :2 ]), native .Uint16 (attrs [j ].Value [2 :4 ]))
327+ case nl .FRA_UID_RANGE :
328+ rule .UIDRange = NewRuleUIDRange (native .Uint32 (attrs [j ].Value [0 :4 ]), native .Uint32 (attrs [j ].Value [4 :8 ]))
329+ case nl .FRA_PROTOCOL :
330+ rule .Protocol = uint8 (attrs [j ].Value [0 ])
331+ }
332+ }
333+
334+ // Some kernels omit FRA_PRIORITY when the rule priority is 0. Since 0 is a
335+ // valid priority (e.g. the local-table rule), default to 0 if no attribute
336+ // was present.
337+ if rule .Priority < 0 {
338+ rule .Priority = 0
339+ }
340+
341+ rule .Family = int (msg .Family )
342+ rule .Type = msg .Type
343+
344+ return * rule , nil
345+ }
346+
347+ // RuleSubscribe takes a chan down which notifications will be sent
348+ // when rules are added or deleted. The 'done' chan must be closed to stop the
349+ // subscription and release the underlying socket/goroutine.
350+ func RuleSubscribe (ch chan <- RuleUpdate , done <- chan struct {}) error {
351+ return ruleSubscribeAt (netns .None (), netns .None (), ch , done , nil , false , 0 , nil , false )
352+ }
353+
354+ // RuleSubscribeAt works like RuleSubscribe plus it allows the caller
355+ // to choose the network namespace in which to subscribe (ns).
356+ // The 'done' chan must be closed to stop the subscription and release resources.
357+ func RuleSubscribeAt (ns netns.NsHandle , ch chan <- RuleUpdate , done <- chan struct {}) error {
358+ return ruleSubscribeAt (ns , netns .None (), ch , done , nil , false , 0 , nil , false )
359+ }
360+
361+ // RuleSubscribeOptions contains a set of options to use with
362+ // RuleSubscribeWithOptions.
363+ type RuleSubscribeOptions struct {
364+ Namespace * netns.NsHandle
365+ ErrorCallback func (error )
366+ ListExisting bool
367+ ReceiveBufferSize int
368+ ReceiveBufferForceSize bool
369+ ReceiveTimeout * unix.Timeval
370+ }
371+
372+ // RuleSubscribeWithOptions works like RuleSubscribe but enables
373+ // additional options to modify the behavior.
374+ func RuleSubscribeWithOptions (ch chan <- RuleUpdate , done <- chan struct {}, options RuleSubscribeOptions ) error {
375+ if options .Namespace == nil {
376+ none := netns .None ()
377+ options .Namespace = & none
378+ }
379+ return ruleSubscribeAt (* options .Namespace , netns .None (), ch , done , options .ErrorCallback , options .ListExisting ,
380+ options .ReceiveBufferSize , options .ReceiveTimeout , options .ReceiveBufferForceSize )
381+ }
382+
383+ func ruleSubscribeAt (newNs , curNs netns.NsHandle , ch chan <- RuleUpdate , done <- chan struct {}, cberr func (error ), listExisting bool ,
384+ rcvbuf int , rcvTimeout * unix.Timeval , rcvBufForce bool ) error {
385+ s , err := nl .SubscribeAt (newNs , curNs , unix .NETLINK_ROUTE , unix .RTNLGRP_IPV4_RULE , unix .RTNLGRP_IPV6_RULE )
386+ if err != nil {
387+ return err
388+ }
389+ if rcvTimeout != nil {
390+ if err := s .SetReceiveTimeout (rcvTimeout ); err != nil {
391+ return err
392+ }
393+ }
394+ if rcvbuf != 0 {
395+ err = s .SetReceiveBufferSize (rcvbuf , rcvBufForce )
396+ if err != nil {
397+ return err
398+ }
399+ }
400+ if done != nil {
401+ go func () {
402+ <- done
403+ s .Close ()
404+ }()
405+ }
406+ if listExisting {
407+ req := pkgHandle ().newNetlinkRequest (unix .RTM_GETRULE , unix .NLM_F_DUMP )
408+ infmsg := nl .NewIfInfomsg (unix .AF_UNSPEC )
409+ req .AddData (infmsg )
410+ if err := s .Send (req ); err != nil {
411+ s .Close ()
412+ return err
413+ }
414+ }
415+ go func () {
416+ defer close (ch )
417+ for {
418+ msgs , from , err := s .Receive ()
419+ if err != nil {
420+ if cberr != nil {
421+ cberr (fmt .Errorf ("Receive failed: %v" , err ))
422+ }
423+ return
424+ }
425+ if from .Pid != nl .PidKernel {
426+ if cberr != nil {
427+ cberr (fmt .Errorf ("Wrong sender portid %d, expected %d" , from .Pid , nl .PidKernel ))
428+ }
429+ continue
430+ }
431+ for _ , m := range msgs {
432+ if m .Header .Flags & unix .NLM_F_DUMP_INTR != 0 && cberr != nil {
433+ cberr (ErrDumpInterrupted )
434+ }
435+ if m .Header .Type == unix .NLMSG_DONE {
436+ continue
437+ }
438+ if m .Header .Type == unix .NLMSG_ERROR {
439+ error := int32 (native .Uint32 (m .Data [0 :4 ]))
440+ if error == 0 {
441+ continue
442+ }
443+ if cberr != nil {
444+ cberr (fmt .Errorf ("error message: %v" , syscall .Errno (- error )))
445+ }
446+ continue
447+ }
448+ rule , err := deserializeRule (m .Data )
449+ if err != nil {
450+ if cberr != nil {
451+ cberr (err )
452+ }
453+ continue
454+ }
455+ ch <- RuleUpdate {Type : m .Header .Type , Rule : rule }
456+ }
457+ }
458+ }()
459+
460+ return nil
461+ }
462+
325463func (pr * RulePortRange ) toRtAttrData () []byte {
326464 b := [][]byte {make ([]byte , 2 ), make ([]byte , 2 )}
327465 native .PutUint16 (b [0 ], pr .Start )
0 commit comments