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