]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/addressvirtual.py
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
7 from ifupdown
. iface
import *
8 from ifupdownaddons
. modulebase
import moduleBase
9 from ifupdownaddons
. iproute2
import iproute2
10 import ifupdown
. ifupdownconfig
as ifupdownConfig
11 import ifupdown
. statemanager
as statemanager
12 from ifupdown
. netlink
import netlink
13 import ifupdown
. ifupdownflags
as ifupdownflags
15 from ipaddr
import IPNetwork
, IPv4Network
20 class addressvirtual ( moduleBase
):
21 """ ifupdown2 addon module to configure virtual addresses """
23 _modinfo
= { 'mhelp' : 'address module configures virtual addresses for ' +
24 'interfaces. It creates a macvlan interface for ' +
25 'every mac ip address-virtual line' ,
28 { 'help' : 'bridge router virtual mac and ips' ,
29 'validvals' : [ '<mac-ip/prefixlen-list>' ,],
30 'example' : [ 'address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24' ]}
34 def __init__ ( self
, * args
, ** kargs
):
35 moduleBase
.__ init
__ ( self
, * args
, ** kargs
)
37 self
._ bridge
_ fdb
_ query
_ cache
= {}
39 def _is_supported ( self
, ifaceobj
):
40 if ifaceobj
. get_attr_value_first ( 'address-virtual' ):
44 def _get_macvlan_prefix ( self
, ifaceobj
):
45 return ' %s- v' %if aceobj
. name
[ 0 : 13 ]. replace ( '.' , '-' )
47 def _add_addresses_to_bridge ( self
, ifaceobj
, hwaddress
):
48 # XXX: batch the addresses
49 if '.' in ifaceobj
. name
:
50 ( bridgename
, vlan
) = ifaceobj
. name
. split ( '.' )
51 if self
. ipcmd
. bridge_is_vlan_aware ( bridgename
):
52 [ self
. ipcmd
. bridge_fdb_add ( bridgename
, addr
,
53 vlan
) for addr
in hwaddress
]
54 elif self
. ipcmd
. is_bridge ( ifaceobj
. name
):
55 [ self
. ipcmd
. bridge_fdb_add ( ifaceobj
. name
, addr
)
56 for addr
in hwaddress
]
58 def _remove_addresses_from_bridge ( self
, ifaceobj
, hwaddress
):
59 # XXX: batch the addresses
60 if '.' in ifaceobj
. name
:
61 ( bridgename
, vlan
) = ifaceobj
. name
. split ( '.' )
62 if self
. ipcmd
. bridge_is_vlan_aware ( bridgename
):
63 for addr
in hwaddress
:
65 self
. ipcmd
. bridge_fdb_del ( bridgename
, addr
, vlan
)
67 self
. logger
. debug ( " %s : %s " %( ifaceobj
. name
, str ( e
)))
69 elif self
. ipcmd
. is_bridge ( ifaceobj
. name
):
70 for addr
in hwaddress
:
72 self
. ipcmd
. bridge_fdb_del ( ifaceobj
. name
, addr
)
74 self
. logger
. debug ( " %s : %s " %( ifaceobj
. name
, str ( e
)))
77 def _get_bridge_fdbs ( self
, bridgename
, vlan
):
78 fdbs
= self
._ bridge
_ fdb
_ query
_ cache
. get ( bridgename
)
80 fdbs
= self
. ipcmd
. bridge_fdb_show_dev ( bridgename
)
83 self
._ bridge
_ fdb
_ query
_ cache
[ bridgename
] = fdbs
86 def _check_addresses_in_bridge ( self
, ifaceobj
, hwaddress
):
87 """ If the device is a bridge, make sure the addresses
89 if '.' in ifaceobj
. name
:
90 ( bridgename
, vlan
) = ifaceobj
. name
. split ( '.' )
91 if self
. ipcmd
. bridge_is_vlan_aware ( bridgename
):
92 fdb_addrs
= self
._ get
_ bridge
_ fdbs
( bridgename
, vlan
)
93 if not fdb_addrs
or hwaddress
not in fdb_addrs
:
97 def _fix_connected_route ( self
, ifaceobj
, vifacename
, addr
):
99 # XXX: Hack to make sure the primary address
100 # is the first in the routing table.
102 # We use `ip route get` on the vrr network to see which
103 # device the kernel returns. if it is the mac vlan device,
104 # flap the macvlan device to adjust the routing table entry.
106 # flapping the macvlan device makes sure the macvlan
107 # connected route goes through delete + add, hence adjusting
108 # the order in the routing table.
111 self
. logger
. info ( ' %s : checking route entry ...' %if aceobj
. name
)
113 route_prefix
= ' %s / %d ' %( ip
. network
, ip
. prefixlen
)
115 dev
= self
. ipcmd
. ip_route_get_dev ( route_prefix
)
116 if dev
and dev
== vifacename
:
117 self
. logger
. info ( ' %s : preferred routing entry ' %if aceobj
. name
+
118 'seems to be of the macvlan dev %s '
120 ' .. flapping macvlan dev to fix entry.' )
121 self
. ipcmd
. link_down ( vifacename
)
122 self
. ipcmd
. link_up ( vifacename
)
124 self
. logger
. debug ( ' %s : fixing route entry failed ( %s )'
128 def _handle_vrf_slaves ( self
, macvlan_ifacename
, ifaceobj
):
129 vrfname
= self
. ipcmd
. link_get_master ( ifaceobj
. name
)
131 self
. ipcmd
. link_set ( macvlan_ifacename
, 'master' , vrfname
)
133 def _get_macs_from_old_config ( self
, ifaceobj
= None ):
134 """ This method returns a list of the mac addresses
135 in the address-virtual attribute for the bridge. """
137 saved_ifaceobjs
= statemanager
. statemanager_api
. get_ifaceobjs ( ifaceobj
. name
)
138 if not saved_ifaceobjs
:
140 # we need the old saved configs from the statemanager
141 for oldifaceobj
in saved_ifaceobjs
:
142 if not oldifaceobj
. get_attr_value ( 'address-virtual' ):
144 for av
in oldifaceobj
. get_attr_value ( 'address-virtual' ):
147 self
. logger
. debug ( " %s : incorrect old address-virtual attrs ' %s '"
148 %( oldifaceobj
. name
, av
))
150 maclist
. append ( macip
[ 0 ])
153 def _apply_address_config ( self
, ifaceobj
, address_virtual_list
):
154 purge_existing
= False if ifupdownflags
. flags
. PERFMODE
else True
156 lower_iface_mtu
= update_mtu
= None
157 if ifupdownConfig
. config
. get ( 'adjust_logical_dev_mtu' , '1' ) != '0' :
158 if ifaceobj
. lowerifaces
and address_virtual_list
:
162 self
. ipcmd
. batch_start ()
164 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
165 for av
in address_virtual_list
:
166 av_attrs
= av
. split ()
167 if len ( av_attrs
) < 2 :
168 self
. log_error ( " %s : incorrect address-virtual attrs ' %s '"
169 %( ifaceobj
. name
, av
), ifaceobj
,
175 if not self
. check_mac_address ( ifaceobj
, mac
):
177 # Create a macvlan device on this device and set the virtual
178 # router mac and ip on it
180 macvlan_ifacename
= ' %s%d ' %( macvlan_prefix
, av_idx
)
181 if not self
. ipcmd
. link_exists ( macvlan_ifacename
):
182 netlink
. link_add_macvlan ( ifaceobj
. name
, macvlan_ifacename
)
187 # customer could have used UPPERCASE for MAC
188 self
. ipcmd
. link_set_hwaddress ( macvlan_ifacename
, mac
)
189 hwaddress
. append ( mac
)
190 self
. ipcmd
. addr_add_multiple ( macvlan_ifacename
, ips
,
193 # If link existed before, flap the link
195 self
._ fix
_ connected
_ route
( ifaceobj
, macvlan_ifacename
,
198 lower_iface_mtu
= self
. ipcmd
. link_get_mtu ( ifaceobj
. lowerifaces
[ 0 ], refresh
= True )
201 if lower_iface_mtu
and lower_iface_mtu
!= self
. ipcmd
. link_get_mtu ( macvlan_ifacename
):
202 self
. ipcmd
. link_set_mtu ( macvlan_ifacename
, lower_iface_mtu
)
204 # set macvlan device to up in anycase.
205 # since we auto create them here..we are responsible
206 # to bring them up here in the case they were brought down
207 # by some other entity in the system.
208 netlink
. link_set_updown ( macvlan_ifacename
, "up" )
211 if ( ifaceobj
. link_privflags
& ifaceLinkPrivFlags
. VRF_SLAVE
):
212 self
._ handle
_ vrf
_ slaves
( macvlan_ifacename
, ifaceobj
)
214 # Disable IPv6 duplicate address detection on VRR interfaces
215 for key
, sysval
in { 'accept_dad' : '0' , 'dad_transmits' : '0' }. iteritems ():
216 syskey
= 'net.ipv6.conf. %s . %s ' % ( macvlan_ifacename
, key
)
217 if self
. sysctl_get ( syskey
) != sysval
:
218 self
. sysctl_set ( syskey
, sysval
)
221 self
. ipcmd
. batch_commit ()
223 # check the statemanager for old configs.
224 # We need to remove only the previously configured FDB entries
225 oldmacs
= self
._ get
_ macs
_ from
_ old
_ config
( ifaceobj
)
226 # get a list of fdbs in old that are not in new config meaning they should
227 # be removed since they are gone from the config
228 removed_macs
= [ mac
for mac
in oldmacs
if mac
. lower () not in hwaddress
]
229 self
._ remove
_ addresses
_ from
_ bridge
( ifaceobj
, removed_macs
)
230 # if ifaceobj is a bridge and bridge is a vlan aware bridge
231 # add the vid to the bridge
232 self
._ add
_ addresses
_ to
_ bridge
( ifaceobj
, hwaddress
)
234 def _remove_running_address_config ( self
, ifaceobj
):
235 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
238 self
. ipcmd
. batch_start ()
239 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
240 for macvlan_ifacename
in glob
. glob ( "/sys/class/net/ %s *" % macvlan_prefix
):
241 macvlan_ifacename
= os
. path
. basename ( macvlan_ifacename
)
242 if not self
. ipcmd
. link_exists ( macvlan_ifacename
):
244 hwaddress
. append ( self
. ipcmd
. link_get_hwaddress ( macvlan_ifacename
))
245 self
. ipcmd
. link_delete ( os
. path
. basename ( macvlan_ifacename
))
246 # XXX: Also delete any fdb addresses. This requires, checking mac address
247 # on individual macvlan interfaces and deleting the vlan from that.
248 self
. ipcmd
. batch_commit ()
250 self
._ remove
_ addresses
_ from
_ bridge
( ifaceobj
, hwaddress
)
252 def _remove_address_config ( self
, ifaceobj
, address_virtual_list
= None ):
253 if not address_virtual_list
:
254 self
._ remove
_ running
_ address
_ config
( ifaceobj
)
257 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
260 self
. ipcmd
. batch_start ()
262 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
263 for av
in address_virtual_list
:
264 av_attrs
= av
. split ()
265 if len ( av_attrs
) < 2 :
266 self
. log_error ( " %s : incorrect address-virtual attrs ' %s '"
267 %( ifaceobj
. name
, av
), ifaceobj
,
272 # Delete the macvlan device on this device
273 macvlan_ifacename
= ' %s%d ' %( macvlan_prefix
, av_idx
)
274 self
. ipcmd
. link_delete ( os
. path
. basename ( macvlan_ifacename
))
275 if av_attrs
[ 0 ] != 'None' :
276 hwaddress
. append ( av_attrs
[ 0 ])
278 self
. ipcmd
. batch_commit ()
279 self
._ remove
_ addresses
_ from
_ bridge
( ifaceobj
, hwaddress
)
281 def check_mac_address ( self
, ifaceobj
, mac
):
286 if int ( mac
. split ( ":" )[ 0 ], 16 ) & 1 :
287 self
. logger
. error ( " %s : Multicast bit is set in the virtual mac address ' %s '" %( ifaceobj
. name
, mac
))
293 def _up ( self
, ifaceobj
):
294 address_virtual_list
= ifaceobj
. get_attr_value ( 'address-virtual' )
295 if not address_virtual_list
:
296 # XXX: address virtual is not present. In which case,
297 # delete stale macvlan devices.
298 self
._ remove
_ address
_ config
( ifaceobj
, address_virtual_list
)
301 if ( ifaceobj
. upperifaces
and
302 not ifaceobj
. link_privflags
& ifaceLinkPrivFlags
. VRF_SLAVE
):
303 self
. log_error ( ' %s : invalid placement of address-virtual lines (must be configured under an interface with no upper interfaces or parent interfaces)'
304 % ( ifaceobj
. name
), ifaceobj
)
307 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
309 self
._ apply
_ address
_ config
( ifaceobj
, address_virtual_list
)
311 def _down ( self
, ifaceobj
):
313 self
._ remove
_ address
_ config
( ifaceobj
,
314 ifaceobj
. get_attr_value ( 'address-virtual' ))
316 self
. log_warn ( str ( e
))
318 def _query_check ( self
, ifaceobj
, ifaceobjcurr
):
319 address_virtual_list
= ifaceobj
. get_attr_value ( 'address-virtual' )
320 if not address_virtual_list
:
322 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
325 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
326 for address_virtual
in address_virtual_list
:
327 av_attrs
= address_virtual
. split ()
328 if len ( av_attrs
) < 2 :
329 self
. logger
. warn ( " %s : incorrect address-virtual attrs ' %s '"
330 %( ifaceobj
. name
, address_virtual
))
334 # Check if the macvlan device on this interface
335 macvlan_ifacename
= ' %s%d ' %( macvlan_prefix
, av_idx
)
336 if not self
. ipcmd
. link_exists ( macvlan_ifacename
):
337 ifaceobjcurr
. update_config_with_status ( 'address-virtual' ,
341 # Check mac and ip address
342 rhwaddress
= self
. ipcmd
. link_get_hwaddress ( macvlan_ifacename
)
343 raddrs
= self
. ipcmd
. addr_get ( macvlan_ifacename
)
344 if not raddrs
or not rhwaddress
:
345 ifaceobjcurr
. update_config_with_status ( 'address-virtual' , '' , 1 )
348 raddrs
= raddrs
. keys ()
350 av_attrs
[ 0 ] = ':' . join ([ i
if len ( i
) == 2 else '0 %s ' % i
351 for i
in av_attrs
[ 0 ]. split ( ':' )])
353 self
. logger
. info ( ' %s : %s : invalid value for address-virtual ( %s )'
357 if ( rhwaddress
== av_attrs
[ 0 ] and raddrs
== av_attrs
[ 1 :] and
358 self
._ check
_ addresses
_ in
_ bridge
( ifaceobj
, av_attrs
[ 0 ])):
359 ifaceobjcurr
. update_config_with_status ( 'address-virtual' ,
362 raddress_virtual
= ' %s %s ' %( rhwaddress
, ' ' . join ( raddrs
))
363 ifaceobjcurr
. update_config_with_status ( 'address-virtual' ,
368 def _query_running ( self
, ifaceobjrunning
):
369 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobjrunning
)
370 address_virtuals
= glob
. glob ( "/sys/class/net/ %s *" % macvlan_prefix
)
371 for av
in address_virtuals
:
372 macvlan_ifacename
= os
. path
. basename ( av
)
373 rhwaddress
= self
. ipcmd
. link_get_hwaddress ( macvlan_ifacename
)
374 raddress
= self
. ipcmd
. addr_get ( macvlan_ifacename
)
376 self
. logger
. warn ( ' %s : no running addresses'
377 %if aceobjrunning
. name
)
379 ifaceobjrunning
. update_config ( 'address-virtual' ,
380 ' %s %s ' %( rhwaddress
, '' . join ( raddress
)))
383 _run_ops
= { 'up' : _up
,
385 'query-checkcurr' : _query_check
,
386 'query-running' : _query_running
}
389 """ returns list of ops supported by this module """
390 return self
._ run
_ ops
. keys ()
392 def _init_command_handlers ( self
):
394 self
. ipcmd
= iproute2 ()
396 def run ( self
, ifaceobj
, operation
, query_ifaceobj
= None , ** extra_args
):
397 """ run vlan configuration on the interface object passed as argument
400 **ifaceobj** (object): iface object
402 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
405 **query_ifaceobj** (object): query check ifaceobject. This is only
406 valid when op is 'query-checkcurr'. It is an object same as
407 ifaceobj, but contains running attribute values and its config
408 status. The modules can use it to return queried running state
409 of interfaces. status is success if the running state is same
410 as user required state in ifaceobj. error otherwise.
412 if ifaceobj
. type == ifaceType
. BRIDGE_VLAN
:
414 op_handler
= self
._ run
_ ops
. get ( operation
)
417 self
._ init
_ command
_ handlers
()
418 if operation
== 'query-checkcurr' :
419 op_handler ( self
, ifaceobj
, query_ifaceobj
)
421 op_handler ( self
, ifaceobj
)