]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/vrf.py
22365d6d67c5eafe74994fa948fab1be9506126a
[mirror_ifupdown2.git] / ifupdown2 / addons / vrf.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import re
8 import os
9 import fcntl
10 import atexit
11 import signal
12
13 from sets import Set
14
15 try:
16 import ifupdown2.ifupdown.policymanager as policymanager
17 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
18 from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
19
20 from ifupdown2.ifupdown.iface import *
21 from ifupdown2.ifupdown.utils import utils
22 from ifupdown2.ifupdown.netlink import netlink
23
24 from ifupdown2.ifupdownaddons.dhclient import dhclient
25 from ifupdown2.ifupdownaddons.utilsbase import *
26 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
27 from ifupdown2.ifupdownaddons.modulebase import moduleBase
28 except ImportError:
29 import ifupdown.policymanager as policymanager
30 import ifupdown.ifupdownflags as ifupdownflags
31 from ifupdown.statemanager import statemanager_api as statemanager
32
33 from ifupdown.iface import *
34 from ifupdown.utils import utils
35 from ifupdown.netlink import netlink
36
37 from ifupdownaddons.dhclient import dhclient
38 from ifupdownaddons.utilsbase import *
39 from ifupdownaddons.LinkUtils import LinkUtils
40 from ifupdownaddons.modulebase import moduleBase
41
42
43 class vrfPrivFlags:
44 PROCESSED = 0x1
45
46 class vrf(moduleBase):
47 """ ifupdown2 addon module to configure vrfs """
48 _modinfo = { 'mhelp' : 'vrf configuration module',
49 'attrs' : {
50 'vrf-table':
51 {'help' : 'vrf device routing table id. key to ' +
52 'creating a vrf device. ' +
53 'Table id is either \'auto\' or '+
54 '\'valid routing table id\'',
55 'validvals': ['auto', '<number>'],
56 'example': ['vrf-table auto', 'vrf-table 1001']},
57 'vrf':
58 {'help' : 'vrf the interface is part of.',
59 'validvals': ['<text>'],
60 'example': ['vrf blue']}}}
61
62 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
63 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
64 '# It contains the vrf name to table mapping.\n' + \
65 '# Reserved table range %s %s\n'
66 VRF_TABLE_START = 1001
67 VRF_TABLE_END = 5000
68
69 system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
70 '253' : 'default', '0' : 'unspec'}
71
72 def __init__(self, *args, **kargs):
73 moduleBase.__init__(self, *args, **kargs)
74 self.ipcmd = None
75 self.bondcmd = None
76 self.dhclientcmd = None
77 self.name = self.__class__.__name__
78 self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
79
80 self.user_reserved_vrf_table = []
81
82 if (ifupdownflags.flags.PERFMODE and
83 not (self.vrf_mgmt_devname and os.path.exists('/sys/class/net/%s'
84 %self.vrf_mgmt_devname))):
85 # if perf mode is set (PERFMODE is set at boot), and this is the first
86 # time we are calling ifup at boot (check for mgmt vrf existance at
87 # boot, make sure this is really the first invocation at boot.
88 # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf
89 # and the second time with all auto interfaces). We want to delete
90 # the map file only the first time. This is to avoid accidently
91 # deleting map file with a valid mgmt vrf entry
92 if os.path.exists(self.iproute2_vrf_filename):
93 try:
94 self.logger.info('vrf: removing file %s'
95 %self.iproute2_vrf_filename)
96 os.remove(self.iproute2_vrf_filename)
97 except Exception, e:
98 self.logger.debug('vrf: removing file failed (%s)'
99 %str(e))
100 try:
101 ip_rules = utils.exec_command('%s rule show'
102 %utils.ip_cmd).splitlines()
103 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
104 except Exception, e:
105 self.ip_rule_cache = []
106 self.logger.warn('vrf: cache v4: %s' % str(e))
107
108 try:
109 ip_rules = utils.exec_command('%s -6 rule show'
110 %utils.ip_cmd).splitlines()
111 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
112 except Exception, e:
113 self.ip6_rule_cache = []
114 self.logger.warn('vrf: cache v6: %s' % str(e))
115
116 #self.logger.debug("vrf: ip rule cache")
117 #self.logger.info(self.ip_rule_cache)
118
119 #self.logger.info("vrf: ip -6 rule cache")
120 #self.logger.info(self.ip6_rule_cache)
121
122 self.l3mdev_checked = False
123 self.l3mdev4_rule = False
124 if self._l3mdev_rule(self.ip_rule_cache):
125 self.l3mdev4_rule = True
126 self.l3mdev_checked = True
127 self.l3mdev6_rule = False
128 if self._l3mdev_rule(self.ip6_rule_cache):
129 self.l3mdev6_rule = True
130 self.l3mdev_checked = True
131 self._iproute2_vrf_map_initialized = False
132 self.iproute2_vrf_map = {}
133 self.iproute2_vrf_map_fd = None
134 self.iproute2_vrf_map_sync_to_disk = False
135
136 self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
137 if not self.vrf_table_id_start:
138 self.vrf_table_id_start = self.VRF_TABLE_START
139 self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end')
140 if not self.vrf_table_id_end:
141 self.vrf_table_id_end = self.VRF_TABLE_END
142
143 self._modinfo['attrs']['vrf-table']['validrange'] = [
144 str(self.vrf_table_id_start),
145 str(self.vrf_table_id_end)
146 ]
147
148 self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count')
149
150 self.vrf_fix_local_table = True
151 self.vrf_count = 0
152 self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper')
153 self.vrf_close_socks_on_down = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-close-socks-on-down')
154 self.warn_on_vrf_map_write_err = True
155
156 def _check_vrf_table_id(self, ifaceobj):
157 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
158 if not vrf_table:
159 return False
160 if (vrf_table != 'auto' and
161 (int(vrf_table) < self.vrf_table_id_start or
162 int(vrf_table) > self.vrf_table_id_end)):
163 self.logger.error('%s: vrf table id %s out of reserved range [%d,%d]'
164 %(ifaceobj.name,
165 vrf_table,
166 self.vrf_table_id_start,
167 self.vrf_table_id_end))
168 return False
169 return True
170
171 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
172 if ifaceobj.link_kind & ifaceLinkKind.VRF:
173 try:
174 check_vrf_table_id = self._check_vrf_table_id(ifaceobj)
175 check_vrf_sys_names = self._check_vrf_system_reserved_names(ifaceobj)
176 return check_vrf_table_id and check_vrf_sys_names
177 except Exception as e:
178 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
179 return False
180 return True
181
182 def _check_vrf_system_reserved_names(self, ifaceobj):
183 system_reserved_names = self.system_reserved_rt_tables.values()
184 if ifaceobj.name in system_reserved_names:
185 self.log_error('cannot use system reserved %s vrf names'
186 % (str(system_reserved_names)), ifaceobj)
187 return False
188 return True
189
190 def _iproute2_vrf_map_initialize(self, writetodisk=True):
191 if self._iproute2_vrf_map_initialized:
192 return
193
194 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
195 self.iproute2_vrf_map = {}
196 iproute2_vrf_map_force_rewrite = False
197 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
198 if os.path.exists(self.iproute2_vrf_filename):
199 with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
200 lines = vrf_map_fd.readlines()
201 for l in lines:
202 l = l.strip()
203 if l[0] == '#':
204 continue
205 try:
206 (table, vrf_name) = l.strip().split()
207 if self.iproute2_vrf_map.get(int(table)):
208 # looks like the existing file has
209 # duplicate entries, force rewrite of the
210 # file
211 iproute2_vrf_map_force_rewrite = True
212 continue
213 self.iproute2_vrf_map[int(table)] = vrf_name
214 except Exception, e:
215 self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e)))
216 pass
217
218 vrfs = self.ipcmd.link_get_vrfs()
219 running_vrf_map = {}
220 if vrfs:
221 for v, lattrs in vrfs.iteritems():
222 table = lattrs.get('table', None)
223 if table:
224 running_vrf_map[int(table)] = v
225
226 if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
227 self.iproute2_vrf_map = running_vrf_map
228 iproute2_vrf_map_force_rewrite = True
229
230 self.iproute2_vrf_map_fd = None
231 if writetodisk:
232 if iproute2_vrf_map_force_rewrite:
233 # reopen the file and rewrite the map
234 self._iproute2_vrf_map_open(True, False)
235 else:
236 self._iproute2_vrf_map_open(False, True)
237
238 self.iproute2_vrf_map_sync_to_disk = False
239 atexit.register(self._iproute2_vrf_map_sync_to_disk)
240
241 self.logger.info("vrf: dumping iproute2_vrf_map")
242 self.logger.info(self.iproute2_vrf_map)
243
244 last_used_vrf_table = None
245 for t in range(self.vrf_table_id_start,
246 self.vrf_table_id_end):
247 if not self.iproute2_vrf_map.get(t):
248 break
249 last_used_vrf_table = t
250 self.last_used_vrf_table = last_used_vrf_table
251 self._iproute2_vrf_map_initialized = True
252 self.vrf_count = len(self.iproute2_vrf_map)
253
254 def _iproute2_map_warn(self, errstr):
255 if self.warn_on_vrf_map_write_err:
256 if not os.path.exists('/etc/iproute2/rt_tables.d/'):
257 self.logger.info('unable to save iproute2 vrf to table ' +
258 'map (%s)\n' %errstr)
259 self.logger.info('cannot find /etc/iproute2/rt_tables.d.' +
260 ' pls check if your iproute2 version' +
261 ' supports rt_tables.d')
262 else:
263 self.logger.warn('unable to open iproute2 vrf to table ' +
264 'map (%s)\n' %errstr)
265 self.warn_on_vrf_map_write_err = False
266
267 def _iproute2_vrf_map_sync_to_disk(self):
268 if (ifupdownflags.flags.DRYRUN or
269 not self.iproute2_vrf_map_sync_to_disk):
270 return
271 self.logger.info('vrf: syncing table map to %s'
272 %self.iproute2_vrf_filename)
273 try:
274 with open(self.iproute2_vrf_filename, 'w') as f:
275 f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
276 self.vrf_table_id_end))
277 for t, v in self.iproute2_vrf_map.iteritems():
278 f.write('%s %s\n' %(t, v))
279 f.flush()
280 except Exception, e:
281 self._iproute2_map_warn(str(e))
282 pass
283
284 def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False):
285 self.logger.info('vrf: syncing table map to %s'
286 %self.iproute2_vrf_filename)
287 if ifupdownflags.flags.DRYRUN:
288 return
289 fmode = 'a+' if append else 'w'
290 try:
291 self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
292 '%s' %fmode)
293 fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
294 except Exception, e:
295 self._iproute2_map_warn(str(e))
296 return
297
298 if not append:
299 # write file header
300 self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
301 %(self.vrf_table_id_start,
302 self.vrf_table_id_end))
303 for t, v in self.iproute2_vrf_map.iteritems():
304 self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
305 self.iproute2_vrf_map_fd.flush()
306
307 def _is_vrf(self, ifaceobj):
308 if ifaceobj.get_attr_value_first('vrf-table'):
309 return True
310 return False
311
312 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
313 """ Returns list of interfaces dependent on ifaceobj """
314
315 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
316 if vrf_table:
317 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
318 ifaceobj.link_kind |= ifaceLinkKind.VRF
319 ifaceobj.role |= ifaceRole.MASTER
320
321 if vrf_table != 'auto':
322 # if the user didn't specify auto we need to store the desired
323 # vrf tables ids, in case the configuration has both auto and
324 # hardcoded vrf-table ids. We need to create them all without
325 # collisions.
326 self.user_reserved_vrf_table.append(int(vrf_table))
327
328 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
329 if not vrf_iface_name:
330 return None
331 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
332 ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
333
334 return [vrf_iface_name]
335
336 def get_upper_ifacenames_running(self, ifaceobj):
337 return None
338
339 def _get_iproute2_vrf_table(self, vrf_dev_name):
340 for t, v in self.iproute2_vrf_map.iteritems():
341 if v == vrf_dev_name:
342 return str(t)
343 return None
344
345 def _get_avail_vrf_table_id(self):
346 if self.last_used_vrf_table == None:
347 table_id_start = self.vrf_table_id_start
348 else:
349 table_id_start = self.last_used_vrf_table + 1
350 for t in range(table_id_start, self.vrf_table_id_end):
351 if (not self.iproute2_vrf_map.get(t)
352 and t not in self.user_reserved_vrf_table):
353 self.last_used_vrf_table = t
354 return str(t)
355 return None
356
357 def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id):
358 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
359 if old_vrf_name and old_vrf_name != vrfifaceobj.name:
360 self.log_error('table id %s already assigned to vrf dev %s'
361 %(table_id, old_vrf_name), vrfifaceobj)
362
363 def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id):
364 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
365 if not old_vrf_name:
366 self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
367 if self.iproute2_vrf_map_fd:
368 self.iproute2_vrf_map_fd.write('%s %s\n'
369 %(table_id, vrfifaceobj.name))
370 self.iproute2_vrf_map_fd.flush()
371 self.vrf_count += 1
372 return
373 if old_vrf_name != vrfifaceobj.name:
374 self.log_error('table id %d already assigned to vrf dev %s'
375 %(table_id, old_vrf_name))
376
377 def _iproute2_vrf_table_entry_del(self, table_id):
378 try:
379 # with any del of vrf map, we need to force sync to disk
380 self.iproute2_vrf_map_sync_to_disk = True
381 del self.iproute2_vrf_map[int(table_id)]
382 except Exception, e:
383 self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)'
384 %(table_id, str(e)))
385 pass
386
387 def _is_vrf_dev(self, ifacename):
388 # Look at iproute2 map for now.
389 # If it was a master we knew about,
390 # it is definately there
391 if ifacename in self.iproute2_vrf_map.values():
392 return True
393 return False
394
395 def _is_dhcp_slave(self, ifaceobj):
396 if (not ifaceobj.addr_method or
397 (ifaceobj.addr_method != 'dhcp' and
398 ifaceobj.addr_method != 'dhcp6')):
399 return False
400 return True
401
402 def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj, vrf_master_objs, ifaceobj_getfunc=None):
403 """ If we have a vrf slave that has dhcp configured, bring up the
404 vrf master now. This is needed because vrf has special handling
405 in dhclient hook which requires the vrf master to be present """
406 vrf_master = None
407 if len(ifaceobj.upperifaces) > 1 and ifaceobj_getfunc:
408 for upper_iface in ifaceobj.upperifaces:
409 upper_ifaceobjs = ifaceobj_getfunc(upper_iface)
410
411 if upper_ifaceobjs:
412 for upper_obj in upper_ifaceobjs:
413 if upper_obj.link_kind & ifaceLinkKind.VRF:
414 vrf_master = upper_obj.name
415 break
416 elif ifaceobj.upperifaces:
417 vrf_master = ifaceobj.upperifaces[0]
418 if not vrf_master:
419 self.logger.warn('%s: vrf master not found' %ifacename)
420 return
421 if os.path.exists('/sys/class/net/%s' %vrf_master):
422 self.logger.info('%s: vrf master %s exists returning'
423 %(ifacename, vrf_master))
424 return
425 self.logger.info('%s: bringing up vrf master %s'
426 %(ifacename, vrf_master))
427 for mobj in vrf_master_objs:
428 vrf_table = mobj.get_attr_value_first('vrf-table')
429 if vrf_table:
430 if vrf_table == 'auto':
431 vrf_table = self._get_avail_vrf_table_id()
432 if not vrf_table:
433 self.log_error('%s: unable to get an auto table id'
434 %mobj.name, ifaceobj)
435 self.logger.info('%s: table id auto: selected table id %s\n'
436 %(mobj.name, vrf_table))
437 try:
438 self._up_vrf_dev(mobj, vrf_table, False)
439 except Exception:
440 raise
441 break
442 self._handle_existing_connections(ifaceobj, vrfname)
443 self.ipcmd.link_set(ifacename, 'master', vrfname)
444 self.enable_ipv6(ifacename)
445 return
446
447 def enable_ipv6(self, ifname):
448 """
449 Only enable ipv6 on former bridge port
450 - workaround for intf moved from bridge port to VRF slave
451 """
452 try:
453 self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
454 except Exception, e:
455 self.logger.info(str(e))
456
457 def _down_dhcp_slave(self, ifaceobj, vrfname):
458 try:
459 dhclient_cmd_prefix = None
460 if (vrfname and self.vrf_exec_cmd_prefix and
461 self.ipcmd.link_exists(vrfname)):
462 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
463 vrfname)
464 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
465 except:
466 # ignore any dhclient release errors
467 pass
468
469 def _handle_existing_connections(self, ifaceobj, vrfname):
470 if not ifaceobj or ifupdownflags.flags.PERFMODE:
471 return
472 if (self.vrf_mgmt_devname and
473 self.vrf_mgmt_devname == vrfname):
474 self._kill_ssh_connections(ifaceobj.name)
475 if self._is_dhcp_slave(ifaceobj):
476 self._down_dhcp_slave(ifaceobj, vrfname)
477
478 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
479 ifaceobj_getfunc=None, vrf_exists=False):
480 try:
481 master_exists = True
482 if vrf_exists or self.ipcmd.link_exists(vrfname):
483 uppers = self.ipcmd.link_get_uppers(ifacename)
484 if not uppers or vrfname not in uppers:
485 self._handle_existing_connections(ifaceobj, vrfname)
486 self.ipcmd.link_set(ifacename, 'master', vrfname)
487 self.enable_ipv6(ifacename)
488 elif ifaceobj:
489 vrf_master_objs = ifaceobj_getfunc(vrfname)
490 if not vrf_master_objs:
491 # this is the case where vrf is assigned to an interface
492 # but user has not provided a vrf interface.
493 # people expect you to warn them but go ahead with the
494 # rest of the config on that interface
495 netlink.link_set_updown(ifacename, "up")
496 self.log_error('vrf master ifaceobj %s not found'
497 %vrfname)
498 return
499 if (ifupdownflags.flags.ALL or
500 ifupdownflags.flags.WITH_DEPENDS or
501 (ifupdownflags.flags.CLASS and
502 ifaceobj.classes and vrf_master_objs[0].classes and
503 Set(ifaceobj.classes).intersection(vrf_master_objs[0].classes))):
504 self._up_vrf_slave_without_master(ifacename, vrfname,
505 ifaceobj,
506 vrf_master_objs,
507 ifaceobj_getfunc)
508 else:
509 master_exists = False
510 else:
511 master_exists = False
512 if master_exists:
513 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
514 netlink.link_set_updown(ifacename, "up")
515 else:
516 self.log_error('vrf %s not around, skipping vrf config'
517 %(vrfname), ifaceobj)
518 except Exception, e:
519 self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
520
521 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
522 pref = 200
523 ip_rule_out_format = '%s: from all %s %s lookup %s'
524 ip_rule_cmd = '%s %s rule del pref %s %s %s table %s'
525
526 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
527 if rule in self.ip_rule_cache:
528 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
529 '', pref, 'oif', vrf_dev_name,
530 vrf_dev_name)
531 utils.exec_command(rule_cmd)
532
533 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
534 if rule in self.ip_rule_cache:
535 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
536 '', pref, 'iif', vrf_dev_name,
537 vrf_dev_name)
538 utils.exec_command(rule_cmd)
539
540 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
541 if rule in self.ip6_rule_cache:
542 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
543 '-6', pref, 'oif', vrf_dev_name,
544 vrf_dev_name)
545 utils.exec_command(rule_cmd)
546
547 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
548 if rule in self.ip6_rule_cache:
549 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
550 '-6', pref, 'iif', vrf_dev_name,
551 vrf_dev_name)
552 utils.exec_command(rule_cmd)
553
554 def _l3mdev_rule(self, ip_rules):
555 for rule in ip_rules:
556 if not re.search(r"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
557 rule):
558 continue
559 return True
560 return False
561
562 def _rule_cache_fill(self):
563 ip_rules = utils.exec_command('%s rule show'
564 %utils.ip_cmd).splitlines()
565 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
566 self.l3mdev4_rule = self._l3mdev_rule(self.ip_rule_cache)
567 ip_rules = utils.exec_command('%s -6 rule show'
568 %utils.ip_cmd).splitlines()
569 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
570 self.l3mdev6_rule = self._l3mdev_rule(self.ip6_rule_cache)
571
572 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
573 pref = 200
574 ip_rule_out_format = '%s: from all %s %s lookup %s'
575 ip_rule_cmd = '%s %s rule add pref %s %s %s table %s'
576 if self.vrf_fix_local_table:
577 self.vrf_fix_local_table = False
578 rule = '0: from all lookup local'
579 if rule in self.ip_rule_cache:
580 try:
581 utils.exec_command('%s rule del pref 0'
582 %utils.ip_cmd)
583 utils.exec_command('%s rule add pref 32765 table local'
584 %utils.ip_cmd)
585 except Exception, e:
586 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
587 pass
588 if rule in self.ip6_rule_cache:
589 try:
590 utils.exec_command('%s -6 rule del pref 0'
591 %utils.ip_cmd)
592 utils.exec_command('%s -6 rule add pref 32765 table local'
593 %utils.ip_cmd)
594 except Exception, e:
595 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
596 pass
597
598 if not self.l3mdev_checked:
599 self._rule_cache_fill()
600 self.l3mdev_checked = True
601 #Example ip rule
602 #200: from all oif blue lookup blue
603 #200: from all iif blue lookup blue
604
605 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
606 if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
607 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
608 '', pref, 'oif', vrf_dev_name,
609 vrf_dev_name)
610 utils.exec_command(rule_cmd)
611
612 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
613 if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
614 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
615 '', pref, 'iif', vrf_dev_name,
616 vrf_dev_name)
617 utils.exec_command(rule_cmd)
618
619 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
620 if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
621 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
622 '-6', pref, 'oif', vrf_dev_name,
623 vrf_dev_name)
624 utils.exec_command(rule_cmd)
625
626 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
627 if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
628 rule_cmd = ip_rule_cmd %(utils.ip_cmd,
629 '-6', pref, 'iif', vrf_dev_name,
630 vrf_dev_name)
631 utils.exec_command(rule_cmd)
632
633 def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves,
634 vrfslave):
635 # Address virtual lines on a vrf slave will create
636 # macvlan devices on the vrf slave and enslave them
637 # to the vrf master. This function checks if the
638 # vrf slave is such a macvlan interface.
639 # XXX: additional possible checks that can be done here
640 # are:
641 # - check if it is also a macvlan device of the
642 # format <vrf_slave>-v<int> created by the
643 # address virtual module
644 vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
645 if vrfslave_lowers:
646 if vrfslave_lowers[0] in config_vrfslaves:
647 return True
648 return False
649
650 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
651 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
652 config_slaves = ifaceobj.lowerifaces
653 if not config_slaves and not running_slaves:
654 return
655
656 if not config_slaves: config_slaves = []
657 if not running_slaves: running_slaves = []
658 add_slaves = set(config_slaves).difference(set(running_slaves))
659 del_slaves = set(running_slaves).difference(set(config_slaves))
660 if add_slaves:
661 for s in add_slaves:
662 try:
663 if not self.ipcmd.link_exists(s):
664 continue
665 sobj = None
666 if ifaceobj_getfunc:
667 sobj = ifaceobj_getfunc(s)
668 self._up_vrf_slave(s, ifaceobj.name,
669 sobj[0] if sobj else None,
670 ifaceobj_getfunc, True)
671 except Exception, e:
672 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
673
674 if del_slaves:
675 for s in del_slaves:
676 try:
677 if self._is_address_virtual_slaves(ifaceobj,
678 config_slaves, s):
679 continue
680 sobj = None
681 if ifaceobj_getfunc:
682 sobj = ifaceobj_getfunc(s)
683 self._down_vrf_slave(s, sobj[0] if sobj else None,
684 ifaceobj.name)
685 except Exception, e:
686 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
687
688 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
689 for s in config_slaves:
690 try:
691 for slave_ifaceobj in ifaceobj_getfunc(s) or []:
692 if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
693 raise Exception("link-down yes: keeping VRF slave down")
694 netlink.link_set_updown(s, "up")
695 except Exception, e:
696 self.logger.debug("%s: %s" % (s, str(e)))
697 pass
698
699 def _set_vrf_dev_processed_flag(self, ifaceobj):
700 ifaceobj.module_flags[self.name] = \
701 ifaceobj.module_flags.setdefault(self.name, 0) | \
702 vrfPrivFlags.PROCESSED
703
704 def _check_vrf_dev_processed_flag(self, ifaceobj):
705 if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
706 return True
707 return False
708
709 def _create_vrf_dev(self, ifaceobj, vrf_table):
710 if not self.ipcmd.link_exists(ifaceobj.name):
711 self._check_vrf_system_reserved_names(ifaceobj)
712
713 if self.vrf_count == self.vrf_max_count:
714 self.log_error('max vrf count %d hit...not '
715 'creating vrf' % self.vrf_count, ifaceobj)
716 if vrf_table == 'auto':
717 vrf_table = self._get_avail_vrf_table_id()
718 if not vrf_table:
719 self.log_error('unable to get an auto table id', ifaceobj)
720 self.logger.info('%s: table id auto: selected table id %s\n'
721 %(ifaceobj.name, vrf_table))
722 else:
723 self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
724 if ifaceobj.name in self.system_reserved_rt_tables.keys():
725 self.log_error('cannot use system reserved %s table ids'
726 %(str(self.system_reserved_rt_tables.keys())),
727 ifaceobj)
728
729 if not vrf_table.isdigit():
730 self.log_error('vrf-table must be an integer or \'auto\'', ifaceobj)
731
732 # XXX: If we decide to not allow vrf id usages out of
733 # the reserved ifupdown range, then uncomment this code.
734 else:
735 if (int(vrf_table) < self.vrf_table_id_start or
736 int(vrf_table) > self.vrf_table_id_end):
737 self.log_error('vrf table id %s out of reserved range [%d,%d]'
738 %(vrf_table,
739 self.vrf_table_id_start,
740 self.vrf_table_id_end), ifaceobj)
741 try:
742 self.ipcmd.link_create(ifaceobj.name, 'vrf',
743 {'table' : '%s' %vrf_table})
744 except Exception, e:
745 self.log_error('create failed (%s)\n' % str(e), ifaceobj)
746 if vrf_table != 'auto':
747 self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
748 else:
749 if vrf_table == 'auto':
750 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
751 if not vrf_table and not ifupdownflags.flags.DRYRUN:
752 self.log_error('unable to get vrf table id', ifaceobj)
753
754 # if the device exists, check if table id is same
755 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
756 if vrfdev_attrs:
757 running_table = vrfdev_attrs.get('table', None)
758 if vrf_table != running_table:
759 self.log_error('cannot change vrf table id,running table id'
760 ' %s is different from config id %s'
761 % (running_table, vrf_table), ifaceobj)
762 return vrf_table
763
764 def _up_vrf_helper(self, ifaceobj, vrf_table):
765 mode = ""
766 if ifupdownflags.flags.PERFMODE:
767 mode = "boot"
768 if self.vrf_helper:
769 utils.exec_command('%s create %s %s %s' %
770 (self.vrf_helper,
771 ifaceobj.name,
772 vrf_table,
773 mode))
774
775 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
776 ifaceobj_getfunc=None):
777
778 # if vrf dev is already processed return. This can happen
779 # if we the slave was configured before.
780 # see self._up_vrf_slave_without_master
781 if self._check_vrf_dev_processed_flag(ifaceobj):
782 return True
783
784 try:
785 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
786 except Exception, e:
787 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
788
789 try:
790 self._add_vrf_rules(ifaceobj.name, vrf_table)
791 self._up_vrf_helper(ifaceobj, vrf_table)
792 if add_slaves:
793 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
794 self._set_vrf_dev_processed_flag(ifaceobj)
795
796 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
797 netlink.link_set_updown(ifaceobj.name, "up")
798 except Exception, e:
799 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
800
801 def _kill_ssh_connections(self, ifacename):
802 try:
803 runningaddrsdict = self.ipcmd.get_running_addrs(None, ifacename)
804 if not runningaddrsdict:
805 return
806 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
807 if not iplist:
808 return
809 proc=[]
810 #Example output:
811 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
812 #users:(("sshd",pid=2528,fd=3))
813 cmdl = [utils.ss_cmd, '-t', '-p']
814 for line in utils.exec_commandl(cmdl).splitlines():
815 citems = line.split()
816 addr = None
817 if '%' in citems[3]:
818 addr = citems[3].split('%')[0]
819 elif ':ssh' in citems[3]:
820 addr = citems[3].split(':')[0]
821 if not addr:
822 continue
823 if addr in iplist:
824 if len(citems) == 6:
825 proc.append(citems[5].split(',')[1].split('=')[1])
826
827 if not proc:
828 return
829 pid = None
830 # outpt of '/usr/bin/pstree -Aps <pid>':
831 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
832 # get the above output to following format
833 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
834 pstree = list(reversed(utils.exec_command('%s -Aps %s' %
835 (utils.pstree_cmd, os.getpid())).strip().split('---')))
836 for index, process in enumerate(pstree):
837 # check the parent of SSH process to make sure
838 # we don't kill SSH server or systemd process
839 if 'sshd' in process and 'sshd' in pstree[index + 1]:
840 pid = filter(lambda x: x.isdigit(), process)
841 break
842 self.logger.info("%s: killing active ssh sessions: %s"
843 %(ifacename, str(proc)))
844
845 if ifupdownflags.flags.DRYRUN:
846 return
847 for id in proc:
848 if id != pid:
849 try:
850 os.kill(int(id), signal.SIGINT)
851 except OSError as e:
852 continue
853
854 # Kill current SSH client
855 if pid in proc:
856 try:
857 forkret = os.fork()
858 except OSError, e:
859 self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
860 if (forkret == 0): # The first child.
861 try:
862 os.setsid()
863 self.logger.info("%s: ifreload continuing in the background" %ifacename)
864 except OSError, (err_no, err_message):
865 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
866 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
867 try:
868 self.logger.info("%s: killing our session: %s"
869 %(ifacename, str(proc)))
870 os.kill(int(pid), signal.SIGINT)
871 return
872 except OSError as e:
873 return
874 except Exception, e:
875 self.logger.info('%s: %s' %(ifacename, str(e)))
876
877 def _up(self, ifaceobj, ifaceobj_getfunc=None):
878 try:
879 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
880 if vrf_table:
881 self._iproute2_vrf_map_initialize()
882 # This is a vrf device
883 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
884 else:
885 vrf = ifaceobj.get_attr_value_first('vrf')
886 if vrf:
887 self._iproute2_vrf_map_initialize()
888 # This is a vrf slave
889 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
890 ifaceobj_getfunc)
891 elif not ifupdownflags.flags.PERFMODE:
892 # check if we were a slave before
893 master = self.ipcmd.link_get_master(ifaceobj.name)
894 if master:
895 self._iproute2_vrf_map_initialize()
896 if self._is_vrf_dev(master):
897 self._down_vrf_slave(ifaceobj.name, ifaceobj,
898 master)
899 except Exception, e:
900 self.log_error(str(e), ifaceobj)
901
902 def _down_vrf_helper(self, ifaceobj, vrf_table):
903 mode = ""
904 if ifupdownflags.flags.PERFMODE:
905 mode = "boot"
906 if self.vrf_helper:
907 utils.exec_command('%s delete %s %s %s' %
908 (self.vrf_helper,
909 ifaceobj.name,
910 vrf_table,
911 mode))
912
913 def _close_sockets(self, ifaceobj, ifindex):
914 if not self.vrf_close_socks_on_down:
915 return
916
917 try:
918 utils.exec_command('%s -aK \"dev == %s\"'
919 %(utils.ss_cmd, ifindex))
920 except Exception, e:
921 self.logger.info('%s: closing socks using ss'
922 ' failed (%s)\n' %(ifaceobj.name, str(e)))
923 pass
924
925 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
926
927 if not self.ipcmd.link_exists(ifaceobj.name):
928 return
929
930 if vrf_table == 'auto':
931 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
932
933 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
934 if running_slaves:
935 for s in running_slaves:
936 if ifaceobj_getfunc:
937 sobj = ifaceobj_getfunc(s)
938 try:
939 self._handle_existing_connections(sobj[0]
940 if sobj else None,
941 ifaceobj.name)
942 except Exception, e:
943 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
944 pass
945 try:
946 self.ipcmd.addr_flush(s)
947 netlink.link_set_updown(s, "down")
948 except Exception, e:
949 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
950 pass
951
952 try:
953 self._down_vrf_helper(ifaceobj, vrf_table)
954 except Exception, e:
955 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
956 pass
957
958 try:
959 self._del_vrf_rules(ifaceobj.name, vrf_table)
960 except Exception, e:
961 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
962 pass
963
964 ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
965
966 if ifindex:
967 try:
968 self.ipcmd.link_delete(ifaceobj.name)
969 except Exception, e:
970 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
971 pass
972
973 self._close_sockets(ifaceobj, ifindex)
974
975 try:
976 self._iproute2_vrf_table_entry_del(vrf_table)
977 except Exception, e:
978 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
979 pass
980
981
982 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
983 try:
984 self._handle_existing_connections(ifaceobj, vrfname)
985 self.ipcmd.link_set(ifacename, 'nomaster')
986 # Down this slave only if it is a slave ifupdown2 manages.
987 # we dont want to down slaves that maybe up'ed by
988 # somebody else. One such example is a macvlan device
989 # which ifupdown2 addressvirtual addon module auto creates
990 if ifaceobj:
991 netlink.link_set_updown(ifacename, "down")
992 except Exception, e:
993 self.logger.warn('%s: %s' %(ifacename, str(e)))
994
995 def _down(self, ifaceobj, ifaceobj_getfunc=None):
996 try:
997 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
998 if vrf_table:
999 self._iproute2_vrf_map_initialize()
1000 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
1001 else:
1002 vrf = ifaceobj.get_attr_value_first('vrf')
1003 if vrf:
1004 self._iproute2_vrf_map_initialize()
1005 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
1006 except Exception, e:
1007 self.log_warn(str(e))
1008
1009 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
1010 try:
1011 master = self.ipcmd.link_get_master(ifaceobj.name)
1012 if not master or master != vrf:
1013 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
1014 else:
1015 ifaceobjcurr.update_config_with_status('vrf', master, 0)
1016 except Exception, e:
1017 self.log_error(str(e), ifaceobjcurr)
1018
1019 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
1020 try:
1021 if not self.ipcmd.link_exists(ifaceobj.name):
1022 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
1023 return
1024 if vrf_table == 'auto':
1025 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
1026 else:
1027 config_table = vrf_table
1028 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
1029 if not vrfdev_attrs:
1030 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
1031 return
1032 running_table = vrfdev_attrs.get('table')
1033 if not running_table:
1034 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
1035 return
1036 if config_table != running_table:
1037 ifaceobjcurr.update_config_with_status('vrf-table',
1038 running_table, 1)
1039 else:
1040 ifaceobjcurr.update_config_with_status('vrf-table',
1041 running_table, 0)
1042 if not ifupdownflags.flags.WITHDEFAULTS:
1043 return
1044 if self.vrf_helper:
1045 try:
1046 utils.exec_command('%s verify %s %s'
1047 %(self.vrf_helper,
1048 ifaceobj.name, config_table))
1049 ifaceobjcurr.update_config_with_status('vrf-helper',
1050 '%s create %s %s'
1051 %(self.vrf_helper,
1052 ifaceobj.name,
1053 config_table), 0)
1054 except Exception, e:
1055 ifaceobjcurr.update_config_with_status('vrf-helper',
1056 '%s create %s %s'
1057 %(self.vrf_helper,
1058 ifaceobj.name,
1059 config_table), 1)
1060 pass
1061 except Exception, e:
1062 self.log_warn(str(e))
1063
1064 def _query_check(self, ifaceobj, ifaceobjcurr):
1065 try:
1066 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
1067 if vrf_table:
1068 self._iproute2_vrf_map_initialize(writetodisk=False)
1069 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
1070 else:
1071 vrf = ifaceobj.get_attr_value_first('vrf')
1072 if vrf:
1073 self._iproute2_vrf_map_initialize(writetodisk=False)
1074 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
1075 except Exception, e:
1076 self.log_warn(str(e))
1077
1078 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
1079 try:
1080 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
1081 if kind == 'vrf':
1082 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
1083 if vrfdev_attrs:
1084 running_table = vrfdev_attrs.get('table')
1085 if running_table:
1086 ifaceobjrunning.update_config('vrf-table',
1087 running_table)
1088 return
1089 slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
1090 if slave_kind == 'vrf_slave':
1091 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
1092 if vrf:
1093 ifaceobjrunning.update_config('vrf', vrf)
1094 except Exception, e:
1095 self.log_warn(str(e))
1096
1097 def _query(self, ifaceobj, **kwargs):
1098 if not self.vrf_helper:
1099 return
1100 if (ifaceobj.link_kind & ifaceLinkKind.VRF):
1101 ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
1102 ifaceobj.name))
1103
1104 _run_ops = {'pre-up' : _up,
1105 'post-down' : _down,
1106 'query-running' : _query_running,
1107 'query-checkcurr' : _query_check,
1108 'query' : _query}
1109
1110 def get_ops(self):
1111 """ returns list of ops supported by this module """
1112 return self._run_ops.keys()
1113
1114 def _init_command_handlers(self):
1115 if not self.ipcmd:
1116 self.ipcmd = self.bondcmd = LinkUtils()
1117 if not self.dhclientcmd:
1118 self.dhclientcmd = dhclient()
1119
1120 def run(self, ifaceobj, operation, query_ifaceobj=None,
1121 ifaceobj_getfunc=None, **extra_args):
1122 """ run bond configuration on the interface object passed as argument
1123
1124 Args:
1125 **ifaceobj** (object): iface object
1126
1127 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1128 'query-running'
1129
1130 Kwargs:
1131 **query_ifaceobj** (object): query check ifaceobject. This is only
1132 valid when op is 'query-checkcurr'. It is an object same as
1133 ifaceobj, but contains running attribute values and its config
1134 status. The modules can use it to return queried running state
1135 of interfaces. status is success if the running state is same
1136 as user required state in ifaceobj. error otherwise.
1137 """
1138 op_handler = self._run_ops.get(operation)
1139 if not op_handler:
1140 return
1141 self._init_command_handlers()
1142 if operation == 'query-checkcurr':
1143 op_handler(self, ifaceobj, query_ifaceobj)
1144 else:
1145 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)