ifstate 1.13.6__py3-none-any.whl → 1.13.8__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.
- ifstate/ifstate.py +4 -4
- ifstate/vrrp.py +1 -1
- {ifstate-1.13.6.dist-info → ifstate-1.13.8.dist-info}/METADATA +1 -1
- {ifstate-1.13.6.dist-info → ifstate-1.13.8.dist-info}/RECORD +19 -19
- {ifstate-1.13.6.dist-info → ifstate-1.13.8.dist-info}/WHEEL +1 -1
- libifstate/__init__.py +16 -13
- libifstate/address/__init__.py +1 -1
- libifstate/exception.py +5 -3
- libifstate/link/base.py +15 -6
- libifstate/link/physical.py +1 -1
- libifstate/link/tun.py +1 -1
- libifstate/log.py +7 -4
- libifstate/netns/__init__.py +14 -10
- libifstate/routing/__init__.py +174 -100
- libifstate/tc/__init__.py +1 -1
- libifstate/wireguard/__init__.py +7 -2
- {ifstate-1.13.6.dist-info → ifstate-1.13.8.dist-info}/entry_points.txt +0 -0
- {ifstate-1.13.6.dist-info → ifstate-1.13.8.dist-info}/licenses/LICENSE +0 -0
- {ifstate-1.13.6.dist-info → ifstate-1.13.8.dist-info}/top_level.txt +0 -0
ifstate/ifstate.py
CHANGED
@@ -128,7 +128,7 @@ def main():
|
|
128
128
|
group = parser.add_mutually_exclusive_group()
|
129
129
|
parser.add_argument('--version', action='version',
|
130
130
|
version='%(prog)s {version}'.format(version=__version__))
|
131
|
-
group.add_argument("-v", "--verbose", action="
|
131
|
+
group.add_argument("-v", "--verbose", action="count", default=0,
|
132
132
|
help="be more verbose")
|
133
133
|
group.add_argument("-q", "--quiet", action="store_true",
|
134
134
|
help="be more quiet, print only warnings and errors")
|
@@ -162,10 +162,10 @@ def main():
|
|
162
162
|
"name", type=str, help="name of the vrrp group or instance")
|
163
163
|
|
164
164
|
args = parser.parse_args()
|
165
|
-
if args.verbose:
|
165
|
+
if args.verbose > 0:
|
166
166
|
lvl = logging.DEBUG
|
167
167
|
elif args.quiet:
|
168
|
-
lvl = logging.
|
168
|
+
lvl = logging.WARNING
|
169
169
|
else:
|
170
170
|
lvl = logging.INFO
|
171
171
|
|
@@ -178,7 +178,7 @@ def main():
|
|
178
178
|
shell()
|
179
179
|
exit(0)
|
180
180
|
|
181
|
-
ifslog = IfStateLogging(lvl, action=args.action)
|
181
|
+
ifslog = IfStateLogging(lvl, args.verbose > 1, action=args.action)
|
182
182
|
|
183
183
|
if args.action in [Actions.SHOW, Actions.SHOWALL]:
|
184
184
|
# preserve dict order on python 3.7+
|
ifstate/vrrp.py
CHANGED
@@ -139,7 +139,7 @@ def vrrp_fifo(args_fifo, ifs_config):
|
|
139
139
|
|
140
140
|
try:
|
141
141
|
status_pattern = re.compile(
|
142
|
-
r'(group|instance) "([^"]+)" (' + '|'.join(KEEPALIVED_STATES) + ')( \d+)?$', re.IGNORECASE)
|
142
|
+
r'(group|instance) "([^"]+)" (' + '|'.join(KEEPALIVED_STATES) + r')( \d+)?$', re.IGNORECASE)
|
143
143
|
|
144
144
|
with open(args_fifo) as fifo:
|
145
145
|
logger.debug("entering fifo loop...")
|
@@ -1,37 +1,37 @@
|
|
1
1
|
ifstate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
ifstate/ifstate.py,sha256=
|
2
|
+
ifstate/ifstate.py,sha256=am8x-ONL1QziOuTfD3Fmr5wVg_Mt2sH9Pi9DTn6YqYY,8921
|
3
3
|
ifstate/shell.py,sha256=7_JFpi4icr9MijynDzbb0v5mxhFsng6PCC4m3uQ255A,2177
|
4
|
-
ifstate/vrrp.py,sha256=
|
5
|
-
ifstate-1.13.
|
6
|
-
libifstate/__init__.py,sha256=
|
7
|
-
libifstate/exception.py,sha256=
|
8
|
-
libifstate/log.py,sha256=
|
4
|
+
ifstate/vrrp.py,sha256=FJ9b1eJseTtZFfknHU-xV68Qz7cPrRrc5PTcjUVj-fY,5953
|
5
|
+
ifstate-1.13.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
6
|
+
libifstate/__init__.py,sha256=9ovYpLf4AVTDNWFru4o4DaR60SQW0tXCrNMXLuoTPpU,31245
|
7
|
+
libifstate/exception.py,sha256=QgG4hdP1KYbdVBNfy-bT59aCcYF0IuFdEwZrY3QrwTc,2688
|
8
|
+
libifstate/log.py,sha256=rG0ISWyeGGlC_jRREoSpkdySc5EmyhOQIgSfvRqlaZA,4651
|
9
9
|
libifstate/util.py,sha256=WzbGKX78iWUunnZ-v_3OREmxMiYt7vVM_QyCgTcMYow,10802
|
10
|
-
libifstate/address/__init__.py,sha256=
|
10
|
+
libifstate/address/__init__.py,sha256=SobrZ9e2ItrmEidnKPBhUMitRDdTEajbkU9Rkmtx-gQ,3165
|
11
11
|
libifstate/bpf/__init__.py,sha256=NVzaunTmJU2PbIQg9eWBMKpFgLh3EnD3ujNa7Yt6rNc,7699
|
12
12
|
libifstate/bpf/ctypes.py,sha256=kLZJHZlba09Vc-tbsJAcKpDwdoNO2IjlYVLCopawHmA,4274
|
13
13
|
libifstate/bpf/map.py,sha256=cLHNMvRBDNW2yVCEf3z242_oRdU0HqVbFEYVkKXng0w,10818
|
14
14
|
libifstate/brport/__init__.py,sha256=NzdA8F4hr2se1bXKNnyKZbvOFlCWBq_cdjwsL1H0Y-o,2964
|
15
15
|
libifstate/fdb/__init__.py,sha256=9dpL5n8ct3CaA-z8I6ZEkD3yL6yWJQeq3fpIe9pc2zw,6486
|
16
16
|
libifstate/link/__init__.py,sha256=epVw6jY8exNeJZUmmUas91yJoeupfgIY7rthq7SGIIw,142
|
17
|
-
libifstate/link/base.py,sha256=
|
17
|
+
libifstate/link/base.py,sha256=PLHJ594UQboe98jEVZVQvaD1Ka_Ah6EySDfcJtYTVH0,34863
|
18
18
|
libifstate/link/dsa.py,sha256=Y3axTtcym6YL1voKblxctx4PoKDZHzpteKQNnEBUrS8,264
|
19
|
-
libifstate/link/physical.py,sha256=
|
20
|
-
libifstate/link/tun.py,sha256=
|
19
|
+
libifstate/link/physical.py,sha256=rmf8ltjDy1s2NEfaQtOwKBTpESlzqjZd53yfcZCfSPo,579
|
20
|
+
libifstate/link/tun.py,sha256=YEW09fYNLvmb0IU4Jb1uNh0iCt0bgIii2pPvOYtgw6U,978
|
21
21
|
libifstate/link/veth.py,sha256=V_jmQCizI5Vv8-pcsfldSfMRTn1knR11wZDZI_yXvks,2249
|
22
22
|
libifstate/neighbour/__init__.py,sha256=FJJpbJvqnxvOEii6QDMYzW5jQDEbiEy71GQOEbqaS48,2463
|
23
|
-
libifstate/netns/__init__.py,sha256
|
23
|
+
libifstate/netns/__init__.py,sha256=-xJ5Qa-x2_MveGHWWR2pv--EBmQzLSZUYeC8vebnLUE,9029
|
24
24
|
libifstate/parser/__init__.py,sha256=byz1W0G7UewVc5FFie-ti3UZjGK3-75wHIaOeq0oySQ,88
|
25
25
|
libifstate/parser/base.py,sha256=VFAo05O3tiKtI381LVUMYfsDTseMKoQGTfkgnEkm3H4,4770
|
26
26
|
libifstate/parser/yaml.py,sha256=MC0kmwqt3P45z61fb_wfUqoj0iZyhFYkdPyr0UqMSZA,1415
|
27
|
-
libifstate/routing/__init__.py,sha256=
|
27
|
+
libifstate/routing/__init__.py,sha256=5kcIzWpM2v-T_ndxFO5qwF8hla-Fqh2yP1QtmohWZl0,22777
|
28
28
|
libifstate/sysctl/__init__.py,sha256=EF52CdOOkVSUFR2t21A99KlG1-PjsD4qOiceQC4eI24,3074
|
29
|
-
libifstate/tc/__init__.py,sha256=
|
30
|
-
libifstate/wireguard/__init__.py,sha256=
|
29
|
+
libifstate/tc/__init__.py,sha256=T8LEBxiHZH4sayPn9lK5hK8eB8dskuw6-kw81W_aMWU,12097
|
30
|
+
libifstate/wireguard/__init__.py,sha256=RPJdMdp7Uv3Kk_koGH8bu0lVQaKlJQwkyobGFqtmp0E,6839
|
31
31
|
libifstate/xdp/__init__.py,sha256=X1xhEIGng7R5d5F4KsChykT2g6H-XBRWbWABijoYDQA,7208
|
32
32
|
schema/ifstate.conf.schema.json,sha256=NOPeI8_r1jXvgAAJeBqz92ZACNvxskId3qMykj-sG7M,201292
|
33
|
-
ifstate-1.13.
|
34
|
-
ifstate-1.13.
|
35
|
-
ifstate-1.13.
|
36
|
-
ifstate-1.13.
|
37
|
-
ifstate-1.13.
|
33
|
+
ifstate-1.13.8.dist-info/METADATA,sha256=dNYDgkvVdmbpS-A7CdZnwhmz-RXww8btSS8YunJ_WUg,1598
|
34
|
+
ifstate-1.13.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
35
|
+
ifstate-1.13.8.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
|
36
|
+
ifstate-1.13.8.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
|
37
|
+
ifstate-1.13.8.dist-info/RECORD,,
|
libifstate/__init__.py
CHANGED
@@ -48,7 +48,7 @@ import json
|
|
48
48
|
import errno
|
49
49
|
import logging
|
50
50
|
|
51
|
-
__version__ = "1.13.
|
51
|
+
__version__ = "1.13.8"
|
52
52
|
|
53
53
|
|
54
54
|
class IfState():
|
@@ -347,8 +347,8 @@ class IfState():
|
|
347
347
|
# ignore if the link is already gone, this might happen
|
348
348
|
# when removing veth link peers
|
349
349
|
if err.code != errno.ENODEV:
|
350
|
-
logger.warning('removing
|
351
|
-
|
350
|
+
logger.warning('removing failed: {}'.format(
|
351
|
+
err.args[1]), extra={'iface': ifname, 'netns': item.netns})
|
352
352
|
return True
|
353
353
|
else:
|
354
354
|
# shutdown physical interfaces
|
@@ -362,8 +362,8 @@ class IfState():
|
|
362
362
|
except Exception as err:
|
363
363
|
if not isinstance(err, netlinkerror_classes):
|
364
364
|
raise
|
365
|
-
logger.warning('updating
|
366
|
-
|
365
|
+
logger.warning('updating failed: {}'.format(
|
366
|
+
err.args[1]), extra={'iface': ifname, 'netns': item.netns})
|
367
367
|
return False
|
368
368
|
|
369
369
|
def _dependencies(self, netns):
|
@@ -455,7 +455,7 @@ class IfState():
|
|
455
455
|
|
456
456
|
# create and destroy namespaces to match config
|
457
457
|
if not by_vrrp and self.namespaces is not None:
|
458
|
-
prepare_netns(do_apply, self.namespaces.keys(), self.new_namespaces)
|
458
|
+
prepare_netns(do_apply, self.namespaces.keys(), self.new_namespaces, self.ignore.get('netns', []))
|
459
459
|
logger.info("")
|
460
460
|
|
461
461
|
# get link dependency tree
|
@@ -467,8 +467,10 @@ class IfState():
|
|
467
467
|
cleanup_items = []
|
468
468
|
for item in self.link_registry.registry:
|
469
469
|
ifname = item.attributes['ifname']
|
470
|
-
# items without a link are orphan - keep them if they match the ignore regex list...
|
471
|
-
if item.link is None and
|
470
|
+
# items without a link are orphan - keep them if they match the ignore (ifname|netns) regex list...
|
471
|
+
if item.link is None and \
|
472
|
+
not any(re.match(regex, ifname) for regex in self.ignore.get('ifname', [])) and \
|
473
|
+
not any(re.match(regex, item.netns.netns or '') for regex in self.ignore.get('netns', [])):
|
472
474
|
# ...or are in a netns namespace while the config has no `namespaces` setting
|
473
475
|
if self.namespaces is not None or item.netns.netns is None:
|
474
476
|
if not had_cleanup:
|
@@ -514,9 +516,8 @@ class IfState():
|
|
514
516
|
if link_dep.netns is None:
|
515
517
|
self._apply_iface(do_apply, self.root_netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
|
516
518
|
else:
|
517
|
-
if link_dep.netns not in self.namespaces:
|
518
|
-
logger.warning("
|
519
|
-
return
|
519
|
+
if self.namespaces is None or link_dep.netns not in self.namespaces:
|
520
|
+
logger.warning("apply link {} failed: netns '{}' is unknown".format(link_dep.ifname, link_dep.netns))
|
520
521
|
else:
|
521
522
|
self._apply_iface(do_apply, self.namespaces[link_dep.netns], link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
|
522
523
|
|
@@ -555,8 +556,10 @@ class IfState():
|
|
555
556
|
|
556
557
|
def _apply_iface(self, do_apply, netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
|
557
558
|
ifname = link_dep.ifname
|
558
|
-
if ifname in netns.links:
|
559
|
-
link = netns
|
559
|
+
if not ifname in netns.links:
|
560
|
+
logger.warning('no link settings found', extra={'iface': ifname, 'netns': netns})
|
561
|
+
return
|
562
|
+
link = netns.links[ifname]
|
560
563
|
|
561
564
|
# check for vrrp mode:
|
562
565
|
# disable: vrrp type & name matches, but vrrp state not
|
libifstate/address/__init__.py
CHANGED
@@ -18,8 +18,8 @@ class Addresses():
|
|
18
18
|
# get ifindex
|
19
19
|
idx = next(iter(self.netns.ipr.link_lookup(ifname=self.iface)), None)
|
20
20
|
|
21
|
+
# check if interface exists
|
21
22
|
if idx == None:
|
22
|
-
logger.warning('link missing', extra={'iface': self.iface, 'netns': self.netns})
|
23
23
|
return
|
24
24
|
|
25
25
|
# get active ip addresses
|
libifstate/exception.py
CHANGED
@@ -12,8 +12,9 @@ except ModuleNotFoundError:
|
|
12
12
|
pass
|
13
13
|
|
14
14
|
class ExceptionCollector():
|
15
|
-
def __init__(self, ifname):
|
15
|
+
def __init__(self, ifname, netns):
|
16
16
|
self.ifname = ifname
|
17
|
+
self.netns = netns
|
17
18
|
self.reset()
|
18
19
|
|
19
20
|
def reset(self):
|
@@ -27,8 +28,9 @@ class ExceptionCollector():
|
|
27
28
|
'args': kwargs,
|
28
29
|
})
|
29
30
|
if not self.quiet:
|
30
|
-
logger.warning('{}
|
31
|
-
op,
|
31
|
+
logger.warning('{} failed: {}'.format(
|
32
|
+
op, excpt.args[1]),
|
33
|
+
extra={'iface': self.ifname, 'netns': self.netns})
|
32
34
|
|
33
35
|
def has_op(self, op):
|
34
36
|
for e in self.excpts:
|
libifstate/link/base.py
CHANGED
@@ -443,7 +443,7 @@ class Link(ABC):
|
|
443
443
|
return self.match_vrrp_select(vrrp_type, vrrp_name) and (vrrp_state in self.vrrp['states'])
|
444
444
|
|
445
445
|
def apply(self, do_apply, sysctl):
|
446
|
-
excpts = ExceptionCollector(self.settings['ifname'])
|
446
|
+
excpts = ExceptionCollector(self.settings['ifname'], self.netns)
|
447
447
|
osettings = copy.deepcopy(self.settings)
|
448
448
|
|
449
449
|
# lookup for attributes requiring a interface index
|
@@ -452,11 +452,20 @@ class Link(ABC):
|
|
452
452
|
netns_attr = "{}_netns".format(attr)
|
453
453
|
netnsid_attr = "{}_netnsid".format(attr)
|
454
454
|
if netns_attr in self.settings:
|
455
|
-
#
|
456
|
-
(
|
457
|
-
self.settings[
|
458
|
-
|
459
|
-
|
455
|
+
# ignore *_netns settings if the netns is the same as the interface's one
|
456
|
+
# (there is no IFLA_LINK_NETNSID attribute in such cases)
|
457
|
+
if self.settings[netns_attr] != self.netns.netns:
|
458
|
+
try:
|
459
|
+
(peer_ipr, peer_nsid) = self.netns.get_netnsid(self.settings[netns_attr])
|
460
|
+
except NetnsUnknown as ex:
|
461
|
+
excpts.add('apply', ex)
|
462
|
+
return excpts
|
463
|
+
self.settings[netnsid_attr] = peer_nsid
|
464
|
+
idx = next(iter(peer_ipr.link_lookup(
|
465
|
+
ifname=self.settings[attr])), None)
|
466
|
+
else:
|
467
|
+
idx = next(iter(self.netns.ipr.link_lookup(
|
468
|
+
ifname=self.settings[attr])), None)
|
460
469
|
|
461
470
|
del(self.settings[netns_attr])
|
462
471
|
else:
|
libifstate/link/physical.py
CHANGED
@@ -10,4 +10,4 @@ class PhysicalLink(Link):
|
|
10
10
|
self.ethtool = ethtool
|
11
11
|
|
12
12
|
def create(self, do_apply, sysctl, excpts, oper="add"):
|
13
|
-
logger.warning('
|
13
|
+
logger.warning('unable to create physical link', extra={'iface': self.settings.get('ifname'), 'netns': self.netns})
|
libifstate/link/tun.py
CHANGED
@@ -18,6 +18,6 @@ class TunLink(Link):
|
|
18
18
|
|
19
19
|
def create(self, do_apply, sysctl, excpts, oper="add"):
|
20
20
|
if not self.cap_create:
|
21
|
-
logger.warning('
|
21
|
+
logger.warning('unable to create non-persistent tuntap link', extra={'iface': self.settings.get('ifname'), 'netns': self.netns})
|
22
22
|
else:
|
23
23
|
super().create(do_apply, sysctl, excpts, oper)
|
libifstate/log.py
CHANGED
@@ -73,13 +73,16 @@ class IfStateLogging:
|
|
73
73
|
return IfStateLogging.ANSI_YELLOW
|
74
74
|
return ""
|
75
75
|
|
76
|
-
def __init__(self, level, handlers=[], action=None, log_stderr=True):
|
76
|
+
def __init__(self, level, global_level, handlers=[], action=None, log_stderr=True):
|
77
77
|
if level != logging.DEBUG:
|
78
78
|
sys.tracebacklimit = 0
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
if global_level:
|
81
|
+
logging.basicConfig(
|
82
|
+
level=level,
|
83
|
+
)
|
84
|
+
else:
|
85
|
+
logger.level = level
|
83
86
|
|
84
87
|
if log_stderr:
|
85
88
|
has_stderr = sys.stderr is not None
|
libifstate/netns/__init__.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from libifstate.exception import NetnsUnknown
|
1
2
|
from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr
|
2
3
|
from libifstate.sysctl import Sysctl
|
3
4
|
|
@@ -68,9 +69,11 @@ class NetNameSpace():
|
|
68
69
|
if peer_netns_name is None:
|
69
70
|
peer_ipr = root_ipr
|
70
71
|
peer_pid = 1
|
71
|
-
|
72
|
+
elif peer_netns_name in netns_name_map:
|
72
73
|
peer_ipr = netns_name_map[peer_netns_name]
|
73
74
|
peer_pid = peer_ipr.child
|
75
|
+
else:
|
76
|
+
raise NetnsUnknown(peer_netns_name)
|
74
77
|
|
75
78
|
result = self.ipr.get_netnsid(pid=peer_pid)
|
76
79
|
if result['nsid'] == 4294967295:
|
@@ -81,7 +84,7 @@ class NetNameSpace():
|
|
81
84
|
|
82
85
|
return (peer_ipr, peer_nsid)
|
83
86
|
|
84
|
-
def prepare_netns(do_apply, target_netns_list, new_netns_list):
|
87
|
+
def prepare_netns(do_apply, target_netns_list, new_netns_list, ignore_netns):
|
85
88
|
logger.info("configure network namespaces...")
|
86
89
|
|
87
90
|
# get mapping of netns names to lists of pids
|
@@ -94,14 +97,15 @@ def prepare_netns(do_apply, target_netns_list, new_netns_list):
|
|
94
97
|
for name in sorted(names_set):
|
95
98
|
# cleanup orphan netns
|
96
99
|
if name not in target_netns_list:
|
97
|
-
if name in
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
100
|
+
if not any(re.match(regex, name) for regex in ignore_netns):
|
101
|
+
if name in ns_pids:
|
102
|
+
logger.warning(
|
103
|
+
'pids: {}'.format(', '.join((str(x) for x in ns_pids[name]))),
|
104
|
+
extra={'iface': name})
|
105
|
+
logger.log_del(name)
|
106
|
+
|
107
|
+
if do_apply:
|
108
|
+
pyroute2.netns.remove(name)
|
105
109
|
|
106
110
|
# create missing netns
|
107
111
|
elif name not in current_netns_list or name in new_netns_list:
|
libifstate/routing/__init__.py
CHANGED
@@ -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
|
@@ -15,6 +15,147 @@ from socket import AF_INET, AF_INET6
|
|
15
15
|
def route_matches(r1, r2, fields=('dst', 'priority', 'proto'), indent=None):
|
16
16
|
return _matches(r1, r2, fields, indent)
|
17
17
|
|
18
|
+
def parse_route(route, implicit_defaults=True):
|
19
|
+
rt = {}
|
20
|
+
|
21
|
+
if implicit_defaults:
|
22
|
+
rt['type'] = route.get('type', 'unicast')
|
23
|
+
|
24
|
+
if 'to' in route:
|
25
|
+
dst = ip_network(route['to'])
|
26
|
+
rt['dst'] = dst.with_prefixlen
|
27
|
+
has_to = True
|
28
|
+
else:
|
29
|
+
has_to = False
|
30
|
+
|
31
|
+
for key, lookup in RT_LOOKUPS_DICT.items():
|
32
|
+
try:
|
33
|
+
if implicit_defaults:
|
34
|
+
rt[key] = route.get(key, RT_LOOKUPS_DEFAULTS[key])
|
35
|
+
rt[key] = lookup.lookup_id(rt[key])
|
36
|
+
else:
|
37
|
+
if key in route:
|
38
|
+
rt[key] = route[key]
|
39
|
+
rt[key] = lookup.lookup_id(rt[key])
|
40
|
+
except KeyError as err:
|
41
|
+
# mapping not available - catch exception and skip it
|
42
|
+
logger.warning('ignoring unknown %s "%s"', key, rt[key],
|
43
|
+
extra={'iface': rt['dst']})
|
44
|
+
rt[key] = RT_LOOKUPS_DEFAULTS[key]
|
45
|
+
|
46
|
+
if 'type' in rt and type(rt['type']) == str:
|
47
|
+
rt['type'] = rt_type[rt['type']]
|
48
|
+
|
49
|
+
if 'dev' in route:
|
50
|
+
rt['oif'] = route['dev']
|
51
|
+
|
52
|
+
if 'via' in route:
|
53
|
+
via = ip_address(route['via'])
|
54
|
+
|
55
|
+
if not has_to or via.version == dst.version:
|
56
|
+
rt['gateway'] = str(via)
|
57
|
+
else:
|
58
|
+
if via.version == 4:
|
59
|
+
rt['via'] = {
|
60
|
+
'family': int(AF_INET),
|
61
|
+
'addr': str(via),
|
62
|
+
}
|
63
|
+
else:
|
64
|
+
rt['via'] = {
|
65
|
+
'family': int(AF_INET6),
|
66
|
+
'addr': str(via),
|
67
|
+
}
|
68
|
+
|
69
|
+
if 'src' in route:
|
70
|
+
rt['prefsrc'] = route['src']
|
71
|
+
|
72
|
+
if 'preference' in route:
|
73
|
+
rt['priority'] = route['preference']
|
74
|
+
elif has_to and implicit_defaults:
|
75
|
+
if isinstance(dst, IPv6Network):
|
76
|
+
rt['priority'] = 1024
|
77
|
+
else:
|
78
|
+
rt['priority'] = 0
|
79
|
+
|
80
|
+
if 'vrrp' in route:
|
81
|
+
rt['_vrrp'] = route['vrrp']
|
82
|
+
|
83
|
+
return rt
|
84
|
+
|
85
|
+
def parse_routes(routes, implicit_defaults=True):
|
86
|
+
return [parse_route(route, implicit_defaults) for route in routes]
|
87
|
+
|
88
|
+
def parse_rule(rule, implicit_defaults=True):
|
89
|
+
ru = {}
|
90
|
+
|
91
|
+
if implicit_defaults:
|
92
|
+
ru['table'] = RTLookups.tables.lookup_id(rule.get('table', 254))
|
93
|
+
ru['protocol'] = RTLookups.protos.lookup_id(rule.get('proto', 0))
|
94
|
+
ru['tos'] = rule.get('tos', 0)
|
95
|
+
else:
|
96
|
+
if 'table' in rule:
|
97
|
+
ru['table'] = RTLookups.tables.lookup_id(rule['table'])
|
98
|
+
if 'proto' in rule:
|
99
|
+
ru['protocol'] = RTLookups.protos.lookup_id(rule['proto'])
|
100
|
+
if 'tos' in rule:
|
101
|
+
ru['tos'] = rule['tos']
|
102
|
+
|
103
|
+
if 'action' in rule and type(rule['action']) == str:
|
104
|
+
ru['action'] = {
|
105
|
+
"to_tbl": "FR_ACT_TO_TBL",
|
106
|
+
"unicast": "FR_ACT_UNICAST",
|
107
|
+
"blackhole": "FR_ACT_BLACKHOLE",
|
108
|
+
"unreachable": "FR_ACT_UNREACHABLE",
|
109
|
+
"prohibit": "FR_ACT_PROHIBIT",
|
110
|
+
"nat": "FR_ACT_NAT",
|
111
|
+
}.get(rule['action'], rule['action'])
|
112
|
+
elif implicit_defaults:
|
113
|
+
ru['action'] = "FR_ACT_TO_TBL"
|
114
|
+
|
115
|
+
if 'fwmark' in rule:
|
116
|
+
ru['fwmark'] = rule['fwmark']
|
117
|
+
|
118
|
+
if 'ipproto' in rule:
|
119
|
+
if type(rule['ipproto']) == str:
|
120
|
+
ru['ip_proto'] = socket.getprotobyname(rule['ipproto'])
|
121
|
+
else:
|
122
|
+
ru['ip_proto'] = rule['ipproto']
|
123
|
+
|
124
|
+
if 'to' in rule:
|
125
|
+
ru['dst'] = str(ip_network(rule['to']).network_address)
|
126
|
+
ru['dst_len'] = ip_network(rule['to']).prefixlen
|
127
|
+
|
128
|
+
if 'from' in rule:
|
129
|
+
if rule['from'] != "all":
|
130
|
+
ru['src'] = str(ip_interface(rule['from']).ip)
|
131
|
+
ru['src_len'] = ip_network(ip_interface(rule['from']).network).prefixlen
|
132
|
+
else:
|
133
|
+
ru['src'] = "0.0.0.0"
|
134
|
+
ru['src_len'] = 0
|
135
|
+
|
136
|
+
if 'iif' in rule:
|
137
|
+
ru['iifname'] = rule['iif']
|
138
|
+
|
139
|
+
if 'oif' in rule:
|
140
|
+
ru['oifname'] = rule['oif']
|
141
|
+
|
142
|
+
if 'priority' in rule:
|
143
|
+
ru['priority'] = rule['priority']
|
144
|
+
|
145
|
+
if 'vrrp' in rule:
|
146
|
+
ru['_vrrp'] = rule['vrrp']
|
147
|
+
|
148
|
+
if 'family' in rule:
|
149
|
+
assert rule['family'] in ("AF_INET", "inet", 2, "AF_INET6", "inet6", 10), "rule['family'] doesn't have a valid value. This should have already been rejected by the schema validation."
|
150
|
+
if rule['family'] in ("AF_INET", "inet", 2):
|
151
|
+
ru['family'] = AF_INET
|
152
|
+
elif rule['family'] in ("AF_INET6", "inet6", 10):
|
153
|
+
ru['family'] = AF_INET6
|
154
|
+
|
155
|
+
return ru
|
156
|
+
|
157
|
+
def parse_rules(rules, implicit_defaults=True):
|
158
|
+
return [parse_rule(rule, implicit_defaults) for rule in rules]
|
18
159
|
|
19
160
|
def rule_matches(r1, r2, fields=('priority', 'iif', 'oif', 'dst', 'metric', 'protocol'), indent=None):
|
20
161
|
return _matches(r1, r2, fields, indent)
|
@@ -133,64 +274,36 @@ class Tables(collections.abc.Mapping):
|
|
133
274
|
def __len__(self):
|
134
275
|
return len(self.tables)
|
135
276
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
rt[key] = RT_LOOKUPS_DEFAULTS[key]
|
152
|
-
|
153
|
-
if type(rt['type']) == str:
|
154
|
-
rt['type'] = rt_type[rt['type']]
|
155
|
-
|
156
|
-
if 'dev' in route:
|
157
|
-
rt['oif'] = route['dev']
|
158
|
-
|
159
|
-
if 'via' in route:
|
160
|
-
via = ip_address(route['via'])
|
161
|
-
|
162
|
-
if via.version == dst.version:
|
163
|
-
rt['gateway'] = str(via)
|
164
|
-
else:
|
165
|
-
if via.version == 4:
|
166
|
-
rt['via'] = {
|
167
|
-
'family': int(AF_INET),
|
168
|
-
'addr': str(via),
|
169
|
-
}
|
170
|
-
else:
|
171
|
-
rt['via'] = {
|
172
|
-
'family': int(AF_INET6),
|
173
|
-
'addr': str(via),
|
174
|
-
}
|
175
|
-
|
176
|
-
if 'src' in route:
|
177
|
-
rt['prefsrc'] = route['src']
|
277
|
+
def parse_ignores(self, ignores):
|
278
|
+
parsed = []
|
279
|
+
for ignore in ignores:
|
280
|
+
ignore = parse_route(ignore, False)
|
281
|
+
|
282
|
+
for attr in ['dev', 'oif', 'iif']:
|
283
|
+
if attr in ignore and type(ignore[attr]) == str:
|
284
|
+
idx = next(
|
285
|
+
iter(self.netns.ipr.link_lookup(ifname=ignore[attr])), None)
|
286
|
+
if idx is None:
|
287
|
+
logger.debug("{} '{}' is unknown, skipping ignore".format(attr, ignore[attr]))
|
288
|
+
continue
|
289
|
+
else:
|
290
|
+
ignore[attr] = idx
|
291
|
+
parsed.append(ignore)
|
178
292
|
|
179
|
-
|
180
|
-
rt['priority'] = route['preference']
|
181
|
-
elif isinstance(dst, IPv6Network):
|
182
|
-
rt['priority'] = 1024
|
183
|
-
else:
|
184
|
-
rt['priority'] = 0
|
293
|
+
return parsed
|
185
294
|
|
186
|
-
|
187
|
-
|
295
|
+
def add(self, route):
|
296
|
+
# convert config dict into IFLA keys and values
|
297
|
+
rt = parse_route(route)
|
188
298
|
|
299
|
+
# track route in the corresponding table
|
189
300
|
if not rt['table'] in self.tables:
|
190
301
|
self.tables[rt['table']] = []
|
191
302
|
self.tables[rt['table']].append(rt)
|
192
303
|
|
193
304
|
def show_routes(self, ignores):
|
305
|
+
ignores = self.parse_ignores(ignores)
|
306
|
+
|
194
307
|
routes = []
|
195
308
|
for route in self.netns.ipr.get_routes(family=AF_INET) + self.netns.ipr.get_routes(family=AF_INET6):
|
196
309
|
# skip routes from local table
|
@@ -322,6 +435,8 @@ class Tables(collections.abc.Mapping):
|
|
322
435
|
return routes
|
323
436
|
|
324
437
|
def apply(self, ignores, do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
|
438
|
+
ignores = self.parse_ignores(ignores)
|
439
|
+
|
325
440
|
for table, croutes in self.tables.items():
|
326
441
|
log_str = RTLookups.tables.lookup_str(table)
|
327
442
|
if self.netns.netns != None:
|
@@ -413,53 +528,10 @@ class Rules():
|
|
413
528
|
self.rules = []
|
414
529
|
|
415
530
|
def add(self, rule):
|
416
|
-
|
417
|
-
|
418
|
-
'protocol': RTLookups.protos.lookup_id(rule.get('proto', 0)),
|
419
|
-
'tos': rule.get('tos', 0),
|
420
|
-
}
|
421
|
-
|
422
|
-
if 'action' in rule and type(rule['action']) == str:
|
423
|
-
ru['action'] = {
|
424
|
-
"to_tbl": "FR_ACT_TO_TBL",
|
425
|
-
"unicast": "FR_ACT_UNICAST",
|
426
|
-
"blackhole": "FR_ACT_BLACKHOLE",
|
427
|
-
"unreachable": "FR_ACT_UNREACHABLE",
|
428
|
-
"prohibit": "FR_ACT_PROHIBIT",
|
429
|
-
"nat": "FR_ACT_NAT",
|
430
|
-
}.get(rule['action'], rule['action'])
|
431
|
-
else:
|
432
|
-
ru['action'] = rule.get('action'.lower(), "FR_ACT_TO_TBL")
|
433
|
-
|
434
|
-
if 'fwmark' in rule:
|
435
|
-
ru['fwmark'] = rule['fwmark']
|
436
|
-
|
437
|
-
if 'ipproto' in rule:
|
438
|
-
if type(rule['ipproto']) == str:
|
439
|
-
ru['ip_proto'] = socket.getprotobyname(rule['ipproto'])
|
440
|
-
else:
|
441
|
-
ru['ip_proto'] = rule['ipproto']
|
442
|
-
|
443
|
-
if 'to' in rule:
|
444
|
-
ru['dst'] = str(ip_network(rule['to']).network_address)
|
445
|
-
ru['dst_len'] = ip_network(rule['to']).prefixlen
|
446
|
-
|
447
|
-
if 'from' in rule:
|
448
|
-
ru['src'] = str(ip_network(rule['from']).network_address)
|
449
|
-
ru['src_len'] = ip_network(rule['from']).prefixlen
|
450
|
-
|
451
|
-
if 'iif' in rule:
|
452
|
-
ru['iifname'] = rule['iif']
|
453
|
-
|
454
|
-
if 'oif' in rule:
|
455
|
-
ru['oifname'] = rule['oif']
|
456
|
-
|
457
|
-
if 'priority' in rule:
|
458
|
-
ru['priority'] = rule['priority']
|
459
|
-
|
460
|
-
if 'vrrp' in rule:
|
461
|
-
ru['_vrrp'] = rule['vrrp']
|
531
|
+
# convert config dict into IFLA keys and values
|
532
|
+
ru = parse_rule(rule)
|
462
533
|
|
534
|
+
# track rule
|
463
535
|
self.rules.append(ru)
|
464
536
|
|
465
537
|
def kernel_rules(self):
|
@@ -491,14 +563,13 @@ class Rules():
|
|
491
563
|
return rules
|
492
564
|
|
493
565
|
def show_rules(self, ignores):
|
566
|
+
ignores = parse_rules(ignores, implicit_defaults=False)
|
567
|
+
|
494
568
|
rules = []
|
495
569
|
for rule in self.kernel_rules():
|
496
570
|
# skip ignored routes
|
497
571
|
ignore = False
|
498
572
|
for irule in ignores:
|
499
|
-
if 'proto' in irule:
|
500
|
-
irule['protocol'] = irule['proto']
|
501
|
-
del irule['proto']
|
502
573
|
if rule_matches(rule, irule, irule.keys()):
|
503
574
|
ignore = True
|
504
575
|
break
|
@@ -534,6 +605,9 @@ class Rules():
|
|
534
605
|
return rules
|
535
606
|
|
536
607
|
def apply(self, ignores, do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
|
608
|
+
ignores = parse_rules(ignores, implicit_defaults=False)
|
609
|
+
|
610
|
+
# get rules that are currently in the kernels list
|
537
611
|
krules = self.kernel_rules()
|
538
612
|
for rule in self.rules:
|
539
613
|
log_str = '#{}'.format(rule['priority'])
|
libifstate/tc/__init__.py
CHANGED
@@ -254,7 +254,7 @@ class TC():
|
|
254
254
|
return changes
|
255
255
|
|
256
256
|
def apply(self, do_apply):
|
257
|
-
excpts = ExceptionCollector(
|
257
|
+
excpts = ExceptionCollector(self.iface, self.netns)
|
258
258
|
|
259
259
|
# get ifindex
|
260
260
|
self.idx = next(iter(self.netns.ipr.link_lookup(ifname=self.iface)), None)
|
libifstate/wireguard/__init__.py
CHANGED
@@ -4,7 +4,9 @@ from wgnlpy import WireGuard as WG
|
|
4
4
|
from ipaddress import ip_network
|
5
5
|
import collections
|
6
6
|
from copy import deepcopy
|
7
|
+
import os
|
7
8
|
import pyroute2.netns
|
9
|
+
from pyroute2 import NetlinkError
|
8
10
|
import socket
|
9
11
|
|
10
12
|
class WireGuard():
|
@@ -56,6 +58,9 @@ class WireGuard():
|
|
56
58
|
try:
|
57
59
|
state = self.wg.get_interface(
|
58
60
|
self.iface, spill_private_key=True, spill_preshared_keys=True)
|
61
|
+
except NetlinkError as err:
|
62
|
+
logger.warning('query wireguard details failed: {}'.format(os.strerror(err.code)), extra={'iface': self.iface, 'netns': self.netns})
|
63
|
+
return
|
59
64
|
except TypeError as err:
|
60
65
|
# wgnlpy 0.1.5 can triggger a TypeError exception
|
61
66
|
# if the WGPEER_A_LAST_HANDSHAKE_TIME NLA does not
|
@@ -113,10 +118,10 @@ class WireGuard():
|
|
113
118
|
for setting in peer.keys():
|
114
119
|
attr = getattr(peers[pubkey], setting)
|
115
120
|
if setting == 'allowedips':
|
116
|
-
attr =
|
121
|
+
attr = [str(ip) for ip in attr]
|
117
122
|
logger.debug(' peer.%s: %s => %s', setting, attr,
|
118
123
|
peer[setting], extra={'iface': self.iface})
|
119
|
-
if type(attr) ==
|
124
|
+
if type(attr) == list:
|
120
125
|
pchange |= not (attr == peer[setting])
|
121
126
|
else:
|
122
127
|
pchange |= str(peer[setting]) != str(getattr(
|
File without changes
|
File without changes
|
File without changes
|