3 # Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; version 2.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # https://www.gnu.org/licenses/gpl-2.0-standalone.html
22 # Daniel Walton, dwalton@cumulusnetworks.com
23 # Julien Fortin, julien@cumulusnetworks.com
28 from collections
import OrderedDict
29 from ipaddr
import IPv4Address
, IPv6Address
30 from nlpacket
import *
31 from select
import select
32 from struct
import pack
, unpack
37 log
= logging
.getLogger(__name__
)
40 class NetlinkError(Exception):
44 class NetlinkNoAddressError(NetlinkError
):
48 class NetlinkInterruptedSystemCall(NetlinkError
):
52 class InvalidInterfaceNameVlanCombo(Exception):
56 class Sequence(object):
66 class NetlinkManager(object):
68 def __init__(self
, pid_offset
=0, use_color
=True, log_level
=None):
69 # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to 0
70 # in the upper space (top 10 bits), which will simply be the PID. Other
71 # NetlinkManager instantiations in the same process can choose other
72 # offsets to avoid conflicts with each other.
73 self
.pid
= os
.getpid() |
(pid_offset
<< 22)
74 self
.sequence
= Sequence()
75 self
.shutdown_flag
= False
78 self
.use_color
= use_color
82 self
.debug_link(False)
83 self
.debug_address(False)
84 self
.debug_neighbor(False)
85 self
.debug_route(False)
88 log
.setLevel(log_level
)
89 set_log_level(log_level
)
92 return 'NetlinkManager'
94 def signal_term_handler(self
, signal
, frame
):
95 log
.info("NetlinkManager: Caught SIGTERM")
96 self
.shutdown_flag
= True
98 def signal_int_handler(self
, signal
, frame
):
99 log
.info("NetlinkManager: Caught SIGINT")
100 self
.shutdown_flag
= True
104 self
.tx_socket
.close()
105 self
.tx_socket
= None
106 log
.info("NetlinkManager: shutdown complete")
108 def _debug_set_clear(self
, msg_types
, enabled
):
110 Enable or disable debugs for all msgs_types messages
120 def debug_link(self
, enabled
):
121 self
._debug
_set
_clear
((RTM_NEWLINK
, RTM_DELLINK
, RTM_GETLINK
, RTM_SETLINK
), enabled
)
123 def debug_address(self
, enabled
):
124 self
._debug
_set
_clear
((RTM_NEWADDR
, RTM_DELADDR
, RTM_GETADDR
), enabled
)
126 def debug_neighbor(self
, enabled
):
127 self
._debug
_set
_clear
((RTM_NEWNEIGH
, RTM_DELNEIGH
, RTM_GETNEIGH
), enabled
)
129 def debug_route(self
, enabled
):
130 self
._debug
_set
_clear
((RTM_NEWROUTE
, RTM_DELROUTE
, RTM_GETROUTE
), enabled
)
132 def debug_netconf(self
, enabled
):
133 self
._debug
_set
_clear
((RTM_GETNETCONF
, RTM_NEWNETCONF
), enabled
)
135 def debug_this_packet(self
, mtype
):
136 if mtype
in self
.debug
:
140 def tx_socket_allocate(self
):
142 The TX socket is used for install requests, sending RTM_GETXXXX
145 self
.tx_socket
= socket
.socket(socket
.AF_NETLINK
, socket
.SOCK_RAW
, 0)
146 self
.tx_socket
.bind((self
.pid
, 0))
148 def tx_nlpacket_raw(self
, message
):
150 TX a bunch of concatenated nlpacket.messages....do NOT wait for an ACK
152 if not self
.tx_socket
:
153 self
.tx_socket_allocate()
154 self
.tx_socket
.sendall(message
)
156 def tx_nlpacket(self
, nlpacket
):
158 TX a netlink packet but do NOT wait for an ACK
160 if not nlpacket
.message
:
161 log
.error('You must first call build_message() to create the packet')
164 if not self
.tx_socket
:
165 self
.tx_socket_allocate()
166 self
.tx_socket
.sendall(nlpacket
.message
)
168 def tx_nlpacket_get_response(self
, nlpacket
):
170 if not nlpacket
.message
:
171 log
.error('You must first call build_message() to create the packet')
174 if not self
.tx_socket
:
175 self
.tx_socket_allocate()
176 self
.tx_socket
.sendall(nlpacket
.message
)
178 # If nlpacket.debug is True we already printed the following in the
179 # build_message() call...so avoid printing two messages for one packet.
180 if not nlpacket
.debug
:
181 log
.debug("TXed %12s, pid %d, seq %d, %d bytes" %
182 (nlpacket
.get_type_string(), nlpacket
.pid
, nlpacket
.seq
, nlpacket
.length
))
184 header_PACK
= NetlinkPacket
.header_PACK
185 header_LEN
= NetlinkPacket
.header_LEN
189 MAX_ERROR_NLE_INTR
= 3
192 # Now listen to our socket and wait for the reply
195 if self
.shutdown_flag
:
196 log
.info('shutdown flag is True, exiting')
199 # Only block for 1 second so we can wake up to see if self.shutdown_flag is True
201 (readable
, writeable
, exceptional
) = select([self
.tx_socket
, ], [], [self
.tx_socket
, ], 1)
202 except Exception as e
:
203 # 4 is Interrupted system call
204 if isinstance(e
.args
, tuple) and e
[0] == 4:
206 log
.info("select() Interrupted system call %d/%d" % (nle_intr_count
, MAX_ERROR_NLE_INTR
))
208 if nle_intr_count
>= MAX_ERROR_NLE_INTR
:
209 raise NetlinkInterruptedSystemCall(error_str
)
220 # Safety net to make sure we do not spend too much time in
221 # this while True loop
222 if null_read
>= MAX_NULL_READS
:
223 log
.info('Socket was not readable for %d attempts' % null_read
)
233 except Exception as e
:
234 # 4 is Interrupted system call
235 if isinstance(e
.args
, tuple) and e
[0] == 4:
237 log
.info("%s: recv() Interrupted system call %d/%d" % (s
, nle_intr_count
, MAX_ERROR_NLE_INTR
))
239 if nle_intr_count
>= MAX_ERROR_NLE_INTR
:
240 raise NetlinkInterruptedSystemCall(error_str
)
247 log
.info('RXed zero length data, the socket is closed')
252 # Extract the length, etc from the header
253 (length
, msgtype
, flags
, seq
, pid
) = unpack(header_PACK
, data
[:header_LEN
])
255 debug_str
= "RXed %12s, pid %d, seq %d, %d bytes" % (NetlinkPacket
.type_to_string
[msgtype
], pid
, seq
, length
)
257 # This shouldn't happen but it would be nice to be aware of it if it does
258 if pid
!= nlpacket
.pid
:
259 log
.debug(debug_str
+ '...we are not interested in this pid %s since ours is %s' %
264 if seq
!= nlpacket
.seq
:
265 log
.debug(debug_str
+ '...we are not interested in this seq %s since ours is %s' %
270 # See if we RXed an ACK for our RTM_GETXXXX
271 if msgtype
== NLMSG_DONE
:
272 log
.debug(debug_str
+ '...this is an ACK')
275 elif msgtype
== NLMSG_ERROR
:
277 msg
= Error(msgtype
, nlpacket
.debug
)
278 msg
.decode_packet(length
, flags
, seq
, pid
, data
)
280 # The error code is a signed negative number.
281 error_code
= abs(msg
.negative_errno
)
283 # 0 is NLE_SUCCESS...everything else is a true error
290 # os.strerror might raise ValueError
291 strerror
= os
.strerror(error_code
)
294 error_str
= "operation failed with '%s' (%s)" % (strerror
, error_code
)
296 error_str
= "operation failed with code %s" % error_code
299 error_str
= "operation failed with code %s" % error_code
301 raise NetlinkError(error_str
)
303 log
.debug('%s code NLE_SUCCESS...this is an ACK' % debug_str
)
306 # No ACK...create a nlpacket object and append it to msgs
310 if msgtype
== RTM_NEWLINK
or msgtype
== RTM_DELLINK
:
311 msg
= Link(msgtype
, nlpacket
.debug
, use_color
=self
.use_color
)
313 elif msgtype
== RTM_NEWADDR
or msgtype
== RTM_DELADDR
:
314 msg
= Address(msgtype
, nlpacket
.debug
, use_color
=self
.use_color
)
316 elif msgtype
== RTM_NEWNEIGH
or msgtype
== RTM_DELNEIGH
:
317 msg
= Neighbor(msgtype
, nlpacket
.debug
, use_color
=self
.use_color
)
319 elif msgtype
== RTM_NEWROUTE
or msgtype
== RTM_DELROUTE
:
320 msg
= Route(msgtype
, nlpacket
.debug
, use_color
=self
.use_color
)
322 elif msgtype
in (RTM_GETNETCONF
, RTM_NEWNETCONF
):
323 msg
= Netconf(msgtype
, nlpacket
.debug
, use_color
=self
.use_color
)
326 raise Exception("RXed unknown netlink message type %s" % msgtype
)
328 msg
.decode_packet(length
, flags
, seq
, pid
, data
)
336 def ip_to_afi(self
, ip
):
339 if type_ip
== IPv4Address
:
340 return socket
.AF_INET
341 elif type_ip
== IPv6Address
:
342 return socket
.AF_INET6
344 raise Exception("%s is an invalid IP type" % type_ip
)
346 def request_dump(self
, rtm_type
, family
, debug
):
348 Issue a RTM_GETROUTE, etc with the NLM_F_DUMP flag
349 set and return the results
352 if rtm_type
== RTM_GETADDR
:
353 msg
= Address(rtm_type
, debug
, use_color
=self
.use_color
)
354 msg
.body
= pack('Bxxxi', family
, 0)
356 elif rtm_type
== RTM_GETLINK
:
357 msg
= Link(rtm_type
, debug
, use_color
=self
.use_color
)
358 msg
.body
= pack('Bxxxiii', family
, 0, 0, 0)
360 elif rtm_type
== RTM_GETNEIGH
:
361 msg
= Neighbor(rtm_type
, debug
, use_color
=self
.use_color
)
362 msg
.body
= pack('Bxxxii', family
, 0, 0)
364 elif rtm_type
== RTM_GETROUTE
:
365 msg
= Route(rtm_type
, debug
, use_color
=self
.use_color
)
366 msg
.body
= pack('Bxxxii', family
, 0, 0)
369 log
.error("request_dump RTM_GET %s is not supported" % rtm_type
)
372 msg
.flags
= NLM_F_REQUEST | NLM_F_DUMP
374 msg
.build_message(self
.sequence
.next(), self
.pid
)
375 return self
.tx_nlpacket_get_response(msg
)
380 def _routes_add_or_delete(self
, add_route
, routes
, ecmp_routes
, table
, protocol
, route_scope
, route_type
):
382 def tx_or_concat_message(total_message
, route
):
384 Adding an ipv4 route only takes 60 bytes, if we are adding thousands
385 of them this can add up to a lot of send calls. Concat several of
386 them together before TXing.
389 if not total_message
:
390 total_message
= route
.message
392 total_message
+= route
.message
394 if len(total_message
) >= PACKET_CONCAT_SIZE
:
395 self
.tx_nlpacket_raw(total_message
)
401 rtm_command
= RTM_NEWROUTE
403 rtm_command
= RTM_DELROUTE
406 PACKET_CONCAT_SIZE
= 16384
407 debug
= rtm_command
in self
.debug
410 for (afi
, ip
, mask
, nexthop
, interface_index
) in routes
:
411 route
= Route(rtm_command
, debug
, use_color
=self
.use_color
)
412 route
.flags
= NLM_F_REQUEST | NLM_F_CREATE
413 route
.body
= pack('BBBBBBBBi', afi
, mask
, 0, 0, table
, protocol
,
414 route_scope
, route_type
, 0)
416 route
.add_attribute(Route
.RTA_DST
, ip
)
418 route
.add_attribute(Route
.RTA_GATEWAY
, nexthop
)
419 route
.add_attribute(Route
.RTA_OIF
, interface_index
)
420 route
.build_message(self
.sequence
.next(), self
.pid
)
421 total_message
= tx_or_concat_message(total_message
, route
)
424 self
.tx_nlpacket_raw(total_message
)
428 for (route_key
, value
) in ecmp_routes
.iteritems():
429 (afi
, ip
, mask
) = route_key
431 route
= Route(rtm_command
, debug
, use_color
=self
.use_color
)
432 route
.flags
= NLM_F_REQUEST | NLM_F_CREATE
433 route
.body
= pack('BBBBBBBBi', afi
, mask
, 0, 0, table
, protocol
,
434 route_scope
, route_type
, 0)
436 route
.add_attribute(Route
.RTA_DST
, ip
)
437 route
.add_attribute(Route
.RTA_MULTIPATH
, value
)
438 route
.build_message(self
.sequence
.next(), self
.pid
)
439 total_message
= tx_or_concat_message(total_message
, route
)
442 self
.tx_nlpacket_raw(total_message
)
444 def routes_add(self
, routes
, ecmp_routes
,
445 table
=Route
.RT_TABLE_MAIN
,
446 protocol
=Route
.RT_PROT_XORP
,
447 route_scope
=Route
.RT_SCOPE_UNIVERSE
,
448 route_type
=Route
.RTN_UNICAST
):
449 self
._routes
_add
_or
_delete
(True, routes
, ecmp_routes
, table
, protocol
, route_scope
, route_type
)
451 def routes_del(self
, routes
, ecmp_routes
,
452 table
=Route
.RT_TABLE_MAIN
,
453 protocol
=Route
.RT_PROT_XORP
,
454 route_scope
=Route
.RT_SCOPE_UNIVERSE
,
455 route_type
=Route
.RTN_UNICAST
):
456 self
._routes
_add
_or
_delete
(False, routes
, ecmp_routes
, table
, protocol
, route_scope
, route_type
)
458 def route_get(self
, ip
, debug
=False):
460 ip must be one of the following:
464 # Transmit a RTM_GETROUTE to query for the route we want
465 route
= Route(RTM_GETROUTE
, debug
, use_color
=self
.use_color
)
466 route
.flags
= NLM_F_REQUEST | NLM_F_ACK
468 # Set everything in the service header as 0 other than the afi
469 afi
= self
.ip_to_afi(ip
)
470 route
.body
= pack('Bxxxxxxxi', afi
, 0)
472 route
.add_attribute(Route
.RTA_DST
, ip
)
473 route
.build_message(self
.sequence
.next(), self
.pid
)
474 return self
.tx_nlpacket_get_response(route
)
476 def routes_dump(self
, family
=socket
.AF_UNSPEC
, debug
=True):
477 return self
.request_dump(RTM_GETROUTE
, family
, debug
)
479 def routes_print(self
, routes
):
481 Print a table of 'routes'
483 print "Prefix Nexthop ifindex"
486 if Route
.RTA_DST
not in x
.attributes
:
487 log
.warning("Route is missing RTA_DST")
490 ip
= "%s/%d" % (x
.attributes
[Route
.RTA_DST
].value
, x
.src_len
)
491 print "%-15s %-15s %s" %\
493 str(x
.attributes
[Route
.RTA_GATEWAY
].value
) if Route
.RTA_GATEWAY
in x
.attributes
else None,
494 x
.attributes
[Route
.RTA_OIF
].value
)
499 def _get_iface_by_name(self
, ifname
):
501 Return a Link object for ifname
503 debug
= RTM_GETLINK
in self
.debug
505 link
= Link(RTM_GETLINK
, debug
, use_color
=self
.use_color
)
506 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
507 link
.body
= pack('=Bxxxiii', socket
.AF_UNSPEC
, 0, 0, 0)
508 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
509 link
.build_message(self
.sequence
.next(), self
.pid
)
512 return self
.tx_nlpacket_get_response(link
)[0]
514 except NetlinkNoAddressError
:
515 log
.info("Netlink did not find interface %s" % ifname
)
518 def _get_iface_by_index(self
, ifindex
):
520 Return a Link object for ifindex
522 debug
= RTM_GETLINK
in self
.debug
524 link
= Link(RTM_GETLINK
, debug
, use_color
=self
.use_color
)
525 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
526 link
.body
= pack('=Bxxxiii', socket
.AF_UNSPEC
, ifindex
, 0, 0)
527 link
.build_message(self
.sequence
.next(), self
.pid
)
529 return self
.tx_nlpacket_get_response(link
)[0]
530 except NetlinkNoAddressError
:
531 log
.info("Netlink did not find interface %s" % ifindex
)
534 def get_iface_index(self
, ifname
):
536 Return the interface index for ifname
538 iface
= self
._get
_iface
_by
_name
(ifname
)
544 def get_iface_name(self
, ifindex
):
545 iface
= self
._get
_iface
_by
_index
(ifindex
)
548 return iface
.attributes
[Link
.IFLA_IFNAME
].get_pretty_value(str)
551 def link_dump(self
, ifname
=None):
552 debug
= RTM_GETLINK
in self
.debug
553 msg
= Link(RTM_GETLINK
, debug
, use_color
=self
.use_color
)
554 msg
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, 0, 0, 0)
555 msg
.flags
= NLM_F_REQUEST | NLM_F_ACK
558 msg
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
560 msg
.flags |
= NLM_F_DUMP
562 msg
.build_message(self
.sequence
.next(), self
.pid
)
563 return self
.tx_nlpacket_get_response(msg
)
565 def link_set_attrs(self
, ifname
, kind
=None, slave_kind
=None, ifindex
=0, ifla
={}, ifla_info_data
={}, ifla_info_slave_data
={}):
566 debug
= RTM_NEWLINK
in self
.debug
568 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
569 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
570 link
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, ifindex
, 0, 0)
572 for nl_attr
, value
in ifla
.items():
573 link
.add_attribute(nl_attr
, value
)
576 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
581 linkinfo
[Link
.IFLA_INFO_KIND
] = kind
582 linkinfo
[Link
.IFLA_INFO_DATA
] = ifla_info_data
584 linkinfo
[Link
.IFLA_INFO_SLAVE_KIND
] = slave_kind
,
585 linkinfo
[Link
.IFLA_INFO_SLAVE_DATA
] = ifla_info_slave_data
587 link
.add_attribute(Link
.IFLA_LINKINFO
, linkinfo
)
588 link
.build_message(self
.sequence
.next(), self
.pid
)
589 return self
.tx_nlpacket_get_response(link
)
591 def link_add_set(self
, kind
,
597 ifla_info_slave_data
={}):
599 Build and TX a RTM_NEWLINK message to add the desired interface
601 debug
= RTM_NEWLINK
in self
.debug
603 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
604 link
.flags
= NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
605 link
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, ifindex
, 0, 0)
607 for nl_attr
, value
in ifla
.items():
608 link
.add_attribute(nl_attr
, value
)
611 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
615 linkinfo
[Link
.IFLA_INFO_KIND
] = kind
616 linkinfo
[Link
.IFLA_INFO_DATA
] = ifla_info_data
618 linkinfo
[Link
.IFLA_INFO_SLAVE_KIND
] = slave_kind
619 linkinfo
[Link
.IFLA_INFO_SLAVE_DATA
] = ifla_info_slave_data
620 link
.add_attribute(Link
.IFLA_LINKINFO
, linkinfo
)
622 link
.build_message(self
.sequence
.next(), self
.pid
)
623 return self
.tx_nlpacket_get_response(link
)
625 def link_del(self
, ifindex
=None, ifname
=None):
626 if not ifindex
and not ifname
:
627 raise ValueError('invalid ifindex and/or ifname')
630 ifindex
= self
.get_iface_index(ifname
)
632 debug
= RTM_DELLINK
in self
.debug
634 link
= Link(RTM_DELLINK
, debug
, use_color
=self
.use_color
)
635 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
636 link
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, ifindex
, 0, 0)
637 link
.build_message(self
.sequence
.next(), self
.pid
)
638 return self
.tx_nlpacket_get_response(link
)
640 def _link_add(self
, ifindex
, ifname
, kind
, ifla_info_data
, mtu
=None):
642 Build and TX a RTM_NEWLINK message to add the desired interface
644 debug
= RTM_NEWLINK
in self
.debug
646 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
647 link
.flags
= NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
648 link
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, 0, 0, 0)
649 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
652 link
.add_attribute(Link
.IFLA_LINK
, ifindex
)
655 link
.add_attribute(Link
.IFLA_MTU
, mtu
)
657 link
.add_attribute(Link
.IFLA_LINKINFO
, {
658 Link
.IFLA_INFO_KIND
: kind
,
659 Link
.IFLA_INFO_DATA
: ifla_info_data
661 link
.build_message(self
.sequence
.next(), self
.pid
)
662 return self
.tx_nlpacket_get_response(link
)
664 def link_add_bridge(self
, ifname
, ifla_info_data
={}, mtu
=None):
665 return self
._link
_add
(ifindex
=None, ifname
=ifname
, kind
='bridge', ifla_info_data
=ifla_info_data
, mtu
=mtu
)
667 def link_add_vlan(self
, ifindex
, ifname
, vlanid
, vlan_protocol
=None):
669 ifindex is the index of the parent interface that this sub-interface
674 If you name an interface swp2.17 but assign it to vlan 12, the kernel
675 will return a very misleading NLE_MSG_OVERFLOW error. It only does
676 this check if the ifname uses dot notation.
678 Do this check here so we can provide a more intuitive error
681 ifname_vlanid
= int(ifname
.split('.')[-1])
683 if ifname_vlanid
!= vlanid
:
684 raise InvalidInterfaceNameVlanCombo("Interface %s must belong "
685 "to VLAN %d (VLAN %d was requested)" %
686 (ifname
, ifname_vlanid
, vlanid
))
688 ifla_info_data
= {Link
.IFLA_VLAN_ID
: vlanid
}
691 ifla_info_data
[Link
.IFLA_VLAN_PROTOCOL
] = vlan_protocol
693 return self
._link
_add
(ifindex
, ifname
, 'vlan', ifla_info_data
)
695 def link_add_macvlan(self
, ifindex
, ifname
):
697 ifindex is the index of the parent interface that this sub-interface
700 return self
._link
_add
(ifindex
, ifname
, 'macvlan', {Link
.IFLA_MACVLAN_MODE
: Link
.MACVLAN_MODE_PRIVATE
})
702 def link_add_xfrm(self
, physdev
, xfrm_ifname
, xfrm_id
):
704 ifindex is the index of the parent interface that this sub-interface
708 Link
.IFLA_XFRM_IF_ID
: int(xfrm_id
),
709 Link
.IFLA_XFRM_LINK
: int(physdev
)
712 return self
._link
_add
(ifindex
=None, ifname
=xfrm_ifname
, kind
='xfrm', ifla_info_data
=ifla_info_data
)
714 def vlan_get(self
, filter_ifindex
=None, filter_vlanid
=None, compress_vlans
=True):
716 filter_ifindex should be a tuple if interface indexes, this is a whitelist filter
717 filter_vlandid should be a tuple if VLAN IDs, this is a whitelist filter
719 debug
= RTM_GETLINK
in self
.debug
721 link
= Link(RTM_GETLINK
, debug
, use_color
=self
.use_color
)
722 link
.family
= AF_BRIDGE
723 link
.flags
= NLM_F_DUMP | NLM_F_REQUEST
724 link
.body
= pack('Bxxxiii', socket
.AF_BRIDGE
, 0, 0, 0)
727 link
.add_attribute(Link
.IFLA_EXT_MASK
, Link
.RTEXT_FILTER_BRVLAN_COMPRESSED
)
729 link
.add_attribute(Link
.IFLA_EXT_MASK
, Link
.RTEXT_FILTER_BRVLAN
)
731 link
.build_message(self
.sequence
.next(), self
.pid
)
732 reply
= self
.tx_nlpacket_get_response(link
)
737 if msg
.family
!= socket
.AF_BRIDGE
:
740 if filter_ifindex
and msg
.ifindex
not in filter_ifindex
:
743 ifla_af_spec
= msg
.get_attribute_value(Link
.IFLA_AF_SPEC
)
748 ifname
= msg
.get_attribute_value(Link
.IFLA_IFNAME
)
753 20: 0x1c001a00 .... Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC
754 21: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
756 23: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
758 25: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
761 for (x_type
, x_value
) in ifla_af_spec
.iteritems():
762 if x_type
== Link
.IFLA_BRIDGE_VLAN_INFO
:
763 for (vlan_flag
, vlan_id
) in x_value
:
764 if filter_vlanid
is None or vlan_id
in filter_vlanid
:
766 if ifname
not in iface_vlans
:
767 iface_vlans
[ifname
] = []
769 # We store these in the tuple as (vlan, flag) instead (flag, vlan)
770 # so that we can sort the list of tuples
771 iface_vlans
[ifname
].append((vlan_id
, vlan_flag
))
775 def vlan_show(self
, filter_ifindex
=None, filter_vlanid
=None, compress_vlans
=True):
777 def vlan_flag_to_string(vlan_flag
):
779 if vlan_flag
& Link
.BRIDGE_VLAN_INFO_PVID
:
780 flag_str
.append('PVID')
782 if vlan_flag
& Link
.BRIDGE_VLAN_INFO_UNTAGGED
:
783 flag_str
.append('Egress Untagged')
785 return ', '.join(flag_str
)
787 iface_vlans
= self
.vlan_get(filter_ifindex
, filter_vlanid
, compress_vlans
)
788 log
.debug("iface_vlans:\n%s\n" % pformat(iface_vlans
))
789 range_begin_vlan_id
= None
792 print " Interface VLAN Flags"
793 print " ========== ==== ====="
795 for (ifname
, vlan_tuples
) in sorted(iface_vlans
.iteritems()):
796 for (vlan_id
, vlan_flag
) in sorted(vlan_tuples
):
798 if vlan_flag
& Link
.BRIDGE_VLAN_INFO_RANGE_BEGIN
:
799 range_begin_vlan_id
= vlan_id
800 range_flag
= vlan_flag
802 elif vlan_flag
& Link
.BRIDGE_VLAN_INFO_RANGE_END
:
803 range_flag |
= vlan_flag
805 if not range_begin_vlan_id
:
806 log
.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id
)
807 range_begin_vlan_id
= vlan_id
809 for x
in xrange(range_begin_vlan_id
, vlan_id
+ 1):
810 print " %10s %4d %s" % (ifname
, x
, vlan_flag_to_string(vlan_flag
))
813 range_begin_vlan_id
= None
817 print " %10s %4d %s" % (ifname
, vlan_id
, vlan_flag_to_string(vlan_flag
))
821 def vlan_modify(self
, msgtype
, ifindex
, vlanid_start
, vlanid_end
=None, bridge_self
=False, bridge_master
=False, pvid
=False, untagged
=False):
823 iproute2 bridge/vlan.c vlan_modify()
825 assert msgtype
in (RTM_SETLINK
, RTM_DELLINK
), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype
826 assert vlanid_start
>= 1 and vlanid_start
<= 4096, "Invalid VLAN start %s" % vlanid_start
828 if vlanid_end
is None:
829 vlanid_end
= vlanid_start
831 assert vlanid_end
>= 1 and vlanid_end
<= 4096, "Invalid VLAN end %s" % vlanid_end
832 assert vlanid_start
<= vlanid_end
, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start
, vlanid_end
)
834 debug
= msgtype
in self
.debug
838 link
= Link(msgtype
, debug
, use_color
=self
.use_color
)
839 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
840 link
.body
= pack('Bxxxiii', socket
.AF_BRIDGE
, ifindex
, 0, 0)
843 bridge_flags |
= Link
.BRIDGE_FLAGS_SELF
846 bridge_flags |
= Link
.BRIDGE_FLAGS_MASTER
849 vlan_info_flags |
= Link
.BRIDGE_VLAN_INFO_PVID
852 vlan_info_flags |
= Link
.BRIDGE_VLAN_INFO_UNTAGGED
854 ifla_af_spec
= OrderedDict()
857 ifla_af_spec
[Link
.IFLA_BRIDGE_FLAGS
] = bridge_flags
860 if vlanid_start
== vlanid_end
:
861 ifla_af_spec
[Link
.IFLA_BRIDGE_VLAN_INFO
] = [(vlan_info_flags
, vlanid_start
), ]
865 ifla_af_spec
[Link
.IFLA_BRIDGE_VLAN_INFO
] = [
866 (vlan_info_flags | Link
.BRIDGE_VLAN_INFO_RANGE_BEGIN
, vlanid_start
),
867 (vlan_info_flags | Link
.BRIDGE_VLAN_INFO_RANGE_END
, vlanid_end
)
870 link
.add_attribute(Link
.IFLA_AF_SPEC
, ifla_af_spec
)
871 link
.build_message(self
.sequence
.next(), self
.pid
)
872 return self
.tx_nlpacket_get_response(link
)
874 def link_add_bridge_vlan(self
, ifindex
, vlanid_start
, vlanid_end
=None, pvid
=False, untagged
=False, master
=False):
876 Add VLAN(s) to a bridge interface
878 bridge_self
= False if master
else True
879 self
.vlan_modify(RTM_SETLINK
, ifindex
, vlanid_start
, vlanid_end
, bridge_self
, master
, pvid
, untagged
)
881 def link_del_bridge_vlan(self
, ifindex
, vlanid_start
, vlanid_end
=None, pvid
=False, untagged
=False, master
=False):
883 Delete VLAN(s) from a bridge interface
885 bridge_self
= False if master
else True
886 self
.vlan_modify(RTM_DELLINK
, ifindex
, vlanid_start
, vlanid_end
, bridge_self
, master
, pvid
, untagged
)
888 def link_set_updown(self
, ifname
, state
):
890 Either bring ifname up or take it down
894 if_flags
= Link
.IFF_UP
895 elif state
== 'down':
898 raise Exception('Unsupported state %s, valid options are "up" and "down"' % state
)
900 debug
= RTM_NEWLINK
in self
.debug
901 if_change
= Link
.IFF_UP
903 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
904 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
905 link
.body
= pack('=BxxxiLL', socket
.AF_UNSPEC
, 0, if_flags
, if_change
)
906 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
907 link
.build_message(self
.sequence
.next(), self
.pid
)
908 return self
.tx_nlpacket_get_response(link
)
910 def link_set_protodown(self
, ifname
, state
):
912 Either bring ifname up or take it down by setting IFLA_PROTO_DOWN on or off
915 protodown
= 1 if state
== "on" else 0
917 debug
= RTM_NEWLINK
in self
.debug
919 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
920 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
921 link
.body
= pack('=BxxxiLL', socket
.AF_UNSPEC
, 0, 0, 0)
922 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
923 link
.add_attribute(Link
.IFLA_PROTO_DOWN
, protodown
)
924 link
.build_message(self
.sequence
.next(), self
.pid
)
925 return self
.tx_nlpacket_get_response(link
)
927 def link_set_master(self
, ifname
, master_ifindex
=0, state
=None):
929 ip link set %ifname master %master_ifindex %state
930 use master_ifindex=0 for nomaster
933 if_change
= Link
.IFF_UP
934 if_flags
= Link
.IFF_UP
935 elif state
== 'down':
936 if_change
= Link
.IFF_UP
942 debug
= RTM_NEWLINK
in self
.debug
944 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
945 link
.flags
= NLM_F_REQUEST | NLM_F_ACK
946 link
.body
= pack('=BxxxiLL', socket
.AF_UNSPEC
, 0, if_flags
, if_change
)
947 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
948 link
.add_attribute(Link
.IFLA_MASTER
, master_ifindex
)
949 link
.build_message(self
.sequence
.next(), self
.pid
)
950 return self
.tx_nlpacket_get_response(link
)
955 def neighbor_add(self
, afi
, ifindex
, ip
, mac
):
956 debug
= RTM_NEWNEIGH
in self
.debug
957 service_hdr_flags
= 0
959 nbr
= Neighbor(RTM_NEWNEIGH
, debug
, use_color
=self
.use_color
)
960 nbr
.flags
= NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
962 nbr
.body
= pack('=BxxxiHBB', afi
, ifindex
, Neighbor
.NUD_REACHABLE
, service_hdr_flags
, Route
.RTN_UNICAST
)
963 nbr
.add_attribute(Neighbor
.NDA_DST
, ip
)
964 nbr
.add_attribute(Neighbor
.NDA_LLADDR
, mac
)
965 nbr
.build_message(self
.sequence
.next(), self
.pid
)
966 return self
.tx_nlpacket_get_response(nbr
)
968 def neighbor_del(self
, afi
, ifindex
, ip
, mac
):
969 debug
= RTM_DELNEIGH
in self
.debug
970 service_hdr_flags
= 0
972 nbr
= Neighbor(RTM_DELNEIGH
, debug
, use_color
=self
.use_color
)
973 nbr
.flags
= NLM_F_REQUEST | NLM_F_ACK
975 nbr
.body
= pack('=BxxxiHBB', afi
, ifindex
, Neighbor
.NUD_REACHABLE
, service_hdr_flags
, Route
.RTN_UNICAST
)
976 nbr
.add_attribute(Neighbor
.NDA_DST
, ip
)
977 nbr
.add_attribute(Neighbor
.NDA_LLADDR
, mac
)
978 nbr
.build_message(self
.sequence
.next(), self
.pid
)
979 return self
.tx_nlpacket_get_response(nbr
)
981 def link_add_vxlan(self
, ifname
, vxlanid
, dstport
=None, local
=None,
982 group
=None, learning
=True, ageing
=None, physdev
=None, ttl
=None):
984 debug
= RTM_NEWLINK
in self
.debug
986 info_data
= {Link
.IFLA_VXLAN_ID
: int(vxlanid
)}
988 info_data
[Link
.IFLA_VXLAN_PORT
] = int(dstport
)
990 info_data
[Link
.IFLA_VXLAN_LOCAL
] = local
992 info_data
[Link
.IFLA_VXLAN_GROUP
] = group
994 info_data
[Link
.IFLA_VXLAN_LEARNING
] = int(learning
)
995 info_data
[Link
.IFLA_VXLAN_TTL
] = ttl
998 info_data
[Link
.IFLA_VXLAN_AGEING
] = int(ageing
)
1001 info_data
[Link
.IFLA_VXLAN_LINK
] = int(physdev
)
1003 link
= Link(RTM_NEWLINK
, debug
, use_color
=self
.use_color
)
1004 link
.flags
= NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
1005 link
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, 0, 0, 0)
1006 link
.add_attribute(Link
.IFLA_IFNAME
, ifname
)
1007 link
.add_attribute(Link
.IFLA_LINKINFO
, {
1008 Link
.IFLA_INFO_KIND
: "vxlan",
1009 Link
.IFLA_INFO_DATA
: info_data
1012 link
.build_message(self
.sequence
.next(), self
.pid
)
1013 return self
.tx_nlpacket_get_response(link
)
1018 def addr_dump(self
):
1020 TODO: add ifname/ifindex filtering:
1021 - via the RTM_GETADDR request packet
1022 - or in python if kernel doesn't support per intf dump
1024 debug
= RTM_GETADDR
in self
.debug
1026 msg
= Address(RTM_GETADDR
, debug
, use_color
=self
.use_color
)
1027 msg
.body
= pack('=Bxxxi', socket
.AF_UNSPEC
, 0)
1028 msg
.flags
= NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP
1030 msg
.build_message(self
.sequence
.next(), self
.pid
)
1031 return self
.tx_nlpacket_get_response(msg
)
1036 def netconf_dump(self
):
1038 The attribute Netconf.NETCONFA_IFINDEX is available but don't let it fool you
1039 it seems like the kernel doesn't really care about this attribute and will dump
1040 everything according of the requested family (AF_UNSPEC for everything).
1041 Device filtering needs to be done afterwards by the user.
1043 debug
= RTM_GETNETCONF
in self
.debug
1044 msg
= Netconf(RTM_GETNETCONF
, debug
, use_color
=self
.use_color
)
1045 msg
.body
= pack('Bxxxiii', socket
.AF_UNSPEC
, 0, 0, 0)
1046 msg
.flags
= NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
1047 msg
.build_message(self
.sequence
.next(), self
.pid
)
1048 return self
.tx_nlpacket_get_response(msg
)