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