ifstate 1.13.7__py3-none-any.whl → 2.0.0rc2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
libifstate/link/base.py CHANGED
@@ -1,4 +1,4 @@
1
- from libifstate.util import logger, IfStateLogging, LinkDependency
1
+ from libifstate.util import logger, format_ether_address, IfStateLogging, LinkDependency, IDENTIFY_LOOKUPS
2
2
  from libifstate.exception import ExceptionCollector, LinkTypeUnknown, NetnsUnknown, netlinkerror_classes
3
3
  from libifstate.brport import BRPort
4
4
  from libifstate.routing import RTLookups
@@ -129,7 +129,7 @@ class Link(ABC):
129
129
  return super().__new__(GenericLink)
130
130
  #raise LinkTypeUnknown()
131
131
 
132
- def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
132
+ def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
133
133
  self.ifstate = ifstate
134
134
  self.netns = netns
135
135
  self.cap_create = True
@@ -139,6 +139,7 @@ class Link(ABC):
139
139
  }
140
140
  self.settings.update(link)
141
141
  self.ethtool = None
142
+ self.hooks = hooks
142
143
  self.vrrp = vrrp
143
144
  if brport:
144
145
  self.brport = BRPort(netns, name, brport)
@@ -154,22 +155,17 @@ class Link(ABC):
154
155
  self.link_ref = LinkDependency(name, self.netns.netns)
155
156
 
156
157
  # prepare link registry search filters
157
- if 'businfo' in self.settings:
158
- self.settings['businfo'] = self.settings['businfo'].lower()
159
- self.link_registry_search_args.append({
160
- 'kind': self.settings['kind'],
161
- 'businfo': self.settings['businfo'],
162
- })
163
-
164
- if 'permaddr' in self.settings:
165
- self.settings['permaddr'] = self.settings['permaddr'].lower()
166
- self.link_registry_search_args.append({
167
- 'kind': self.settings['kind'],
168
- 'permaddr': self.settings['permaddr'],
169
- })
158
+ if identify:
159
+ search_args = {}
160
+ for attr in IDENTIFY_LOOKUPS.keys():
161
+ if attr in identify:
162
+ search_args[attr] = identify[attr]
163
+ if search_args:
164
+ search_args['kind'] = self.settings['kind']
165
+ self.link_registry_search_args.append(search_args)
170
166
 
171
167
  if 'address' in self.settings and self.settings['kind'] == 'physical':
