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