]>
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-ipaddr/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
)
205 if ( ifaceobj
. link_privflags
& ifaceLinkPrivFlags
. VRF_SLAVE
):
206 self
._ handle
_ vrf
_ slaves
( macvlan_ifacename
, ifaceobj
)
208 # Disable IPv6 duplicate address detection on VRR interfaces
209 for key
, sysval
in { 'accept_dad' : '0' , 'dad_transmits' : '0' }. iteritems ():
210 syskey
= 'net.ipv6.conf. %s . %s ' % ( macvlan_ifacename
, key
)
211 if self
. sysctl_get ( syskey
) != sysval
:
212 self
. sysctl_set ( syskey
, sysval
)
215 self
. ipcmd
. batch_commit ()
217 # check the statemanager for old configs.
218 # We need to remove only the previously configured FDB entries
219 oldmacs
= self
._ get
_ macs
_ from
_ old
_ config
( ifaceobj
)
220 # get a list of fdbs in old that are not in new config meaning they should
221 # be removed since they are gone from the config
222 removed_macs
= [ mac
for mac
in oldmacs
if mac
. lower () not in hwaddress
]
223 self
._ remove
_ addresses
_ from
_ bridge
( ifaceobj
, removed_macs
)
224 # if ifaceobj is a bridge and bridge is a vlan aware bridge
225 # add the vid to the bridge
226 self
._ add
_ addresses
_ to
_ bridge
( ifaceobj
, hwaddress
)
228 def _remove_running_address_config ( self
, ifaceobj
):
229 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
232 self
. ipcmd
. batch_start ()
233 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
234 for macvlan_ifacename
in glob
. glob ( "/sys/class/net/ %s *" % macvlan_prefix
):
235 macvlan_ifacename
= os
. path
. basename ( macvlan_ifacename
)
236 if not self
. ipcmd
. link_exists ( macvlan_ifacename
):
238 hwaddress
. append ( self
. ipcmd
. link_get_hwaddress ( macvlan_ifacename
))
239 self
. ipcmd
. link_delete ( os
. path
. basename ( macvlan_ifacename
))
240 # XXX: Also delete any fdb addresses. This requires, checking mac address
241 # on individual macvlan interfaces and deleting the vlan from that.
242 self
. ipcmd
. batch_commit ()
244 self
._ remove
_ addresses
_ from
_ bridge
( ifaceobj
, hwaddress
)
246 def _remove_address_config ( self
, ifaceobj
, address_virtual_list
= None ):
247 if not address_virtual_list
:
248 self
._ remove
_ running
_ address
_ config
( ifaceobj
)
251 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
254 self
. ipcmd
. batch_start ()
256 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
257 for av
in address_virtual_list
:
258 av_attrs
= av
. split ()
259 if len ( av_attrs
) < 2 :
260 self
. log_error ( " %s : incorrect address-virtual attrs ' %s '"
261 %( ifaceobj
. name
, av
), ifaceobj
,
266 # Delete the macvlan device on this device
267 macvlan_ifacename
= ' %s%d ' %( macvlan_prefix
, av_idx
)
268 self
. ipcmd
. link_delete ( os
. path
. basename ( macvlan_ifacename
))
269 if av_attrs
[ 0 ] != 'None' :
270 hwaddress
. append ( av_attrs
[ 0 ])
272 self
. ipcmd
. batch_commit ()
273 self
._ remove
_ addresses
_ from
_ bridge
( ifaceobj
, hwaddress
)
275 def check_mac_address ( self
, ifaceobj
, mac
):
280 if int ( mac
. split ( ":" )[ 0 ], 16 ) & 1 :
281 self
. logger
. error ( " %s : Multicast bit is set in the virtual mac address ' %s '" %( ifaceobj
. name
, mac
))
287 def _up ( self
, ifaceobj
):
288 address_virtual_list
= ifaceobj
. get_attr_value ( 'address-virtual' )
289 if not address_virtual_list
:
290 # XXX: address virtual is not present. In which case,
291 # delete stale macvlan devices.
292 self
._ remove
_ address
_ config
( ifaceobj
, address_virtual_list
)
295 if ( ifaceobj
. upperifaces
and
296 not ifaceobj
. link_privflags
& ifaceLinkPrivFlags
. VRF_SLAVE
):
297 self
. log_error ( ' %s : invalid placement of address-virtual lines (must be configured under an interface with no upper interfaces or parent interfaces)'
298 % ( ifaceobj
. name
), ifaceobj
)
301 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
303 self
._ apply
_ address
_ config
( ifaceobj
, address_virtual_list
)
305 def _down ( self
, ifaceobj
):
307 self
._ remove
_ address
_ config
( ifaceobj
,
308 ifaceobj
. get_attr_value ( 'address-virtual' ))
310 self
. log_warn ( str ( e
))
312 def _query_check ( self
, ifaceobj
, ifaceobjcurr
):
313 address_virtual_list
= ifaceobj
. get_attr_value ( 'address-virtual' )
314 if not address_virtual_list
:
316 if not self
. ipcmd
. link_exists ( ifaceobj
. name
):
319 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobj
)
320 for address_virtual
in address_virtual_list
:
321 av_attrs
= address_virtual
. split ()
322 if len ( av_attrs
) < 2 :
323 self
. logger
. warn ( " %s : incorrect address-virtual attrs ' %s '"
324 %( ifaceobj
. name
, address_virtual
))
328 # Check if the macvlan device on this interface
329 macvlan_ifacename
= ' %s%d ' %( macvlan_prefix
, av_idx
)
330 if not self
. ipcmd
. link_exists ( macvlan_ifacename
):
331 ifaceobjcurr
. update_config_with_status ( 'address-virtual' ,
335 # Check mac and ip address
336 rhwaddress
= self
. ipcmd
. link_get_hwaddress ( macvlan_ifacename
)
337 raddrs
= self
. ipcmd
. addr_get ( macvlan_ifacename
)
338 if not raddrs
or not rhwaddress
:
339 ifaceobjcurr
. update_config_with_status ( 'address-virtual' , '' , 1 )
342 raddrs
= raddrs
. keys ()
344 av_attrs
[ 0 ] = ':' . join ([ i
if len ( i
) == 2 else '0 %s ' % i
345 for i
in av_attrs
[ 0 ]. split ( ':' )])
347 self
. logger
. info ( ' %s : %s : invalid value for address-virtual ( %s )'
351 if ( rhwaddress
== av_attrs
[ 0 ] and raddrs
== av_attrs
[ 1 :] and
352 self
._ check
_ addresses
_ in
_ bridge
( ifaceobj
, av_attrs
[ 0 ])):
353 ifaceobjcurr
. update_config_with_status ( 'address-virtual' ,
356 raddress_virtual
= ' %s %s ' %( rhwaddress
, ' ' . join ( raddrs
))
357 ifaceobjcurr
. update_config_with_status ( 'address-virtual' ,
362 def _query_running ( self
, ifaceobjrunning
):
363 macvlan_prefix
= self
._ get
_ macvlan
_ prefix
( ifaceobjrunning
)
364 address_virtuals
= glob
. glob ( "/sys/class/net/ %s *" % macvlan_prefix
)
365 for av
in address_virtuals
:
366 macvlan_ifacename
= os
. path
. basename ( av
)
367 rhwaddress
= self
. ipcmd
. link_get_hwaddress ( macvlan_ifacename
)
368 raddress
= self
. ipcmd
. addr_get ( macvlan_ifacename
)
370 self
. logger
. warn ( ' %s : no running addresses'
371 %if aceobjrunning
. name
)
373 ifaceobjrunning
. update_config ( 'address-virtual' ,
374 ' %s %s ' %( rhwaddress
, '' . join ( raddress
)))
377 _run_ops
= { 'up' : _up
,
379 'query-checkcurr' : _query_check
,
380 'query-running' : _query_running
}
383 """ returns list of ops supported by this module """
384 return self
._ run
_ ops
. keys ()
386 def _init_command_handlers ( self
):
388 self
. ipcmd
= iproute2 ()
390 def run ( self
, ifaceobj
, operation
, query_ifaceobj
= None , ** extra_args
):
391 """ run vlan configuration on the interface object passed as argument
394 **ifaceobj** (object): iface object
396 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
399 **query_ifaceobj** (object): query check ifaceobject. This is only
400 valid when op is 'query-checkcurr'. It is an object same as
401 ifaceobj, but contains running attribute values and its config
402 status. The modules can use it to return queried running state
403 of interfaces. status is success if the running state is same
404 as user required state in ifaceobj. error otherwise.
406 if ifaceobj
. type == ifaceType
. BRIDGE_VLAN
:
408 op_handler
= self
._ run
_ ops
. get ( operation
)
411 self
._ init
_ command
_ handlers
()
412 if operation
== 'query-checkcurr' :
413 op_handler ( self
, ifaceobj
, query_ifaceobj
)
415 op_handler ( self
, ifaceobj
)