]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/bridge.py
addons: bridge: enable ipv6 on SVD brport
[mirror_ifupdown2.git] / ifupdown2 / addons / bridge.py
CommitLineData
35681c06 1#!/usr/bin/env python3
d486dd0d
JF
2#
3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
7import re
8import time
9import itertools
10
d486dd0d
JF
11from collections import Counter
12
13try:
5f4ab659 14 from ifupdown2.lib.addon import Bridge
223ba5af 15
d486dd0d
JF
16 import ifupdown2.ifupdown.exceptions as exceptions
17 import ifupdown2.ifupdown.policymanager as policymanager
18 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
19
20 from ifupdown2.nlmanager.nlmanager import Link
21
22 from ifupdown2.ifupdown.iface import *
23 from ifupdown2.ifupdown.utils import utils
d486dd0d
JF
24
25 from ifupdown2.ifupdownaddons.cache import *
d486dd0d 26 from ifupdown2.ifupdownaddons.modulebase import moduleBase
bd441a51 27except (ImportError, ModuleNotFoundError):
5f4ab659 28 from lib.addon import Bridge
223ba5af 29
d486dd0d
JF
30 import ifupdown.exceptions as exceptions
31 import ifupdown.policymanager as policymanager
32 import ifupdown.ifupdownflags as ifupdownflags
33
34 from nlmanager.nlmanager import Link
35
36 from ifupdown.iface import *
37 from ifupdown.utils import utils
d486dd0d
JF
38
39 from ifupdownaddons.cache import *
d486dd0d
JF
40 from ifupdownaddons.modulebase import moduleBase
41
42
43class bridgeFlags:
44 PORT_PROCESSED = 0x1
45 PORT_PROCESSED_OVERRIDE = 0x2
46
47
5f4ab659 48class bridge(Bridge, moduleBase):
d486dd0d
JF
49 """ ifupdown2 addon module to configure linux bridges """
50
223ba5af
JF
51 _modinfo = {
52 "mhelp": "Bridge configuration module. Supports both vlan aware and non "
53 "vlan aware bridges. For the vlan aware bridge, the port "
54 "specific attributes must be specified under the port. And for "
55 "vlan unaware bridge port specific attributes must be specified "
56 "under the bridge.",
57 "attrs": {
58 "bridge-vlan-aware": {
59 "help": "vlan aware bridge. Setting this "
60 "attribute to yes enables vlan filtering"
61 " on the bridge",
62 "validvals": ["yes", "no"],
63 "example": ["bridge-vlan-aware yes/no"],
64 "default": "no"
65 },
66 "bridge-ports": {
67 "help": "bridge ports",
68 "multivalue": True,
69 "required": True,
70 "validvals": ["<interface-list>"],
71 "example": [
72 "bridge-ports swp1.100 swp2.100 swp3.100",
73 "bridge-ports glob swp1-3.100",
74 "bridge-ports regex (swp[1|2|3].100)"
75 ]
76 },
77 "bridge-stp": {
78 "help": "bridge-stp yes/no",
79 "example": ["bridge-stp no"],
80 "validvals": ["yes", "on", "off", "no"],
81 "default": "no"
82 },
83 "bridge-bridgeprio": {
84 "help": "bridge priority",
85 "validrange": ["0", "65535"],
86 "example": ["bridge-bridgeprio 32768"],
87 "default": "32768"
88 },
89 "bridge-ageing": {
90 "help": "bridge ageing",
91 "validrange": ["0", "65535"],
92 "example": ["bridge-ageing 300"],
93 "default": "300"
94 },
95 "bridge-fd": {
96 "help": "bridge forward delay",
97 "validrange": ["0", "255"],
98 "example": ["bridge-fd 15"],
99 "default": "15"
100 },
101 # XXX: recheck values
102 "bridge-gcint": {
103 "help": "bridge garbage collection interval in secs",
104 "validrange": ["0", "255"],
105 "example": ["bridge-gcint 4"],
106 "default": "4",
107 "compat": True,
108 "deprecated": True
109 },
110 "bridge-hello": {
111 "help": "bridge set hello time",
112 "validrange": ["0", "255"],
113 "example": ["bridge-hello 2"],
114 "default": "2"
115 },
116 "bridge-maxage": {
117 "help": "bridge set maxage",
118 "validrange": ["0", "255"],
119 "example": ["bridge-maxage 20"],
120 "default": "20"
121 },
122 "bridge-pathcosts": {
123 "help": "bridge set port path costs",
124 "validvals": ["<interface-range-list>"],
125 "validrange": ["0", "65535"],
126 "example": [
127 "under the port (for vlan aware bridge): bridge-pathcosts 100",
128 "under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100"
129 ],
130 "default": "100"
131 },
132 "bridge-portprios": {
133 "help": "bridge port prios",
134 "validvals": ["<interface-range-list>"],
135 "validrange": ["0", "65535"],
136 "example": [
137 "under the port (for vlan aware bridge): bridge-portprios 32",
138 "under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32"
139 ],
140 },
141 "bridge-mclmc": {
142 "help": "set multicast last member count",
143 "validrange": ["0", "255"],
144 "example": ["bridge-mclmc 2"],
145 "default": "2"
146 },
147 "bridge-mcrouter": {
148 "help": "Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes",
149 "validvals": ["yes", "no", "0", "1", "2"],
150 "example": ["bridge-mcrouter 1"],
151 "default": "yes"
152 },
153 "bridge-mcsnoop": {
154 "help": "set multicast snooping",
155 "validvals": ["yes", "no", "0", "1"],
156 "default": "yes",
157 "example": ["bridge-mcsnoop yes"]
158 },
159 "bridge-mcsqc": {
160 "help": "set multicast startup query count",
161 "validrange": ["0", "255"],
162 "default": "2",
163 "example": ["bridge-mcsqc 2"]
164 },
165 "bridge-mcqifaddr": {
166 "help": "set multicast query to use ifaddr",
167 "validvals": ["yes", "no", "0", "1"],
168 "default": "no",
169 "example": ["bridge-mcqifaddr no"]
170 },
171 "bridge-mcquerier": {
172 "help": "set multicast querier",
173 "validvals": ["yes", "no", "0", "1"],
174 "default": "no",
175 "example": ["bridge-mcquerier no"]
176 },
177 "bridge-hashel": {
178 "help": "set hash elasticity",
179 "validrange": ["0", "4096"],
180 "default": "4",
181 "example": ["bridge-hashel 4096"]
182 },
183 "bridge-hashmax": {
184 "help": "set hash max",
185 "validrange": ["0", "4096"],
186 "default": "512",
187 "example": ["bridge-hashmax 4096"]
188 },
189 "bridge-mclmi": {
190 "help": "set multicast last member interval (in secs)",
191 "validrange": ["0", "255"],
192 "default": "1",
193 "example": ["bridge-mclmi 1"]
194 },
195 "bridge-mcmi": {
196 "help": "set multicast membership interval (in secs)",
223ba5af
JF
197 "default": "260",
198 "example": ["bridge-mcmi 260"]
199 },
200 "bridge-mcqpi": {
201 "help": "set multicast querier interval (in secs)",
202 "validrange": ["0", "255"],
203 "default": "255",
204 "example": ["bridge-mcqpi 255"]
205 },
206 "bridge-mcqi": {
207 "help": "set multicast query interval (in secs)",
208 "validrange": ["0", "255"],
209 "default": "125",
210 "example": ["bridge-mcqi 125"]
211 },
212 "bridge-mcqri": {
213 "help": "set multicast query response interval (in secs)",
214 "validrange": ["0", "255"],
215 "default": "10",
216 "example": ["bridge-mcqri 10"]
217 },
218 "bridge-mcsqi": {
219 "help": "set multicast startup query interval (in secs)",
220 "validrange": ["0", "255"],
221 "default": "31",
222 "example": ["bridge-mcsqi 31"]
223 },
224 "bridge-mcqv4src": {
225 "help": "set per VLAN v4 multicast querier source address",
226 "validvals": ["<number-ipv4-list>", ],
227 "multivalue": True,
228 "compat": True,
229 "example": ["bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1"]
230 },
231 "bridge-portmcrouter": {
232 "help": "Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled",
233 "validvals": ["<interface-disabled-automatic-enabled>"],
234 "example": [
235 "under the port (for vlan aware bridge): bridge-portmcrouter 0",
236 "under the port (for vlan aware bridge): bridge-portmcrouter 1",
237 "under the port (for vlan aware bridge): bridge-portmcrouter 2",
238 "under the port (for vlan aware bridge): bridge-portmcrouter disabled",
239 "under the port (for vlan aware bridge): bridge-portmcrouter automatic",
240 "under the port (for vlan aware bridge): bridge-portmcrouter enabled",
241 "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2",
242 "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled",
243 "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1",
244 ]
245 },
246 "bridge-portmcfl": {
247 "help": "port multicast fast leave.",
248 "validvals": ["<interface-yes-no-0-1-list>"],
249 "default": "no",
250 "example": [
251 "under the port (for vlan aware bridge): bridge-portmcfl no",
252 "under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no"
253 ]
254 },
255 "bridge-waitport": {
256 "help": "wait for a max of time secs for the"
257 " specified ports to become available,"
258 "if no ports are specified then those"
259 " specified on bridge-ports will be"
260 " used here. Specifying no ports here "
261 "should not be used if we are using "
262 "regex or \"all\" on bridge_ports,"
263 "as it wouldnt work.",
264 "default": "0",
265 "validvals": ["<number-interface-list>"],
266 "example": ["bridge-waitport 4 swp1 swp2"]
267 },
268 "bridge-maxwait": {
269 "help": "forces to time seconds the maximum time "
270 "that the Debian bridge setup scripts will "
271 "wait for the bridge ports to get to the "
272 "forwarding status, doesn\"t allow factional "
273 "part. If it is equal to 0 then no waiting"
274 " is done",
275 "validrange": ["0", "255"],
276 "default": "0",
277 "example": ["bridge-maxwait 3"]
278 },
279 "bridge-vids": {
280 "help": "bridge port vids. Can be specified "
281 "under the bridge or under the port. "
282 "If specified under the bridge the ports "
283 "inherit it unless overridden by a "
284 "bridge-vids attribute under the port",
285 "multivalue": True,
286 "validvals": ["<number-comma-range-list>"],
287 "example": [
288 "bridge-vids 4000",
289 "bridge-vids 2000 2200-3000"
290 ],
291 "aliases": ["bridge-trunk"]
292 },
293 "bridge-pvid": {
294 "help": "bridge port pvid. Must be specified under"
295 " the bridge port",
296 "validrange": ["0", "4096"],
297 "example": ["bridge-pvid 1"]
298 },
299 "bridge-access": {
300 "help": "bridge port access vlan. Must be "
301 "specified under the bridge port",
302 "validrange": ["1", "4094"],
303 "example": ["bridge-access 300"]
304 },
305 "bridge-allow-untagged": {
306 "help": "indicate if the bridge port accepts "
307 "untagged packets or not. Must be "
308 "specified under the bridge port. "
309 "Default is \"yes\"",
310 "validvals": ["yes", "no"],
311 "example": ["bridge-allow-untagged yes"],
312 "default": "yes"
313 },
314 "bridge-port-vids": {
315 "help": "bridge vlans",
316 "compat": True,
317 "example": ["bridge-port-vids bond0=1-1000,1010-1020"]
318 },
319 "bridge-port-pvids": {
320 "help": "bridge port vlans",
321 "compat": True,
322 "example": ["bridge-port-pvids bond0=100 bond1=200"]
323 },
324 "bridge-learning": {
325 "help": "bridge port learning flag",
326 "validvals": ["on", "off", "<interface-on-off-list>"],
327 "default": "on",
328 "example": ["bridge-learning off"]
329 },
330 "bridge-igmp-version": {
331 "help": "mcast igmp version",
332 "validvals": ["2", "3"],
333 "default": "2",
334 "example": ["bridge-igmp-version 2"]
335 },
336 "bridge-mld-version": {
337 "help": "mcast mld version",
338 "validvals": ["1", "2"],
339 "default": "1",
340 "example": ["bridge-mld-version 1"]
341 },
342 "bridge-unicast-flood": {
343 "help": "bridge port unicast flood flag",
344 "validvals": ["on", "off", "<interface-on-off-list>"],
345 "default": "on",
346 "example": ["under the port (for vlan aware bridge): bridge-unicast-flood on",
347 "under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on"]
348 },
349 "bridge-multicast-flood": {
350 "help": "bridge port multicast flood flag",
351 "validvals": ["on", "off", "<interface-on-off-list>"],
352 "default": "on",
353 "example": [
354 "under the port (for vlan aware bridge): bridge-multicast-flood on",
355 "under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on"
356 ]
357 },
358 "bridge-broadcast-flood": {
359 "help": "bridge port broadcast flood flag",
360 "validvals": ["on", "off", "<interface-on-off-list>"],
361 "default": "on",
362 "example": [
363 "under the port (for vlan aware bridge): bridge-broadcast-flood on",
364 "under the bridge (for vlan unaware bridge): bridge-broadcast-flood swp1=on swp2=on"
365 ]
366 },
367 "bridge-vlan-protocol": {
368 "help": "bridge vlan protocol",
369 "default": "802.1q",
370 "validvals": ["802.1q", "802.1ad"],
371 "example": ["bridge-vlan-protocol 802.1q"]
372 },
373 "bridge-vlan-stats": {
374 "help": "bridge vlan stats",
375 "default": "off",
376 "validvals": ["on", "off"],
377 "example": ["bridge-vlan-stats off"]
378 },
379 "bridge-arp-nd-suppress": {
380 "help": "bridge port arp nd suppress flag",
381 "validvals": ["on", "off", "<interface-on-off-list>"],
382 "default": "off",
383 "example": [
384 "under the port (for vlan aware bridge): bridge-arp-nd-suppress on",
385 "under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on"
386 ]
387 },
388 "bridge-mcstats": {
389 "help": "bridge multicast stats",
390 "default": "off",
391 "validvals": ["on", "off", "1", "0", "yes", "no"],
392 "example": ["bridge-mcstats off"]
393 },
394 "bridge-l2protocol-tunnel": {
395 "help": "layer 2 protocol tunneling",
396 "validvals": [ # XXX: lists all combinations, should move to
397 # a better representation
398 "all",
399 "cdp",
400 "cdp lacp",
401 "cdp lacp lldp",
402 "cdp lacp lldp pvst",
403 "cdp lacp lldp stp",
404 "cdp lacp pvst",
405 "cdp lacp pvst stp",
406 "cdp lacp stp",
407 "cdp lldp",
408 "cdp lldp pvst",
409 "cdp lldp pvst stp",
410 "cdp lldp stp",
411 "cdp pvst",
412 "cdp pvst stp",
413 "cdp stp",
414 "lacp",
415 "lacp lldp",
416 "lacp lldp pvst",
417 "lacp lldp pvst stp",
418 "lacp lldp stp",
419 "lacp pvst",
420 "lacp pvst stp",
421 "lacp stp",
422 "lldp",
423 "lldp pvst",
424 "lldp pvst stp",
425 "lldp stp",
426 "pvst",
427 "pvst stp",
428 "stp",
429 "<interface-l2protocol-tunnel-list>"],
430 "example": [
431 "under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all",
432 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst",
433 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst",
434 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp",
435 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel all"
436 ]
437 },
438 "bridge-ports-condone-regex": {
439 "help": "bridge ports to ignore/condone when reloading config / removing interfaces",
440 "required": False,
441 "example": ["bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$"]
442 },
e537a6e6
JF
443 "bridge-vlan-vni-map": {
444 "help": "Single vxlan support",
f54c45de 445 "example": ["bridge-vlan-vni-map 1000-1001=1000-1001"],
e537a6e6 446 },
7840bd2c
JF
447 "bridge-always-up": {
448 "help": "Enabling this attribute on a bridge will enslave a dummy interface to the bridge",
449 "required": False,
450 "validvals": ["yes", "no", "on", "off"]
451 }
223ba5af
JF
452 }
453 }
454
455 bridge_utils_missing_warning = True
d486dd0d
JF
456
457 # Netlink attributes not associated with ifupdown2
458 # attributes are left commented-out for a future use
459 # and kept in order :)
223ba5af 460 _ifla_br_attributes_map = {
d486dd0d 461 # Link.IFLA_BR_UNSPEC,
223ba5af
JF
462 'bridge-fd': Link.IFLA_BR_FORWARD_DELAY,
463 'bridge-hello': Link.IFLA_BR_HELLO_TIME,
464 'bridge-maxage': Link.IFLA_BR_MAX_AGE,
465 'bridge-ageing': Link.IFLA_BR_AGEING_TIME,
466 'bridge-stp': Link.IFLA_BR_STP_STATE,
01f27c67 467 # 'bridge-bridgeprio': Link.IFLA_BR_PRIORITY,
223ba5af
JF
468 'bridge-vlan-aware': Link.IFLA_BR_VLAN_FILTERING,
469 'bridge-vlan-protocol': Link.IFLA_BR_VLAN_PROTOCOL,
d486dd0d
JF
470 # Link.IFLA_BR_GROUP_FWD_MASK,
471 # Link.IFLA_BR_ROOT_ID,
472 # Link.IFLA_BR_BRIDGE_ID,
473 # Link.IFLA_BR_ROOT_PORT,
474 # (Link.IFLA_BR_ROOT_PATH_COST,,
475 # Link.IFLA_BR_TOPOLOGY_CHANGE,
476 # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
477 # Link.IFLA_BR_HELLO_TIMER,
478 # Link.IFLA_BR_TCN_TIMER,
479 # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
480 # Link.IFLA_BR_GC_TIMER,
481 # Link.IFLA_BR_GROUP_ADDR,
482 # Link.IFLA_BR_FDB_FLUSH,
223ba5af 483 'bridge-mcrouter': Link.IFLA_BR_MCAST_ROUTER,
d486dd0d 484 #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr
223ba5af
JF
485 'bridge-mcqifaddr': Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
486 'bridge-mcquerier': Link.IFLA_BR_MCAST_QUERIER,
487 'bridge-hashel': Link.IFLA_BR_MCAST_HASH_ELASTICITY,
488 'bridge-hashmax': Link.IFLA_BR_MCAST_HASH_MAX,
489 'bridge-mclmc': Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
490 'bridge-mcsqc': Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
491 'bridge-mclmi': Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
492 'bridge-mcmi': Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
493 'bridge-mcqpi': Link.IFLA_BR_MCAST_QUERIER_INTVL,
494 'bridge-mcqi': Link.IFLA_BR_MCAST_QUERY_INTVL,
495 'bridge-mcqri': Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
496 'bridge-mcsqi': Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
d486dd0d
JF
497 # Link.IFLA_BR_NF_CALL_IPTABLES,
498 # Link.IFLA_BR_NF_CALL_IP6TABLES,
499 # Link.IFLA_BR_NF_CALL_ARPTABLES,
500 # Link.IFLA_BR_VLAN_DEFAULT_PVID,
501 # Link.IFLA_BR_PAD,
502 # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), # already dealt with, in a separate loop
223ba5af
JF
503 'bridge-igmp-version': Link.IFLA_BR_MCAST_IGMP_VERSION,
504 'bridge-mcstats': Link.IFLA_BR_MCAST_STATS_ENABLED,
505 'bridge-mld-version': Link.IFLA_BR_MCAST_MLD_VERSION
506 }
d486dd0d
JF
507 # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today
508 # they are supported. It is done this way because this dictionary is used
509 # in a loop, but these attributes require additional work. Thus they are
510 # excluded from this loop without overhead.
511
d486dd0d
JF
512 _ifla_br_attributes_translate_user_config_to_netlink_map = dict(
513 (
514 # Link.IFLA_BR_UNSPEC,
515 (Link.IFLA_BR_FORWARD_DELAY, lambda x: int(x) * 100),
516 (Link.IFLA_BR_HELLO_TIME, lambda x: int(x) * 100),
517 (Link.IFLA_BR_MAX_AGE, lambda x: int(x) * 100),
518 (Link.IFLA_BR_AGEING_TIME, lambda x: int(x) * 100),
519 # Link.IFLA_BR_STP_STATE, # STP is treated outside the loop
520 (Link.IFLA_BR_PRIORITY, int),
521 (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string),
223ba5af 522 (Link.IFLA_BR_VLAN_PROTOCOL, str.upper),
d486dd0d
JF
523 # Link.IFLA_BR_GROUP_FWD_MASK,
524 # Link.IFLA_BR_ROOT_ID,
525 # Link.IFLA_BR_BRIDGE_ID,
526 # Link.IFLA_BR_ROOT_PORT,
527 # Link.IFLA_BR_ROOT_PATH_COST,
528 # Link.IFLA_BR_TOPOLOGY_CHANGE,
529 # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
530 # Link.IFLA_BR_HELLO_TIMER,
531 # Link.IFLA_BR_TCN_TIMER,
532 # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
533 # Link.IFLA_BR_GC_TIMER,
534 # Link.IFLA_BR_GROUP_ADDR,
535 # Link.IFLA_BR_FDB_FLUSH,
536 (Link.IFLA_BR_MCAST_ROUTER, utils.get_int_from_boolean_and_string),
537 (Link.IFLA_BR_MCAST_SNOOPING, utils.get_boolean_from_string),
538 (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, utils.get_boolean_from_string),
539 (Link.IFLA_BR_MCAST_QUERIER, utils.get_boolean_from_string),
540 (Link.IFLA_BR_MCAST_HASH_ELASTICITY, int),
541 (Link.IFLA_BR_MCAST_HASH_MAX, int),
542 (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, int),
543 (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, int),
544 (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, lambda x: int(x) * 100),
545 (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, lambda x: int(x) * 100),
546 (Link.IFLA_BR_MCAST_QUERIER_INTVL, lambda x: int(x) * 100),
547 (Link.IFLA_BR_MCAST_QUERY_INTVL, lambda x: int(x) * 100),
548 (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, lambda x: int(x) * 100),
549 (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, lambda x: int(x) * 100),
550 # Link.IFLA_BR_NF_CALL_IPTABLES,
551 # Link.IFLA_BR_NF_CALL_IP6TABLES,
552 # Link.IFLA_BR_NF_CALL_ARPTABLES,
553 # Link.IFLA_BR_VLAN_DEFAULT_PVID,
554 # Link.IFLA_BR_PAD,
555 (Link.IFLA_BR_VLAN_STATS_ENABLED, utils.get_boolean_from_string),
556 (Link.IFLA_BR_MCAST_IGMP_VERSION, int),
557 (Link.IFLA_BR_MCAST_STATS_ENABLED, utils.get_boolean_from_string),
558 (Link.IFLA_BR_MCAST_MLD_VERSION, int)
559 )
560 )
561
223ba5af 562 _ifla_brport_attributes_map = {
d486dd0d
JF
563 # Link.IFLA_BRPORT_UNSPEC,
564 # Link.IFLA_BRPORT_STATE,
223ba5af
JF
565 'bridge-portprios': Link.IFLA_BRPORT_PRIORITY,
566 'bridge-pathcosts': Link.IFLA_BRPORT_COST,
d486dd0d
JF
567 # Link.IFLA_BRPORT_MODE,
568 # Link.IFLA_BRPORT_GUARD,
569 # Link.IFLA_BRPORT_PROTECT,
223ba5af
JF
570 'bridge-portmcfl': Link.IFLA_BRPORT_FAST_LEAVE,
571 'bridge-learning': Link.IFLA_BRPORT_LEARNING,
572 'bridge-unicast-flood': Link.IFLA_BRPORT_UNICAST_FLOOD,
d486dd0d
JF
573 # Link.IFLA_BRPORT_PROXYARP,
574 # Link.IFLA_BRPORT_LEARNING_SYNC,
575 # Link.IFLA_BRPORT_PROXYARP_WIFI,
576 # Link.IFLA_BRPORT_ROOT_ID,
577 # Link.IFLA_BRPORT_BRIDGE_ID,
578 # Link.IFLA_BRPORT_DESIGNATED_PORT,
579 # Link.IFLA_BRPORT_DESIGNATED_COST,
580 # Link.IFLA_BRPORT_ID,
581 # Link.IFLA_BRPORT_NO,
582 # Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
583 # Link.IFLA_BRPORT_CONFIG_PENDING,
584 # Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
585 # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
586 # Link.IFLA_BRPORT_HOLD_TIMER,
587 # Link.IFLA_BRPORT_FLUSH,
223ba5af 588 'bridge-portmcrouter': Link.IFLA_BRPORT_MULTICAST_ROUTER,
d486dd0d 589 # Link.IFLA_BRPORT_PAD,
223ba5af 590 'bridge-multicast-flood': Link.IFLA_BRPORT_MCAST_FLOOD,
d486dd0d
JF
591 # Link.IFLA_BRPORT_MCAST_TO_UCAST,
592 # Link.IFLA_BRPORT_VLAN_TUNNEL,
223ba5af
JF
593 'bridge-broadcast-flood': Link.IFLA_BRPORT_BCAST_FLOOD,
594 'bridge-l2protocol-tunnel': Link.IFLA_BRPORT_GROUP_FWD_MASK,
d486dd0d
JF
595 # Link.IFLA_BRPORT_PEER_LINK,
596 # Link.IFLA_BRPORT_DUAL_LINK,
223ba5af
JF
597 'bridge-arp-nd-suppress': Link.IFLA_BRPORT_NEIGH_SUPPRESS,
598 }
d486dd0d
JF
599
600 _ifla_brport_multicast_router_dict_to_int = {
601 'disabled': 0,
602 '0': 0,
603 'no': 0,
604 'automatic': 1,
605 '1': 1,
606 'yes': 1,
607 'enabled': 2,
608 '2': 2,
609 }
610
223ba5af
JF
611 _ifla_brport_multicast_router_dict_int_to_str = {
612 0: "disabled",
613 1: "automatic",
614 2: "enabled"
615 }
616
d486dd0d
JF
617 # callable to translate <interface-yes-no-0-1-list> to netlink value
618 _ifla_brport_attributes_translate_user_config_to_netlink_map = dict(
619 (
620 (Link.IFLA_BRPORT_PRIORITY, int),
621 (Link.IFLA_BRPORT_COST, int),
622 (Link.IFLA_BRPORT_MULTICAST_ROUTER, lambda x: bridge._ifla_brport_multicast_router_dict_to_int.get(x, 0)),
623 (Link.IFLA_BRPORT_FAST_LEAVE, utils.get_boolean_from_string),
624 (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string),
625 (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string),
626 (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string),
223ba5af 627 (Link.IFLA_BRPORT_BCAST_FLOOD, utils.get_boolean_from_string),
d486dd0d 628 (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
223ba5af 629 (Link.IFLA_BRPORT_NEIGH_SUPPRESS, utils.get_boolean_from_string)
d486dd0d
JF
630 )
631 )
632
633 def __init__(self, *args, **kargs):
5f4ab659 634 Bridge.__init__(self)
d486dd0d 635 moduleBase.__init__(self, *args, **kargs)
d486dd0d 636 self.name = self.__class__.__name__
d486dd0d
JF
637 self._resv_vlan_range = self._get_reserved_vlan_range()
638 self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range)))
639
640 self.default_stp_on = utils.get_boolean_from_string(
641 policymanager.policymanager_api.get_attr_default(
642 module_name=self.__class__.__name__,
643 attr='bridge-stp'
644 )
645 )
646
647 self.default_vlan_stats = policymanager.policymanager_api.get_attr_default(
648 module_name=self.__class__.__name__,
649 attr='bridge-vlan-stats'
650 )
651
652 self.warn_on_untagged_bridge_absence = utils.get_boolean_from_string(
653 policymanager.policymanager_api.get_module_globals(
654 module_name=self.__class__.__name__,
655 attr='warn_on_untagged_bridge_absence'
656 )
657 )
658 self.logger.debug('bridge: init: warn_on_untagged_bridge_absence=%s'
659 % self.warn_on_untagged_bridge_absence)
660
661 self._vxlan_bridge_default_igmp_snooping = policymanager.policymanager_api.get_module_globals(
662 self.__class__.__name__,
663 'vxlan_bridge_default_igmp_snooping'
664 )
665 self.logger.debug('bridge: init: vxlan_bridge_default_igmp_snooping=%s'
666 % self._vxlan_bridge_default_igmp_snooping)
667
668 self.arp_nd_suppress_only_on_vxlan = utils.get_boolean_from_string(
669 policymanager.policymanager_api.get_module_globals(
670 module_name=self.__class__.__name__,
671 attr='allow_arp_nd_suppress_only_on_vxlan'
672 )
673 )
674 self.logger.debug('bridge: init: arp_nd_suppress_only_on_vxlan=%s' % self.arp_nd_suppress_only_on_vxlan)
675
1b845b1b
AP
676 self.bridge_always_up_dummy_brport = policymanager.policymanager_api.get_module_globals(
677 module_name=self.__class__.__name__,
678 attr='bridge_always_up_dummy_brport'
679 )
680 self.logger.debug('bridge: init: bridge_always_up_dummy_brport=%s' % self.bridge_always_up_dummy_brport)
681
d486dd0d
JF
682 try:
683 self.bridge_allow_multiple_vlans = utils.get_boolean_from_string(
684 self.sysctl_get('net.bridge.bridge-allow-multiple-vlans')
685 )
3218f49d 686 except Exception:
d486dd0d
JF
687 # Cumulus Linux specific variable. Failure probably means that
688 # ifupdown2 is running a a different system.
689 self.bridge_allow_multiple_vlans = True
690 self.logger.debug('bridge: init: multiple vlans allowed %s' % self.bridge_allow_multiple_vlans)
691
692 self.bridge_mac_iface_list = policymanager.policymanager_api.get_module_globals(self.__class__.__name__, 'bridge_mac_iface') or []
693 self.bridge_mac_iface = None, None # ifname, mac
694
695 self.bridge_set_static_mac_from_port = utils.get_boolean_from_string(
696 policymanager.policymanager_api.get_module_globals(
697 self.__class__.__name__, 'bridge_set_static_mac_from_port'
698 )
699 )
700
80adb3c1
JF
701 self.vxlan_bridge_igmp_snooping_enable_port_mcrouter = utils.get_boolean_from_string(
702 policymanager.policymanager_api.get_module_globals(
703 module_name=self.__class__.__name__,
704 attr="vxlan_bridge_igmp_snooping_enable_port_mcrouter"
705 ),
706 default=True
707 )
708
223ba5af
JF
709 self.bridge_vxlan_arp_nd_suppress = utils.get_boolean_from_string(
710 policymanager.policymanager_api.get_module_globals(
711 module_name=self.__class__.__name__,
712 attr="bridge-vxlan-arp-nd-suppress"
713 ),
714 default=False
715 )
716 self.bridge_vxlan_arp_nd_suppress_int = int(self.bridge_vxlan_arp_nd_suppress)
717
d486dd0d
JF
718 self.l2protocol_tunnel_callback = {
719 'all': self._l2protocol_tunnel_set_all,
720 'stp': self._l2protocol_tunnel_set_stp,
721 'cdp': self._l2protocol_tunnel_set_cdp,
722 'pvst': self._l2protocol_tunnel_set_pvst,
723 'lldp': self._l2protocol_tunnel_set_lldp,
724 'lacp': self._l2protocol_tunnel_set_lacp
725 }
726
727 self.query_check_l2protocol_tunnel_callback = {
728 'all': self._query_check_l2protocol_tunnel_all,
729 'stp': self._query_check_l2protocol_tunnel_stp,
730 'cdp': self._query_check_l2protocol_tunnel_cdp,
731 'pvst': self._query_check_l2protocol_tunnel_pvst,
732 'lldp': self._query_check_l2protocol_tunnel_lldp,
733 'lacp': self._query_check_l2protocol_tunnel_lacp
734 }
735
223ba5af
JF
736 self._bridge_attribute_query_check_handler = {
737 "bridge-maxwait": (self._query_check_br_attr_wait, None),
738 "bridge-waitport": (self._query_check_br_attr_wait, None),
739
740 "bridge-stp": (self._query_check_br_attr_stp, Link.IFLA_BR_STP_STATE),
741
742 "bridge-mcstats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_MCAST_STATS_ENABLED),
743 "bridge-vlan-stats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_VLAN_STATS_ENABLED),
744
745 "bridge-vlan-aware": (self._query_check_br_attr_boolean, Link.IFLA_BR_VLAN_FILTERING),
746 "bridge-mcqifaddr": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
747 "bridge-mcsnoop": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_SNOOPING),
748 "bridge-mcquerier": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERIER),
749 "bridge-mcrouter": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_ROUTER),
750
751 "bridge-vlan-protocol": (self._query_check_br_attr_string, Link.IFLA_BR_VLAN_PROTOCOL),
752
753 "bridge-mcsqc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
754 "bridge-mclmc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
755 "bridge-hashmax": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_MAX),
756 "bridge-hashel": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_ELASTICITY),
757 "bridge-bridgeprio": (self._query_check_br_attr_int, Link.IFLA_BR_PRIORITY),
758 "bridge-igmp-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_IGMP_VERSION),
759 "bridge-mld-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_MLD_VERSION),
760
761 "bridge-maxage": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MAX_AGE),
762 "bridge-fd": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_FORWARD_DELAY),
763 "bridge-hello": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_HELLO_TIME),
764 "bridge-ageing": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_AGEING_TIME),
765 "bridge-mcmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
766 "bridge-mcsqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
767 "bridge-mclmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
768 "bridge-mcqri": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
769 "bridge-mcqpi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERIER_INTVL),
770 "bridge-mcqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_INTVL),
771 }
772
773 self._brport_attribute_query_check_handler = {
774 "bridge-pathcosts": self._query_check_brport_attr_int,
775 "bridge-portprios": self._query_check_brport_attr_int,
776 "bridge-portmcfl": self._query_check_brport_attr_boolean_yes_no,
777 "bridge-learning": self._query_check_brport_attr_boolean_on_off,
778 "bridge-arp-nd-suppress": self._query_check_brport_attr_boolean_on_off,
779 "bridge-unicast-flood": self._query_check_brport_attr_boolean_on_off,
780 "bridge-multicast-flood": self._query_check_brport_attr_boolean_on_off,
781 "bridge-broadcast-flood": self._query_check_brport_attr_boolean_on_off,
782 "bridge-portmcrouter": self._query_check_brport_attr_portmcrouter,
783 }
784
785 self.bridge_vxlan_port_learning = utils.get_boolean_from_string(
786 policymanager.policymanager_api.get_module_globals(
787 self.__class__.__name__,
788 "bridge_vxlan_port_learning"
789 ),
790 default=True
791 )
792
20649b09
JF
793 # To avoid disabling ipv6 on SVD we need to keep track of them
794 self.svd_list = set()
795
d486dd0d
JF
796 @staticmethod
797 def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
798 if not ifla_brport_group_maskhi:
799 ifla_brport_group_maskhi = 0x1
800 else:
801 ifla_brport_group_maskhi |= 0x1
d486dd0d
JF
802 return ifla_brport_group_mask, ifla_brport_group_maskhi
803
804 @staticmethod
805 def _l2protocol_tunnel_set_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
806 if not ifla_brport_group_maskhi:
807 ifla_brport_group_maskhi = 0x2
808 else:
809 ifla_brport_group_maskhi |= 0x2
d486dd0d
JF
810 return ifla_brport_group_mask, ifla_brport_group_maskhi
811
812 @staticmethod
813 def _l2protocol_tunnel_set_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
814 if not ifla_brport_group_mask:
815 ifla_brport_group_mask = 0x1
816 else:
817 ifla_brport_group_mask |= 0x1
d486dd0d
JF
818 return ifla_brport_group_mask, ifla_brport_group_maskhi
819
820 @staticmethod
821 def _l2protocol_tunnel_set_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
822 if not ifla_brport_group_mask:
823 ifla_brport_group_mask = 0x4
824 else:
825 ifla_brport_group_mask |= 0x4
d486dd0d
JF
826 return ifla_brport_group_mask, ifla_brport_group_maskhi
827
828 @staticmethod
829 def _l2protocol_tunnel_set_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
830 if not ifla_brport_group_mask:
831 ifla_brport_group_mask = 0x4000
832 else:
833 ifla_brport_group_mask |= 0x4000
d486dd0d
JF
834 return ifla_brport_group_mask, ifla_brport_group_maskhi
835
836 @staticmethod
837 def _l2protocol_tunnel_set_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
838 # returns new values for ifla_brport_group_mask and ifla_brport_group_maskhi
839 return 0x1 | 0x4 | 0x4000, 0x1 | 0x2
840
841 @staticmethod
842 def _query_check_l2protocol_tunnel_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 843 return ifla_brport_group_mask and ifla_brport_group_mask & 0x1
d486dd0d
JF
844
845 @staticmethod
846 def _query_check_l2protocol_tunnel_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 847 return ifla_brport_group_maskhi and ifla_brport_group_maskhi & 0x2
d486dd0d
JF
848
849 @staticmethod
850 def _query_check_l2protocol_tunnel_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 851 return ifla_brport_group_maskhi and ifla_brport_group_maskhi & 0x1
d486dd0d
JF
852
853 @staticmethod
854 def _query_check_l2protocol_tunnel_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 855 return ifla_brport_group_mask and ifla_brport_group_mask & 0x4000
d486dd0d
JF
856
857 @staticmethod
858 def _query_check_l2protocol_tunnel_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 859 return ifla_brport_group_mask and ifla_brport_group_mask & 0x4
d486dd0d
JF
860
861 @staticmethod
862 def _query_check_l2protocol_tunnel_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
863 return ifla_brport_group_mask == (0x1 | 0x4 | 0x4000) and ifla_brport_group_maskhi == (0x1 | 0x2)
864
865 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
866 retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc)
867 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
868 if not self.check_bridge_port_vid_attrs(ifaceobj):
869 retval = False
870 c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_getfunc)
871 c2 = self.syntax_check_bridge_allow_multiple_vlans(ifaceobj, ifaceobj_getfunc)
872 return retval and c1 #and c2
873
874 def syntax_check_bridge_allow_multiple_vlans(self, ifaceobj, ifaceobj_getfunc):
875 result = True
876 if not self.bridge_allow_multiple_vlans and ifaceobj.link_kind & ifaceLinkKind.BRIDGE and ifaceobj.lowerifaces:
877 vlan_id = None
878 for brport_name in ifaceobj.lowerifaces:
879 for obj in ifaceobj_getfunc(brport_name) or []:
880 if obj.link_kind & ifaceLinkKind.VLAN:
881 sub_intf_vlan_id = self._get_vlan_id(obj)
882 if vlan_id and vlan_id != sub_intf_vlan_id:
883 self.logger.error('%s: ignore %s: multiple vlans not allowed under bridge '
884 '(sysctl net.bridge.bridge-allow-multiple-vlans not set)'
885 % (ifaceobj.name, brport_name))
886 result = False
887 continue
888 vlan_id = sub_intf_vlan_id
889 return result
890
891 def check_bridge_port_vid_attrs(self, ifaceobj):
892 if (ifaceobj.get_attr_value('bridge-access') and
893 (self.get_ifaceobj_bridge_vids_value(ifaceobj) or
894 ifaceobj.get_attr_value('bridge-pvid'))):
c46af1c9 895 self.logger.warning('%s: bridge-access given, bridge-vids and bridge-pvid '
d486dd0d
JF
896 'will be ignored' % ifaceobj.name)
897 return False
898 return True
899
900 def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc):
901 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
902 ports = self._get_bridge_port_list(ifaceobj)
903 if not ports:
904 return True
905 result = True
906 for port_name in ports:
907 port_obj_l = ifaceobj_getfunc(port_name)
908 if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN:
909 self.logger.error('%s: %s: vlan sub-interface is not '
910 'supported in a vlan-aware bridge'
911 % (ifaceobj.name, port_name))
912 result = False
913 if (port_obj_l and
914 port_obj_l[0].get_attr_value('bridge-arp-nd-suppress') and
915 self.arp_nd_suppress_only_on_vxlan and
916 not port_obj_l[0].link_kind & ifaceLinkKind.VXLAN):
917 self.log_error('\'bridge-arp-nd-suppress\' is not '
918 'supported on a non-vxlan port %s'
919 %port_obj_l[0].name)
920 result = False
921 return result
922 return True
923
924 def _error_vxlan_in_vlan_aware_br(self, ifaceobj, bridgename):
d38e9b12
JF
925 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
926 self.log_error('`bridge-access` attribute is mandatory when vxlan '
927 'device (%s) is part of vlan aware bridge (%s)'
928 % (ifaceobj.name, bridgename), ifaceobj)
929 return False
930 return True
d486dd0d
JF
931
932 def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
933 if not ifaceobj_getfunc:
934 return True
e537a6e6 935 if (ifaceobj.link_kind & ifaceLinkKind.VXLAN and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
d486dd0d
JF
936 if ifaceobj.get_attr_value('bridge-access'):
937 return True
938 for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
939 ifaceobj_upper_list = ifaceobj_getfunc(iface)
940 if not ifaceobj_upper_list:
941 continue
942 ifaceobj_upper = ifaceobj_upper_list[0]
943 bridge_vids = self._get_bridge_vids(iface, ifaceobj_getfunc)
944 if ifaceobj_upper.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
945 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
946 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
947 if (not vids
948 or not pvid
949 or not self._compare_vids(bridge_vids,
950 vids,
951 pvid=pvid)):
d38e9b12
JF
952 if not self._error_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_upper.name):
953 return False
d486dd0d
JF
954 return True
955
956 @staticmethod
957 def _is_bridge(ifaceobj):
958 return (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
959 ifaceobj.get_attr_value_first('bridge-ports') or
960 ifaceobj.get_attr_value_first('bridge-vlan-aware'))
961
d1265fd5
JF
962 def _get_ifaceobj_bridge_ports(self, ifaceobj):
963 bridge_ports = []
964
965 for brport in ifaceobj.get_attr_value('bridge-ports') or []:
966 if brport != 'none':
967 bridge_ports.extend(brport.split())
968
969 return ' '.join(bridge_ports)
d486dd0d 970
52ac1cfc 971 def check_valid_bridge(self, ifaceobj, ifname):
223ba5af 972 if self.cache.link_exists(ifname) and not self.cache.link_is_bridge(ifname):
52ac1cfc
JF
973 self.log_error('misconfiguration of bridge attribute(s) on existing non-bridge interface (%s)' % ifname, ifaceobj=ifaceobj)
974 return False
975 return True
976
59ab29fb 977 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs=False):
20649b09
JF
978
979 if not old_ifaceobjs and (ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN or ifaceobj.get_attr_value_first("bridge-vlan-vni-map")):
980 self.svd_list.add(ifaceobj.name)
981
52ac1cfc 982 if not self._is_bridge(ifaceobj) or not self.check_valid_bridge(ifaceobj, ifaceobj.name):
d486dd0d
JF
983 return None
984 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
985 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
986 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
987 # for special vlan aware bridges, we need to add another bit
988 if utils.get_boolean_from_string(ifaceobj.get_attr_value_first('bridge-vlan-aware')):
989 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
990 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
5f4ab659
JF
991
992 # store the name of all bridge vlan aware in a global list
993 self.bridge_vlan_aware_list.append(ifaceobj.name)
994
d486dd0d
JF
995 ifaceobj.role |= ifaceRole.MASTER
996 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
997 return self.parse_port_list(ifaceobj.name,
998 self._get_ifaceobj_bridge_ports(ifaceobj),
999 ifacenames_all)
1000
1001 def get_dependent_ifacenames_running(self, ifaceobj):
223ba5af 1002 if not self.cache.bridge_exists(ifaceobj.name):
d486dd0d 1003 return None
223ba5af 1004 return self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
1005
1006 def _get_bridge_port_list(self, ifaceobj):
1007
1008 # port list is also available in the previously
1009 # parsed dependent list. Use that if available, instead
1010 # of parsing port expr again
1011 port_list = ifaceobj.lowerifaces
1012 if port_list:
1013 return port_list
1014 ports = self._get_ifaceobj_bridge_ports(ifaceobj)
1015 if ports:
1016 return self.parse_port_list(ifaceobj.name, ports)
1017 else:
1018 return None
1019
1020 def _get_bridge_port_list_user_ordered(self, ifaceobj):
1021 # When enslaving bridge-ports we need to return the exact user
1022 # configured bridge ports list (bridge will inherit the mac of the
1023 # first device.
1024 ports = self._get_ifaceobj_bridge_ports(ifaceobj)
1025 return self.parse_port_list(ifaceobj.name, ports) if ports else None
1026
f5c97c0d
MW
1027 def _get_bridge_port_condone_regex(self, ifaceobj, get_string = False):
1028 bridge_port_condone_regex = ifaceobj.get_attr_value_first('bridge-ports-condone-regex')
1029 # If bridge-ports-ignore-regex is configured, do NOT use the parse_port_list()
1030 # function to gather a list of ports matching the regex here and now but set
1031 # up a compiled regex to be used in a match later. This way we try to avoid
1032 # a race condition where an (possibly VM) interface is created after this
1033 # function has been called but before the bridgeports are validated.
1034 if bridge_port_condone_regex:
1035 if get_string:
1036 return bridge_port_condone_regex
1037 return re.compile (r"%s" % bridge_port_condone_regex)
1038 return None
1039
d486dd0d
JF
1040 def _process_bridge_waitport(self, ifaceobj, portlist):
1041 waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
1042 if not waitport_value: return
1043 try:
1044 waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
1045 if not waitportvals: return
1046 try:
1047 waitporttime = int(waitportvals[0])
3218f49d 1048 except Exception:
d486dd0d
JF
1049 self.log_warn('%s: invalid waitport value \'%s\''
1050 %(ifaceobj.name, waitportvals[0]))
1051 return
1052 if waitporttime <= 0: return
1053 try:
1054 waitportlist = self.parse_port_list(ifaceobj.name,
1055 waitportvals[1])
3b01ed76 1056 except IndexError as e:
d486dd0d
JF
1057 # ignore error and use all bridge ports
1058 waitportlist = portlist
1059 pass
1060 if not waitportlist: return
1061 self.logger.info('%s: waiting for ports %s to exist ...'
1062 %(ifaceobj.name, str(waitportlist)))
1063 starttime = time.time()
1064 while ((time.time() - starttime) < waitporttime):
1065 if all([False for p in waitportlist
223ba5af 1066 if not self.cache.link_exists(p)]):
d486dd0d
JF
1067 break;
1068 time.sleep(1)
3b01ed76 1069 except Exception as e:
d486dd0d
JF
1070 self.log_warn('%s: unable to process waitport: %s'
1071 %(ifaceobj.name, str(e)))
1072
1073 def _enable_disable_ipv6(self, port, enable='1'):
1074 try:
1075 self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
3b01ed76 1076 except Exception as e:
d486dd0d
JF
1077 self.logger.info(str(e))
1078
223ba5af 1079 def handle_ipv6(self, ports, state):
d486dd0d
JF
1080 for p in ports:
1081 self._enable_disable_ipv6(p, state)
1082
1083 def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports):
1084 """ pretty print bridge port add errors.
1085 since the commands are batched and the kernel only returns error
1086 codes, this function tries to interpret some error codes
1087 and prints clearer errors """
1088
1089 if re.search('RTNETLINK answers: Invalid argument', errstr):
1090 # Cumulus Linux specific error checks
1091 try:
1092 if self.sysctl_get('net.bridge.bridge-allow-multiple-vlans') == '0':
1093 vlanid = None
1094 for bport in bridgeports:
1095 currvlanid = self._get_vlan_id_from_ifacename(bport)
1096 if vlanid:
1097 if currvlanid != vlanid:
1098 self.log_error('%s: ' %bridgeifaceobj.name +
1099 'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj)
1100 break
1101 if currvlanid:
1102 vlanid = currvlanid
1103 except Exception as e:
1104 errstr += '\n%s' % str(e)
1105 self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj)
1106
1107 def _add_ports(self, ifaceobj, ifaceobj_getfunc):
1108 bridgeports = self._get_bridge_port_list(ifaceobj)
f5c97c0d 1109 bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj)
d486dd0d
JF
1110 runningbridgeports = []
1111
7840bd2c
JF
1112 # bridge-always-up #####################################################
1113 bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")
1114 dummy_brport = None
1115
1116 if utils.get_boolean_from_string(bridge_always_up):
1117 # the dummy port will be added to the bridgeports list so the
1118 # following code don't de-enslave the dummy device.
1119 dummy_brport = self.bridge_always_up(ifaceobj.name, bridgeports)
1120
1121 ########################################################################
1122
d486dd0d 1123 self._process_bridge_waitport(ifaceobj, bridgeports)
d486dd0d
JF
1124 # Delete active ports not in the new port list
1125 if not ifupdownflags.flags.PERFMODE:
223ba5af 1126 runningbridgeports = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
1127 if runningbridgeports:
1128 for bport in runningbridgeports:
1129 if not bridgeports or bport not in bridgeports:
f5c97c0d
MW
1130 if bridgeportscondoneregex and bridgeportscondoneregex.match(bport):
1131 self.logger.info("%s: port %s will stay enslaved as it matches with bridge-ports-condone-regex" % (ifaceobj.name, bport))
1132 continue
223ba5af 1133 self.netlink.link_set_nomaster(bport)
d486dd0d
JF
1134 # set admin DOWN on all removed ports
1135 # that don't have config outside bridge
1136 if not ifaceobj_getfunc(bport):
223ba5af 1137 self.netlink.link_down(bport)
d486dd0d
JF
1138 # enable ipv6 for ports that were removed
1139 self.handle_ipv6([bport], '0')
1140 else:
1141 runningbridgeports = []
1142 if not bridgeports:
d486dd0d
JF
1143 return []
1144 err = 0
3b01ed76 1145 newbridgeports = set(bridgeports).difference(set(runningbridgeports))
d486dd0d
JF
1146 newly_enslaved_ports = []
1147
1148 newbridgeports_ordered = []
1149 for br_port in self._get_bridge_port_list_user_ordered(ifaceobj):
1150 if br_port in newbridgeports:
1151 newbridgeports_ordered.append(br_port)
1152
7840bd2c
JF
1153 if dummy_brport:
1154 # add the dummy port to the list of interface to enslave
1155 # link_set_master should make sure that the device is not
1156 # already enslaved.
1157 newbridgeports_ordered.append(dummy_brport)
1158
223ba5af
JF
1159 self.iproute2.batch_start()
1160
d486dd0d
JF
1161 for bridgeport in newbridgeports_ordered:
1162 try:
1163 if (not ifupdownflags.flags.DRYRUN and
223ba5af 1164 not self.cache.link_exists(bridgeport)):
d486dd0d
JF
1165 self.log_error('%s: bridge port %s does not exist'
1166 %(ifaceobj.name, bridgeport), ifaceobj)
1167 err += 1
1168 continue
223ba5af 1169 hwaddress = self.cache.get_link_address(bridgeport)
ca45cd9e 1170 if not ifupdownflags.flags.DRYRUN and not self._valid_ethaddr(hwaddress):
d486dd0d
JF
1171 self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
1172 bridgeport) + 'invalid ether addr %s'
1173 %hwaddress)
1174 continue
223ba5af 1175 self.iproute2.link_set_master(bridgeport, ifaceobj.name)
d486dd0d 1176 newly_enslaved_ports.append(bridgeport)
20649b09
JF
1177
1178 # dont disable ipv6 for SVD
1179 if bridgeport not in self.svd_list:
1180 self.handle_ipv6([bridgeport], '1')
1181
223ba5af 1182 self.iproute2.addr_flush(bridgeport)
3b01ed76 1183 except Exception as e:
d486dd0d
JF
1184 self.logger.error(str(e))
1185 pass
223ba5af
JF
1186
1187 self.iproute2.batch_commit()
1188 self.cache.force_add_slave_list(ifaceobj.name, newly_enslaved_ports)
d486dd0d
JF
1189
1190 if err:
1191 self.log_error('bridge configuration failed (missing ports)')
1192
7840bd2c
JF
1193 try:
1194 # to avoid any side effect we remove the dummy brport from the
1195 # list of supposedly newly configured ports.
1196 newly_enslaved_ports.remove(dummy_brport)
3218f49d 1197 except Exception:
7840bd2c
JF
1198 pass
1199
d486dd0d
JF
1200 return newly_enslaved_ports
1201
7840bd2c
JF
1202 def get_dummy_brport_name_for_bridge(self, bridge_name):
1203 """
1b845b1b
AP
1204 dummy brport will have user provided name if it's defined in 'bridge_always_up_dummy_brport' policy
1205 Otherwise dummy brport will have pre-formated name: brport-if$BRIDGE_IFINDEX
7840bd2c
JF
1206 That way we can avoid collision with existing interfaces
1207 """
1b845b1b
AP
1208 if self.bridge_always_up_dummy_brport:
1209 return self.bridge_always_up_dummy_brport
7840bd2c
JF
1210 # this can raise: NetlinkCacheIfnameNotFoundError
1211 return "brport-if%d" % self.cache.get_ifindex(bridge_name)
1212
1213 def bridge_always_up(self, bridge_name, newbridgeports_ordered):
1214 dummy_brport = self.get_dummy_brport_name_for_bridge(bridge_name)
1215
1216 if not self.cache.link_exists(dummy_brport):
1217 self.logger.info("%s: bridge-always-up yes: enslaving dummy port: %s" % (bridge_name, dummy_brport))
1218 self.netlink.link_add(ifname=dummy_brport, kind="dummy")
1219 self.netlink.link_up_force(dummy_brport)
1220
1221 newbridgeports_ordered.append(dummy_brport)
1222 return dummy_brport
1223
d486dd0d
JF
1224 def _process_bridge_maxwait(self, ifaceobj, portlist):
1225 maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
1226 if not maxwait: return
1227 try:
1228 maxwait = int(maxwait)
3218f49d 1229 except Exception:
d486dd0d
JF
1230 self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
1231 maxwait))
1232 return
1233 if not maxwait: return
1234 self.logger.info('%s: waiting for ports to go to fowarding state ..'
1235 %ifaceobj.name)
1236 try:
1237 starttime = time.time()
1238 while ((time.time() - starttime) < maxwait):
1239 if all([False for p in portlist
1240 if self.read_file_oneline(
1241 '/sys/class/net/%s/brif/%s/state'
1242 %(ifaceobj.name, p)) != '3']):
1243 break;
1244 time.sleep(1)
3b01ed76 1245 except Exception as e:
d486dd0d
JF
1246 self.log_warn('%s: unable to process maxwait: %s'
1247 %(ifaceobj.name, str(e)))
1248
1249 def _ints_to_ranges(self, ints):
3b01ed76 1250 for a, b in itertools.groupby(enumerate(ints), lambda x_y: x_y[1] - x_y[0]):
d486dd0d
JF
1251 b = list(b)
1252 yield b[0][1], b[-1][1]
1253
1254 def _ranges_to_ints(self, rangelist):
1255 """ returns expanded list of integers given set of string ranges
1256 example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
1257 """
1258 result = []
1259 try:
1260 for part in rangelist:
1261 if '-' in part:
1262 a, b = part.split('-')
1263 a, b = int(a), int(b)
3b01ed76 1264 result.extend(list(range(a, b + 1)))
d486dd0d
JF
1265 else:
1266 a = int(part)
1267 result.append(a)
3218f49d 1268 except Exception:
c46af1c9 1269 self.logger.warning('unable to parse vids \'%s\''
d486dd0d
JF
1270 %''.join(rangelist))
1271 pass
1272 return result
1273
1274 def _compress_into_ranges(self, vids_ints):
1275 return ['%d' %start if start == end else '%d-%d' %(start, end)
1276 for start, end in self._ints_to_ranges(vids_ints)]
1277
1278 def _diff_vids(self, vids1_ints, vids2_ints):
3b01ed76 1279 return set(vids2_ints).difference(vids1_ints), set(vids1_ints).difference(vids2_ints)
d486dd0d 1280
223ba5af 1281 def _compare_vids(self, vids1, vids2, pvid=None, expand_range=True):
d486dd0d
JF
1282 """ Returns true if the vids are same else return false """
1283
223ba5af
JF
1284 if expand_range:
1285 vids1_ints = self._ranges_to_ints(vids1)
1286 vids2_ints = self._ranges_to_ints(vids2)
1287 else:
1288 vids1_ints = self._ranges_to_ints(vids1)
1289 vids2_ints = vids2
3b01ed76 1290 set_diff = set(vids1_ints).symmetric_difference(vids2_ints)
d486dd0d
JF
1291 if pvid and int(pvid) in set_diff:
1292 set_diff.remove(int(pvid))
1293 if set_diff:
1294 return False
1295 else:
1296 return True
1297
1298 def _set_bridge_mcqv4src_compat(self, ifaceobj):
1299 #
1300 # Sets old style igmp querier
1301 #
1302 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
1303 if attrval:
1304 running_mcqv4src = {}
1305 if not ifupdownflags.flags.PERFMODE:
223ba5af 1306 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
d486dd0d
JF
1307 mcqs = {}
1308 srclist = attrval.split()
1309 for s in srclist:
1310 k, v = s.split('=')
1311 mcqs[k] = v
1312
3b01ed76 1313 k_to_del = set(list(running_mcqv4src.keys())).difference(list(mcqs.keys()))
d486dd0d 1314 for v in k_to_del:
223ba5af 1315 self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
3b01ed76 1316 for v in list(mcqs.keys()):
223ba5af 1317 self.iproute2.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
d486dd0d 1318 elif not ifupdownflags.flags.PERFMODE:
223ba5af 1319 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
d486dd0d 1320 if running_mcqv4src:
3b01ed76 1321 for v in list(running_mcqv4src.keys()):
223ba5af 1322 self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
d486dd0d
JF
1323
1324 def _set_bridge_vidinfo_compat(self, ifaceobj):
1325 #
1326 # Supports old style vlan vid info format
1327 # for compatibility
1328 #
1329 bridge_port_pvids = ifaceobj.get_attr_value_first('bridge-port-pvids')
1330 bridge_port_vids = ifaceobj.get_attr_value_first('bridge-port-vids')
1331 if not bridge_port_pvids and not bridge_port_vids:
1332 return
1333
1334 # Handle bridge vlan attrs
1335 # Install pvids
1336 if bridge_port_pvids:
1337 portlist = self.parse_port_list(ifaceobj.name, bridge_port_pvids)
1338 if not portlist:
1339 self.log_warn('%s: could not parse \'%s %s\''
1340 %(ifaceobj.name, 'bridge-port-pvids',
1341 bridge_port_pvids))
1342 return
1343 for p in portlist:
1344 try:
1345 (port, pvid) = p.split('=')
1346 pvid = int(pvid)
223ba5af 1347 running_pvid = self.cache.get_pvid(port)
d486dd0d
JF
1348 if running_pvid:
1349 if running_pvid == pvid:
1350 continue
1351 else:
223ba5af
JF
1352 self.iproute2.bridge_vlan_del_pvid(port, running_pvid)
1353 self.iproute2.bridge_vlan_add_pvid(port, pvid)
3b01ed76 1354 except Exception as e:
d486dd0d
JF
1355 self.log_warn('%s: failed to set pvid `%s` (%s)'
1356 %(ifaceobj.name, p, str(e)))
1357
1358 # install port vids
1359 if bridge_port_vids:
1360 portlist = self.parse_port_list(ifaceobj.name, bridge_port_vids)
1361 if not portlist:
1362 self.log_warn('%s: could not parse \'%s %s\'' %(ifaceobj.name,
1363 'bridge-port-vids', bridge_port_vids))
1364 return
1365 for p in portlist:
1366 try:
1367 (port, val) = p.split('=')
1368 vids = val.split(',')
1369 vids_int = self._ranges_to_ints(vids)
223ba5af 1370 _, running_vids = self.cache.get_pvid_and_vids(port)
d486dd0d
JF
1371 if running_vids:
1372 (vids_to_del, vids_to_add) = \
1373 self._diff_vids(vids_int, running_vids)
1374 if vids_to_del:
223ba5af 1375 self.iproute2.bridge_vlan_del_vid_list(port,
d486dd0d
JF
1376 self._compress_into_ranges(vids_to_del))
1377 if vids_to_add:
223ba5af 1378 self.iproute2.bridge_vlan_add_vid_list(port,
d486dd0d
JF
1379 self._compress_into_ranges(vids_to_add))
1380 else:
223ba5af 1381 self.iproute2.bridge_vlan_add_vid_list(port, vids_int)
3b01ed76 1382 except Exception as e:
d486dd0d
JF
1383 self.log_warn('%s: failed to set vid `%s` (%s)'
1384 %(ifaceobj.name, p, str(e)))
1385
1386 def _is_running_stp_state_on(self, bridgename):
1387 """ Returns True if running stp state is on, else False """
1388
1389 stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
1390 try:
1391 running_stp_state = self.read_file_oneline(stp_state_file)
1392 return running_stp_state and running_stp_state != '0'
3218f49d 1393 except Exception:
d486dd0d
JF
1394 return False
1395
1396 def _is_config_stp_state_on(self, ifaceobj):
1397 """ Returns true if user specified stp state is on, else False """
1398
1399 stp_attr = ifaceobj.get_attr_value_first('bridge-stp')
1400 if not stp_attr:
1401 return self.default_stp_on
1402 return utils.get_boolean_from_string(stp_attr)
1403
1404 def get_bridge_mcsnoop_value(self, ifaceobj):
1405 mcsnoop = ifaceobj.get_attr_value_first('bridge-mcsnoop')
3832dce6
JF
1406
1407 if mcsnoop:
1408 return mcsnoop
1409
1410 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
1411 if self._vxlan_bridge_default_igmp_snooping is not None:
1412 return self._vxlan_bridge_default_igmp_snooping
1413
1414 return self.get_attr_default_value("bridge-mcsnoop")
d486dd0d
JF
1415
1416 def fill_ifla_info_data_with_ifla_br_attribute(self,
1417 ifla_info_data,
1418 link_just_created,
1419 ifname,
1420 nl_attr,
1421 attr_name,
223ba5af
JF
1422 user_config,
1423 cached_value):
d486dd0d
JF
1424 try:
1425 translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr)
1426
1427 if not callable(translate_func):
1428 return
1429
1430 if not user_config:
1431 user_config = policymanager.policymanager_api.get_iface_default(
1432 module_name=self.__class__.__name__,
1433 ifname=ifname,
1434 attr=attr_name
1435 )
1436
223ba5af
JF
1437 if not link_just_created and cached_value is None:
1438 # the link already exists but we don't have any value
1439 # cached for this attr, it probably means that the
1440 # capability is not available on this system (i.e old kernel)
1441 self.logger.debug("%s: ignoring %s %s: capability probably not supported on this system"
1442 % (ifname, attr_name, user_config))
1443 return
d486dd0d
JF
1444
1445 if not user_config and not link_just_created and cached_value is not None:
1446 # there is no user configuration for this attribute
1447 # if the bridge existed before we need to check if
1448 # this attribute needs to be reset to default value
1449 default_value = self.get_attr_default_value(attr_name)
1450
1451 if default_value:
1452 # the attribute has a default value, we need to convert it to
1453 # netlink format to compare it with the cache value
1454 default_value_nl = translate_func(default_value) # default_value.lower()
1455
1456 if default_value_nl != cached_value:
1457 # the running value difers from the default value
1458 # but the user didn't specify any config
1459 # resetting attribute to default
1460 ifla_info_data[nl_attr] = default_value_nl
1461 self.logger.info('%s: reset %s to default: %s' % (ifname, attr_name, default_value))
1462 elif user_config:
1463 user_config_nl = translate_func(user_config) # user_config.lower()
1464
1465 if user_config_nl != cached_value:
1466 ifla_info_data[nl_attr] = user_config_nl
1467
1468 if cached_value is not None:
1469 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
1470 else:
1471 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
1472 except Exception as e:
1473 self.logger.warning('%s: %s: %s' % (ifname, attr_name, str(e)))
1474
1475 def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_aware):
1476 ifla_info_data = dict()
1477 ifname = ifaceobj.name
1478
223ba5af 1479 self.logger.info('%s: applying bridge settings' % ifname)
d486dd0d 1480
223ba5af
JF
1481 cached_ifla_info_data = self.cache.get_link_info_data(ifname)
1482
1483 try:
1484 # we compare the user value (or policy value) with the current running state
1485 # we need to divide the cached value by 100 to ignore small difference.
1486 # i.e. our default value is 31 but the kernel default seems to be 3125
c817ba4d 1487 cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] //= 100
223ba5af 1488 cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] *= 100
3218f49d 1489 except Exception:
223ba5af
JF
1490 pass
1491
3b01ed76 1492 for attr_name, nl_attr in self._ifla_br_attributes_map.items():
d486dd0d
JF
1493 self.fill_ifla_info_data_with_ifla_br_attribute(
1494 ifla_info_data=ifla_info_data,
1495 link_just_created=link_just_created,
1496 ifname=ifname,
1497 nl_attr=nl_attr,
1498 attr_name=attr_name,
223ba5af
JF
1499 user_config=ifaceobj.get_attr_value_first(attr_name),
1500 cached_value=cached_ifla_info_data.get(nl_attr)
d486dd0d
JF
1501 )
1502
01f27c67
JF
1503 # special cases ########################################################
1504
1505 # bridge-bridgeprio
1506 # if mstpctl-treeprio is configured on the bridge
1507 # do not reset the bridge-bridgeprio to the default value
1508 # NOTE: this is the case for every bridge/mstpctl attribute pairs.
1509 # TODO: more code should be added to handle this in the future.
1510 mstpctl_treeprio = ifaceobj.get_attr_value_first("mstpctl-treeprio")
1511 bridge_bridgeprio = ifaceobj.get_attr_value_first("bridge-bridgeprio")
1512
1513 if mstpctl_treeprio:
1514 self.logger.info("%s: mstpctl-treeprio attribute is set - ignorning bridge-bridgeprio" % ifname)
1515 else:
1516 self.fill_ifla_info_data_with_ifla_br_attribute(
1517 ifla_info_data=ifla_info_data,
1518 link_just_created=link_just_created,
1519 ifname=ifname,
1520 nl_attr=Link.IFLA_BR_PRIORITY,
1521 attr_name='bridge-bridgeprio',
1522 user_config=bridge_bridgeprio,
1523 cached_value=cached_ifla_info_data.get(Link.IFLA_BR_PRIORITY)
1524 )
1525
d486dd0d
JF
1526 # bridge-mcsnoop
1527 self.fill_ifla_info_data_with_ifla_br_attribute(
1528 ifla_info_data=ifla_info_data,
1529 link_just_created=link_just_created,
1530 ifname=ifname,
1531 nl_attr=Link.IFLA_BR_MCAST_SNOOPING,
1532 attr_name='bridge-mcsnoop',
223ba5af
JF
1533 user_config=self.get_bridge_mcsnoop_value(ifaceobj),
1534 cached_value=cached_ifla_info_data.get(Link.IFLA_BR_MCAST_SNOOPING)
1535
d486dd0d
JF
1536 )
1537
1538 # bridge-vlan-stats
1539 if bridge_vlan_aware:
1540 self.fill_ifla_info_data_with_ifla_br_attribute(
1541 ifla_info_data=ifla_info_data,
1542 link_just_created=link_just_created,
1543 ifname=ifname,
1544 nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED,
1545 attr_name='bridge-vlan-stats',
223ba5af
JF
1546 user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats,
1547 cached_value=cached_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
d486dd0d
JF
1548 )
1549
1550 try:
1551 if self._is_config_stp_state_on(ifaceobj):
1552 if not self._is_running_stp_state_on(ifname):
1553 ifla_info_data[Link.IFLA_BR_STP_STATE] = 1
1554 self.logger.info('%s: stp state reset, reapplying port settings' % ifname)
1555 ifaceobj.module_flags[ifaceobj.name] = \
1556 ifaceobj.module_flags.setdefault(self.name, 0) | \
1557 bridgeFlags.PORT_PROCESSED_OVERRIDE
1558 else:
1559 # If stp not specified and running stp state on, set it to off
1560 if self._is_running_stp_state_on(ifname):
1561 self.logger.info('%s: bridge-stp not specified but running: turning stp off')
1562 ifla_info_data[Link.IFLA_BR_STP_STATE] = 0
1563 except Exception as e:
1564 self.logger.warning('%s: bridge stp: %s' % (ifname, str(e)))
1565
1566 if ifla_info_data:
24931ffa 1567 self.netlink.link_set_bridge_info_data(ifname, ifla_info_data)
d486dd0d
JF
1568
1569 def _check_vids(self, ifaceobj, vids):
1570 ret = True
1571 for v in vids:
1572 try:
1573 if '-' in v:
1574 va, vb = v.split('-')
1575 va, vb = int(va), int(vb)
1576 self._handle_reserved_vlan(va, ifaceobj.name, end=vb)
1577 else:
1578 va = int(v)
1579 self._handle_reserved_vlan(va, ifaceobj.name)
1580 except exceptions.ReservedVlanException as e:
1581 raise e
1582 except Exception:
c46af1c9 1583 self.logger.warning('%s: unable to parse vid \'%s\''
d486dd0d
JF
1584 %(ifaceobj.name, v))
1585 return ret
1586
d486dd0d 1587 def _get_running_vids_n_pvid_str(self, ifacename):
223ba5af 1588 pvid, vids = self.cache.get_pvid_and_vids(ifacename)
d486dd0d
JF
1589
1590 if vids:
1591 ret_vids = self._compress_into_ranges(vids)
1592 else:
1593 ret_vids = None
1594
1595 if pvid:
1596 ret_pvid = '%s' %pvid
1597 else:
1598 ret_pvid = None
1599 return (ret_vids, ret_pvid)
1600
1601 def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid,
1602 isbridge):
1603 """ This method is a combination of methods _apply_bridge_vids and
1604 _apply_bridge_port_pvids above. A combined function is
1605 found necessary to do the deletes first and the adds later
1606 because kernel does honor vid info flags during deletes.
1607
1608 """
e537a6e6 1609 if not isbridge and (bportifaceobj.link_kind & ifaceLinkKind.VXLAN and not bportifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
d486dd0d
JF
1610 if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
1611 self._error_vxlan_in_vlan_aware_br(bportifaceobj,
1612 bportifaceobj.upperifaces[0])
1613 return
1614
1615 vids_int = self._ranges_to_ints(vids)
1616 try:
1617 pvid_int = int(pvid) if pvid else 0
1618 except Exception:
c46af1c9 1619 self.logger.warning('%s: unable to parse pvid \'%s\''
d486dd0d
JF
1620 %(bportifaceobj.name, pvid))
1621 pvid_int = 0
1622 pass
1623
1624 vids_to_del = []
1625 vids_to_add = vids_int
1626 pvid_to_del = None
1627 pvid_to_add = pvid_int
1628
1629 try:
1630 if not self._check_vids(bportifaceobj, vids):
1631 return
1632
223ba5af 1633 running_pvid, running_vids = self.cache.get_pvid_and_vids(bportifaceobj.name)
d486dd0d
JF
1634
1635 if not running_vids and not running_pvid:
1636 # There cannot be a no running pvid.
1637 # It might just not be in our cache:
1638 # this can happen if at the time we were
1639 # creating the bridge vlan cache, the port
1640 # was not part of the bridge. And we need
1641 # to make sure both vids and pvid is not in
1642 # the cache, to declare that our cache may
1643 # be stale.
1644 running_pvid = 1
1645 running_vids = [1]
1646
1647 if running_vids:
1648 (vids_to_del, vids_to_add) = \
1649 self._diff_vids(vids_to_add, running_vids)
1650
1651 if running_pvid:
1652 if running_pvid != pvid_int and running_pvid != 0:
1653 pvid_to_del = running_pvid
1654
1655 if (pvid_to_del and (pvid_to_del in vids_int) and
1656 (pvid_to_del not in vids_to_add)):
1657 # kernel deletes dont take into account
1658 # bridge vid flags and its possible that
1659 # the pvid deletes we do end up deleting
1660 # the vids. Be proactive and add the pvid
1661 # to the vid add list if it is in the vids
1662 # and not already part of vids_to_add.
1663 # This helps with a small corner case:
1664 # - running
1665 # pvid 100
1666 # vid 101 102
1667 # - new change is going to move the state to
1668 # pvid 101
1669 # vid 100 102
1670 vids_to_add.add(pvid_to_del)
1671 except exceptions.ReservedVlanException as e:
1672 raise e
3b01ed76 1673 except Exception as e:
d486dd0d
JF
1674 self.log_error('%s: failed to process vids/pvids'
1675 %bportifaceobj.name + ' vids = %s' %str(vids) +
1676 'pvid = %s ' %pvid + '(%s)' %str(e),
1677 bportifaceobj, raise_error=False)
1678 try:
1679 if vids_to_del:
1680 if pvid_to_add in vids_to_del:
1681 vids_to_del.remove(pvid_to_add)
e537a6e6
JF
1682
1683 vids_to_del = self.remove_bridge_vlans_mapped_to_vnis_from_vids_list(None, bportifaceobj, vids_to_del)
1684
223ba5af 1685 self.iproute2.bridge_vlan_del_vid_list_self(bportifaceobj.name,
d486dd0d
JF
1686 self._compress_into_ranges(
1687 vids_to_del), isbridge)
3b01ed76 1688 except Exception as e:
d486dd0d
JF
1689 self.log_warn('%s: failed to del vid `%s` (%s)'
1690 %(bportifaceobj.name, str(vids_to_del), str(e)))
1691
1692 try:
1693 if pvid_to_del:
223ba5af 1694 self.iproute2.bridge_vlan_del_pvid(bportifaceobj.name,
d486dd0d 1695 pvid_to_del)
3b01ed76 1696 except Exception as e:
d486dd0d
JF
1697 self.log_warn('%s: failed to del pvid `%s` (%s)'
1698 %(bportifaceobj.name, pvid_to_del, str(e)))
1699
1700 try:
1701 if vids_to_add:
223ba5af 1702 self.iproute2.bridge_vlan_add_vid_list_self(bportifaceobj.name,
d486dd0d
JF
1703 self._compress_into_ranges(
1704 vids_to_add), isbridge)
3b01ed76 1705 except Exception as e:
d486dd0d
JF
1706 self.log_error('%s: failed to set vid `%s` (%s)'
1707 %(bportifaceobj.name, str(vids_to_add),
1708 str(e)), bportifaceobj, raise_error=False)
1709
1710 try:
1711 if pvid_to_add and pvid_to_add != running_pvid:
223ba5af 1712 self.iproute2.bridge_vlan_add_pvid(bportifaceobj.name,
d486dd0d 1713 pvid_to_add)
3b01ed76 1714 except Exception as e:
d486dd0d
JF
1715 self.log_error('%s: failed to set pvid `%s` (%s)'
1716 %(bportifaceobj.name, pvid_to_add, str(e)),
1717 bportifaceobj)
1718
e537a6e6
JF
1719 def get_bridge_vlans_mapped_to_vnis_as_integer_list(self, ifaceobj):
1720 """
1721 Get all vlans that the user wants to configured in vlan-vni maps
1722 """
1723 try:
1724 vids = []
1725
1726 for vlans_vnis_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
1727 vids.extend(self._ranges_to_ints([vlans_vnis_map.split("=")[0]]))
1728
1729 return vids
1730 except Exception as e:
1731 self.logger.debug("get_bridge_vlans_mapped_to_vnis_as_integer_list: %s" % str(e))
1732 return []
1733
1734 def remove_bridge_vlans_mapped_to_vnis_from_vids_list(self, bridge_ifaceobj, vxlan_ifaceobj, vids_list):
1735 """
1736 For single vxlan we need to remove the vlans mapped to vnis
1737 from the vids list otherwise they will get removed from the brport
1738 """
1739 if not (vxlan_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
1740 return vids_list
1741
1742 user_config_vids = []
1743
1744 if bridge_ifaceobj:
1745 for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(bridge_ifaceobj):
1746 user_config_vids.append(vid)
1747
1748 if vxlan_ifaceobj:
1749 for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(vxlan_ifaceobj):
1750 user_config_vids.append(vid)
1751
1752 for vlan in user_config_vids:
1753 try:
1754 vids_list.remove(vlan)
3218f49d 1755 except Exception:
e537a6e6
JF
1756 pass
1757
1758 return vids_list
1759
d486dd0d
JF
1760 def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
1761 bridge_vids=None,
1762 bridge_pvid=None):
1763 vids = None
1764 pvids = None
1765 vids_final = []
1766 pvid_final = None
1767 bport_access = bportifaceobj.get_attr_value_first('bridge-access')
1768 if bport_access:
1769 vids = re.split(r'[\s\t]\s*', bport_access)
1770 pvids = vids
1771 allow_untagged = 'yes'
1772 self.check_bridge_port_vid_attrs(bportifaceobj)
1773 else:
1774 allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
1775
1776 bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
1777 if bport_vids:
1778 vids = re.split(r'[\s\t,]\s*', bport_vids)
1779
1780 bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
1781 if bport_pvids:
1782 pvids = re.split(r'[\s\t]\s*', bport_pvids)
1783
1784 if vids:
1785 vids_final = vids
1786 elif bridge_vids:
1787 vids_final = bridge_vids
1788
1789 if allow_untagged == 'yes':
1790 if pvids:
1791 pvid_final = pvids[0]
1792 elif bridge_pvid:
1793 pvid_final = bridge_pvid
1794 else:
1795 pvid_final = '1'
1796 else:
1797 pvid_final = None
1798
1799 self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
1800 pvid_final, False)
1801
1802 def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware):
1803 err = False
1804
1805 if (ifaceobj.get_attr_value_first('bridge-port-vids') and
1806 ifaceobj.get_attr_value_first('bridge-port-pvids')):
1807 # Old style bridge port vid info
1808 # skip new style setting on ports
1809 return
1810 self.logger.info('%s: applying bridge configuration '
1811 %ifaceobj.name + 'specific to ports')
1812
1813 bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
1814 if bridge_vids:
1815 bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
1816 else:
1817 bridge_vids = None
1818
1819 bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
1820 if bridge_pvid:
1821 bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
1822 else:
1823 bridge_pvid = None
1824
1825 if (ifaceobj.module_flags.get(self.name, 0x0) &
1826 bridgeFlags.PORT_PROCESSED_OVERRIDE):
1827 port_processed_override = True
1828 else:
1829 port_processed_override = False
1830
1831 bridgeports = self._get_bridge_port_list(ifaceobj)
1832 if not bridgeports:
1833 self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
1834 return
223ba5af 1835 self.iproute2.batch_start()
d486dd0d 1836 for bport in bridgeports:
223ba5af
JF
1837 # on link_set_master we need to wait until we cache the correct
1838 # notification and register the brport as slave
1839 if not self.cache.bridge_port_exists(ifaceobj.name, bport):
d486dd0d
JF
1840 self.logger.info('%s: skipping bridge config' %ifaceobj.name +
1841 ' for port %s (missing port)' %bport)
1842 continue
1843 self.logger.info('%s: processing bridge config for port %s'
1844 %(ifaceobj.name, bport))
1845 bportifaceobjlist = ifaceobj_getfunc(bport)
1846 if not bportifaceobjlist:
223ba5af 1847 continue
d486dd0d
JF
1848 for bportifaceobj in bportifaceobjlist:
1849 # Dont process bridge port if it already has been processed
1850 # and there is no override on port_processed
1851 if (not port_processed_override and
1852 (bportifaceobj.module_flags.get(self.name,0x0) &
1853 bridgeFlags.PORT_PROCESSED)):
1854 continue
1855 try:
1856 # Add attributes specific to the vlan aware bridge
1857 if bridge_vlan_aware:
1858 self._apply_bridge_vlan_aware_port_settings_all(
1859 bportifaceobj, bridge_vids, bridge_pvid)
1860 elif self.warn_on_untagged_bridge_absence:
1861 self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
1862 except exceptions.ReservedVlanException as e:
1863 raise e
3b01ed76 1864 except Exception as e:
d486dd0d 1865 err = True
c46af1c9 1866 self.logger.warning('%s: %s' %(ifaceobj.name, str(e)))
d486dd0d 1867 pass
223ba5af 1868 self.iproute2.batch_commit()
d486dd0d
JF
1869 if err:
1870 raise Exception('%s: errors applying port settings' %ifaceobj.name)
1871
1872 def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
1873 if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
1874 lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
1875 if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
1876 not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
c46af1c9 1877 self.logger.warning('%s: untagged bridge not found. Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.' % bridgename)
d486dd0d
JF
1878 self.warn_on_untagged_bridge_absence = False
1879
1880 def bridge_port_get_bridge_name(self, ifaceobj):
223ba5af 1881 bridgename = self.cache.get_bridge_name_from_port(ifaceobj.name)
d486dd0d
JF
1882 if not bridgename:
1883 # bridge port is not enslaved to a bridge we need to find
1884 # the bridge in it's upper ifaces then enslave it
1885 for u in ifaceobj.upperifaces:
223ba5af 1886 if self.cache.link_is_bridge(u):
d486dd0d
JF
1887 return True, u
1888 return False, None
1889 # return should_enslave port, bridgename
1890 return False, bridgename
1891
1892 def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
1893 if should_enslave_port:
223ba5af 1894 self.netlink.link_set_master(ifaceobj.name, bridge_name)
20649b09
JF
1895
1896 if ifaceobj.name not in self.svd_list:
1897 self.handle_ipv6([ifaceobj.name], '1')
d486dd0d
JF
1898
1899 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
1900 bridge_pvid = self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)
1901 try:
1902 self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, bridge_vids, bridge_pvid)
1903 except Exception as e:
1904 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
1905 return
1906
1907 def up_bridge_port(self, ifaceobj, ifaceobj_getfunc):
1908 should_enslave_port, bridge_name = self.bridge_port_get_bridge_name(ifaceobj)
1909
1910 if not bridge_name:
1911 # bridge doesn't exist
1912 return
1913
223ba5af 1914 vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridge_name)
d486dd0d
JF
1915 if vlan_aware_bridge:
1916 self.up_bridge_port_vlan_aware_bridge(ifaceobj,
1917 ifaceobj_getfunc,
1918 bridge_name,
1919 should_enslave_port)
1920
1921 bridge_ifaceobj = ifaceobj_getfunc(bridge_name)[0]
1922
1923 self.up_apply_brports_attributes(target_ports=[ifaceobj.name],
1924 ifaceobj=bridge_ifaceobj,
1925 ifaceobj_getfunc=ifaceobj_getfunc,
1926 bridge_vlan_aware=vlan_aware_bridge)
1927
1928 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
1929
223ba5af 1930 def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_created):
d486dd0d
JF
1931 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
1932 if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
1933 return False
223ba5af
JF
1934 if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name):
1935 # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports
d486dd0d
JF
1936 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
1937 return True
1938 return False
1939
1940 @staticmethod
1941 def parse_interface_list_value(user_config):
1942 config = dict()
1943 for entry in user_config.split():
1944 ifname, value = entry.split('=')
1945 config[ifname] = value
1946 return config
1947
223ba5af 1948 def sync_bridge_learning_to_vxlan_brport(self, bridge_name, brport_ifaceobj, brport_name, brport_ifla_info_slave_data, user_config_brport_learning_nl, cached_brport_learning):
d486dd0d
JF
1949 """
1950 brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
1951 and
1952 brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
1953
1954 Checks are not performed in this function and must be verified
1955 before. This is done this way to avoid calling this method on
1956 non vlan & bridge port interfaces thus wasting a bit less time
1957 """
1958
1959 kind = None
1960 ifla_info_data = {}
1961
223ba5af
JF
1962 if user_config_brport_learning_nl is None:
1963 user_config_brport_learning_nl = self.bridge_vxlan_port_learning
1964 # bridge-learning is not configured by the user or by a policy
1965 # use "bridge-vxlan-port-learning" policy to set bridge-learning (default on)
d486dd0d 1966
223ba5af
JF
1967 if user_config_brport_learning_nl != cached_brport_learning:
1968 brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] \
1969 = cached_brport_learning \
1970 = user_config_brport_learning_nl
d486dd0d 1971
223ba5af
JF
1972 self.logger.info(
1973 "%s: %s: set bridge-learning %s"
1974 % (bridge_name, brport_name, "on" if user_config_brport_learning_nl else "off")
1975 )
1976 else:
1977 # in this case, the current bridge-learning value is properly configured and
1978 # doesn't need to be reset. We need to make sure that BRPORT_LEARNING is not
1979 # part of ifla_info_slave_data.
1980 try:
1981 del brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING]
3218f49d 1982 except Exception:
223ba5af
JF
1983 pass
1984
1985 #
1986 # vxlan-learning sync:
1987 #
d486dd0d 1988
223ba5af
JF
1989 brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first("vxlan-learning")
1990 # if vxlan-learning is defined by the user or via policy file we need
1991 # to honor his config and not sync vxlan-learning with bridge-learning
d486dd0d 1992
223ba5af
JF
1993 if not brport_vxlan_learning_config:
1994 # check policy file
1995 brport_vxlan_learning_config = policymanager.policymanager_api.get_attr_default("vxlan", "vxlan-learning")
d486dd0d 1996
223ba5af
JF
1997 # convert vxlan-learning string to netlink value (if None use brport-learning value instead)
1998 brport_vxlan_learning_config_nl = utils.get_boolean_from_string(brport_vxlan_learning_config) \
1999 if brport_vxlan_learning_config \
2000 else cached_brport_learning
d486dd0d 2001
223ba5af
JF
2002 if brport_vxlan_learning_config_nl != self.cache.get_link_info_data_attribute(brport_name, Link.IFLA_VXLAN_LEARNING):
2003 self.logger.info(
2004 "%s: %s: vxlan learning and bridge learning out of sync: set vxlan-learning %s"
2005 % (bridge_name, brport_name, "on" if brport_vxlan_learning_config_nl else "off")
2006 )
2007 ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_vxlan_learning_config_nl}
2008 kind = "vxlan"
d486dd0d
JF
2009
2010 # if kind and ifla_info_data are set they will be added to the
2011 # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
2012 return kind, ifla_info_data
2013
d486dd0d
JF
2014 def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
2015 ifname = ifaceobj.name
e537a6e6 2016 single_vxlan_device_ifaceobj = None
d486dd0d
JF
2017
2018 try:
2019 brports_ifla_info_slave_data = dict()
2020 brport_ifaceobj_dict = dict()
223ba5af
JF
2021 brport_name_list = []
2022
2023 cache_brports_ifla_info_slave_data = {}
2024
2025 port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE
d486dd0d 2026
223ba5af 2027 running_brports = self.cache.get_slaves(ifname)
d486dd0d 2028
223ba5af
JF
2029 # If target_ports is specified we want to configure only this
2030 # sub-list of port, we need to check if these ports are already
2031 # enslaved, if not they will be ignored.
2032 # If target_ports is not populated we will apply the brport
2033 # attributes on all running brport.
d486dd0d
JF
2034 if target_ports:
2035 new_targets = []
2036 for brport_name in target_ports:
2037 if brport_name not in running_brports:
2038 self.logger.info('%s: not enslaved to bridge %s: ignored for now' % (brport_name, ifname))
2039 else:
2040 new_targets.append(brport_name)
2041 running_brports = new_targets
2042
d486dd0d
JF
2043 for port in running_brports:
2044 brport_list = ifaceobj_getfunc(port)
2045 if brport_list:
223ba5af
JF
2046 port_already_processed = False
2047
2048 # ports just added to the bridge have to be processed
2049 if port not in newly_enslaved_ports:
2050 # check if brport was already processed
2051 for brportifaceobj in brport_list:
2052 if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED:
2053 # skip port if already processed (probably by `up_bridge_port`)
2054 port_already_processed = True
2055 self.logger.info("%s: port %s: already processed" % (ifname, port))
2056 break
2057
2058 if not port_already_processed:
2059 brport_name_list.append(port)
2060 brport_ifaceobj_dict[port] = brport_list[0]
2061 brports_ifla_info_slave_data[port] = dict()
2062
2063 if not ifupdownflags.flags.PERFMODE and port not in newly_enslaved_ports:
2064 # if the port has just been enslaved, info_slave_data is not cached yet
2065 cache_brports_ifla_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
2066 else:
2067 cache_brports_ifla_info_slave_data[port] = {}
2068
2069 if not brport_name_list:
2070 self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
2071 return
2072
2073 self.logger.info('%s: applying bridge port configuration: %s' % (ifname, brport_name_list))
2074
2075 cached_bridge_mcsnoop = self.cache.get_bridge_multicast_snooping(ifname)
d486dd0d
JF
2076
2077 bridge_ports_learning = {}
223ba5af
JF
2078 bridge_ports_vxlan_arp_suppress = {}
2079 cached_bridge_ports_learning = {}
d486dd0d
JF
2080
2081 # we iterate through all IFLA_BRPORT supported attributes
3b01ed76 2082 for attr_name, nl_attr in self._ifla_brport_attributes_map.items():
d486dd0d
JF
2083 br_config = ifaceobj.get_attr_value_first(attr_name)
2084 translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
2085
2086 if not translate_func:
2087 # if no translation function is found,
2088 # we ignore this attribute and continue
2089 continue
2090
2091 if not br_config:
2092 # user didn't specify any value for this attribute
2093 # looking at policy overrides
2094 br_config = policymanager.policymanager_api.get_iface_default(
2095 module_name=self.__class__.__name__,
2096 ifname=ifname,
2097 attr=attr_name
2098 )
2099
2100 if br_config:
2101 #if bridge_vlan_aware:
2102 # self.logger.info('%s: is a vlan-aware bridge, "%s %s" '
2103 # 'should be configured under the ports'
2104 # % (ifname, attr_name, br_config))
2105
2106 # convert the <interface-yes-no-0-1-list> and <interface-range-list> value to subdict
2107 # brport_name: { attr: value }
2108 # example:
2109 # bridge-portprios swp1=5 swp2=32
2110 # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32}
2111 if '=' in br_config:
2112 try:
2113 br_config = self.parse_interface_list_value(br_config)
3218f49d 2114 except Exception:
d486dd0d
JF
2115 self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
2116 continue
2117
3b01ed76 2118 for brport_ifaceobj in list(brport_ifaceobj_dict.values()):
d486dd0d
JF
2119 brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
2120 brport_name = brport_ifaceobj.name
2121
223ba5af
JF
2122 if not ifupdownflags.flags.PERFMODE:
2123 cached_value = cache_brports_ifla_info_slave_data.get(brport_name, {}).get(nl_attr, None)
d486dd0d
JF
2124 else:
2125 cached_value = None
2126
2127 if not brport_config:
2128 # if a brport attribute was specified under the bridge and not under the port
2129 # we assign the bridge value to the port. If an attribute is both defined under
2130 # the bridge and the brport we keep the value of the port and ignore the br val.
2131 if type(br_config) == dict:
2132 # if the attribute value was in the format interface-list-value swp1=XX swp2=YY
2133 # br_config is a dictionary, example:
2134 # bridge-portprios swp1=5 swp2=32 = {swp1: 5, swp2: 32}
2135 brport_config = br_config.get(brport_name)
2136 else:
2137 brport_config = br_config
2138
2139 if not brport_config:
2140 brport_config = policymanager.policymanager_api.get_iface_default(
2141 module_name=self.__class__.__name__,
2142 ifname=brport_name,
2143 attr=attr_name
2144 )
2145
2146 user_config = brport_config
2147
2148 # attribute specific work
2149 # This shouldn't be here but we don't really have a choice otherwise this
2150 # will require too much code duplication and will make the code very complex
223ba5af
JF
2151 if nl_attr == Link.IFLA_BRPORT_NEIGH_SUPPRESS:
2152 bridge_ports_vxlan_arp_suppress[brport_name] = user_config
d486dd0d 2153 try:
223ba5af
JF
2154 if user_config:
2155 if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2156 self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
2157 'is not supported on a non-vxlan port'
2158 % (ifaceobj.name, brport_name))
2159 continue
2160 elif bridge_vlan_aware:
2161 if not self.arp_nd_suppress_only_on_vxlan:
2162 user_config = self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
2163 elif self.arp_nd_suppress_only_on_vxlan and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2164 # ignore the case of VXLAN brport - handled later in the code
2165 continue
3218f49d 2166 except Exception:
d486dd0d
JF
2167 continue
2168 elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
2169 # special handking for group_fwd_mask because Cisco proprietary
2170 # protocol needs to be set via a private netlink attribute
2171 self.ifla_brport_group_fwd_mask(ifname, brport_name,
2172 brports_ifla_info_slave_data,
2173 user_config, cached_value)
2174 continue
2175
2176 #if brport_config:
2177 # if not bridge_vlan_aware:
2178 # self.logger.info('%s: %s: is not a vlan-aware bridge, "%s %s" '
2179 # 'should be configured under the bridge'
2180 # % (ifname, brport_name,
2181 # attr_name, brport_config))
2182
2183 if user_config:
2184 user_config_nl = translate_func(user_config)
2185 # check config value against running value
2186 if user_config_nl != cached_value:
2187 brports_ifla_info_slave_data[brport_name][nl_attr] = user_config_nl
2188 self.logger.info('%s: %s: set %s %s' % (ifname, brport_name, attr_name, user_config))
2189 self.logger.debug('(cache %s)' % cached_value)
2190
2191 if nl_attr == Link.IFLA_BRPORT_LEARNING:
2192 # for vxlan-learning sync purposes we need to save the user config for each brports.
2193 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
2194 # IFLA_BRPORT_LEARNING if the user value is already configured and running
2195 # nevertheless we still need to check if the vxlan-learning is rightly synced with
2196 # the brport since it might go out of sync for X and Y reasons.
223ba5af 2197 # we also store the cached value to avoid an extra cache lookup.
d486dd0d 2198 bridge_ports_learning[brport_name] = user_config_nl
223ba5af 2199 cached_bridge_ports_learning[brport_name] = cached_value
d486dd0d
JF
2200
2201 elif cached_value is not None:
2202 # no config found, do we need to reset to default?
2203 default = self.get_attr_default_value(attr_name)
2204 if default:
2205 default_netlink = translate_func(default)
2206
223ba5af
JF
2207 if nl_attr == Link.IFLA_BRPORT_LEARNING:
2208 # for vxlan-learning sync purposes we need to save the user config for each brports.
2209 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
2210 # IFLA_BRPORT_LEARNING if the user value is already configured and running
2211 # nevertheless we still need to check if the vxlan-learning is rightly synced with
2212 # the brport since it might go out of sync for X and Y reasons.
2213 # we also store the cached value to avoid an extra cache lookup.
2214 cached_bridge_ports_learning[brport_name] = cached_value
2215 bridge_ports_learning[brport_name] = self.bridge_vxlan_port_learning
2216
2217 if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2218 # bridge-learning for vxlan device is handled separatly in sync_bridge_learning_to_vxlan_brport
2219 continue
2220
2221 if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
2222 # We don't query new slaves and not during boot
2223 try:
2224 if self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_PEER_LINK):
2225 if default_netlink != cached_value:
2226 self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
2227 % (ifname, brport_name))
2228 continue
2229 except Exception as e:
2230 self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
d486dd0d
JF
2231
2232 if default_netlink != cached_value:
2233 self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
2234 % (ifname, brport_name, attr_name, default))
2235 self.logger.debug('(cache %s)' % cached_value)
2236 brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
2237
223ba5af
JF
2238 # is the current bridge (ifaceobj) a QinQ bridge?
2239 # This variable is initialized to None and will be
2240 # change to True/False, so that the check is only
2241 # performed once
2242 qinq_bridge = None
2243
d486dd0d 2244 # applying bridge port configuration via netlink
3b01ed76 2245 for brport_name, brport_ifla_info_slave_data in list(brports_ifla_info_slave_data.items()):
d486dd0d
JF
2246
2247 brport_ifaceobj = brport_ifaceobj_dict.get(brport_name)
2248 if (brport_ifaceobj
2249 and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
2250 and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
2251 # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
2252 # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
2253 kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
d486dd0d
JF
2254 brport_ifaceobj,
2255 brport_name,
2256 brport_ifla_info_slave_data,
223ba5af
JF
2257 bridge_ports_learning.get(brport_name),
2258 cached_bridge_ports_learning.get(brport_name))
9f8f3b0d
JF
2259
2260 if (self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string(
80adb3c1 2261 self.get_bridge_mcsnoop_value(ifaceobj)
9f8f3b0d 2262 )) or cached_bridge_mcsnoop:
80adb3c1 2263 # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter"
9f8f3b0d
JF
2264 # is on and mcsnoop is on (or mcsnoop is already enabled on the
2265 # bridge, set 'bridge-portmcrouter 2' on vxlan ports (if not set by the user)
223ba5af
JF
2266 if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER) \
2267 and self.cache.get_bridge_port_multicast_router(brport_name) != 2:
80adb3c1
JF
2268 brport_ifla_info_slave_data[Link.IFLA_BRPORT_MULTICAST_ROUTER] = 2
2269 self.logger.info("%s: %s: vxlan bridge igmp snooping: enable port multicast router" % (ifname, brport_name))
223ba5af
JF
2270
2271 #
2272 # handling attribute: bridge-arp-nd-suppress
2273 # defaults to bridge-vxlan-arp-nd-suppress policy (default False)
2274 #
2275 user_config_neigh_suppress = bridge_ports_vxlan_arp_suppress.get(brport_name)
2276
2277 if user_config_neigh_suppress is None:
2278
2279 if qinq_bridge is None:
2280 # QinQ bridge hasn't been checked yet
2281 qinq_bridge = self.is_qinq_bridge(
2282 ifaceobj,
2283 brport_name,
2284 running_brports,
2285 brport_ifaceobj_dict,
2286 ifaceobj_getfunc
2287 )
2288
2289 if qinq_bridge:
2290 # exclude QinQ bridge from arp-nd-suppress default policy on
2291 config_neigh_suppress = 0
2292 self.logger.info("%s: QinQ bridge detected: %s: set bridge-arp-nd-suppress off" % (ifname, brport_name))
2293 else:
2294 config_neigh_suppress = self.bridge_vxlan_arp_nd_suppress_int
2295 else:
2296 config_neigh_suppress = int(utils.get_boolean_from_string(user_config_neigh_suppress))
2297
2298 brport_neigh_suppress_cached_value = self.cache.get_link_info_slave_data_attribute(
2299 brport_name,
2300 Link.IFLA_BRPORT_NEIGH_SUPPRESS
2301 )
2302
2303 if config_neigh_suppress != brport_neigh_suppress_cached_value:
2304 brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] = config_neigh_suppress
2305
2306 if not user_config_neigh_suppress:
2307 # if the configuration is not explicitely defined by the user
2308 # we need report that the default behavior is enabled by policy
2309 self.logger.info(
2310 "%s: set bridge-arp-nd-suppress %s by default on vxlan port (%s)"
2311 % (ifname, "on" if self.bridge_vxlan_arp_nd_suppress else "off", brport_name)
2312 )
2313 else:
2314 # the user configuration (or policy) is already configured and running
2315 # we need to remove this attribute from the request dictionary
2316 try:
2317 del brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS]
3218f49d 2318 except Exception:
223ba5af
JF
2319 pass
2320
2321 #
e537a6e6 2322 # SINGLE VXLAN - enable IFLA_BRPORT_VLAN_TUNNEL
223ba5af
JF
2323 #
2324
e537a6e6
JF
2325 if brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
2326 single_vxlan_device_ifaceobj = brport_ifaceobj
2327 brport_vlan_tunnel_cached_value = self.cache.get_link_info_slave_data_attribute(
2328 brport_name,
2329 Link.IFLA_BRPORT_VLAN_TUNNEL
2330 )
2331
2332 if not brport_vlan_tunnel_cached_value:
2333 self.logger.info("%s: %s: enabling vlan_tunnel on single vxlan device" % (ifname, brport_name))
2334 brport_ifla_info_slave_data[Link.IFLA_BRPORT_VLAN_TUNNEL] = 1
2335
d486dd0d
JF
2336 else:
2337 kind = None
2338 ifla_info_data = {}
2339
2340 if brport_ifla_info_slave_data or ifla_info_data:
2341 try:
223ba5af
JF
2342 self.netlink.link_set_brport_with_info_slave_data(
2343 ifname=brport_name,
2344 kind=kind,
2345 ifla_info_data=ifla_info_data,
2346 ifla_info_slave_data=brport_ifla_info_slave_data
2347 )
d486dd0d
JF
2348 except Exception as e:
2349 self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
2350
223ba5af 2351 self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
d486dd0d
JF
2352
2353 except Exception as e:
2354 self.log_error(str(e), ifaceobj)
2355
e537a6e6
JF
2356 if single_vxlan_device_ifaceobj:
2357 self.apply_bridge_port_vlan_vni_map(single_vxlan_device_ifaceobj)
2358
2359 def apply_bridge_port_vlan_vni_map(self, ifaceobj):
2360 """
2361 bridge vlan add vid <vlan-id> dev vxlan0
2362 bridge vlan add dev vxlan0 vid <vlan-id> tunnel_info id <vni>
2363 """
2364 vxlan_name = ifaceobj.name
2365 try:
2366 self.iproute2.batch_start()
2367 for vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
2368
2369 try:
2370 vlans_str, vni_str = vlan_vni_map.split("=")
3218f49d 2371 except Exception:
e537a6e6
JF
2372 return self.__warn_bridge_vlan_vni_map_syntax_error(vlan_vni_map)
2373
2374 vlans = self._ranges_to_ints([vlans_str])
2375 vnis = self._ranges_to_ints([vni_str])
2376
2377 if len(vlans) != len(vnis):
2378 return self.__warn_bridge_vlan_vni_map_syntax_error(vlan_vni_map)
2379
2380 # TODO: query the cache prio to executing those commands
2381 self.iproute2.bridge_vlan_add_vid_list_self(vxlan_name, vlans, False)
2382 self.iproute2.bridge_vlan_add_vlan_tunnel_info(vxlan_name, vlans, vnis)
2383
2384 self.iproute2.batch_commit()
2385 except Exception as e:
2386 self.log_error("%s: error while processing bridge-vlan-vni-map attribute: %s" % (vxlan_name, str(e)))
2387
2388 def __warn_bridge_vlan_vni_map_syntax_error(self, user_config_vlan_vni_map):
2389 self.logger.warning("%s: syntax error: bridge-vlan-vni-map %s" % user_config_vlan_vni_map)
2390
223ba5af
JF
2391 def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc):
2392 """ Detect QinQ bridge
2393 Potential improvement: We could add a ifaceobj.link_privflags called
2394 BRIDGE_QINQ but for now it is not necessary.
2395 """
2396
2397 # bridge-vlan-aware case
2398 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
2399 return (ifaceobj.get_attr_value_first("bridge-vlan-protocol") or "").lower() == "802.1ad"
2400
2401 # old-bridge
2402 else:
2403 for qinq_running_brport in running_brports:
2404 if qinq_running_brport == brport_name:
2405 continue
2406
2407 qinq_running_brport_ifaceobj = brport_ifaceobj_dict.get(qinq_running_brport)
2408
2409 if not qinq_running_brport_ifaceobj:
2410 continue
2411
2412 if qinq_running_brport_ifaceobj.link_kind & ifaceLinkKind.VLAN:
2413 for lower_iface in qinq_running_brport_ifaceobj.lowerifaces or []:
2414 for lower_ifaceobj in ifaceobj_getfunc(lower_iface) or []:
2415 if (lower_ifaceobj.get_attr_value_first("vlan-protocol") or "").lower() == "802.1ad":
2416 return True
2417 return False
2418
2419 def bridge_process_vidinfo_mcqv4src_maxwait(self, ifaceobj):
2420 self._set_bridge_vidinfo_compat(ifaceobj)
2421 self._set_bridge_mcqv4src_compat(ifaceobj)
2422 self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
2423
d486dd0d
JF
2424 def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
2425 """
2426 Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
2427 Since this is the only ifupdown2 attribute dealing with more than 1 netlink
2428 field we need to have special handling for that.
2429 """
2430 ifla_brport_group_fwd_mask = 0
2431 ifla_brport_group_fwd_maskhi = 0
9dfc8a44 2432
d486dd0d 2433 if user_config:
9dfc8a44 2434 for group in user_config.replace(",", " ").split():
d486dd0d
JF
2435 if not group:
2436 continue
2437
2438 callback = self.l2protocol_tunnel_callback.get(group)
2439
2440 if not callable(callback):
2441 self.logger.warning('%s: %s: bridge-l2protocol-tunnel ignoring invalid parameter \'%s\'' % (ifname, brport_name, group))
2442 else:
2443 ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
2444
2445 # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
223ba5af 2446 cached_ifla_brport_group_fwd_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
d486dd0d
JF
2447
2448 log_mask_change = True
2449 # if user specify bridge-l2protocol-tunnel stp cdp
2450 # we need to set both MASK and MASKHI but we only want to log once
2451
2452 if cached_ifla_brport_group_fwd_mask is None:
2453 cached_ifla_brport_group_fwd_mask = 0
2454 if cached_ifla_brport_group_fwd_maskhi is None:
2455 cached_ifla_brport_group_fwd_maskhi = 0
2456
2457 # if the cache value is None it means that the kernel doesn't support this attribute
2458 # or that the cache is stale, we dumped this intf before it was enslaved in the bridge
2459
2460 if ifla_brport_group_fwd_mask != cached_ifla_brport_group_fwd_mask:
2461 if log_mask_change:
2462 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
2463 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_mask)
2464 log_mask_change = False
2465 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASK] = ifla_brport_group_fwd_mask
2466
2467 if ifla_brport_group_fwd_maskhi != cached_ifla_brport_group_fwd_maskhi:
2468 if log_mask_change:
2469 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
2470 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_maskhi)
2471 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASKHI] = ifla_brport_group_fwd_maskhi
2472
255ffb1f
JF
2473 def get_bridge_mtu(self, ifaceobj):
2474 user_config_mtu = ifaceobj.get_attr_value_first("mtu")
2475
2476 if not user_config_mtu:
2477 user_config_mtu = policymanager.policymanager_api.get_attr_default(
393524ab 2478 module_name="address",
255ffb1f
JF
2479 attr="mtu"
2480 )
2481
2482 try:
2483 if user_config_mtu:
393524ab
JF
2484 int(user_config_mtu)
2485 self.logger.info("%s: set bridge mtu %s" % (ifaceobj.name, user_config_mtu))
47877f26 2486 return str(user_config_mtu)
255ffb1f
JF
2487 except Exception as e:
2488 self.logger.warning("%s: invalid bridge mtu %s: %s" % (ifaceobj.name, user_config_mtu, str(e)))
393524ab 2489 return None
255ffb1f 2490
d486dd0d
JF
2491 def up_bridge(self, ifaceobj, ifaceobj_getfunc):
2492 ifname = ifaceobj.name
2493
2494 if ifupdownflags.flags.PERFMODE:
d486dd0d
JF
2495 link_exists = False
2496 else:
223ba5af 2497 link_exists = self.cache.link_exists(ifaceobj.name)
d486dd0d
JF
2498
2499 if not link_exists:
393524ab 2500 self.netlink.link_add_bridge(ifname)
223ba5af 2501 link_just_created = True
393524ab
JF
2502
2503 bridge_mtu = self.get_bridge_mtu(ifaceobj)
2504 if bridge_mtu:
2505 self.sysfs.link_set_mtu(ifname, bridge_mtu, int(bridge_mtu))
d486dd0d 2506 else:
223ba5af 2507 link_just_created = False
d486dd0d
JF
2508 self.logger.info('%s: bridge already exists' % ifname)
2509
223ba5af 2510 bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created)
d486dd0d
JF
2511
2512 self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
2513
2514 try:
2515 newly_enslaved_ports = self._add_ports(ifaceobj, ifaceobj_getfunc)
2516 self.up_apply_brports_attributes(ifaceobj, ifaceobj_getfunc, bridge_vlan_aware,
2517 newly_enslaved_ports=newly_enslaved_ports)
2518 except Exception as e:
2519 self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))
2520
2521 running_ports = ''
2522 try:
223ba5af 2523 running_ports = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
2524 if not running_ports:
2525 return
d486dd0d
JF
2526 self._apply_bridge_port_settings_all(ifaceobj,
2527 ifaceobj_getfunc=ifaceobj_getfunc,
2528 bridge_vlan_aware=bridge_vlan_aware)
2529 except exceptions.ReservedVlanException as e:
2530 raise e
2531 except Exception as e:
2532 self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
2533 finally:
2534 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
223ba5af 2535 self.iproute2.batch_start()
d486dd0d 2536 for p in running_ports:
1d35560d
JF
2537 ifaceobj_list = ifaceobj_getfunc(p)
2538 if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN):
223ba5af 2539 self.iproute2.link_down(p)
d486dd0d 2540 continue
223ba5af
JF
2541 self.iproute2.link_up(p)
2542 try:
2543 self.iproute2.batch_commit()
2544 except Exception as e:
2545 # link set up on bridge ports failed - ignore and log debug
2546 self.logger.debug("%s: %s" % (ifname, str(e)))
d486dd0d
JF
2547
2548 try:
2549 self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
2550 except Exception as e:
2551 self.logger.warning('%s: setting bridge mac address: %s' % (ifaceobj.name, str(e)))
2552
2553 def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc):
2554 if self.bridge_mac_iface and self.bridge_mac_iface[0] and self.bridge_mac_iface[1]:
2555 return self.bridge_mac_iface
2556
2557 if self.bridge_mac_iface_list:
2558 self.logger.debug('bridge mac iface list: %s' % self.bridge_mac_iface_list)
2559
2560 for bridge_mac_intf in self.bridge_mac_iface_list:
2561 ifaceobj_list = ifaceobj_getfunc(bridge_mac_intf)
2562 iface_mac = None
2563
2564 if ifaceobj_list:
2565 for obj in ifaceobj_list:
2566 iface_user_configured_hwaddress = utils.strip_hwaddress(obj.get_attr_value_first('hwaddress'))
2567 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2568 if iface_user_configured_hwaddress:
2569 iface_mac = iface_user_configured_hwaddress
2570
223ba5af 2571 if not iface_mac and not self.cache.link_exists(bridge_mac_intf):
d486dd0d
JF
2572 continue
2573
2574 if not iface_mac:
223ba5af 2575 iface_mac = self.cache.get_link_address(bridge_mac_intf)
d486dd0d
JF
2576 # if hwaddress attribute is not configured we use the running mac addr
2577
2578 self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
2579 return self.bridge_mac_iface
2580 elif self.bridge_set_static_mac_from_port:
2581 # no policy was provided, we need to get the first physdev or bond ports
2582 # and use its hwaddress to set the bridge mac
2583 for port in self._get_bridge_port_list_user_ordered(ifaceobj) or []:
2584 # iterate through the bridge-port list
2585 for port_obj in ifaceobj_getfunc(port) or []:
2586 # check if the port is a physdev (link_kind is null) or a bon
2587 if port_obj.link_kind != ifaceLinkKind.VXLAN:
2588 iface_user_configured_hwaddress = utils.strip_hwaddress(port_obj.get_attr_value_first('hwaddress'))
2589 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2590 if iface_user_configured_hwaddress:
2591 iface_mac = iface_user_configured_hwaddress.lower()
2592 # we need to "normalize" the user provided MAC so it can match with
2593 # what we have in the cache (data retrieved via a netlink dump by
2594 # nlmanager). nlmanager return all macs in lower-case
2595 else:
223ba5af 2596 iface_mac = self.cache.get_link_address(port)
d486dd0d
JF
2597
2598 if iface_mac:
2599 self.bridge_mac_iface = (port, iface_mac)
2600 return self.bridge_mac_iface
2601
2602 return None, None
2603
2604 def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
2605 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
223ba5af 2606 self.iproute2.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
d486dd0d
JF
2607
2608 def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
2609 """
2610 We have a day one bridge mac changing problem with changing ports
2611 (basically bridge mac changes when the port it inherited the mac from
2612 gets de-enslaved).
2613
2614 We have discussed this problem many times before and tabled it.
2615 The issue has aggravated with vxlan bridge ports having auto-generated
2616 random macs...which change on every reboot.
2617
2618 ifupdown2 extract from policy files an iface to select a mac from and
2619 configure it automatically.
2620 """
2621 if ifaceobj.get_attr_value('hwaddress'):
2622 # if the user configured a static hwaddress
2623 # there is no need to assign one
2624 return
2625
2626 ifname = ifaceobj.name
2627 mac_intf, bridge_mac = self._get_bridge_mac(ifaceobj, ifname, ifaceobj_getfunc)
2628 self.logger.debug("%s: _get_bridge_mac returned (%s, %s)"
2629 %(ifname, mac_intf, bridge_mac))
2630
2631 if bridge_mac:
2632 # if an interface is configured with the following attribute:
2633 # hwaddress 08:00:27:42:42:4
2634 # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
2635 # from the kernel. The only way to counter that is to convert all mac to int
2636 # and compare the ints, it will increase perfs and be safer.
223ba5af 2637 cached_value = self.cache.get_link_address(ifname)
d486dd0d 2638 self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
223ba5af 2639 if cached_value and utils.mac_str_to_int(cached_value) == utils.mac_str_to_int(bridge_mac):
d486dd0d
JF
2640 # the bridge mac is already set to the bridge_mac_intf's mac
2641 return
2642
2643 self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
2644 try:
223ba5af 2645 self.netlink.link_set_address(ifname, bridge_mac) # force=True
d486dd0d
JF
2646 except Exception as e:
2647 self.logger.info('%s: %s' % (ifname, str(e)))
2648 # log info this error because the user didn't explicitly configured this
2649 else:
223ba5af 2650 self._add_bridge_mac_to_fdb(ifaceobj, self.cache.get_link_address(ifname))
d486dd0d
JF
2651
2652 def _up(self, ifaceobj, ifaceobj_getfunc=None):
2653 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
2654 self.up_bridge_port(ifaceobj, ifaceobj_getfunc)
2655
2656 elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
2657 self.up_bridge(ifaceobj, ifaceobj_getfunc)
2658
c8e0a11e 2659 else:
3b01ed76 2660 bridge_attributes = list(self._modinfo.get('attrs', {}).keys())
c8e0a11e 2661
3b01ed76 2662 for ifaceobj_config_attr in list(ifaceobj.config.keys()):
c8e0a11e
JF
2663 if ifaceobj_config_attr in bridge_attributes:
2664 self.logger.warning('%s: invalid use of bridge attribute (%s) on non-bridge stanza'
2665 % (ifaceobj.name, ifaceobj_config_attr))
2666
d486dd0d
JF
2667 def _down(self, ifaceobj, ifaceobj_getfunc=None):
2668 if not self._is_bridge(ifaceobj):
2669 return
2670 ifname = ifaceobj.name
223ba5af 2671 if not self.cache.link_exists(ifname):
d486dd0d 2672 return
7840bd2c
JF
2673
2674 try:
2675 self.netlink.link_del(self.get_dummy_brport_name_for_bridge(ifname))
3218f49d 2676 except Exception:
7840bd2c
JF
2677 pass
2678
d486dd0d 2679 try:
223ba5af 2680 running_ports = self.cache.get_slaves(ifname)
d486dd0d
JF
2681 if running_ports:
2682 self.handle_ipv6(running_ports, '0')
2683 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
7f208e56
JF
2684 for p in running_ports:
2685 self.netlink.link_down(p)
d486dd0d
JF
2686 except Exception as e:
2687 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
2688 try:
223ba5af 2689 self.netlink.link_del(ifname)
d486dd0d
JF
2690 except Exception as e:
2691 ifaceobj.set_status(ifaceStatus.ERROR)
2692 self.logger.error(str(e))
2693 # netlink exception already contains the ifname
2694
2695 def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
2696 running_attrs = {}
2697 if ports:
2698 running_bridge_port_vids = ''
2699 for p in ports:
2700 try:
223ba5af 2701 _, running_vids = self.cache.get_pvid_and_vids(p)
d486dd0d
JF
2702 if running_vids:
2703 running_bridge_port_vids += ' %s=%s' %(p,
2704 ','.join(running_vids))
2705 except Exception:
2706 pass
2707 running_attrs['bridge-port-vids'] = running_bridge_port_vids
2708
2709 running_bridge_port_pvid = ''
2710 for p in ports:
2711 try:
223ba5af 2712 running_pvid = self.cache.get_pvid(p)
d486dd0d
JF
2713 if running_pvid:
2714 running_bridge_port_pvid += ' %s=%s' %(p,
2715 running_pvid)
2716 except Exception:
2717 pass
2718 running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
2719
223ba5af 2720 _, running_bridge_vids = self.cache.get_pvid_and_vids(ifaceobjrunning.name)
d486dd0d
JF
2721 if running_bridge_vids:
2722 running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
2723 return running_attrs
2724
2725 def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
2726 bridgeports=None):
2727 running_attrs = {}
2728
2729 # 'bridge-vids' under the bridge is all about 'vids' on the port.
2730 # so query the ports
2731 running_bridgeport_vids = []
2732 running_bridgeport_pvids = []
2733 for bport in bridgeports:
2734 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2735 if vids:
2736 running_bridgeport_vids.append(' '.join(vids))
2737 if pvid:
2738 running_bridgeport_pvids.append(pvid)
2739
2740 bridge_vids = None
2741 if running_bridgeport_vids:
2742 (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
2743 if freq == len(bridgeports):
2744 running_attrs['bridge-vids'] = vidval
2745 bridge_vids = vidval.split()
2746
2747 bridge_pvid = None
2748 if running_bridgeport_pvids:
2749 (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
2750 if freq == len(bridgeports) and vidval != '1':
2751 running_attrs['bridge-pvid'] = vidval
2752 bridge_pvid = vidval.split()[0]
2753
2754 # Go through all bridge ports and find their vids
2755 for bport in bridgeports:
2756 bportifaceobj = ifaceobj_getfunc(bport)
2757 if not bportifaceobj:
2758 continue
2759 bport_vids = []
2760 bport_pvid = None
2761 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2762 if vids and vids != bridge_vids:
2763 bport_vids = vids
2764 if pvid and pvid != bridge_pvid:
2765 bport_pvid = pvid
2766 if bport_vids and bport_pvid in bport_vids:
2767 bport_vids.remove(bport_pvid)
2768 if (not bport_vids and bport_pvid and bport_pvid != '1'):
2769 bportifaceobj[0].replace_config('bridge-access', bport_pvid)
2770 bportifaceobj[0].delete_config('bridge-pvid')
2771 bportifaceobj[0].delete_config('bridge-vids')
2772 else:
2773 if bport_pvid and bport_pvid != '1':
2774 bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
2775 else:
2776 # delete any stale bridge-vids under ports
2777 bportifaceobj[0].delete_config('bridge-pvid')
2778 if bport_vids:
2779 bportifaceobj[0].replace_config('bridge-vids',
2780 ' '.join(bport_vids))
2781 else:
2782 # delete any stale bridge-vids under ports
2783 bportifaceobj[0].delete_config('bridge-vids')
2784 return running_attrs
2785
2786 def _query_running_mcqv4src(self, ifaceobjrunning):
223ba5af 2787 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobjrunning.name)
3b01ed76 2788 mcqs = ['%s=%s' %(v, i) for v, i in list(running_mcqv4src.items())]
d486dd0d
JF
2789 mcqs.sort()
2790 mcq = ' '.join(mcqs)
2791 return mcq
2792
2793 def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
2794 bridge_vlan_aware=False):
223ba5af
JF
2795
2796 ifname = ifaceobjrunning.name
d486dd0d
JF
2797 bridgeattrdict = {}
2798 userspace_stp = 0
2799 ports = None
d486dd0d
JF
2800 try:
2801 if self.systcl_get_net_bridge_stp_user_space() == '1':
2802 userspace_stp = 1
2803 except Exception as e:
2804 self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
2805
223ba5af
JF
2806 bridge_ifla_info_data = self.cache.get_link_info_data(ifname)
2807
d486dd0d
JF
2808
2809 # Fill bridge_ports and bridge stp attributes first
223ba5af
JF
2810 #
2811 # bridge-ports
2812 #
2813 bridgeattrdict["bridge-ports"] = [" ".join(self.cache.get_slaves(ifname))]
2814
2815 #
2816 # bridge-stp
2817 #
2818 cached_stp = bool(bridge_ifla_info_data.get(Link.IFLA_BR_STP_STATE))
2819
2820 if cached_stp != utils.get_boolean_from_string(
2821 self.get_mod_subattr("bridge-stp", "default")
2822 ):
2823 bridgeattrdict['bridge-stp'] = ["yes" if cached_stp else "no"]
2824
2825 skip_kernel_stp_attrs = cached_stp and userspace_stp
2826
2827 if skip_kernel_stp_attrs:
2828 bridge_attributes_map = {
2829 "bridge-mcqifaddr": Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
2830 "bridge-mcquerier": Link.IFLA_BR_MCAST_QUERIER,
2831 "bridge-mcrouter": Link.IFLA_BR_MCAST_ROUTER,
2832 "bridge-mcstats": Link.IFLA_BR_MCAST_STATS_ENABLED,
2833 "bridge-mcsnoop": Link.IFLA_BR_MCAST_SNOOPING,
2834 "bridge-mclmc": Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
2835 "bridge-mclmi": Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
2836 "bridge-mcqri": Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
2837 "bridge-mcqpi": Link.IFLA_BR_MCAST_QUERIER_INTVL,
2838 "bridge-mcsqc": Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
2839 "bridge-mcsqi": Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
2840 "bridge-mcmi": Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
2841 "bridge-mcqi": Link.IFLA_BR_MCAST_QUERY_INTVL,
2842 }
2843 else:
2844 bridge_attributes_map = dict(self._ifla_br_attributes_map)
2845 try:
2846 del bridge_attributes_map[Link.IFLA_BR_STP_STATE]
3218f49d 2847 except Exception:
223ba5af
JF
2848 pass
2849
2850 #
2851 # bridge-vlan-stats
2852 #
2853 cached_vlan_stats = bridge_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
2854
2855 if cached_vlan_stats != utils.get_boolean_from_string(
2856 self.get_mod_subattr("bridge-vlan-stats", "default")
2857 ):
2858 bridgeattrdict['bridge-vlan-stats'] = ["on" if cached_vlan_stats else "off"]
2859
2860 try:
2861 del bridge_attributes_map[Link.IFLA_BR_VLAN_STATS_ENABLED]
3218f49d 2862 except Exception:
223ba5af 2863 pass
d486dd0d 2864
c817ba4d 2865 lambda_nl_value_int_divide100 = lambda x: str(x // 100)
223ba5af
JF
2866 lambda_nl_value_to_yes_no_boolean = lambda x: "yes" if x else "no"
2867
2868 bridge_attr_value_netlink_to_string_dict = {
2869 Link.IFLA_BR_VLAN_PROTOCOL: lambda x: x.lower(), # return lower case vlan protocol
2870 Link.IFLA_BR_AGEING_TIME: lambda_nl_value_int_divide100,
2871 Link.IFLA_BR_MAX_AGE: lambda_nl_value_int_divide100,
2872 Link.IFLA_BR_FORWARD_DELAY: lambda_nl_value_int_divide100,
2873 Link.IFLA_BR_HELLO_TIME: lambda_nl_value_int_divide100,
2874 Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL: lambda_nl_value_int_divide100,
2875 Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: lambda_nl_value_int_divide100,
2876 Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL: lambda_nl_value_int_divide100,
2877 Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: lambda_nl_value_int_divide100,
2878 Link.IFLA_BR_MCAST_QUERIER_INTVL: lambda_nl_value_int_divide100,
2879 Link.IFLA_BR_MCAST_QUERY_INTVL: lambda_nl_value_int_divide100,
2880 Link.IFLA_BR_VLAN_FILTERING: lambda_nl_value_to_yes_no_boolean,
2881 Link.IFLA_BR_MCAST_QUERY_USE_IFADDR: lambda_nl_value_to_yes_no_boolean,
2882 Link.IFLA_BR_MCAST_SNOOPING: lambda_nl_value_to_yes_no_boolean,
2883 Link.IFLA_BR_MCAST_QUERIER: lambda_nl_value_to_yes_no_boolean,
2884 Link.IFLA_BR_MCAST_ROUTER: lambda_nl_value_to_yes_no_boolean,
2885 }
2886
3b01ed76 2887 for attr_name, attr_nl in bridge_attributes_map.items():
223ba5af
JF
2888 default_value = self.get_mod_subattr(attr_name, "default")
2889 cached_value = bridge_ifla_info_data.get(attr_nl)
2890
2891 if cached_value is None:
d486dd0d 2892 continue
223ba5af
JF
2893
2894 cached_value_string = bridge_attr_value_netlink_to_string_dict.get(attr_nl, str)(cached_value)
2895
2896 if default_value != cached_value_string:
2897 bridgeattrdict[attr_name] = [cached_value_string]
d486dd0d
JF
2898
2899 if bridge_vlan_aware:
2900 if not ports:
2901 ports = {}
2902 bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
2903 ifaceobj_getfunc,
3b01ed76 2904 list(ports.keys()))
d486dd0d
JF
2905 else:
2906 bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
2907 ports)
2908 if bridgevidinfo:
3b01ed76 2909 bridgeattrdict.update({k : [v] for k, v in list(bridgevidinfo.items())
d486dd0d
JF
2910 if v})
2911
2912 mcq = self._query_running_mcqv4src(ifaceobjrunning)
2913 if mcq:
2914 bridgeattrdict['bridge-mcqv4src'] = [mcq]
2915
2916 if skip_kernel_stp_attrs:
2917 return bridgeattrdict
2918
2919 # Do this only for vlan-UNAWARE-bridge
2920 if ports and not bridge_vlan_aware:
2921 portconfig = {'bridge-pathcosts' : '',
2922 'bridge-portprios' : '',
2923 'bridge-learning' : '',
2924 'bridge-unicast-flood' : '',
2925 'bridge-multicast-flood' : '',
223ba5af 2926 'bridge-broadcast-flood' : '',
d486dd0d
JF
2927 'bridge-arp-nd-suppress' : '',
2928 }
3b01ed76 2929 for p, v in list(ports.items()):
223ba5af 2930 v = str(self.cache.get_brport_cost(p))
d486dd0d
JF
2931 if v and v != self.get_mod_subattr('bridge-pathcosts',
2932 'default'):
2933 portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
2934
223ba5af 2935 v = str(self.cache.get_brport_priority(p))
d486dd0d
JF
2936 if v and v != self.get_mod_subattr('bridge-portprios',
2937 'default'):
2938 portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
2939
223ba5af 2940 v = utils.get_onff_from_onezero(self.cache.get_brport_learning(p))
d486dd0d
JF
2941 if (v and
2942 v != self.get_mod_subattr('bridge-learning', 'default')):
2943 portconfig['bridge-learning'] += ' %s=%s' %(p, v)
2944
223ba5af 2945 v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(p))
d486dd0d
JF
2946 if (v and
2947 v != self.get_mod_subattr('bridge-unicast-flood',
2948 'default')):
2949 portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
2950
223ba5af 2951 v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(p))
d486dd0d
JF
2952 if (v and
2953 v != self.get_mod_subattr('bridge-multicast-flood',
2954 'default')):
2955 portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
2956
223ba5af
JF
2957 v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(p))
2958 if (v and
2959 v != self.get_mod_subattr('bridge-broadcast-flood',
2960 'default')):
2961 portconfig['bridge-broadcast-flood'] += ' %s=%s' %(p, v)
2962
2963 v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(p))
d486dd0d
JF
2964 if (v and
2965 v != self.get_mod_subattr('bridge-arp-nd-suppress',
2966 'default')):
2967 portconfig['bridge-arp-nd-suppress'] += ' %s=%s' %(p, v)
2968
3b01ed76 2969 bridgeattrdict.update({k : [v] for k, v in list(portconfig.items())
d486dd0d
JF
2970 if v})
2971
2972 return bridgeattrdict
2973
2974 def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
2975 running_mcqs = self._query_running_mcqv4src(ifaceobj)
2976 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
2977 if attrval:
2978 mcqs = attrval.split()
2979 mcqs.sort()
2980 mcqsout = ' '.join(mcqs)
2981 ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
2982 running_mcqs, 1 if running_mcqs != mcqsout else 0)
2983
223ba5af
JF
2984 def _query_check_bridge_vidinfo(self, ifname, ifaceobj, ifaceobjcurr):
2985 #
2986 # bridge-port-vids
2987 #
2988 bridge_port_vids_user_config = ifaceobj.get_attr_value_first("bridge-port-vids")
2989 if bridge_port_vids_user_config:
2990
2991 port_list = self.parse_port_list(ifname, bridge_port_vids_user_config)
2992
2993 if not port_list:
2994 self.log_warn("%s: could not parse 'bridge-port-vids %s'"
2995 % (ifname, bridge_port_vids_user_config))
2996 ifaceobjcurr.update_config_with_status("bridge-port-vids", "ERROR", 1)
d486dd0d 2997 return
223ba5af
JF
2998
2999 error = False
3000 for port_config in port_list:
d486dd0d 3001 try:
223ba5af
JF
3002 port, vids_raw = port_config.split("=")
3003 packed_vids = vids_raw.split(",")
3004
3005 running_pvid, running_vids = self.cache.get_pvid_and_vids(port)
3006
3007 if not self._compare_vids(packed_vids, running_vids, pvid=running_pvid, expand_range=False):
3008 error = True
d486dd0d 3009
223ba5af
JF
3010 except Exception as e:
3011 self.log_warn("%s: failure checking vid %s (%s)" % (ifname, port_config, str(e)))
3012
3013 ifaceobjcurr.update_config_with_status("bridge-port-vids", bridge_port_vids_user_config, error)
3014
3015 #
3016 # bridge-port-pvids
3017 #
d486dd0d
JF
3018 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
3019 if attrval:
3020 portlist = self.parse_port_list(ifaceobj.name, attrval)
3021 if not portlist:
3022 self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
223ba5af 3023 % (ifname, attrval))
d486dd0d 3024 return
223ba5af
JF
3025
3026 error = False
3027 running_pvid_config = []
d486dd0d 3028 for p in portlist:
223ba5af
JF
3029 (port, pvid) = p.split('=')
3030 running_pvid, _ = self.cache.get_pvid_and_vids(port)
3031
3032 running_pvid_config.append("%s=%s" % (port, running_pvid))
3033
3034 if running_pvid != int(pvid):
3035 error = True
3036
3037 ifaceobjcurr.update_config_with_status(
3038 "bridge-port-pvids",
3039 " ".join(running_pvid_config),
3040 int(error)
3041 )
d486dd0d
JF
3042
3043 vids = self.get_ifaceobj_bridge_vids(ifaceobj)
3044 if vids[1]:
3045 ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
3046
3047 def _query_check_snooping_wdefault(self, ifaceobj):
3048 if (ifupdownflags.flags.WITHDEFAULTS
3049 and not self._vxlan_bridge_default_igmp_snooping
3050 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
3051 ifaceobj.replace_config('bridge-mcsnoop', 'no')
3052
223ba5af 3053 def _query_check_bridge(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
d486dd0d
JF
3054 if not self._is_bridge(ifaceobj):
3055 return
d486dd0d 3056
223ba5af 3057 ifname = ifaceobj.name
d486dd0d 3058
223ba5af
JF
3059 if not self.cache.bridge_exists(ifname):
3060 self.logger.info("%s: bridge: does not exist" % (ifname))
d486dd0d 3061 return
d486dd0d 3062
223ba5af 3063 self._query_check_snooping_wdefault(ifaceobj)
d486dd0d 3064
223ba5af 3065 user_config_attributes = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
d486dd0d 3066
223ba5af
JF
3067 # add default attributes if --with-defaults is set
3068 if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in user_config_attributes:
3069 user_config_attributes.append('bridge-stp')
d486dd0d 3070
223ba5af
JF
3071 if not user_config_attributes:
3072 return
d486dd0d 3073
223ba5af
JF
3074 if "bridge-ports" in user_config_attributes:
3075 self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, self.cache.get_slaves(ifname), ifaceobj_getfunc)
d1265fd5 3076
223ba5af 3077 if "bridge-ports-condone-regex" in user_config_attributes:
f5c97c0d
MW
3078 ifaceobjcurr.update_config_with_status(
3079 "bridge-ports-condone-regex",
3080 self._get_bridge_port_condone_regex(ifaceobj, True),
3081 0
3082 )
223ba5af
JF
3083
3084 # Those attributes require separate handling
3085 filter_attributes = [
3086 "bridge-trunk",
3087 "bridge-ports",
3088 "bridge-vids",
3089 "bridge-trunk",
3090 "bridge-mcqv4src",
3091 "bridge-port-vids",
3092 "bridge-port-pvids",
3093 "bridge-l2protocol-tunnel",
3094 "bridge-ports-condone-regex"
3095 ]
3096
3097 ignore_attributes = (
3098 # bridge-pvid and bridge-vids on a bridge does not correspond
3099 # directly to a running config on the bridge. They correspond to
3100 # default values for the bridge ports. And they are already checked
3101 # against running config of the bridge port and reported against a
3102 # bridge port. So, ignore these attributes under the bridge. Use '2'
3103 # for ignore today. XXX: '2' will be mapped to a defined value in
3104 # subsequent patches.
3105 "bridge-pvid",
3106 "bridge-allow-untagged",
3107 )
3108 for attr in ignore_attributes:
3109 if attr in user_config_attributes:
3110 ifaceobjcurr.update_config_with_status(attr, ifaceobj.get_attr_value_first(attr), 2)
3111 filter_attributes.append(attr)
3112
3b01ed76 3113 bridge_config = set(user_config_attributes).difference(filter_attributes)
223ba5af
JF
3114 cached_ifla_info_data = self.cache.get_link_info_data(ifname)
3115
3116 self._query_check_bridge_attributes(ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data)
3117 self._query_check_brport_attributes_on_bridge(ifname, ifaceobj, ifaceobjcurr, bridge_config)
3118 self._query_check_bridge_vidinfo(ifname, ifaceobj, ifaceobjcurr)
3119 self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
3120 self._query_check_l2protocol_tunnel_on_bridge(ifname, ifaceobj, ifaceobjcurr)
3121
7840bd2c
JF
3122 def _query_check_bridge_always_up(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
3123 bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")
3124
3125 if bridge_always_up:
3126 bridge_config.remove("bridge-always-up")
3127
3128 if utils.get_boolean_from_string(bridge_always_up):
3129 try:
3130 link_exists = self.cache.link_exists(self.get_dummy_brport_name_for_bridge(ifname))
3218f49d 3131 except Exception:
7840bd2c
JF
3132 link_exists = False
3133
3134 ifaceobjcurr.update_config_with_status(
3135 "bridge-always-up",
3136 "yes" if link_exists else "no",
3137 not link_exists
3138 )
3139
223ba5af
JF
3140 def _query_check_bridge_attributes(self, ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data):
3141 for attr in list(bridge_config):
3142 query_check_handler, netlink_attr = self._bridge_attribute_query_check_handler.get(attr, (None, None))
3143
3144 if callable(query_check_handler):
3145 query_check_handler(attr, ifaceobj.get_attr_value_first(attr), ifaceobjcurr, cached_ifla_info_data.get(netlink_attr))
3146 bridge_config.remove(attr)
3147
7840bd2c
JF
3148 self._query_check_bridge_always_up(ifaceobj.name, ifaceobj, ifaceobjcurr, bridge_config)
3149
223ba5af
JF
3150 def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
3151 brports_info_slave_data = {}
3152 # bridge_config should only have bridge-port-list attributes
3153 for attr in bridge_config:
3154 attr_nl = self._ifla_brport_attributes_map.get(attr)
3155 brport_query_check_handler = self._brport_attribute_query_check_handler.get(attr)
3156
3157 if not attr_nl or not brport_query_check_handler:
3158 self.logger.warning("%s: query-check: missing handler for attribute: %s (%s)" % (ifname, attr, attr_nl))
d486dd0d 3159 continue
223ba5af
JF
3160
3161 running_config = []
3162 status = 0
3163
3164 for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []:
3165 port, config = port_config.split("=")
3166
3167 if not port in brports_info_slave_data:
3168 info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
d486dd0d 3169 else:
223ba5af
JF
3170 info_slave_data = brports_info_slave_data[port]
3171
3172 port_config, port_status = brport_query_check_handler(port, config, info_slave_data.get(attr_nl))
3173
3174 running_config.append(port_config)
3175
3176 if port_status:
3177 status = 1
3178
3179 ifaceobjcurr.update_config_with_status(
3180 attr,
3181 " ".join(running_config),
3182 status
3183 )
3184
3185 @staticmethod
3186 def _query_check_br_attr_wait(attr, wait_value, ifaceobjcurr, __):
3187 ifaceobjcurr.update_config_with_status(attr, wait_value, 0)
3188
3189 def _query_check_br_attr_stp(self, attr, stp_value, ifaceobjcurr, cached_value):
3190 if not stp_value:
3191 if ifupdownflags.flags.WITHDEFAULTS:
3192 stp_value = "on" if self.default_stp_on else "off"
d486dd0d 3193 else:
223ba5af 3194 return
d486dd0d 3195
223ba5af 3196 user_config_to_nl = utils.get_boolean_from_string(stp_value)
d486dd0d 3197
223ba5af
JF
3198 ifaceobjcurr.update_config_with_status(
3199 attr,
3200 "yes" if cached_value else "no",
3201 user_config_to_nl != bool(cached_value)
3202 )
3203
3204 @staticmethod
3205 def _query_check_br_attr_int(attr, user_config, ifaceobjcurr, cached_value):
3206 ifaceobjcurr.update_config_with_status(
3207 attr,
3208 str(cached_value),
3209 int(user_config) != cached_value
3210 )
3211
3212 @staticmethod
3213 def _query_check_br_attr_int_divided100(attr, user_config, ifaceobjcurr, cached_value):
c817ba4d 3214 value = cached_value // 100
223ba5af
JF
3215 ifaceobjcurr.update_config_with_status(
3216 attr,
3217 str(value),
3218 int(user_config) != value
3219 )
3220
3221 @staticmethod
3222 def _query_check_br_attr_boolean(attr, user_config, ifaceobjcurr, cached_value):
3223 ifaceobjcurr.update_config_with_status(
3224 attr,
3225 "yes" if cached_value else "no",
3226 utils.get_boolean_from_string(user_config) != cached_value
3227 )
3228
3229 @staticmethod
3230 def _query_check_br_attr_boolean_on_off(attr, user_config, ifaceobjcurr, cached_value):
3231 ifaceobjcurr.update_config_with_status(
3232 attr,
3233 "on" if cached_value else "off",
3234 utils.get_boolean_from_string(user_config) != cached_value
3235 )
3236
3237 @staticmethod
3238 def _query_check_br_attr_string(attr, user_config, ifaceobjcurr, cached_value):
3239 ifaceobjcurr.update_config_with_status(
3240 attr,
3241 cached_value,
3242 user_config.lower() != cached_value
3243 )
3244
3245 @staticmethod
3246 def _query_check_brport_attr_boolean_on_off(port, user_config, cached_value):
3247 return "%s=%s" % (port, "on" if cached_value else "off"), utils.get_boolean_from_string(user_config) != cached_value
3248
3249 @staticmethod
3250 def _query_check_brport_attr_boolean_yes_no(port, user_config, cached_value):
3251 return "%s=%s" % (port, "yes" if cached_value else "no"), utils.get_boolean_from_string(user_config) != cached_value
3252
3253 @staticmethod
3254 def _query_check_brport_attr_int(port, user_config, cached_value):
3255 return "%s=%s" % (port, cached_value), int(user_config) != cached_value
3256
3257 @classmethod
3258 def _query_check_brport_attr_portmcrouter(cls, port, user_config, cached_value):
3259 return (
3260 "%s=%s" % (port, cls._ifla_brport_multicast_router_dict_int_to_str.get(cached_value)),
3261 cls._ifla_brport_multicast_router_dict_to_int.get(user_config) != cached_value
3262 )
3263
3264 ####################################################################################################################
d486dd0d 3265
d1265fd5 3266 def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):
7840bd2c
JF
3267
3268 # if bridge-always-up is set we need to remove the dummy brport from the running_port_list
3269 if utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-always-up")):
3270 try:
3271 running_port_list.remove(self.get_dummy_brport_name_for_bridge(ifaceobj.name))
3218f49d 3272 except Exception:
7840bd2c
JF
3273 pass
3274
d1265fd5
JF
3275 bridge_all_ports = []
3276 for obj in ifaceobj_getfunc(ifaceobj.name) or []:
cc99b41d 3277 bridge_all_ports.extend(self._get_bridge_port_list(obj) or [])
d1265fd5
JF
3278
3279 if not running_port_list and not bridge_all_ports:
3280 return
3281
d1265fd5
JF
3282 try:
3283 port_list = self._get_ifaceobj_bridge_ports(ifaceobj).split()
3284 # we want to display the same bridge-ports list as provided
3285 # in the interfaces file but if this list contains regexes or
3286 # globs, for now, we won't try to change it.
3287 if 'regex' in port_list or 'glob' in port_list:
3288 port_list = running_port_list
3289 else:
3290 ordered = []
3291 for i in range(0, len(port_list)):
3292 if port_list[i] in running_port_list:
3293 ordered.append(port_list[i])
3294 port_list = ordered
3218f49d 3295 except Exception:
d1265fd5 3296 port_list = running_port_list
f5c97c0d
MW
3297
3298 difference = set(running_port_list).symmetric_difference(bridge_all_ports)
3299 bridge_port_condone_regex = self._get_bridge_port_condone_regex(ifaceobj)
3300
3301 if bridge_port_condone_regex:
3302 # Drop any condoned port from the difference set
3303 condone_ports = [port for port in difference if bridge_port_condone_regex.match(port)]
3304
3305 for port in condone_ports:
3306 try:
3307 difference.remove(port)
3308 except ValueError:
3309 pass
3310
3311 # Tag all condoned ports in brackets in output
3312 if port not in bridge_all_ports:
3313 port_list.append("(%s)" % port)
3314
3315 ifaceobjcurr.update_config_with_status(
3316 "bridge-ports",
3317 " ".join(port_list) if port_list else "",
3318 0 if not difference else 1
3319 )
d1265fd5 3320
d486dd0d
JF
3321 def get_ifaceobj_bridge_vids(self, ifaceobj):
3322 vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
3323 if not vids[1]:
3324 vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
3325 return vids
3326
3327 def get_ifaceobj_bridge_vids_value(self, ifaceobj):
3328 return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
3329
3330 def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
3331 ifaceobjs = ifaceobj_getfunc(bridgename)
3332 for ifaceobj in ifaceobjs:
3333 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
3334 if vids: return re.split(r'[\s\t,]\s*', vids)
3335 return None
3336
3337 def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
3338 ifaceobjs = ifaceobj_getfunc(bridgename)
3339 pvid = None
3340 for ifaceobj in ifaceobjs:
3341 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
3342 if pvid:
3343 break
3344 return pvid
3345
223ba5af
JF
3346 def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc):
3347 running_pvid, running_vids = self.cache.get_pvid_and_vids(ifname)
3348
3349 #
3350 # bridge-access
3351 #
3352 brport_vid_access_user_config = ifaceobj.get_attr_value_first("bridge-access")
3353
3354 if brport_vid_access_user_config:
3355 try:
3356 vid_int = int(brport_vid_access_user_config)
3357 except ValueError as e:
3358 ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1)
3359 raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e)))
3360
3361 ifaceobjcurr.update_config_with_status(
3362 "bridge-access",
3363 str(running_pvid),
3364 running_pvid != vid_int or running_vids[0] != vid_int
3365 )
d486dd0d
JF
3366 return
3367
223ba5af
JF
3368 #
3369 # bridge-pvid
3370 #
3371 brport_pvid_user_config = ifaceobj.get_attr_value_first("bridge-pvid")
3372
3373 if brport_pvid_user_config:
3374 try:
3375 pvid = int(brport_pvid_user_config)
3376 except ValueError as e:
3377 ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1)
3378 raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e)))
3379
3380 ifaceobjcurr.update_config_with_status(
3381 "bridge-pvid",
3382 str(running_pvid),
3383 running_pvid != pvid
3384 )
d486dd0d
JF
3385 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
3386 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
3387 (ifaceobj.flags & iface.OLDEST_SIBLING))):
3388 # if the interface has multiple iface sections,
3389 # we check the below only for the oldest sibling
3390 # or the last iface section
223ba5af
JF
3391 try:
3392 pvid = int(self._get_bridge_pvid(bridge_name, ifaceobj_getfunc))
3393 except (TypeError, ValueError):
3394 pvid = 0
d486dd0d
JF
3395 if pvid:
3396 if not running_pvid or running_pvid != pvid:
3397 ifaceobjcurr.status = ifaceStatus.ERROR
3398 ifaceobjcurr.status_str = 'bridge pvid error'
223ba5af 3399 elif not running_pvid or running_pvid != 1:
d486dd0d
JF
3400 ifaceobjcurr.status = ifaceStatus.ERROR
3401 ifaceobjcurr.status_str = 'bridge pvid error'
3402
3403 attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
3404 if vids:
3405 vids = re.split(r'[\s\t]\s*', vids)
e537a6e6
JF
3406
3407 # Special treatment to make sure that the vlans mapped with vnis
3408 # (in single-vxlan context) are not mistaken for regular vlans.
3409 # We need to proactively remove them from the "running_vids"
3410 vlans_mapped_with_vnis = self.get_bridge_vlans_mapped_to_vnis_as_integer_list(ifaceobj)
3411 new_running_vids = []
3412 user_config_vids = self._ranges_to_ints(vids)
3413 for v in running_vids:
3414 if v in user_config_vids:
3415 new_running_vids.append(v)
3416 elif v not in vlans_mapped_with_vnis:
3417 new_running_vids.append(v)
3418 running_vids = new_running_vids
3419 #####################################################################
3420
223ba5af 3421 if not running_vids or not self._compare_vids(vids, running_vids, running_pvid, expand_range=False):
6d94c17b 3422 running_vids = [str(o) for o in running_vids]
d486dd0d
JF
3423 ifaceobjcurr.update_config_with_status(attr_name,
3424 ' '.join(running_vids), 1)
3425 else:
3426 ifaceobjcurr.update_config_with_status(attr_name,
3427 ' '.join(vids), 0)
3428 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
3429 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
3430 (ifaceobj.flags & iface.OLDEST_SIBLING))):
3431 # if the interface has multiple iface sections,
3432 # we check the below only for the oldest sibling
3433 # or the last iface section
3434
3435 # check if it matches the bridge vids
223ba5af 3436 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
d486dd0d 3437 if (bridge_vids and (not running_vids or
223ba5af 3438 not self._compare_vids(bridge_vids, running_vids, running_pvid, expand_range=False))):
d486dd0d
JF
3439 ifaceobjcurr.status = ifaceStatus.ERROR
3440 ifaceobjcurr.status_str = 'bridge vid error'
3441
223ba5af
JF
3442 _query_check_brport_attributes = (
3443 "bridge-pvid",
3444 "bridge-vids",
3445 "bridge-trunk",
3446 "bridge-access",
3447 "bridge-pathcosts",
3448 "bridge-portprios",
3449 "bridge-portmcrouter",
3450 "bridge-learning",
3451 "bridge-portmcfl",
3452 "bridge-unicast-flood",
3453 "bridge-multicast-flood",
3454 "bridge-broadcast-flood",
3455 "bridge-arp-nd-suppress",
3456 "bridge-l2protocol-tunnel"
3457 )
3458
d486dd0d
JF
3459 def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
3460 ifaceobj_getfunc):
223ba5af
JF
3461
3462 ifname = ifaceobj.name
3463
3464 if not self.cache.link_is_bridge_port(ifname):
3465 # Mark all bridge brport attributes as failed
3466 ifaceobjcurr.check_n_update_config_with_status_many(
3467 ifaceobj, self._query_check_brport_attributes, 1
3468 )
d486dd0d 3469 return
223ba5af
JF
3470
3471 bridge_name = self.cache.get_bridge_name_from_port(ifname)
3472 if not bridge_name:
c46af1c9 3473 self.logger.warning("%s: unable to determine bridge name" % ifname)
d486dd0d
JF
3474 return
3475
223ba5af
JF
3476 if self.cache.bridge_is_vlan_aware(bridge_name):
3477 self._query_check_bridge_port_vidinfo(ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
3478
3479 brport_info_slave_data = self.cache.get_link_info_slave_data(ifname)
3480
3481 #
3482 # bridge-portmcfl
3483 #
3484 portmcfl = ifaceobj.get_attr_value_first("bridge-portmcfl")
3485
3486 if portmcfl:
3487 cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE)
3488
3489 ifaceobjcurr.update_config_with_status(
3490 "bridge-portmcfl",
3491 "yes" if cached_value else "no",
3492 utils.get_boolean_from_string(portmcfl) != cached_value
3493 )
3494
3495 #
3496 # bridge-portmcrouter
3497 #
3498 portmcrouter = ifaceobj.get_attr_value_first("bridge-portmcrouter")
3499
3500 if portmcrouter:
3501 cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER)
3502
3503 ifaceobjcurr.update_config_with_status(
3504 "bridge-portmcrouter",
3505 self._ifla_brport_multicast_router_dict_int_to_str.get(cached_value),
3506 self._ifla_brport_multicast_router_dict_to_int.get(portmcrouter) != cached_value
3507 )
3508
3509 #
3510 # bridge-learning
3511 # bridge-unicast-flood
3512 # bridge-multicast-flood
3513 # bridge-broadcast-flood
3514 # bridge-arp-nd-suppress
3515 #
3516 for attr_name, attr_nl in (
3517 ("bridge-learning", Link.IFLA_BRPORT_LEARNING),
3518 ("bridge-unicast-flood", Link.IFLA_BRPORT_UNICAST_FLOOD),
3519 ("bridge-multicast-flood", Link.IFLA_BRPORT_MCAST_FLOOD),
3520 ("bridge-broadcast-flood", Link.IFLA_BRPORT_BCAST_FLOOD),
3521 ("bridge-arp-nd-suppress", Link.IFLA_BRPORT_NEIGH_SUPPRESS),
3522 ):
3523 attribute_value = ifaceobj.get_attr_value_first(attr_name)
3524
3525 if not attribute_value:
3526 continue
3527
3528 cached_value = brport_info_slave_data.get(attr_nl)
3529
3530 ifaceobjcurr.update_config_with_status(
3531 attr_name,
3532 "on" if cached_value else "off",
3533 utils.get_boolean_from_string(attribute_value) != cached_value
3534 )
3535
3536 #
3537 # bridge-pathcosts
3538 # bridge-portprios
3539 #
3540 for attr_name, attr_nl in (
3541 ("bridge-pathcosts", Link.IFLA_BRPORT_COST),
3542 ("bridge-portprios", Link.IFLA_BRPORT_PRIORITY),
3543 ):
3544 attribute_value = ifaceobj.get_attr_value_first(attr_name)
3545
3546 if not attribute_value:
d486dd0d
JF
3547 continue
3548
223ba5af
JF
3549 cached_value = brport_info_slave_data.get(attr_nl)
3550
d486dd0d 3551 try:
223ba5af
JF
3552 ifaceobjcurr.update_config_with_status(
3553 attr_name,
3554 str(cached_value),
3555 int(attribute_value) != cached_value
3556 )
3557 except ValueError as e:
3558 ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1)
3559 raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e)))
d486dd0d
JF
3560
3561 self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
3562
e537a6e6
JF
3563 #
3564 # bridge-vlan-vni-map
3565 #
3566 fail = False
3567 cached_vlans, cached_vnis = self.get_vlan_vni_ranges(self.cache.get_vlan_vni(ifaceobj.name))
3568
8a78cd07 3569 for bridge_vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map") or []:
e537a6e6
JF
3570
3571 if fail:
3572 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3573 continue
3574
3575 try:
3576 vlans_str, vni_str = bridge_vlan_vni_map.split("=")
3218f49d 3577 except Exception:
e537a6e6
JF
3578 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3579 return self.__warn_bridge_vlan_vni_map_syntax_error(bridge_vlan_vni_map)
3580
3581 vlans_list = self._ranges_to_ints([vlans_str]) # self.bridge_vlan_vni_map_convert_user_config_to_set(vlans_str)
3582 vnis_list = self._ranges_to_ints([vni_str]) #self.bridge_vlan_vni_map_convert_user_config_to_set(vni_str)
3583
3584 # since there can be multiple entry of bridge-vlan-vni-map
3585 # we could simply check that all vlans and vnis are correctly
3586 # set on the vxlan but we would probably miss the case where extra
3587 # vlans and vnis were added. So we ned to keep a copy of the cache
3588 # entry and pop vlans and svis from the cache copy as we iterate
3589 # through the user config. After processing only extra vlans and
3590 # vnis should be left.
3591 try:
3592 for vlan in vlans_list:
3593 cached_vlans.remove(vlan)
3218f49d 3594 except Exception:
e537a6e6
JF
3595 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3596 fail = True
3597 continue
3598
3599 try:
3600 for vni in vnis_list:
3601 cached_vnis.remove(vni)
3218f49d 3602 except Exception:
e537a6e6
JF
3603 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3604 fail = True
3605 continue
3606
3607 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 0)
3608
3609 if not fail and (cached_vlans or cached_vnis):
3610 # cached_vlans and cached_vnis are not empty, it means more
3611 # vlans-vni maps were configured on the bridge port
3612 ifaceobjcurr.update_config_with_status(
3613 "bridge-vlan-vni-map",
3614 "%s=%s" % (cached_vlans, cached_vnis),
3615 1
3616 )
3617
3618 @staticmethod
3619 def get_vlan_vni_ranges(bridge_vlan_tunnel):
3620 vlans = []
3621 vnis = []
3622
8a78cd07
JF
3623 if not bridge_vlan_tunnel:
3624 return vlans, vnis
3625
e537a6e6
JF
3626 tunnel_vlan_range = None
3627 tunnel_vni_range = None
3628
3629 for tunnel_vlan, tunnel_vni, tunnel_flags in bridge_vlan_tunnel:
3630
3631 if tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
3632 tunnel_vlan_range = tunnel_vlan
3633 tunnel_vni_range = tunnel_vni
3634
3635 elif tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_END:
3636 vlans.extend(range(tunnel_vlan_range, tunnel_vlan + 1))
3637 vnis.extend(range(tunnel_vni_range, tunnel_vni + 1))
3638
3639 else:
3640 vlans.append(tunnel_vlan)
3641 vnis.append(tunnel_vni)
3642
3643 return vlans, vnis
3644
d486dd0d
JF
3645 def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
3646 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3647
3648 if user_config_l2protocol_tunnel:
3649 result = 0
3650 try:
3651 self._query_check_l2protocol_tunnel(ifaceobj.name, user_config_l2protocol_tunnel)
3652 except Exception as e:
3653 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
3654 result = 1
3655 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
3656
223ba5af 3657 def _query_check_l2protocol_tunnel_on_bridge(self, ifname, ifaceobj, ifaceobjcurr):
d486dd0d
JF
3658 """
3659 In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
3660 We need to make sure that all ports comply with the mask given under the bridge
3661 """
3662 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3663
3664 if user_config_l2protocol_tunnel:
3665 if '=' in user_config_l2protocol_tunnel:
3666 try:
3667 config_per_port_dict = self.parse_interface_list_value(user_config_l2protocol_tunnel)
3b01ed76 3668 brport_list = list(config_per_port_dict.keys())
3218f49d 3669 except Exception:
d486dd0d
JF
3670 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
3671 return
3672 else:
3673 config_per_port_dict = {}
223ba5af
JF
3674 brport_list = self.cache.get_slaves(ifname)
3675
d486dd0d
JF
3676 try:
3677 for brport_name in brport_list:
3678 self._query_check_l2protocol_tunnel(
3679 brport_name,
3680 config_per_port_dict.get(brport_name) if config_per_port_dict else user_config_l2protocol_tunnel
3681 )
3682 result = 0
3683 except Exception as e:
3684 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
3685 result = 1
3686 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
3687
3688 def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
223ba5af
JF
3689 cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
3690 cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
d486dd0d
JF
3691
3692 for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
3693 callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
3694
3695 if callable(callback):
3696 if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3697 raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)'
3698 % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
3699
3700 def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
223ba5af
JF
3701 cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
3702 cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
d486dd0d 3703 running_protocols = []
3b01ed76 3704 for protocol_name, callback in list(self.query_check_l2protocol_tunnel_callback.items()):
d486dd0d 3705 if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3b01ed76 3706 running_protocols = list(self.query_check_l2protocol_tunnel_callback.keys())
d486dd0d
JF
3707 running_protocols.remove('all')
3708 break
3709 elif callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3710 running_protocols.append(protocol_name)
3711 if running_protocols:
3712 if brport_ifaceobj:
3713 brport_ifaceobj.update_config('bridge-l2protocol-tunnel', ' '.join(running_protocols))
3714 elif bridge_ifaceobj:
3715 current_config = bridge_ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3716
3717 if current_config:
3718 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s %s=%s' % (current_config, brport_name, ','.join(running_protocols)))
3719 else:
3720 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s=%s' % (brport_name, ','.join(running_protocols)))
3721
3722 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
3723 if self._is_bridge(ifaceobj):
d1265fd5 3724 self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
d486dd0d
JF
3725 else:
3726 self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
3727 ifaceobj_getfunc)
3728
3729 def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
223ba5af 3730 if self.cache.bridge_is_vlan_aware(ifaceobjrunning.name):
d486dd0d
JF
3731 ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
3732 ifaceobjrunning.update_config_dict(self._query_running_attrs(
3733 ifaceobjrunning,
3734 ifaceobj_getfunc,
3735 bridge_vlan_aware=True))
3736 else:
3737 ifaceobjrunning.update_config_dict(self._query_running_attrs(
3738 ifaceobjrunning, None))
3739
3740 def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
3741 if self.systcl_get_net_bridge_stp_user_space() == '1':
3742 return
3743
223ba5af 3744 v = str(self.cache.get_brport_cost(ifaceobjrunning.name))
d486dd0d
JF
3745 if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
3746 ifaceobjrunning.update_config('bridge-pathcosts', v)
3747
223ba5af 3748 v = str(self.cache.get_brport_priority(ifaceobjrunning.name))
d486dd0d
JF
3749 if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
3750 ifaceobjrunning.update_config('bridge-portprios', v)
3751
3752 def _query_running_bridge_port(self, ifaceobjrunning,
3753 ifaceobj_getfunc=None):
3754
223ba5af 3755 bridgename = self.cache.get_bridge_name_from_port(
d486dd0d
JF
3756 ifaceobjrunning.name)
3757 bridge_vids = None
3758 bridge_pvid = None
3759 if not bridgename:
c46af1c9 3760 self.logger.warning('%s: unable to find bridgename'
d486dd0d
JF
3761 %ifaceobjrunning.name)
3762 return
3763
223ba5af 3764 if not self.cache.bridge_is_vlan_aware(bridgename):
d486dd0d
JF
3765 try:
3766 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
3767 except Exception as e:
3768 self.logger.debug('%s: q_query_running_bridge_l2protocol_tunnel: %s' % (ifaceobjrunning.name, str(e)))
3769 return
3770
3771 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, brport_ifaceobj=ifaceobjrunning)
3772
3773 (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
3774 ifaceobjrunning.name)
3775 if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
3776 bridge_port_vids.remove(bridge_port_pvid)
3777
3778 bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
3779 if bridgeifaceobjlist:
3780 bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
3781 bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
3782
3783 if not bridge_port_vids and bridge_port_pvid:
3784 # must be an access port
3785 if bridge_port_pvid != '1':
3786 ifaceobjrunning.update_config('bridge-access',
3787 bridge_port_pvid)
3788 else:
3789 if bridge_port_vids:
3790 if (not bridge_vids or bridge_port_vids != bridge_vids):
3791 ifaceobjrunning.update_config('bridge-vids',
3792 ' '.join(bridge_port_vids))
3793 if bridge_port_pvid and bridge_port_pvid != '1':
3794 if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
3795 ifaceobjrunning.update_config('bridge-pvid',
3796 bridge_port_pvid)
3797
223ba5af 3798 v = utils.get_onff_from_onezero(self.cache.get_brport_learning(ifaceobjrunning.name))
d486dd0d
JF
3799 if v and v != self.get_mod_subattr('bridge-learning', 'default'):
3800 ifaceobjrunning.update_config('bridge-learning', v)
3801
223ba5af 3802 v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(ifaceobjrunning.name))
d486dd0d
JF
3803 if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
3804 ifaceobjrunning.update_config('bridge-unicast-flood', v)
3805
223ba5af 3806 v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(ifaceobjrunning.name))
d486dd0d
JF
3807 if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
3808 ifaceobjrunning.update_config('bridge-multicast-flood', v)
3809
223ba5af
JF
3810 v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(ifaceobjrunning.name))
3811 if v and v != self.get_mod_subattr('bridge-broadcast-flood', 'default'):
3812 ifaceobjrunning.update_config('bridge-broadcast-flood', v)
3813
3814 v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(ifaceobjrunning.name))
d486dd0d
JF
3815 # Display running 'arp-nd-suppress' only on vxlan ports
3816 # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
3817 # otherwise, display on all bridge-ports
3818
3819 bportifaceobj = ifaceobj_getfunc(ifaceobjrunning.name)[0]
3820 if (v and
3821 v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default') and
3822 (not self.arp_nd_suppress_only_on_vxlan or
3823 (self.arp_nd_suppress_only_on_vxlan and
3824 bportifaceobj.link_kind & ifaceLinkKind.VXLAN))):
3825 ifaceobjrunning.update_config('bridge-arp-nd-suppress', v)
3826
3827 self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
3828
3829 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
3830 try:
223ba5af 3831 if self.cache.bridge_exists(ifaceobjrunning.name):
d486dd0d 3832 self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
223ba5af 3833 elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
d486dd0d
JF
3834 self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
3835 except Exception as e:
3836 raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
3837
3838 def _query(self, ifaceobj, **kwargs):
3839 """ add default policy attributes supported by the module """
3840 if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
3841 ifaceobj.get_attr_value_first('bridge-stp')):
3842 return
3843 if self.default_stp_on:
3844 ifaceobj.update_config('bridge-stp', 'yes')
3845
223ba5af 3846 def __re_evaluate_bridge_vxlan(self, ifaceobj, ifaceobj_getfunc=None):
d486dd0d 3847 """
223ba5af
JF
3848 Quick fix for BRIDGE_VXLAN
3849
3850 BRIDGE_VXLAN is not set on the bridge because the VXLAN hasn't been processed yet
3851 (because its defined after the bridge in /e/n/i), here is what happens:
3852
3853 - ifupdownmain:populate_dependency_info()
3854 - loops over all the intf from /e/n/i (with the example config:
3855 ['lo', 'eth0', 'swp1', 'swp2', 'bridge', 'vni-10', 'bridge.100', 'vlan100'])
3856 ----> bridge is first in the list of interface (that we care about)
3857
3858 - ifupdownmain:query_lowerifaces()
3859 - bridge:get_dependent is called (debug: bridge: evaluating port expr '['swp1', 'swp2', 'vni-10']')
3860 - ifupdownmain:preprocess_dependency_list()
3861 - calls ifupdownmain:_set_iface_role_n_kind() on all the brports:
3862
3863 in _set_iface_role_n_kind:
3864 ifaceobj is the brport
3865 upperifaceobj is the bridge
3866
3867 it tries to see if the bridge has a VXLAN:
3868
3869 if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
3870 and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
3871 upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
3872
3873 but because the bridge is first in the /e/n/i ifupdown2 didn't
3874 call vxlan:get_dependent_ifacenames so VXLAN is not set on ifaceobj
3875
3876 :return:
d486dd0d 3877 """
223ba5af
JF
3878 if not ifaceobj_getfunc:
3879 return
d486dd0d 3880
223ba5af
JF
3881 if ifaceobj.link_kind & ifaceLinkKind.BRIDGE and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
3882 for port in self._get_bridge_port_list(ifaceobj) or []:
3883 for brport_ifaceobj in ifaceobj_getfunc(port):
3884 if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
3885 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
3886 return
3887
3888 elif ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
3889 for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
3890 for bridge_ifaceobj in ifaceobj_getfunc(iface) or []:
3891 bridge_ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
d486dd0d
JF
3892
3893 _run_ops = {
3894 'pre-up': _up,
3895 'post-down': _down,
3896 'query-checkcurr': _query_check,
3897 'query-running': _query_running,
3898 'query': _query
3899 }
3900
3901 def get_ops(self):
3902 """ returns list of ops supported by this module """
3b01ed76 3903 return list(self._run_ops.keys())
d486dd0d 3904
d486dd0d
JF
3905 def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
3906 """ run bridge configuration on the interface object passed as
3907 argument. Can create bridge interfaces if they dont exist already
3908
3909 Args:
3910 **ifaceobj** (object): iface object
3911
3912 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
3913 'query-running'
3914
3915 Kwargs:
3916 **query_ifaceobj** (object): query check ifaceobject. This is only
3917 valid when op is 'query-checkcurr'. It is an object same as
3918 ifaceobj, but contains running attribute values and its config
3919 status. The modules can use it to return queried running state
3920 of interfaces. status is success if the running state is same
3921 as user required state in ifaceobj. error otherwise.
3922 """
3923 op_handler = self._run_ops.get(operation)
3924 if not op_handler:
3925 return
d486dd0d 3926
223ba5af 3927 if (not self.requirements.bridge_utils_is_installed
d486dd0d 3928 and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
223ba5af 3929 and self.bridge_utils_missing_warning):
d486dd0d
JF
3930 self.logger.warning('%s: missing - bridge operation may not work as expected. '
3931 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
223ba5af
JF
3932 self.bridge_utils_missing_warning = False
3933
3934 # make sure BRIDGE_VXLAN is set if we have a vxlan port
3935 self.__re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc)
d486dd0d
JF
3936
3937 if operation == 'query-checkcurr':
3938 op_handler(self, ifaceobj, query_ifaceobj,
3939 ifaceobj_getfunc=ifaceobj_getfunc)
3940 else:
3941 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)