172
- self.settings['address'] = self.settings['address'].lower()
168
+ self.settings['address'] = format_ether_address(self.settings['address'])
173
169
  self.link_registry_search_args.append({
174
170
  'kind': self.settings['kind'],
175
171
  'address': self.settings['address'],
@@ -249,11 +245,8 @@ class Link(ABC):
249
245
  return None
250
246
 
251
247
  def get_if_attr(self, key):
252
- if key in ["state", "permaddr", "businfo"]:
253
- if key in self.iface:
254
- return self.iface[key]
255
- else:
256
- return None
248
+ if key == "state":
249
+ return self.iface.get(key)
257
250
 
258
251
  if key in self.attr_map:
259
252
  return self._drill_attr(self.iface, self.attr_map[key])
@@ -522,13 +515,6 @@ class Link(ABC):
522
515
  if self.idx is not None:
523
516
  self.iface = item.netns.ipr.get_link(self.idx)
524
517
  if self.idx is not None and self.iface is not None:
525
- permaddr = item.netns.ipr.get_permaddr(self.iface.get_attr('IFLA_IFNAME'))
526
- if not permaddr is None:
527
- self.iface['permaddr'] = permaddr
528
- businfo = item.netns.ipr.get_businfo(self.iface.get_attr('IFLA_IFNAME'))
529
- if not businfo is None:
530
- self.iface['businfo'] = businfo
531
-
532
518
  # check for ifname collisions
533
519
  idx = next(iter(self.netns.ipr.link_lookup(
534
520
  ifname=self.settings['ifname'])), None)
@@ -631,6 +617,9 @@ class Link(ABC):
631
617
  if not isinstance(err, netlinkerror_classes):
632
618
  raise
633
619
  excpts.add('set', err, state=state)
620
+
621
+ # get kernel state
622
+ self.iface = self.netns.ipr.get_link(self.idx)
634
623
  except Exception as err:
635
624
  if not isinstance(err, netlinkerror_classes):
636
625
  raise
@@ -743,7 +732,7 @@ class Link(ABC):
743
732
  if do_apply:
744
733
  # temp. remove special settings
745
734
  skipped_settings = {}
746
- for setting in ['state', 'peer', 'kind', 'businfo', 'permaddr']:
735
+ for setting in ['state', 'peer', 'kind']:
747
736
  if setting in self.settings:
748
737
  skipped_settings[setting] = self.settings.pop(setting)
749
738
 
@@ -838,6 +827,9 @@ class Link(ABC):
838
827
  else:
839
828
  logger.log_ok('link')
840
829
 
830
+ # update kernel state
831
+ self.iface = self.netns.ipr.get_link(self.idx)
832
+
841
833
  def depends(self):
842
834
  deps = []
843
835
 
@@ -900,5 +892,5 @@ class Link(ABC):
900
892
 
901
893
 
902
894
  class GenericLink(Link):
903
- def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
904
- super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
895
+ def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
896
+ super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
@@ -3,8 +3,8 @@ from libifstate.link.base import Link
3
3
  from libifstate.exception import LinkCannotAdd
4
4
 
5
5
  class PhysicalLink(Link):
6
- def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
7
- super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
6
+ def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
7
+ super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
8
8
  self.cap_create = False
9
9
  self.cap_ethtool = True
10
10
  self.ethtool = ethtool
libifstate/link/tun.py CHANGED
@@ -6,14 +6,14 @@ from pwd import getpwnam
6
6
  from grp import getgrnam
7
7
 
8
8
  class TunLink(Link):
9
- def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
9
+ def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
10
10
  if 'tun_owner' in link and isinstance(link['tun_owner'], str):
11
11
  link['tun_owner'] = getpwnam(link['tun_owner'])[2]
12
12
 
13
13
  if 'tun_group' in link and isinstance(link['tun_group'], str):
14
14
  link['tun_group'] = getgrnam(link['tun_group'])[2]
15
15
 
16
- super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
16
+ super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
17
17
  self.cap_create = bool(link.get('tun_persist'))
18
18
 
19
19
  def create(self, do_apply, sysctl, excpts, oper="add"):
libifstate/link/veth.py CHANGED
@@ -3,14 +3,14 @@ from libifstate.link.base import Link
3
3
  from libifstate.exception import LinkCannotAdd, NetnsUnknown
4
4
 
5
5
  class VethLink(Link):
6
- def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
6
+ def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
7
7
  # use the bind_netns implementation to create the peer in the
8
8
  # target netns
9
9
  if 'peer_netns' in link:
10
10
  link['bind_netns'] = link['peer_netns']
11
11
  del(link['peer_netns'])
12
12
 
13
- super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
13
+ super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
14
14
 
15
15
  def create(self, do_apply, sysctl, excpts, oper="add"):
16
16
  '''
@@ -1,10 +1,11 @@
1
- from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr
1
+ from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr, root_iw, IDENTIFY_LOOKUPS
2
2
  from libifstate.sysctl import Sysctl
3
3
 
4
4
  import atexit
5
5
  from copy import deepcopy
6
6
  import logging
7
7
  import pyroute2
8
+ from pyroute2.netlink.exceptions import NetlinkError
8
9
  import re
9
10
  import secrets
10
11
  import shutil
@@ -13,6 +14,7 @@ import subprocess
13
14
  netns_name_map = {}
14
15
  netns_name_root = None
15
16
  netns_nsid_map = {}
17
+ iw_ifindex_phy_map = {}
16
18
 
17
19
  @atexit.register
18
20
  def close_netns():
@@ -44,12 +46,27 @@ class NetNameSpace():
44
46
 
45
47
  if name is None:
46
48
  self.ipr = root_ipr
49
+ self.iw = root_iw
47
50
  self.mount = b''
48
51
  else:
49
52
  self.ipr = NetNSExt(name)
50
53
  netns_name_map[name] = self.ipr
54
+
55
+ # check for wireless phys
56
+ if root_iw:
57
+ pyroute2.netns.pushns(name)
58
+ self.iw = pyroute2.IW()
59
+ pyroute2.netns.popns()
60
+ else:
61
+ self.iw = None
62
+
51
63
  self.mount = name.encode("utf-8")
52
64
 
65
+ if self.iw is not None:
66
+ for ifname, ifdict in self.iw.get_interfaces_dict().items():
67
+ # ifIndex => phyIndex
68
+ iw_ifindex_phy_map[ifdict[0]] = ifdict[3]
69
+
53
70
  def __deepcopy__(self, memo):
54
71
  '''
55
72
  Add custom deepcopy implementation to keep single IPRoute and NetNS instances.
@@ -59,7 +76,10 @@ class NetNameSpace():
59
76
  memo[id(self)] = result
60
77
  for k, v in self.__dict__.items():
61
78
  if k == 'ipr':
62
- setattr(result, k, v)
79
+ if self.netns is None:
80
+ setattr(result, k, IPRouteExt())
81
+ else:
82
+ setattr(result, k, NetNSExt(self.netns))
63
83
  else:
64
84
  setattr(result, k, deepcopy(v, memo))
65
85
  return result
@@ -223,8 +243,16 @@ class LinkRegistryItem():
223
243
  self.attributes['kind'] = linkinfo.get_attr('IFLA_INFO_KIND')
224
244
  else:
225
245
  self.attributes['kind'] = "physical"
226
- self.attributes['businfo'] = self.netns.ipr.get_businfo(self.attributes['ifname'])
227
- self.attributes['permaddr'] = link.get_attr('IFLA_PERM_ADDRESS')
246
+
247
+ # add iw phy for wireless interfaces
248
+ if link['index'] in iw_ifindex_phy_map:
249
+ self.attributes['wiphy'] = iw_ifindex_phy_map[link['index']]
250
+
251
+ # add identify attributes
252
+ for attr, lookup in IDENTIFY_LOOKUPS.items():
253
+ value = lookup(netns, link)
254
+ if value is not None:
255
+ self.attributes[attr] = value
228
256
 
229
257
  def __ipr_link(self, command, **kwargs):
230
258
  logger.debug("ip link set netns={} {}".format(
@@ -271,7 +299,14 @@ class LinkRegistryItem():
271
299
  # ToDo
272
300
  self.update_ifname( self.registry.get_random_name('__netns__') )
273
301
 
274
- self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
302
+ if self.attributes.get('wiphy') is not None:
303
+ # move phy instead of iface for wireless devices
304
+ if netns.netns is None:
305
+ self.netns.iw.set_wiphy_netns_by_pid(self.attributes['wiphy'], 1)
306
+ else:
307
+ self.netns.iw.set_wiphy_netns_by_pid(self.attributes['wiphy'], netns.ipr.child)
308
+ else:
309
+ self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
275
310
  self.netns = netns
276
311
  self.attributes['index'] = next(iter(self.netns.ipr.link_lookup(ifname=self.attributes['ifname'])), None)
277
312
 
libifstate/parser/base.py CHANGED
@@ -6,7 +6,6 @@ from copy import deepcopy
6
6
 
7
7
  class Parser(ABC):
8
8
  _default_lo_link = {
9
- 'name': 'lo',
10
9
  'addresses': [
11
10
  '127.0.0.1/8',
12
11
  '::1/128',
@@ -18,79 +17,96 @@ class Parser(ABC):
18
17
  }
19
18
  }
20
19
  _default_ifstates = {
21
- 'ignore': {
22
- 'ipaddr_builtin': [
23
- 'fe80::/10'
24
- ],
25
- 'ipaddr_dynamic': True,
26
- 'ifname_builtin': [
27
- r'^br-[\da-f]{12}',
28
- r'^docker\d+',
29
- r'^ppp\d+$',
30
- r'^veth',
31
- r'^virbr\d+',
32
- r'^vrrp\d*\.\d+$'
33
- ],
34
- 'fdb_builtin': [
35
- r'^33:33:',
36
- r'^01:00:5e:'
37
- ],
38
- 'routes_builtin': [
39
- {'proto': 1},
40
- {'proto': 2},
41
- {'proto': 8},
42
- {'proto': 9},
43
- {'proto': 10},
44
- {'proto': 11},
45
- {'proto': 12},
46
- {'proto': 13},
47
- {'proto': 14},
48
- {'proto': 15},
49
- {'proto': 16},
50
- {'proto': 18},
51
- {'proto': 42},
52
- {'proto': 186},
53
- {'proto': 187},
54
- {'proto': 188},
55
- {'proto': 189},
56
- {'proto': 192},
57
- {'to': 'ff00::/8'},
58
- ],
59
- 'rules_builtin': [
60
- {'proto': 1},
61
- {'proto': 2},
62
- {'proto': 8},
63
- {'proto': 9},
64
- {'proto': 10},
65
- {'proto': 11},
66
- {'proto': 12},
67
- {'proto': 13},
68
- {'proto': 14},
69
- {'proto': 15},
70
- {'proto': 16},
71
- {'proto': 18},
72
- {'proto': 42},
73
- {'proto': 186},
74
- {'proto': 187},
75
- {'proto': 188},
76
- {'proto': 189},
77
- {'proto': 192},
78
- ],
79
- },
80
- 'cshaper': {
81
- 'default': {
82
- 'egress_qdisc': {
83
- 'kind': 'cake',
84
- 'handle': '1:',
85
- },
86
- 'ingress_qdisc': {
87
- 'kind': 'cake',
88
- 'handle': '1:',
89
- },
90
- 'ingress_ifname': {
91
- 'search': r'^\D{1,3}',
92
- 'replace': 'ifb',
20
+ 'parameters': {
21
+ 'cshaper': {
22
+ 'default': {
23
+ 'egress_qdisc': {
24
+ 'kind': 'cake',
25
+ 'handle': '1:',
26
+ },
27
+ 'ingress_qdisc': {
28
+ 'kind': 'cake',
29
+ 'handle': '1:',
30
+ },
31
+ 'ingress_ifname': {
32
+ 'search': r'^\D{1,3}',
33
+ 'replace': 'ifb',
34
+ }
35
+ }
36
+ },
37
+ 'defaults_builtin': [
38
+ {
39
+ 'match': [
40
+ {'ifname': ''}
41
+ ],
42
+ 'clear_addresses': True,
43
+ 'clear_fdb': True,
44
+ 'clear_neighbours': True,
45
+ 'clear_tc': True,
46
+ 'link': {
47
+ 'state': 'down',
48
+ 'master': None
49
+ }
93
50
  }
51
+ ],
52
+ 'ignore': {
53
+ 'ipaddr_builtin': [
54
+ 'fe80::/10'
55
+ ],
56
+ 'ipaddr_dynamic': True,
57
+ 'ifname_builtin': [
58
+ r'^br-[\da-f]{12}',
59
+ r'^docker\d+',
60
+ r'^ppp\d+$',
61
+ r'^veth',
62
+ r'^virbr\d+',
63
+ r'^vrrp\d*\.\d+$'
64
+ ],
65
+ 'fdb_builtin': [
66
+ r'^33:33:',
67
+ r'^01:00:5e:'
68
+ ],
69
+ 'routes_builtin': [
70
+ {'proto': 1},
71
+ {'proto': 2},
72
+ {'proto': 8},
73
+ {'proto': 9},
74
+ {'proto': 10},
75
+ {'proto': 11},
76
+ {'proto': 12},
77
+ {'proto': 13},
78
+ {'proto': 14},
79
+ {'proto': 15},
80
+ {'proto': 16},
81
+ {'proto': 18},
82
+ {'proto': 42},
83
+ {'proto': 186},
84
+ {'proto': 187},
85
+ {'proto': 188},
86
+ {'proto': 189},
87
+ {'proto': 192},
88
+ {'to': 'ff00::/8'},
89
+ ],
90
+ 'rules_builtin': [
91
+ {'proto': 1},
92
+ {'proto': 2},
93
+ {'proto': 8},
94
+ {'proto': 9},
95
+ {'proto': 10},
96
+ {'proto': 11},
97
+ {'proto': 12},
98
+ {'proto': 13},
99
+ {'proto': 14},
100
+ {'proto': 15},
101
+ {'proto': 16},
102
+ {'proto': 18},
103
+ {'proto': 42},
104
+ {'proto': 186},
105
+ {'proto': 187},
106
+ {'proto': 188},
107
+ {'proto': 189},
108
+ {'proto': 192},
109
+ ],
94
110
  }
95
111
  }
96
112
  }
@@ -114,11 +130,11 @@ class Parser(ABC):
114
130
 
115
131
  def _update_lo(self, cfg):
116
132
  if 'interfaces' in cfg:
117
- lo = next((idx for idx, iface in enumerate(cfg['interfaces']) if iface['name'] == 'lo'), None)
118
- if lo is not None:
119
- cfg['interfaces'][lo] = self.merge(deepcopy(Parser._default_lo_link), cfg['interfaces'][lo])
133
+ if 'lo' in cfg['interfaces']:
134
+ cfg['interfaces']['lo'] = self.merge(
135
+ deepcopy(Parser._default_lo_link), cfg['interfaces']['lo'])
120
136
  else:
121
- cfg['interfaces'].append(Parser._default_lo_link)
137
+ cfg['interfaces']['lo'] = Parser._default_lo_link
122
138
 
123
139
  def config(self):
124
140
  # merge builtin defaults with config
@@ -126,7 +142,7 @@ class Parser(ABC):
126
142
 
127
143
  # 'ignore' should still be an object
128
144
  try:
129
- iter(cfg["ignore"])
145
+ iter(cfg["parameters"]["ignore"])
130
146
  except TypeError:
131
147
  raise ParserValidationError("$.ignore: is not of type 'object'")
132
148
 
@@ -136,18 +152,25 @@ class Parser(ABC):
136
152
  self._update_lo(cfg['namespaces'][namespace])
137
153
 
138
154
  # merge builtin defaults
139
- for k in list(cfg["ignore"]):
155
+ if "defaults" in cfg["parameters"]:
156
+ cfg["parameters"]["defaults"].extend(cfg["parameters"]["defaults_builtin"])
157
+ else:
158
+ cfg["parameters"]["defaults"] = cfg["parameters"]["defaults_builtin"]
159
+ del cfg["parameters"]["defaults_builtin"]
160
+
161
+ # merge builtin ignore lists
162
+ for k in list(cfg["parameters"]["ignore"]):
140
163
  if k.endswith("_builtin"):
141
164
  n = k[:-8]
142
- if n in cfg["ignore"]:
165
+ if n in cfg["parameters"]["ignore"]:
143
166
  try:
144
- cfg["ignore"][n] += cfg["ignore"][k]
167
+ cfg["parameters"]["ignore"][n] += cfg["parameters"]["ignore"][k]
145
168
  except TypeError:
146
169
  raise ParserValidationError("$.ignore.{}: is not of type '{}'".format(
147
- n, type(cfg["ignore"][k]).__name__))
170
+ n, type(cfg["parameters"]["ignore"][k]).__name__))
148
171
  else:
149
- cfg["ignore"][n] = cfg["ignore"][k]
150
- del(cfg["ignore"][k])
172
+ cfg["parameters"]["ignore"][n] = cfg["parameters"]["ignore"][k]
173
+ del (cfg["parameters"]["ignore"][k])
151
174
 
152
175
  return cfg
153
176
 
@@ -1,6 +1,6 @@
1
1
  from libifstate.util import logger, IfStateLogging
2
2
  from libifstate.exception import RouteDuplicate, netlinkerror_classes
3
- from ipaddress import ip_address, ip_network, IPv6Network
3
+ from ipaddress import ip_address, ip_interface, ip_network, IPv6Network
4
4
  from pyroute2.netlink.rtnl.fibmsg import FR_ACT_VALUES
5
5
  from pyroute2.netlink.rtnl import rt_type
6
6
  import collections.abc
@@ -284,8 +284,8 @@ class Tables(collections.abc.Mapping):
284
284
  idx = next(
285
285
  iter(self.netns.ipr.link_lookup(ifname=ignore[attr])), None)
286
286
  if idx is None:
287
- logger.debug("{} '{}' is unknown".format(attr, ignore[attr]))
288
- del ignore[attr]
287
+ logger.debug("{} '{}' is unknown, skipping ignore".format(attr, ignore[attr]))
288
+ continue
289
289
  else:
290
290
  ignore[attr] = idx
291
291
  parsed.append(ignore)
@@ -321,7 +321,7 @@ class Tables(collections.abc.Mapping):
321
321
  continue
322
322
 
323
323
  if route['dst_len'] > 0:
324
- dst = ip_network(
324
+ dst = ip_interface(
325
325
  '{}/{}'.format(route.get_attr('RTA_DST'), route['dst_len'])).with_prefixlen
326
326
  elif route['family'] == AF_INET:
327
327
  dst = ip_network('0.0.0.0/0').with_prefixlen
@@ -546,13 +546,18 @@ class Rules():
546
546
  'tos': rule['tos'],
547
547
  }
548
548
 
549
- if rule['dst_len'] > 0:
550
- ru['dst'] = rule.get_attr('FRA_DST')
551
- ru['dst_len'] = rule['dst_len']
549
+ # add source and destination ip spaces even if they are not given by pyroute2
550
+ # as the user might specify something like "from: ::/0" explicitly which would not
551
+ # be present on a krule (because default values are ommited by pyroute2) and therefore
552
+ # would not match, even though it is the same rule
553
+ # if the user doesn't specify 'from' or 'to', these fields are just ignored because
554
+ # rule_matches is instructed to only check fields that are present on the config rule,
555
+ # meaning that these just get ignored
556
+ ru['dst'] = rule.get_attr('FRA_DST', default=("::" if rule['family'] == AF_INET6 else "0.0.0.0"))
557
+ ru['dst_len'] = rule['dst_len']
552
558
 
553
- if rule['src_len'] > 0:
554
- ru['src'] = rule.get_attr('FRA_SRC')
555
- ru['src_len'] = rule['src_len']
559
+ ru['src'] = rule.get_attr('FRA_SRC', default=("::" if rule['family'] == AF_INET6 else "0.0.0.0"))
560
+ ru['src_len'] = rule['src_len']
556
561
 
557
562
  for field in ['iifname', 'oifname', 'fwmark', 'ip_proto']:
558
563
  value = rule.get_attr('FRA_{}'.format(field.upper()))
@@ -593,7 +598,7 @@ class Rules():
593
598
  del rule['protocol']
594
599
 
595
600
  if 'src_len' in rule and rule['src_len'] > 0:
596
- rule['from'] = ip_network(
601
+ rule['from'] = ip_interface(
597
602
  '{}/{}'.format(rule['src'], rule['src_len'])).with_prefixlen
598
603
 
599
604
  for key in ['src', 'src_len', 'tos', 'protocol']:
@@ -609,32 +614,51 @@ class Rules():
609
614
 
610
615
  # get rules that are currently in the kernels list
611
616
  krules = self.kernel_rules()
617
+
618
+ # loop over all rules that are wanted by the config
612
619
  for rule in self.rules:
613
620
  log_str = '#{}'.format(rule['priority'])
621
+
622
+ if rule.get('family', None) == AF_INET6:
623
+ log_str += " (inet6)"
624
+ elif rule.get('family', None) == AF_INET:
625
+ log_str += " (inet)"
626
+
614
627
  if self.netns.netns != None:
615
628
  log_str += "[netns={}]".format(self.netns.netns)
616
629
 
630
+ vrrp_matched = vrrp_match(rule, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
631
+ # 'found' indicates whether the rule wanted by the config was found
632
+ # in the kernels rules. if not, it is going to be added below
617
633
  found = False
618
- matched = vrrp_match(rule, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
619
634
  for i, krule in enumerate(krules):
620
635
  if rule_matches(
621
636
  rule,
622
637
  krule,
623
638
  # skip helper attrs like _vrrp
639
+ # note that rule_matches it told to only check fields that are
640
+ # present on the config rule, so additional ones from the krule
641
+ # just get ignored
624
642
  [key for key in rule.keys() if key[0] != '_']
625
643
  ):
626
644
  found = True
627
645
 
628
- # remove kernel routes due to vrrp_match
629
- if matched != VRRP_MATCH_DISABLE:
646
+ # we've just confirmed that this krule matches a rule
647
+ # that is wanted by the config so we delete it from the
648
+ # krules because any that remain in krule are unwanted
649
+ # and get deleted below
650
+ # EXCEPT if vrrp tells us to leave it be
651
+ if vrrp_matched != VRRP_MATCH_DISABLE:
630
652
  del krules[i]
631
653
 
632
654
  break
633
655
 
634
- if matched not in [VRRP_MATCH_IGNORE, VRRP_MATCH_DISABLE]:
656
+ if vrrp_matched not in [VRRP_MATCH_IGNORE, VRRP_MATCH_DISABLE]:
635
657
  if found:
658
+ # rule already exists
636
659
  logger.log_ok(log_str)
637
660
  else:
661
+ # rule doesn't exist, add it
638
662
  logger.log_add(log_str)
639
663
 
640
664
  logger.debug("ip rule add: {}".format(
@@ -647,7 +671,12 @@ class Rules():
647
671
  raise
648
672
  logger.warning('rule setup failed: {}'.format(err.args[1]))
649
673
 
674
+ # at this point all wanted rules have been deleted from the krules list,
675
+ # leaving the rest to be deleted here (see 'del krules[i]' above)
650
676
  for rule in krules:
677
+
678
+ # if this rule falls into the ignored ones (like the ones set by the kernel itself by default)
679
+ # we will ignore it and continue
651
680
  ignore = False
652
681
  for irule in ignores:
653
682
  if 'proto' in irule:
@@ -659,9 +688,26 @@ class Rules():
659
688
  if ignore:
660
689
  continue
661
690
 
662
- logger.log_del('#{}'.format(rule['priority']))
691
+ log_str = '#{}'.format(rule['priority'])
692
+
693
+ if rule.get('family', None) == AF_INET6:
694
+ log_str += " (inet6)"
695
+ elif rule.get('family', None) == AF_INET:
696
+ log_str += " (inet)"
697
+
698
+ logger.log_del(log_str)
699
+
663
700
  try:
664
701
  if do_apply:
702
+ # for some reason pyroute2 raises an exception when
703
+ # we pass an address like "0.0.0.0" or "::" with an
704
+ # 'src_len' or 'dst_len' of 0.
705
+ # using this as a workaround:
706
+ if rule['src_len'] == 0:
707
+ del rule['src']
708
+ if rule['dst_len'] == 0:
709
+ del rule['dst']
710
+
665
711
  self.netns.ipr.rule('del', **rule)
666
712
  except Exception as err:
667
713
  if not isinstance(err, netlinkerror_classes):