]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/bridge.py
addons: bridge-vlan-vni-map: support multiple vni mapping per entry
[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"):
b2751c6f
JF
1766 for vlans_vni_map in vlans_vnis_map.split():
1767 vids.extend(self._ranges_to_ints([vlans_vni_map.split("=")[0]]))
e537a6e6
JF
1768
1769 return vids
1770 except Exception as e:
1771 self.logger.debug("get_bridge_vlans_mapped_to_vnis_as_integer_list: %s" % str(e))
1772 return []
1773
1774 def remove_bridge_vlans_mapped_to_vnis_from_vids_list(self, bridge_ifaceobj, vxlan_ifaceobj, vids_list):
1775 """
1776 For single vxlan we need to remove the vlans mapped to vnis
1777 from the vids list otherwise they will get removed from the brport
1778 """
1779 if not (vxlan_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
1780 return vids_list
1781
1782 user_config_vids = []
1783
1784 if bridge_ifaceobj:
1785 for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(bridge_ifaceobj):
1786 user_config_vids.append(vid)
1787
1788 if vxlan_ifaceobj:
1789 for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(vxlan_ifaceobj):
1790 user_config_vids.append(vid)
1791
1792 for vlan in user_config_vids:
1793 try:
1794 vids_list.remove(vlan)
3218f49d 1795 except Exception:
e537a6e6
JF
1796 pass
1797
1798 return vids_list
1799
d486dd0d
JF
1800 def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
1801 bridge_vids=None,
1802 bridge_pvid=None):
1803 vids = None
1804 pvids = None
1805 vids_final = []
1806 pvid_final = None
1807 bport_access = bportifaceobj.get_attr_value_first('bridge-access')
1808 if bport_access:
1809 vids = re.split(r'[\s\t]\s*', bport_access)
1810 pvids = vids
1811 allow_untagged = 'yes'
1812 self.check_bridge_port_vid_attrs(bportifaceobj)
1813 else:
1814 allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
1815
1816 bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
1817 if bport_vids:
1818 vids = re.split(r'[\s\t,]\s*', bport_vids)
1819
1820 bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
1821 if bport_pvids:
1822 pvids = re.split(r'[\s\t]\s*', bport_pvids)
1823
1824 if vids:
1825 vids_final = vids
1826 elif bridge_vids:
1827 vids_final = bridge_vids
1828
1829 if allow_untagged == 'yes':
1830 if pvids:
1831 pvid_final = pvids[0]
1832 elif bridge_pvid:
1833 pvid_final = bridge_pvid
1834 else:
1835 pvid_final = '1'
1836 else:
1837 pvid_final = None
1838
1839 self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
1840 pvid_final, False)
1841
1842 def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware):
1843 err = False
1844
1845 if (ifaceobj.get_attr_value_first('bridge-port-vids') and
1846 ifaceobj.get_attr_value_first('bridge-port-pvids')):
1847 # Old style bridge port vid info
1848 # skip new style setting on ports
1849 return
1850 self.logger.info('%s: applying bridge configuration '
1851 %ifaceobj.name + 'specific to ports')
1852
1853 bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
1854 if bridge_vids:
1855 bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
1856 else:
1857 bridge_vids = None
1858
1859 bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
1860 if bridge_pvid:
1861 bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
1862 else:
1863 bridge_pvid = None
1864
1865 if (ifaceobj.module_flags.get(self.name, 0x0) &
1866 bridgeFlags.PORT_PROCESSED_OVERRIDE):
1867 port_processed_override = True
1868 else:
1869 port_processed_override = False
1870
1871 bridgeports = self._get_bridge_port_list(ifaceobj)
1872 if not bridgeports:
1873 self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
1874 return
223ba5af 1875 self.iproute2.batch_start()
d486dd0d 1876 for bport in bridgeports:
223ba5af
JF
1877 # on link_set_master we need to wait until we cache the correct
1878 # notification and register the brport as slave
1879 if not self.cache.bridge_port_exists(ifaceobj.name, bport):
d486dd0d
JF
1880 self.logger.info('%s: skipping bridge config' %ifaceobj.name +
1881 ' for port %s (missing port)' %bport)
1882 continue
1883 self.logger.info('%s: processing bridge config for port %s'
1884 %(ifaceobj.name, bport))
1885 bportifaceobjlist = ifaceobj_getfunc(bport)
1886 if not bportifaceobjlist:
223ba5af 1887 continue
d486dd0d
JF
1888 for bportifaceobj in bportifaceobjlist:
1889 # Dont process bridge port if it already has been processed
1890 # and there is no override on port_processed
1891 if (not port_processed_override and
1892 (bportifaceobj.module_flags.get(self.name,0x0) &
1893 bridgeFlags.PORT_PROCESSED)):
1894 continue
1895 try:
1896 # Add attributes specific to the vlan aware bridge
1897 if bridge_vlan_aware:
1898 self._apply_bridge_vlan_aware_port_settings_all(
1899 bportifaceobj, bridge_vids, bridge_pvid)
1900 elif self.warn_on_untagged_bridge_absence:
1901 self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
1902 except exceptions.ReservedVlanException as e:
1903 raise e
3b01ed76 1904 except Exception as e:
d486dd0d 1905 err = True
c46af1c9 1906 self.logger.warning('%s: %s' %(ifaceobj.name, str(e)))
d486dd0d 1907 pass
223ba5af 1908 self.iproute2.batch_commit()
d486dd0d
JF
1909 if err:
1910 raise Exception('%s: errors applying port settings' %ifaceobj.name)
1911
1912 def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
1913 if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
1914 lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
1915 if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
1916 not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
c46af1c9 1917 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
1918 self.warn_on_untagged_bridge_absence = False
1919
1920 def bridge_port_get_bridge_name(self, ifaceobj):
223ba5af 1921 bridgename = self.cache.get_bridge_name_from_port(ifaceobj.name)
d486dd0d
JF
1922 if not bridgename:
1923 # bridge port is not enslaved to a bridge we need to find
1924 # the bridge in it's upper ifaces then enslave it
1925 for u in ifaceobj.upperifaces:
223ba5af 1926 if self.cache.link_is_bridge(u):
d486dd0d
JF
1927 return True, u
1928 return False, None
1929 # return should_enslave port, bridgename
1930 return False, bridgename
1931
1932 def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
1933 if should_enslave_port:
223ba5af 1934 self.netlink.link_set_master(ifaceobj.name, bridge_name)
20649b09
JF
1935
1936 if ifaceobj.name not in self.svd_list:
1937 self.handle_ipv6([ifaceobj.name], '1')
d486dd0d
JF
1938
1939 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
1940 bridge_pvid = self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)
1941 try:
1942 self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, bridge_vids, bridge_pvid)
1943 except Exception as e:
1944 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
1945 return
1946
1947 def up_bridge_port(self, ifaceobj, ifaceobj_getfunc):
1948 should_enslave_port, bridge_name = self.bridge_port_get_bridge_name(ifaceobj)
1949
1950 if not bridge_name:
1951 # bridge doesn't exist
1952 return
1953
6a1110e3
JF
1954 # check for bridge-learning on l2 vni in evpn setup
1955 self.syntax_check_learning_l2_vni_evpn(ifaceobj)
1956
a037ffc5
JF
1957 # detect and warn when arp suppression is enabled and there is no vlan configured
1958 self.syntax_check_bridge_arp_vni_vlan(ifaceobj, ifaceobj_getfunc)
1959
223ba5af 1960 vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridge_name)
d486dd0d
JF
1961 if vlan_aware_bridge:
1962 self.up_bridge_port_vlan_aware_bridge(ifaceobj,
1963 ifaceobj_getfunc,
1964 bridge_name,
1965 should_enslave_port)
1966
1967 bridge_ifaceobj = ifaceobj_getfunc(bridge_name)[0]
1968
1969 self.up_apply_brports_attributes(target_ports=[ifaceobj.name],
1970 ifaceobj=bridge_ifaceobj,
1971 ifaceobj_getfunc=ifaceobj_getfunc,
1972 bridge_vlan_aware=vlan_aware_bridge)
1973
1974 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
1975
223ba5af 1976 def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_created):
d486dd0d
JF
1977 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
1978 if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
1979 return False
223ba5af
JF
1980 if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name):
1981 # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports
d486dd0d
JF
1982 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
1983 return True
1984 return False
1985
1986 @staticmethod
1987 def parse_interface_list_value(user_config):
1988 config = dict()
1989 for entry in user_config.split():
1990 ifname, value = entry.split('=')
1991 config[ifname] = value
1992 return config
1993
223ba5af 1994 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
1995 """
1996 brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
1997 and
1998 brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
1999
2000 Checks are not performed in this function and must be verified
2001 before. This is done this way to avoid calling this method on
2002 non vlan & bridge port interfaces thus wasting a bit less time
2003 """
2004
2005 kind = None
2006 ifla_info_data = {}
2007
223ba5af
JF
2008 if user_config_brport_learning_nl is None:
2009 user_config_brport_learning_nl = self.bridge_vxlan_port_learning
2010 # bridge-learning is not configured by the user or by a policy
2011 # use "bridge-vxlan-port-learning" policy to set bridge-learning (default on)
d486dd0d 2012
223ba5af
JF
2013 if user_config_brport_learning_nl != cached_brport_learning:
2014 brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] \
2015 = cached_brport_learning \
2016 = user_config_brport_learning_nl
d486dd0d 2017
223ba5af
JF
2018 self.logger.info(
2019 "%s: %s: set bridge-learning %s"
2020 % (bridge_name, brport_name, "on" if user_config_brport_learning_nl else "off")
2021 )
2022 else:
2023 # in this case, the current bridge-learning value is properly configured and
2024 # doesn't need to be reset. We need to make sure that BRPORT_LEARNING is not
2025 # part of ifla_info_slave_data.
2026 try:
2027 del brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING]
3218f49d 2028 except Exception:
223ba5af
JF
2029 pass
2030
2031 #
2032 # vxlan-learning sync:
2033 #
d486dd0d 2034
223ba5af
JF
2035 brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first("vxlan-learning")
2036 # if vxlan-learning is defined by the user or via policy file we need
2037 # to honor his config and not sync vxlan-learning with bridge-learning
d486dd0d 2038
223ba5af
JF
2039 if not brport_vxlan_learning_config:
2040 # check policy file
2041 brport_vxlan_learning_config = policymanager.policymanager_api.get_attr_default("vxlan", "vxlan-learning")
d486dd0d 2042
223ba5af
JF
2043 # convert vxlan-learning string to netlink value (if None use brport-learning value instead)
2044 brport_vxlan_learning_config_nl = utils.get_boolean_from_string(brport_vxlan_learning_config) \
2045 if brport_vxlan_learning_config \
2046 else cached_brport_learning
d486dd0d 2047
223ba5af
JF
2048 if brport_vxlan_learning_config_nl != self.cache.get_link_info_data_attribute(brport_name, Link.IFLA_VXLAN_LEARNING):
2049 self.logger.info(
2050 "%s: %s: vxlan learning and bridge learning out of sync: set vxlan-learning %s"
2051 % (bridge_name, brport_name, "on" if brport_vxlan_learning_config_nl else "off")
2052 )
2053 ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_vxlan_learning_config_nl}
2054 kind = "vxlan"
d486dd0d
JF
2055
2056 # if kind and ifla_info_data are set they will be added to the
2057 # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
2058 return kind, ifla_info_data
2059
d486dd0d
JF
2060 def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
2061 ifname = ifaceobj.name
e537a6e6 2062 single_vxlan_device_ifaceobj = None
d486dd0d
JF
2063
2064 try:
2065 brports_ifla_info_slave_data = dict()
2066 brport_ifaceobj_dict = dict()
223ba5af
JF
2067 brport_name_list = []
2068
2069 cache_brports_ifla_info_slave_data = {}
2070
2071 port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE
d486dd0d 2072
223ba5af 2073 running_brports = self.cache.get_slaves(ifname)
d486dd0d 2074
223ba5af
JF
2075 # If target_ports is specified we want to configure only this
2076 # sub-list of port, we need to check if these ports are already
2077 # enslaved, if not they will be ignored.
2078 # If target_ports is not populated we will apply the brport
2079 # attributes on all running brport.
d486dd0d
JF
2080 if target_ports:
2081 new_targets = []
2082 for brport_name in target_ports:
2083 if brport_name not in running_brports:
2084 self.logger.info('%s: not enslaved to bridge %s: ignored for now' % (brport_name, ifname))
2085 else:
2086 new_targets.append(brport_name)
2087 running_brports = new_targets
2088
d486dd0d
JF
2089 for port in running_brports:
2090 brport_list = ifaceobj_getfunc(port)
2091 if brport_list:
223ba5af
JF
2092 port_already_processed = False
2093
2094 # ports just added to the bridge have to be processed
2095 if port not in newly_enslaved_ports:
2096 # check if brport was already processed
2097 for brportifaceobj in brport_list:
2098 if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED:
2099 # skip port if already processed (probably by `up_bridge_port`)
2100 port_already_processed = True
2101 self.logger.info("%s: port %s: already processed" % (ifname, port))
2102 break
2103
2104 if not port_already_processed:
2105 brport_name_list.append(port)
2106 brport_ifaceobj_dict[port] = brport_list[0]
2107 brports_ifla_info_slave_data[port] = dict()
2108
2109 if not ifupdownflags.flags.PERFMODE and port not in newly_enslaved_ports:
2110 # if the port has just been enslaved, info_slave_data is not cached yet
2111 cache_brports_ifla_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
2112 else:
2113 cache_brports_ifla_info_slave_data[port] = {}
2114
2115 if not brport_name_list:
2116 self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
2117 return
2118
2119 self.logger.info('%s: applying bridge port configuration: %s' % (ifname, brport_name_list))
2120
2121 cached_bridge_mcsnoop = self.cache.get_bridge_multicast_snooping(ifname)
d486dd0d
JF
2122
2123 bridge_ports_learning = {}
223ba5af
JF
2124 bridge_ports_vxlan_arp_suppress = {}
2125 cached_bridge_ports_learning = {}
d486dd0d
JF
2126
2127 # we iterate through all IFLA_BRPORT supported attributes
3b01ed76 2128 for attr_name, nl_attr in self._ifla_brport_attributes_map.items():
d486dd0d
JF
2129 br_config = ifaceobj.get_attr_value_first(attr_name)
2130 translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
2131
2132 if not translate_func:
2133 # if no translation function is found,
2134 # we ignore this attribute and continue
2135 continue
2136
2137 if not br_config:
2138 # user didn't specify any value for this attribute
2139 # looking at policy overrides
2140 br_config = policymanager.policymanager_api.get_iface_default(
2141 module_name=self.__class__.__name__,
2142 ifname=ifname,
2143 attr=attr_name
2144 )
2145
2146 if br_config:
2147 #if bridge_vlan_aware:
2148 # self.logger.info('%s: is a vlan-aware bridge, "%s %s" '
2149 # 'should be configured under the ports'
2150 # % (ifname, attr_name, br_config))
2151
2152 # convert the <interface-yes-no-0-1-list> and <interface-range-list> value to subdict
2153 # brport_name: { attr: value }
2154 # example:
2155 # bridge-portprios swp1=5 swp2=32
2156 # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32}
2157 if '=' in br_config:
2158 try:
2159 br_config = self.parse_interface_list_value(br_config)
3218f49d 2160 except Exception:
d486dd0d
JF
2161 self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
2162 continue
2163
3b01ed76 2164 for brport_ifaceobj in list(brport_ifaceobj_dict.values()):
d486dd0d
JF
2165 brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
2166 brport_name = brport_ifaceobj.name
2167
223ba5af
JF
2168 if not ifupdownflags.flags.PERFMODE:
2169 cached_value = cache_brports_ifla_info_slave_data.get(brport_name, {}).get(nl_attr, None)
d486dd0d
JF
2170 else:
2171 cached_value = None
2172
2173 if not brport_config:
2174 # if a brport attribute was specified under the bridge and not under the port
2175 # we assign the bridge value to the port. If an attribute is both defined under
2176 # the bridge and the brport we keep the value of the port and ignore the br val.
2177 if type(br_config) == dict:
2178 # if the attribute value was in the format interface-list-value swp1=XX swp2=YY
2179 # br_config is a dictionary, example:
2180 # bridge-portprios swp1=5 swp2=32 = {swp1: 5, swp2: 32}
2181 brport_config = br_config.get(brport_name)
2182 else:
2183 brport_config = br_config
2184
2185 if not brport_config:
2186 brport_config = policymanager.policymanager_api.get_iface_default(
2187 module_name=self.__class__.__name__,
2188 ifname=brport_name,
2189 attr=attr_name
2190 )
2191
2192 user_config = brport_config
2193
2194 # attribute specific work
2195 # This shouldn't be here but we don't really have a choice otherwise this
2196 # will require too much code duplication and will make the code very complex
223ba5af
JF
2197 if nl_attr == Link.IFLA_BRPORT_NEIGH_SUPPRESS:
2198 bridge_ports_vxlan_arp_suppress[brport_name] = user_config
d486dd0d 2199 try:
223ba5af
JF
2200 if user_config:
2201 if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2202 self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
2203 'is not supported on a non-vxlan port'
2204 % (ifaceobj.name, brport_name))
2205 continue
2206 elif bridge_vlan_aware:
2207 if not self.arp_nd_suppress_only_on_vxlan:
2208 user_config = self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
2209 elif self.arp_nd_suppress_only_on_vxlan and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2210 # ignore the case of VXLAN brport - handled later in the code
2211 continue
3218f49d 2212 except Exception:
d486dd0d
JF
2213 continue
2214 elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
2215 # special handking for group_fwd_mask because Cisco proprietary
2216 # protocol needs to be set via a private netlink attribute
2217 self.ifla_brport_group_fwd_mask(ifname, brport_name,
2218 brports_ifla_info_slave_data,
2219 user_config, cached_value)
2220 continue
2221
2222 #if brport_config:
2223 # if not bridge_vlan_aware:
2224 # self.logger.info('%s: %s: is not a vlan-aware bridge, "%s %s" '
2225 # 'should be configured under the bridge'
2226 # % (ifname, brport_name,
2227 # attr_name, brport_config))
2228
2229 if user_config:
2230 user_config_nl = translate_func(user_config)
2231 # check config value against running value
2232 if user_config_nl != cached_value:
2233 brports_ifla_info_slave_data[brport_name][nl_attr] = user_config_nl
2234 self.logger.info('%s: %s: set %s %s' % (ifname, brport_name, attr_name, user_config))
2235 self.logger.debug('(cache %s)' % cached_value)
2236
2237 if nl_attr == Link.IFLA_BRPORT_LEARNING:
2238 # for vxlan-learning sync purposes we need to save the user config for each brports.
2239 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
2240 # IFLA_BRPORT_LEARNING if the user value is already configured and running
2241 # nevertheless we still need to check if the vxlan-learning is rightly synced with
2242 # the brport since it might go out of sync for X and Y reasons.
223ba5af 2243 # we also store the cached value to avoid an extra cache lookup.
d486dd0d 2244 bridge_ports_learning[brport_name] = user_config_nl
223ba5af 2245 cached_bridge_ports_learning[brport_name] = cached_value
d486dd0d
JF
2246
2247 elif cached_value is not None:
2248 # no config found, do we need to reset to default?
2249 default = self.get_attr_default_value(attr_name)
2250 if default:
2251 default_netlink = translate_func(default)
2252
223ba5af
JF
2253 if nl_attr == Link.IFLA_BRPORT_LEARNING:
2254 # for vxlan-learning sync purposes we need to save the user config for each brports.
2255 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
2256 # IFLA_BRPORT_LEARNING if the user value is already configured and running
2257 # nevertheless we still need to check if the vxlan-learning is rightly synced with
2258 # the brport since it might go out of sync for X and Y reasons.
2259 # we also store the cached value to avoid an extra cache lookup.
2260 cached_bridge_ports_learning[brport_name] = cached_value
2261 bridge_ports_learning[brport_name] = self.bridge_vxlan_port_learning
2262
2263 if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2264 # bridge-learning for vxlan device is handled separatly in sync_bridge_learning_to_vxlan_brport
2265 continue
2266
2267 if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
2268 # We don't query new slaves and not during boot
2269 try:
2270 if self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_PEER_LINK):
2271 if default_netlink != cached_value:
2272 self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
2273 % (ifname, brport_name))
2274 continue
2275 except Exception as e:
2276 self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
d486dd0d
JF
2277
2278 if default_netlink != cached_value:
2279 self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
2280 % (ifname, brport_name, attr_name, default))
2281 self.logger.debug('(cache %s)' % cached_value)
2282 brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
2283
223ba5af
JF
2284 # is the current bridge (ifaceobj) a QinQ bridge?
2285 # This variable is initialized to None and will be
2286 # change to True/False, so that the check is only
2287 # performed once
2288 qinq_bridge = None
2289
d486dd0d 2290 # applying bridge port configuration via netlink
3b01ed76 2291 for brport_name, brport_ifla_info_slave_data in list(brports_ifla_info_slave_data.items()):
d486dd0d
JF
2292
2293 brport_ifaceobj = brport_ifaceobj_dict.get(brport_name)
2294 if (brport_ifaceobj
2295 and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
2296 and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
2297 # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
2298 # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
2299 kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
d486dd0d
JF
2300 brport_ifaceobj,
2301 brport_name,
2302 brport_ifla_info_slave_data,
223ba5af
JF
2303 bridge_ports_learning.get(brport_name),
2304 cached_bridge_ports_learning.get(brport_name))
9f8f3b0d
JF
2305
2306 if (self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string(
80adb3c1 2307 self.get_bridge_mcsnoop_value(ifaceobj)
9f8f3b0d 2308 )) or cached_bridge_mcsnoop:
80adb3c1 2309 # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter"
9f8f3b0d
JF
2310 # is on and mcsnoop is on (or mcsnoop is already enabled on the
2311 # bridge, set 'bridge-portmcrouter 2' on vxlan ports (if not set by the user)
223ba5af
JF
2312 if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER) \
2313 and self.cache.get_bridge_port_multicast_router(brport_name) != 2:
80adb3c1
JF
2314 brport_ifla_info_slave_data[Link.IFLA_BRPORT_MULTICAST_ROUTER] = 2
2315 self.logger.info("%s: %s: vxlan bridge igmp snooping: enable port multicast router" % (ifname, brport_name))
223ba5af
JF
2316
2317 #
2318 # handling attribute: bridge-arp-nd-suppress
2319 # defaults to bridge-vxlan-arp-nd-suppress policy (default False)
2320 #
2321 user_config_neigh_suppress = bridge_ports_vxlan_arp_suppress.get(brport_name)
2322
2323 if user_config_neigh_suppress is None:
2324
2325 if qinq_bridge is None:
2326 # QinQ bridge hasn't been checked yet
2327 qinq_bridge = self.is_qinq_bridge(
2328 ifaceobj,
2329 brport_name,
2330 running_brports,
2331 brport_ifaceobj_dict,
2332 ifaceobj_getfunc
2333 )
2334
2335 if qinq_bridge:
2336 # exclude QinQ bridge from arp-nd-suppress default policy on
2337 config_neigh_suppress = 0
2338 self.logger.info("%s: QinQ bridge detected: %s: set bridge-arp-nd-suppress off" % (ifname, brport_name))
2339 else:
2340 config_neigh_suppress = self.bridge_vxlan_arp_nd_suppress_int
2341 else:
2342 config_neigh_suppress = int(utils.get_boolean_from_string(user_config_neigh_suppress))
2343
2344 brport_neigh_suppress_cached_value = self.cache.get_link_info_slave_data_attribute(
2345 brport_name,
2346 Link.IFLA_BRPORT_NEIGH_SUPPRESS
2347 )
2348
2349 if config_neigh_suppress != brport_neigh_suppress_cached_value:
2350 brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] = config_neigh_suppress
2351
2352 if not user_config_neigh_suppress:
2353 # if the configuration is not explicitely defined by the user
2354 # we need report that the default behavior is enabled by policy
2355 self.logger.info(
2356 "%s: set bridge-arp-nd-suppress %s by default on vxlan port (%s)"
2357 % (ifname, "on" if self.bridge_vxlan_arp_nd_suppress else "off", brport_name)
2358 )
2359 else:
2360 # the user configuration (or policy) is already configured and running
2361 # we need to remove this attribute from the request dictionary
2362 try:
2363 del brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS]
3218f49d 2364 except Exception:
223ba5af
JF
2365 pass
2366
2367 #
e537a6e6 2368 # SINGLE VXLAN - enable IFLA_BRPORT_VLAN_TUNNEL
223ba5af
JF
2369 #
2370
e537a6e6
JF
2371 if brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
2372 single_vxlan_device_ifaceobj = brport_ifaceobj
2373 brport_vlan_tunnel_cached_value = self.cache.get_link_info_slave_data_attribute(
2374 brport_name,
2375 Link.IFLA_BRPORT_VLAN_TUNNEL
2376 )
2377
2378 if not brport_vlan_tunnel_cached_value:
2379 self.logger.info("%s: %s: enabling vlan_tunnel on single vxlan device" % (ifname, brport_name))
2380 brport_ifla_info_slave_data[Link.IFLA_BRPORT_VLAN_TUNNEL] = 1
2381
d486dd0d
JF
2382 else:
2383 kind = None
2384 ifla_info_data = {}
2385
2386 if brport_ifla_info_slave_data or ifla_info_data:
2387 try:
223ba5af
JF
2388 self.netlink.link_set_brport_with_info_slave_data(
2389 ifname=brport_name,
2390 kind=kind,
2391 ifla_info_data=ifla_info_data,
2392 ifla_info_slave_data=brport_ifla_info_slave_data
2393 )
d486dd0d
JF
2394 except Exception as e:
2395 self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
2396
223ba5af 2397 self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
d486dd0d
JF
2398
2399 except Exception as e:
2400 self.log_error(str(e), ifaceobj)
2401
e537a6e6
JF
2402 if single_vxlan_device_ifaceobj:
2403 self.apply_bridge_port_vlan_vni_map(single_vxlan_device_ifaceobj)
2404
2405 def apply_bridge_port_vlan_vni_map(self, ifaceobj):
2406 """
2407 bridge vlan add vid <vlan-id> dev vxlan0
2408 bridge vlan add dev vxlan0 vid <vlan-id> tunnel_info id <vni>
2409 """
2410 vxlan_name = ifaceobj.name
2411 try:
2412 self.iproute2.batch_start()
b2751c6f 2413 for bridge_vlan_vni_map_entry in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
e537a6e6 2414
b2751c6f
JF
2415 if not bridge_vlan_vni_map_entry:
2416 continue
e537a6e6 2417
b2751c6f
JF
2418 for vlan_vni_map in bridge_vlan_vni_map_entry.split():
2419 try:
2420 vlans_str, vni_str = vlan_vni_map.split("=")
2421 except:
2422 return self.__warn_bridge_vlan_vni_map_syntax_error(vxlan_name, vlan_vni_map)
2423
2424 vlans = self._ranges_to_ints([vlans_str])
2425 vnis = self._ranges_to_ints([vni_str])
e537a6e6 2426
b2751c6f
JF
2427 if len(vlans) != len(vnis):
2428 return self.__warn_bridge_vlan_vni_map_syntax_error(vxlan_name, vlan_vni_map)
e537a6e6 2429
b2751c6f
JF
2430 # TODO: query the cache prio to executing those commands
2431 self.iproute2.bridge_vlan_add_vid_list_self(vxlan_name, vlans, False)
2432 self.iproute2.bridge_vlan_add_vlan_tunnel_info(vxlan_name, vlans, vnis)
e537a6e6
JF
2433
2434 self.iproute2.batch_commit()
2435 except Exception as e:
2436 self.log_error("%s: error while processing bridge-vlan-vni-map attribute: %s" % (vxlan_name, str(e)))
2437
f422ae7c
JF
2438 def __warn_bridge_vlan_vni_map_syntax_error(self, ifname, user_config_vlan_vni_map):
2439 self.logger.warning("%s: syntax error: bridge-vlan-vni-map %s" % (ifname, user_config_vlan_vni_map))
e537a6e6 2440
223ba5af
JF
2441 def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc):
2442 """ Detect QinQ bridge
2443 Potential improvement: We could add a ifaceobj.link_privflags called
2444 BRIDGE_QINQ but for now it is not necessary.
2445 """
2446
2447 # bridge-vlan-aware case
2448 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
2449 return (ifaceobj.get_attr_value_first("bridge-vlan-protocol") or "").lower() == "802.1ad"
2450
2451 # old-bridge
2452 else:
2453 for qinq_running_brport in running_brports:
2454 if qinq_running_brport == brport_name:
2455 continue
2456
2457 qinq_running_brport_ifaceobj = brport_ifaceobj_dict.get(qinq_running_brport)
2458
2459 if not qinq_running_brport_ifaceobj:
2460 continue
2461
2462 if qinq_running_brport_ifaceobj.link_kind & ifaceLinkKind.VLAN:
2463 for lower_iface in qinq_running_brport_ifaceobj.lowerifaces or []:
2464 for lower_ifaceobj in ifaceobj_getfunc(lower_iface) or []:
2465 if (lower_ifaceobj.get_attr_value_first("vlan-protocol") or "").lower() == "802.1ad":
2466 return True
2467 return False
2468
2469 def bridge_process_vidinfo_mcqv4src_maxwait(self, ifaceobj):
2470 self._set_bridge_vidinfo_compat(ifaceobj)
2471 self._set_bridge_mcqv4src_compat(ifaceobj)
2472 self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
2473
d486dd0d
JF
2474 def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
2475 """
2476 Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
2477 Since this is the only ifupdown2 attribute dealing with more than 1 netlink
2478 field we need to have special handling for that.
2479 """
2480 ifla_brport_group_fwd_mask = 0
2481 ifla_brport_group_fwd_maskhi = 0
9dfc8a44 2482
d486dd0d 2483 if user_config:
9dfc8a44 2484 for group in user_config.replace(",", " ").split():
d486dd0d
JF
2485 if not group:
2486 continue
2487
2488 callback = self.l2protocol_tunnel_callback.get(group)
2489
2490 if not callable(callback):
2491 self.logger.warning('%s: %s: bridge-l2protocol-tunnel ignoring invalid parameter \'%s\'' % (ifname, brport_name, group))
2492 else:
2493 ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
2494
2495 # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
223ba5af 2496 cached_ifla_brport_group_fwd_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
d486dd0d
JF
2497
2498 log_mask_change = True
2499 # if user specify bridge-l2protocol-tunnel stp cdp
2500 # we need to set both MASK and MASKHI but we only want to log once
2501
2502 if cached_ifla_brport_group_fwd_mask is None:
2503 cached_ifla_brport_group_fwd_mask = 0
2504 if cached_ifla_brport_group_fwd_maskhi is None:
2505 cached_ifla_brport_group_fwd_maskhi = 0
2506
2507 # if the cache value is None it means that the kernel doesn't support this attribute
2508 # or that the cache is stale, we dumped this intf before it was enslaved in the bridge
2509
2510 if ifla_brport_group_fwd_mask != cached_ifla_brport_group_fwd_mask:
2511 if log_mask_change:
2512 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
2513 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_mask)
2514 log_mask_change = False
2515 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASK] = ifla_brport_group_fwd_mask
2516
2517 if ifla_brport_group_fwd_maskhi != cached_ifla_brport_group_fwd_maskhi:
2518 if log_mask_change:
2519 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
2520 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_maskhi)
2521 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASKHI] = ifla_brport_group_fwd_maskhi
2522
255ffb1f
JF
2523 def get_bridge_mtu(self, ifaceobj):
2524 user_config_mtu = ifaceobj.get_attr_value_first("mtu")
2525
2526 if not user_config_mtu:
2527 user_config_mtu = policymanager.policymanager_api.get_attr_default(
393524ab 2528 module_name="address",
255ffb1f
JF
2529 attr="mtu"
2530 )
2531
2532 try:
2533 if user_config_mtu:
393524ab
JF
2534 int(user_config_mtu)
2535 self.logger.info("%s: set bridge mtu %s" % (ifaceobj.name, user_config_mtu))
47877f26 2536 return str(user_config_mtu)
255ffb1f
JF
2537 except Exception as e:
2538 self.logger.warning("%s: invalid bridge mtu %s: %s" % (ifaceobj.name, user_config_mtu, str(e)))
393524ab 2539 return None
255ffb1f 2540
d486dd0d
JF
2541 def up_bridge(self, ifaceobj, ifaceobj_getfunc):
2542 ifname = ifaceobj.name
2543
2544 if ifupdownflags.flags.PERFMODE:
d486dd0d
JF
2545 link_exists = False
2546 else:
223ba5af 2547 link_exists = self.cache.link_exists(ifaceobj.name)
d486dd0d
JF
2548
2549 if not link_exists:
393524ab 2550 self.netlink.link_add_bridge(ifname)
223ba5af 2551 link_just_created = True
393524ab
JF
2552
2553 bridge_mtu = self.get_bridge_mtu(ifaceobj)
2554 if bridge_mtu:
2555 self.sysfs.link_set_mtu(ifname, bridge_mtu, int(bridge_mtu))
d486dd0d 2556 else:
223ba5af 2557 link_just_created = False
d486dd0d
JF
2558 self.logger.info('%s: bridge already exists' % ifname)
2559
223ba5af 2560 bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created)
d486dd0d
JF
2561
2562 self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
2563
2564 try:
2565 newly_enslaved_ports = self._add_ports(ifaceobj, ifaceobj_getfunc)
2566 self.up_apply_brports_attributes(ifaceobj, ifaceobj_getfunc, bridge_vlan_aware,
2567 newly_enslaved_ports=newly_enslaved_ports)
2568 except Exception as e:
2569 self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))
2570
2571 running_ports = ''
2572 try:
223ba5af 2573 running_ports = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
2574 if not running_ports:
2575 return
d486dd0d
JF
2576 self._apply_bridge_port_settings_all(ifaceobj,
2577 ifaceobj_getfunc=ifaceobj_getfunc,
2578 bridge_vlan_aware=bridge_vlan_aware)
2579 except exceptions.ReservedVlanException as e:
2580 raise e
2581 except Exception as e:
2582 self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
2583 finally:
2584 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
223ba5af 2585 self.iproute2.batch_start()
d486dd0d 2586 for p in running_ports:
1d35560d
JF
2587 ifaceobj_list = ifaceobj_getfunc(p)
2588 if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN):
223ba5af 2589 self.iproute2.link_down(p)
d486dd0d 2590 continue
223ba5af
JF
2591 self.iproute2.link_up(p)
2592 try:
2593 self.iproute2.batch_commit()
2594 except Exception as e:
2595 # link set up on bridge ports failed - ignore and log debug
2596 self.logger.debug("%s: %s" % (ifname, str(e)))
d486dd0d
JF
2597
2598 try:
2599 self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
2600 except Exception as e:
2601 self.logger.warning('%s: setting bridge mac address: %s' % (ifaceobj.name, str(e)))
2602
2603 def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc):
2604 if self.bridge_mac_iface and self.bridge_mac_iface[0] and self.bridge_mac_iface[1]:
2605 return self.bridge_mac_iface
2606
2607 if self.bridge_mac_iface_list:
2608 self.logger.debug('bridge mac iface list: %s' % self.bridge_mac_iface_list)
2609
2610 for bridge_mac_intf in self.bridge_mac_iface_list:
2611 ifaceobj_list = ifaceobj_getfunc(bridge_mac_intf)
2612 iface_mac = None
2613
2614 if ifaceobj_list:
2615 for obj in ifaceobj_list:
2616 iface_user_configured_hwaddress = utils.strip_hwaddress(obj.get_attr_value_first('hwaddress'))
2617 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2618 if iface_user_configured_hwaddress:
2619 iface_mac = iface_user_configured_hwaddress
2620
223ba5af 2621 if not iface_mac and not self.cache.link_exists(bridge_mac_intf):
d486dd0d
JF
2622 continue
2623
2624 if not iface_mac:
223ba5af 2625 iface_mac = self.cache.get_link_address(bridge_mac_intf)
d486dd0d
JF
2626 # if hwaddress attribute is not configured we use the running mac addr
2627
2628 self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
2629 return self.bridge_mac_iface
2630 elif self.bridge_set_static_mac_from_port:
2631 # no policy was provided, we need to get the first physdev or bond ports
2632 # and use its hwaddress to set the bridge mac
f54468e3
JF
2633
2634 # first we need to make sure that the bridge mac is not already inherited from one of it's port
2635 bridge_ports = self._get_bridge_port_list_user_ordered(ifaceobj)
2636 current_mac = self.cache.get_link_address(ifname)
2637
2638 for port in bridge_ports or []:
2639 if not self.is_vxlan(ifaceobj_getfunc(port)):
2640 port_mac = self.cache.get_link_address(port)
2641
2642 if current_mac == port_mac:
2643 self.logger.info("bridge mac is already inherited from %s" % port)
2644 self.bridge_mac_iface = (port, port_mac)
2645 return self.bridge_mac_iface
2646
2647 for port in bridge_ports or []:
d486dd0d
JF
2648 # iterate through the bridge-port list
2649 for port_obj in ifaceobj_getfunc(port) or []:
2650 # check if the port is a physdev (link_kind is null) or a bon
2651 if port_obj.link_kind != ifaceLinkKind.VXLAN:
2652 iface_user_configured_hwaddress = utils.strip_hwaddress(port_obj.get_attr_value_first('hwaddress'))
2653 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2654 if iface_user_configured_hwaddress:
2655 iface_mac = iface_user_configured_hwaddress.lower()
2656 # we need to "normalize" the user provided MAC so it can match with
2657 # what we have in the cache (data retrieved via a netlink dump by
2658 # nlmanager). nlmanager return all macs in lower-case
2659 else:
223ba5af 2660 iface_mac = self.cache.get_link_address(port)
d486dd0d
JF
2661
2662 if iface_mac:
2663 self.bridge_mac_iface = (port, iface_mac)
2664 return self.bridge_mac_iface
2665
2666 return None, None
2667
f54468e3
JF
2668 @staticmethod
2669 def is_vxlan(port_obj_list):
2670 # checking if the port is a vxlan by checking the ifaceobjs
2671 # instead of checking to cache (saving on locking time)
2672 for _port_obj in port_obj_list or []:
2673 if _port_obj.link_kind == ifaceLinkKind.VXLAN:
2674 return True
2675 return False
2676
d486dd0d
JF
2677 def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
2678 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
223ba5af 2679 self.iproute2.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
d486dd0d
JF
2680
2681 def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
2682 """
2683 We have a day one bridge mac changing problem with changing ports
2684 (basically bridge mac changes when the port it inherited the mac from
2685 gets de-enslaved).
2686
2687 We have discussed this problem many times before and tabled it.
2688 The issue has aggravated with vxlan bridge ports having auto-generated
2689 random macs...which change on every reboot.
2690
2691 ifupdown2 extract from policy files an iface to select a mac from and
2692 configure it automatically.
2693 """
2694 if ifaceobj.get_attr_value('hwaddress'):
2695 # if the user configured a static hwaddress
2696 # there is no need to assign one
2697 return
2698
2699 ifname = ifaceobj.name
2700 mac_intf, bridge_mac = self._get_bridge_mac(ifaceobj, ifname, ifaceobj_getfunc)
2701 self.logger.debug("%s: _get_bridge_mac returned (%s, %s)"
2702 %(ifname, mac_intf, bridge_mac))
2703
2704 if bridge_mac:
2705 # if an interface is configured with the following attribute:
2706 # hwaddress 08:00:27:42:42:4
2707 # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
2708 # from the kernel. The only way to counter that is to convert all mac to int
2709 # and compare the ints, it will increase perfs and be safer.
223ba5af 2710 cached_value = self.cache.get_link_address(ifname)
d486dd0d 2711 self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
223ba5af 2712 if cached_value and utils.mac_str_to_int(cached_value) == utils.mac_str_to_int(bridge_mac):
d486dd0d
JF
2713 # the bridge mac is already set to the bridge_mac_intf's mac
2714 return
2715
2716 self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
2717 try:
223ba5af 2718 self.netlink.link_set_address(ifname, bridge_mac) # force=True
d486dd0d
JF
2719 except Exception as e:
2720 self.logger.info('%s: %s' % (ifname, str(e)))
2721 # log info this error because the user didn't explicitly configured this
2722 else:
223ba5af 2723 self._add_bridge_mac_to_fdb(ifaceobj, self.cache.get_link_address(ifname))
d486dd0d
JF
2724
2725 def _up(self, ifaceobj, ifaceobj_getfunc=None):
2726 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
2727 self.up_bridge_port(ifaceobj, ifaceobj_getfunc)
2728
2729 elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
2730 self.up_bridge(ifaceobj, ifaceobj_getfunc)
2731
c8e0a11e 2732 else:
3b01ed76 2733 bridge_attributes = list(self._modinfo.get('attrs', {}).keys())
c8e0a11e 2734
3b01ed76 2735 for ifaceobj_config_attr in list(ifaceobj.config.keys()):
c8e0a11e
JF
2736 if ifaceobj_config_attr in bridge_attributes:
2737 self.logger.warning('%s: invalid use of bridge attribute (%s) on non-bridge stanza'
2738 % (ifaceobj.name, ifaceobj_config_attr))
2739
d486dd0d
JF
2740 def _down(self, ifaceobj, ifaceobj_getfunc=None):
2741 if not self._is_bridge(ifaceobj):
2742 return
2743 ifname = ifaceobj.name
223ba5af 2744 if not self.cache.link_exists(ifname):
d486dd0d 2745 return
7840bd2c
JF
2746
2747 try:
2748 self.netlink.link_del(self.get_dummy_brport_name_for_bridge(ifname))
3218f49d 2749 except Exception:
7840bd2c
JF
2750 pass
2751
d486dd0d 2752 try:
223ba5af 2753 running_ports = self.cache.get_slaves(ifname)
d486dd0d
JF
2754 if running_ports:
2755 self.handle_ipv6(running_ports, '0')
2756 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
7f208e56
JF
2757 for p in running_ports:
2758 self.netlink.link_down(p)
d486dd0d
JF
2759 except Exception as e:
2760 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
2761 try:
223ba5af 2762 self.netlink.link_del(ifname)
d486dd0d
JF
2763 except Exception as e:
2764 ifaceobj.set_status(ifaceStatus.ERROR)
2765 self.logger.error(str(e))
2766 # netlink exception already contains the ifname
2767
2768 def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
2769 running_attrs = {}
2770 if ports:
2771 running_bridge_port_vids = ''
2772 for p in ports:
2773 try:
223ba5af 2774 _, running_vids = self.cache.get_pvid_and_vids(p)
d486dd0d
JF
2775 if running_vids:
2776 running_bridge_port_vids += ' %s=%s' %(p,
2777 ','.join(running_vids))
2778 except Exception:
2779 pass
2780 running_attrs['bridge-port-vids'] = running_bridge_port_vids
2781
2782 running_bridge_port_pvid = ''
2783 for p in ports:
2784 try:
223ba5af 2785 running_pvid = self.cache.get_pvid(p)
d486dd0d
JF
2786 if running_pvid:
2787 running_bridge_port_pvid += ' %s=%s' %(p,
2788 running_pvid)
2789 except Exception:
2790 pass
2791 running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
2792
223ba5af 2793 _, running_bridge_vids = self.cache.get_pvid_and_vids(ifaceobjrunning.name)
d486dd0d
JF
2794 if running_bridge_vids:
2795 running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
2796 return running_attrs
2797
2798 def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
2799 bridgeports=None):
2800 running_attrs = {}
2801
2802 # 'bridge-vids' under the bridge is all about 'vids' on the port.
2803 # so query the ports
2804 running_bridgeport_vids = []
2805 running_bridgeport_pvids = []
2806 for bport in bridgeports:
2807 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2808 if vids:
2809 running_bridgeport_vids.append(' '.join(vids))
2810 if pvid:
2811 running_bridgeport_pvids.append(pvid)
2812
2813 bridge_vids = None
2814 if running_bridgeport_vids:
2815 (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
2816 if freq == len(bridgeports):
2817 running_attrs['bridge-vids'] = vidval
2818 bridge_vids = vidval.split()
2819
2820 bridge_pvid = None
2821 if running_bridgeport_pvids:
2822 (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
2823 if freq == len(bridgeports) and vidval != '1':
2824 running_attrs['bridge-pvid'] = vidval
2825 bridge_pvid = vidval.split()[0]
2826
2827 # Go through all bridge ports and find their vids
2828 for bport in bridgeports:
2829 bportifaceobj = ifaceobj_getfunc(bport)
2830 if not bportifaceobj:
2831 continue
2832 bport_vids = []
2833 bport_pvid = None
2834 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2835 if vids and vids != bridge_vids:
2836 bport_vids = vids
2837 if pvid and pvid != bridge_pvid:
2838 bport_pvid = pvid
2839 if bport_vids and bport_pvid in bport_vids:
2840 bport_vids.remove(bport_pvid)
2841 if (not bport_vids and bport_pvid and bport_pvid != '1'):
2842 bportifaceobj[0].replace_config('bridge-access', bport_pvid)
2843 bportifaceobj[0].delete_config('bridge-pvid')
2844 bportifaceobj[0].delete_config('bridge-vids')
2845 else:
2846 if bport_pvid and bport_pvid != '1':
2847 bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
2848 else:
2849 # delete any stale bridge-vids under ports
2850 bportifaceobj[0].delete_config('bridge-pvid')
2851 if bport_vids:
2852 bportifaceobj[0].replace_config('bridge-vids',
2853 ' '.join(bport_vids))
2854 else:
2855 # delete any stale bridge-vids under ports
2856 bportifaceobj[0].delete_config('bridge-vids')
2857 return running_attrs
2858
2859 def _query_running_mcqv4src(self, ifaceobjrunning):
223ba5af 2860 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobjrunning.name)
3b01ed76 2861 mcqs = ['%s=%s' %(v, i) for v, i in list(running_mcqv4src.items())]
d486dd0d
JF
2862 mcqs.sort()
2863 mcq = ' '.join(mcqs)
2864 return mcq
2865
2866 def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
2867 bridge_vlan_aware=False):
223ba5af
JF
2868
2869 ifname = ifaceobjrunning.name
d486dd0d
JF
2870 bridgeattrdict = {}
2871 userspace_stp = 0
2872 ports = None
d486dd0d
JF
2873 try:
2874 if self.systcl_get_net_bridge_stp_user_space() == '1':
2875 userspace_stp = 1
2876 except Exception as e:
2877 self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
2878
223ba5af
JF
2879 bridge_ifla_info_data = self.cache.get_link_info_data(ifname)
2880
d486dd0d
JF
2881
2882 # Fill bridge_ports and bridge stp attributes first
223ba5af
JF
2883 #
2884 # bridge-ports
2885 #
2886 bridgeattrdict["bridge-ports"] = [" ".join(self.cache.get_slaves(ifname))]
2887
2888 #
2889 # bridge-stp
2890 #
2891 cached_stp = bool(bridge_ifla_info_data.get(Link.IFLA_BR_STP_STATE))
2892
2893 if cached_stp != utils.get_boolean_from_string(
2894 self.get_mod_subattr("bridge-stp", "default")
2895 ):
2896 bridgeattrdict['bridge-stp'] = ["yes" if cached_stp else "no"]
2897
2898 skip_kernel_stp_attrs = cached_stp and userspace_stp
2899
2900 if skip_kernel_stp_attrs:
2901 bridge_attributes_map = {
2902 "bridge-mcqifaddr": Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
2903 "bridge-mcquerier": Link.IFLA_BR_MCAST_QUERIER,
2904 "bridge-mcrouter": Link.IFLA_BR_MCAST_ROUTER,
2905 "bridge-mcstats": Link.IFLA_BR_MCAST_STATS_ENABLED,
2906 "bridge-mcsnoop": Link.IFLA_BR_MCAST_SNOOPING,
2907 "bridge-mclmc": Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
2908 "bridge-mclmi": Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
2909 "bridge-mcqri": Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
2910 "bridge-mcqpi": Link.IFLA_BR_MCAST_QUERIER_INTVL,
2911 "bridge-mcsqc": Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
2912 "bridge-mcsqi": Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
2913 "bridge-mcmi": Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
2914 "bridge-mcqi": Link.IFLA_BR_MCAST_QUERY_INTVL,
2915 }
2916 else:
2917 bridge_attributes_map = dict(self._ifla_br_attributes_map)
2918 try:
2919 del bridge_attributes_map[Link.IFLA_BR_STP_STATE]
3218f49d 2920 except Exception:
223ba5af
JF
2921 pass
2922
2923 #
2924 # bridge-vlan-stats
2925 #
2926 cached_vlan_stats = bridge_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
2927
2928 if cached_vlan_stats != utils.get_boolean_from_string(
2929 self.get_mod_subattr("bridge-vlan-stats", "default")
2930 ):
2931 bridgeattrdict['bridge-vlan-stats'] = ["on" if cached_vlan_stats else "off"]
2932
2933 try:
2934 del bridge_attributes_map[Link.IFLA_BR_VLAN_STATS_ENABLED]
3218f49d 2935 except Exception:
223ba5af 2936 pass
d486dd0d 2937
c817ba4d 2938 lambda_nl_value_int_divide100 = lambda x: str(x // 100)
223ba5af
JF
2939 lambda_nl_value_to_yes_no_boolean = lambda x: "yes" if x else "no"
2940
2941 bridge_attr_value_netlink_to_string_dict = {
2942 Link.IFLA_BR_VLAN_PROTOCOL: lambda x: x.lower(), # return lower case vlan protocol
2943 Link.IFLA_BR_AGEING_TIME: lambda_nl_value_int_divide100,
2944 Link.IFLA_BR_MAX_AGE: lambda_nl_value_int_divide100,
2945 Link.IFLA_BR_FORWARD_DELAY: lambda_nl_value_int_divide100,
2946 Link.IFLA_BR_HELLO_TIME: lambda_nl_value_int_divide100,
2947 Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL: lambda_nl_value_int_divide100,
2948 Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: lambda_nl_value_int_divide100,
2949 Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL: lambda_nl_value_int_divide100,
2950 Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: lambda_nl_value_int_divide100,
2951 Link.IFLA_BR_MCAST_QUERIER_INTVL: lambda_nl_value_int_divide100,
2952 Link.IFLA_BR_MCAST_QUERY_INTVL: lambda_nl_value_int_divide100,
2953 Link.IFLA_BR_VLAN_FILTERING: lambda_nl_value_to_yes_no_boolean,
2954 Link.IFLA_BR_MCAST_QUERY_USE_IFADDR: lambda_nl_value_to_yes_no_boolean,
2955 Link.IFLA_BR_MCAST_SNOOPING: lambda_nl_value_to_yes_no_boolean,
2956 Link.IFLA_BR_MCAST_QUERIER: lambda_nl_value_to_yes_no_boolean,
2957 Link.IFLA_BR_MCAST_ROUTER: lambda_nl_value_to_yes_no_boolean,
2958 }
2959
3b01ed76 2960 for attr_name, attr_nl in bridge_attributes_map.items():
223ba5af
JF
2961 default_value = self.get_mod_subattr(attr_name, "default")
2962 cached_value = bridge_ifla_info_data.get(attr_nl)
2963
2964 if cached_value is None:
d486dd0d 2965 continue
223ba5af
JF
2966
2967 cached_value_string = bridge_attr_value_netlink_to_string_dict.get(attr_nl, str)(cached_value)
2968
2969 if default_value != cached_value_string:
2970 bridgeattrdict[attr_name] = [cached_value_string]
d486dd0d
JF
2971
2972 if bridge_vlan_aware:
2973 if not ports:
2974 ports = {}
2975 bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
2976 ifaceobj_getfunc,
3b01ed76 2977 list(ports.keys()))
d486dd0d
JF
2978 else:
2979 bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
2980 ports)
2981 if bridgevidinfo:
3b01ed76 2982 bridgeattrdict.update({k : [v] for k, v in list(bridgevidinfo.items())
d486dd0d
JF
2983 if v})
2984
2985 mcq = self._query_running_mcqv4src(ifaceobjrunning)
2986 if mcq:
2987 bridgeattrdict['bridge-mcqv4src'] = [mcq]
2988
2989 if skip_kernel_stp_attrs:
2990 return bridgeattrdict
2991
2992 # Do this only for vlan-UNAWARE-bridge
2993 if ports and not bridge_vlan_aware:
2994 portconfig = {'bridge-pathcosts' : '',
2995 'bridge-portprios' : '',
2996 'bridge-learning' : '',
2997 'bridge-unicast-flood' : '',
2998 'bridge-multicast-flood' : '',
223ba5af 2999 'bridge-broadcast-flood' : '',
d486dd0d
JF
3000 'bridge-arp-nd-suppress' : '',
3001 }
3b01ed76 3002 for p, v in list(ports.items()):
223ba5af 3003 v = str(self.cache.get_brport_cost(p))
d486dd0d
JF
3004 if v and v != self.get_mod_subattr('bridge-pathcosts',
3005 'default'):
3006 portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
3007
223ba5af 3008 v = str(self.cache.get_brport_priority(p))
d486dd0d
JF
3009 if v and v != self.get_mod_subattr('bridge-portprios',
3010 'default'):
3011 portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
3012
223ba5af 3013 v = utils.get_onff_from_onezero(self.cache.get_brport_learning(p))
d486dd0d
JF
3014 if (v and
3015 v != self.get_mod_subattr('bridge-learning', 'default')):
3016 portconfig['bridge-learning'] += ' %s=%s' %(p, v)
3017
223ba5af 3018 v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(p))
d486dd0d
JF
3019 if (v and
3020 v != self.get_mod_subattr('bridge-unicast-flood',
3021 'default')):
3022 portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
3023
223ba5af 3024 v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(p))
d486dd0d
JF
3025 if (v and
3026 v != self.get_mod_subattr('bridge-multicast-flood',
3027 'default')):
3028 portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
3029
223ba5af
JF
3030 v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(p))
3031 if (v and
3032 v != self.get_mod_subattr('bridge-broadcast-flood',
3033 'default')):
3034 portconfig['bridge-broadcast-flood'] += ' %s=%s' %(p, v)
3035
3036 v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(p))
d486dd0d
JF
3037 if (v and
3038 v != self.get_mod_subattr('bridge-arp-nd-suppress',
3039 'default')):
3040 portconfig['bridge-arp-nd-suppress'] += ' %s=%s' %(p, v)
3041
3b01ed76 3042 bridgeattrdict.update({k : [v] for k, v in list(portconfig.items())
d486dd0d
JF
3043 if v})
3044
3045 return bridgeattrdict
3046
3047 def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
3048 running_mcqs = self._query_running_mcqv4src(ifaceobj)
3049 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
3050 if attrval:
3051 mcqs = attrval.split()
3052 mcqs.sort()
3053 mcqsout = ' '.join(mcqs)
3054 ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
3055 running_mcqs, 1 if running_mcqs != mcqsout else 0)
3056
223ba5af
JF
3057 def _query_check_bridge_vidinfo(self, ifname, ifaceobj, ifaceobjcurr):
3058 #
3059 # bridge-port-vids
3060 #
3061 bridge_port_vids_user_config = ifaceobj.get_attr_value_first("bridge-port-vids")
3062 if bridge_port_vids_user_config:
3063
3064 port_list = self.parse_port_list(ifname, bridge_port_vids_user_config)
3065
3066 if not port_list:
3067 self.log_warn("%s: could not parse 'bridge-port-vids %s'"
3068 % (ifname, bridge_port_vids_user_config))
3069 ifaceobjcurr.update_config_with_status("bridge-port-vids", "ERROR", 1)
d486dd0d 3070 return
223ba5af
JF
3071
3072 error = False
3073 for port_config in port_list:
d486dd0d 3074 try:
223ba5af
JF
3075 port, vids_raw = port_config.split("=")
3076 packed_vids = vids_raw.split(",")
3077
3078 running_pvid, running_vids = self.cache.get_pvid_and_vids(port)
3079
3080 if not self._compare_vids(packed_vids, running_vids, pvid=running_pvid, expand_range=False):
3081 error = True
d486dd0d 3082
223ba5af
JF
3083 except Exception as e:
3084 self.log_warn("%s: failure checking vid %s (%s)" % (ifname, port_config, str(e)))
3085
3086 ifaceobjcurr.update_config_with_status("bridge-port-vids", bridge_port_vids_user_config, error)
3087
3088 #
3089 # bridge-port-pvids
3090 #
d486dd0d
JF
3091 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
3092 if attrval:
3093 portlist = self.parse_port_list(ifaceobj.name, attrval)
3094 if not portlist:
3095 self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
223ba5af 3096 % (ifname, attrval))
d486dd0d 3097 return
223ba5af
JF
3098
3099 error = False
3100 running_pvid_config = []
d486dd0d 3101 for p in portlist:
223ba5af
JF
3102 (port, pvid) = p.split('=')
3103 running_pvid, _ = self.cache.get_pvid_and_vids(port)
3104
3105 running_pvid_config.append("%s=%s" % (port, running_pvid))
3106
3107 if running_pvid != int(pvid):
3108 error = True
3109
3110 ifaceobjcurr.update_config_with_status(
3111 "bridge-port-pvids",
3112 " ".join(running_pvid_config),
3113 int(error)
3114 )
d486dd0d
JF
3115
3116 vids = self.get_ifaceobj_bridge_vids(ifaceobj)
3117 if vids[1]:
3118 ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
3119
3120 def _query_check_snooping_wdefault(self, ifaceobj):
3121 if (ifupdownflags.flags.WITHDEFAULTS
3122 and not self._vxlan_bridge_default_igmp_snooping
3123 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
3124 ifaceobj.replace_config('bridge-mcsnoop', 'no')
3125
223ba5af 3126 def _query_check_bridge(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
d486dd0d
JF
3127 if not self._is_bridge(ifaceobj):
3128 return
d486dd0d 3129
223ba5af 3130 ifname = ifaceobj.name
d486dd0d 3131
223ba5af
JF
3132 if not self.cache.bridge_exists(ifname):
3133 self.logger.info("%s: bridge: does not exist" % (ifname))
d486dd0d 3134 return
d486dd0d 3135
223ba5af 3136 self._query_check_snooping_wdefault(ifaceobj)
d486dd0d 3137
223ba5af 3138 user_config_attributes = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
d486dd0d 3139
223ba5af
JF
3140 # add default attributes if --with-defaults is set
3141 if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in user_config_attributes:
3142 user_config_attributes.append('bridge-stp')
d486dd0d 3143
223ba5af
JF
3144 if not user_config_attributes:
3145 return
d486dd0d 3146
223ba5af
JF
3147 if "bridge-ports" in user_config_attributes:
3148 self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, self.cache.get_slaves(ifname), ifaceobj_getfunc)
d1265fd5 3149
223ba5af 3150 if "bridge-ports-condone-regex" in user_config_attributes:
f5c97c0d
MW
3151 ifaceobjcurr.update_config_with_status(
3152 "bridge-ports-condone-regex",
3153 self._get_bridge_port_condone_regex(ifaceobj, True),
3154 0
3155 )
223ba5af
JF
3156
3157 # Those attributes require separate handling
3158 filter_attributes = [
3159 "bridge-trunk",
3160 "bridge-ports",
3161 "bridge-vids",
3162 "bridge-trunk",
3163 "bridge-mcqv4src",
3164 "bridge-port-vids",
3165 "bridge-port-pvids",
3166 "bridge-l2protocol-tunnel",
3167 "bridge-ports-condone-regex"
3168 ]
3169
3170 ignore_attributes = (
3171 # bridge-pvid and bridge-vids on a bridge does not correspond
3172 # directly to a running config on the bridge. They correspond to
3173 # default values for the bridge ports. And they are already checked
3174 # against running config of the bridge port and reported against a
3175 # bridge port. So, ignore these attributes under the bridge. Use '2'
3176 # for ignore today. XXX: '2' will be mapped to a defined value in
3177 # subsequent patches.
3178 "bridge-pvid",
3179 "bridge-allow-untagged",
3180 )
3181 for attr in ignore_attributes:
3182 if attr in user_config_attributes:
3183 ifaceobjcurr.update_config_with_status(attr, ifaceobj.get_attr_value_first(attr), 2)
3184 filter_attributes.append(attr)
3185
3b01ed76 3186 bridge_config = set(user_config_attributes).difference(filter_attributes)
223ba5af
JF
3187 cached_ifla_info_data = self.cache.get_link_info_data(ifname)
3188
3189 self._query_check_bridge_attributes(ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data)
3190 self._query_check_brport_attributes_on_bridge(ifname, ifaceobj, ifaceobjcurr, bridge_config)
3191 self._query_check_bridge_vidinfo(ifname, ifaceobj, ifaceobjcurr)
3192 self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
3193 self._query_check_l2protocol_tunnel_on_bridge(ifname, ifaceobj, ifaceobjcurr)
3194
7840bd2c
JF
3195 def _query_check_bridge_always_up(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
3196 bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")
3197
3198 if bridge_always_up:
3199 bridge_config.remove("bridge-always-up")
3200
3201 if utils.get_boolean_from_string(bridge_always_up):
3202 try:
3203 link_exists = self.cache.link_exists(self.get_dummy_brport_name_for_bridge(ifname))
3218f49d 3204 except Exception:
7840bd2c
JF
3205 link_exists = False
3206
3207 ifaceobjcurr.update_config_with_status(
3208 "bridge-always-up",
3209 "yes" if link_exists else "no",
3210 not link_exists
3211 )
3212
223ba5af
JF
3213 def _query_check_bridge_attributes(self, ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data):
3214 for attr in list(bridge_config):
3215 query_check_handler, netlink_attr = self._bridge_attribute_query_check_handler.get(attr, (None, None))
3216
3217 if callable(query_check_handler):
3218 query_check_handler(attr, ifaceobj.get_attr_value_first(attr), ifaceobjcurr, cached_ifla_info_data.get(netlink_attr))
3219 bridge_config.remove(attr)
3220
7840bd2c
JF
3221 self._query_check_bridge_always_up(ifaceobj.name, ifaceobj, ifaceobjcurr, bridge_config)
3222
223ba5af
JF
3223 def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
3224 brports_info_slave_data = {}
3225 # bridge_config should only have bridge-port-list attributes
3226 for attr in bridge_config:
3227 attr_nl = self._ifla_brport_attributes_map.get(attr)
3228 brport_query_check_handler = self._brport_attribute_query_check_handler.get(attr)
3229
3230 if not attr_nl or not brport_query_check_handler:
3231 self.logger.warning("%s: query-check: missing handler for attribute: %s (%s)" % (ifname, attr, attr_nl))
d486dd0d 3232 continue
223ba5af
JF
3233
3234 running_config = []
3235 status = 0
3236
3237 for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []:
3238 port, config = port_config.split("=")
3239
3240 if not port in brports_info_slave_data:
3241 info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
d486dd0d 3242 else:
223ba5af
JF
3243 info_slave_data = brports_info_slave_data[port]
3244
3245 port_config, port_status = brport_query_check_handler(port, config, info_slave_data.get(attr_nl))
3246
3247 running_config.append(port_config)
3248
3249 if port_status:
3250 status = 1
3251
3252 ifaceobjcurr.update_config_with_status(
3253 attr,
3254 " ".join(running_config),
3255 status
3256 )
3257
3258 @staticmethod
3259 def _query_check_br_attr_wait(attr, wait_value, ifaceobjcurr, __):
3260 ifaceobjcurr.update_config_with_status(attr, wait_value, 0)
3261
3262 def _query_check_br_attr_stp(self, attr, stp_value, ifaceobjcurr, cached_value):
3263 if not stp_value:
3264 if ifupdownflags.flags.WITHDEFAULTS:
3265 stp_value = "on" if self.default_stp_on else "off"
d486dd0d 3266 else:
223ba5af 3267 return
d486dd0d 3268
223ba5af 3269 user_config_to_nl = utils.get_boolean_from_string(stp_value)
d486dd0d 3270
223ba5af
JF
3271 ifaceobjcurr.update_config_with_status(
3272 attr,
3273 "yes" if cached_value else "no",
3274 user_config_to_nl != bool(cached_value)
3275 )
3276
3277 @staticmethod
3278 def _query_check_br_attr_int(attr, user_config, ifaceobjcurr, cached_value):
3279 ifaceobjcurr.update_config_with_status(
3280 attr,
3281 str(cached_value),
3282 int(user_config) != cached_value
3283 )
3284
3285 @staticmethod
3286 def _query_check_br_attr_int_divided100(attr, user_config, ifaceobjcurr, cached_value):
c817ba4d 3287 value = cached_value // 100
223ba5af
JF
3288 ifaceobjcurr.update_config_with_status(
3289 attr,
3290 str(value),
3291 int(user_config) != value
3292 )
3293
3294 @staticmethod
3295 def _query_check_br_attr_boolean(attr, user_config, ifaceobjcurr, cached_value):
3296 ifaceobjcurr.update_config_with_status(
3297 attr,
3298 "yes" if cached_value else "no",
3299 utils.get_boolean_from_string(user_config) != cached_value
3300 )
3301
3302 @staticmethod
3303 def _query_check_br_attr_boolean_on_off(attr, user_config, ifaceobjcurr, cached_value):
3304 ifaceobjcurr.update_config_with_status(
3305 attr,
3306 "on" if cached_value else "off",
3307 utils.get_boolean_from_string(user_config) != cached_value
3308 )
3309
3310 @staticmethod
3311 def _query_check_br_attr_string(attr, user_config, ifaceobjcurr, cached_value):
3312 ifaceobjcurr.update_config_with_status(
3313 attr,
3314 cached_value,
3315 user_config.lower() != cached_value
3316 )
3317
3318 @staticmethod
3319 def _query_check_brport_attr_boolean_on_off(port, user_config, cached_value):
3320 return "%s=%s" % (port, "on" if cached_value else "off"), utils.get_boolean_from_string(user_config) != cached_value
3321
3322 @staticmethod
3323 def _query_check_brport_attr_boolean_yes_no(port, user_config, cached_value):
3324 return "%s=%s" % (port, "yes" if cached_value else "no"), utils.get_boolean_from_string(user_config) != cached_value
3325
3326 @staticmethod
3327 def _query_check_brport_attr_int(port, user_config, cached_value):
3328 return "%s=%s" % (port, cached_value), int(user_config) != cached_value
3329
3330 @classmethod
3331 def _query_check_brport_attr_portmcrouter(cls, port, user_config, cached_value):
3332 return (
3333 "%s=%s" % (port, cls._ifla_brport_multicast_router_dict_int_to_str.get(cached_value)),
3334 cls._ifla_brport_multicast_router_dict_to_int.get(user_config) != cached_value
3335 )
3336
3337 ####################################################################################################################
d486dd0d 3338
d1265fd5 3339 def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):
7840bd2c
JF
3340
3341 # if bridge-always-up is set we need to remove the dummy brport from the running_port_list
3342 if utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-always-up")):
3343 try:
3344 running_port_list.remove(self.get_dummy_brport_name_for_bridge(ifaceobj.name))
3218f49d 3345 except Exception:
7840bd2c
JF
3346 pass
3347
d1265fd5
JF
3348 bridge_all_ports = []
3349 for obj in ifaceobj_getfunc(ifaceobj.name) or []:
cc99b41d 3350 bridge_all_ports.extend(self._get_bridge_port_list(obj) or [])
d1265fd5
JF
3351
3352 if not running_port_list and not bridge_all_ports:
3353 return
3354
d1265fd5
JF
3355 try:
3356 port_list = self._get_ifaceobj_bridge_ports(ifaceobj).split()
3357 # we want to display the same bridge-ports list as provided
3358 # in the interfaces file but if this list contains regexes or
3359 # globs, for now, we won't try to change it.
3360 if 'regex' in port_list or 'glob' in port_list:
3361 port_list = running_port_list
3362 else:
3363 ordered = []
3364 for i in range(0, len(port_list)):
3365 if port_list[i] in running_port_list:
3366 ordered.append(port_list[i])
3367 port_list = ordered
3218f49d 3368 except Exception:
d1265fd5 3369 port_list = running_port_list
f5c97c0d
MW
3370
3371 difference = set(running_port_list).symmetric_difference(bridge_all_ports)
3372 bridge_port_condone_regex = self._get_bridge_port_condone_regex(ifaceobj)
3373
3374 if bridge_port_condone_regex:
3375 # Drop any condoned port from the difference set
3376 condone_ports = [port for port in difference if bridge_port_condone_regex.match(port)]
3377
3378 for port in condone_ports:
3379 try:
3380 difference.remove(port)
3381 except ValueError:
3382 pass
3383
3384 # Tag all condoned ports in brackets in output
3385 if port not in bridge_all_ports:
3386 port_list.append("(%s)" % port)
3387
3388 ifaceobjcurr.update_config_with_status(
3389 "bridge-ports",
3390 " ".join(port_list) if port_list else "",
3391 0 if not difference else 1
3392 )
d1265fd5 3393
d486dd0d
JF
3394 def get_ifaceobj_bridge_vids(self, ifaceobj):
3395 vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
3396 if not vids[1]:
3397 vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
3398 return vids
3399
3400 def get_ifaceobj_bridge_vids_value(self, ifaceobj):
3401 return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
3402
3403 def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
3404 ifaceobjs = ifaceobj_getfunc(bridgename)
3405 for ifaceobj in ifaceobjs:
3406 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
3407 if vids: return re.split(r'[\s\t,]\s*', vids)
3408 return None
3409
3410 def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
3411 ifaceobjs = ifaceobj_getfunc(bridgename)
3412 pvid = None
3413 for ifaceobj in ifaceobjs:
3414 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
3415 if pvid:
3416 break
3417 return pvid
3418
223ba5af
JF
3419 def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc):
3420 running_pvid, running_vids = self.cache.get_pvid_and_vids(ifname)
3421
3422 #
3423 # bridge-access
3424 #
3425 brport_vid_access_user_config = ifaceobj.get_attr_value_first("bridge-access")
3426
3427 if brport_vid_access_user_config:
3428 try:
3429 vid_int = int(brport_vid_access_user_config)
3430 except ValueError as e:
3431 ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1)
3432 raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e)))
3433
3434 ifaceobjcurr.update_config_with_status(
3435 "bridge-access",
3436 str(running_pvid),
3437 running_pvid != vid_int or running_vids[0] != vid_int
3438 )
d486dd0d
JF
3439 return
3440
223ba5af
JF
3441 #
3442 # bridge-pvid
3443 #
3444 brport_pvid_user_config = ifaceobj.get_attr_value_first("bridge-pvid")
3445
3446 if brport_pvid_user_config:
3447 try:
3448 pvid = int(brport_pvid_user_config)
3449 except ValueError as e:
3450 ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1)
3451 raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e)))
3452
3453 ifaceobjcurr.update_config_with_status(
3454 "bridge-pvid",
3455 str(running_pvid),
3456 running_pvid != pvid
3457 )
d486dd0d
JF
3458 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
3459 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
3460 (ifaceobj.flags & iface.OLDEST_SIBLING))):
3461 # if the interface has multiple iface sections,
3462 # we check the below only for the oldest sibling
3463 # or the last iface section
223ba5af
JF
3464 try:
3465 pvid = int(self._get_bridge_pvid(bridge_name, ifaceobj_getfunc))
3466 except (TypeError, ValueError):
3467 pvid = 0
d486dd0d
JF
3468 if pvid:
3469 if not running_pvid or running_pvid != pvid:
3470 ifaceobjcurr.status = ifaceStatus.ERROR
3471 ifaceobjcurr.status_str = 'bridge pvid error'
223ba5af 3472 elif not running_pvid or running_pvid != 1:
d486dd0d
JF
3473 ifaceobjcurr.status = ifaceStatus.ERROR
3474 ifaceobjcurr.status_str = 'bridge pvid error'
3475
3476 attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
3477 if vids:
3478 vids = re.split(r'[\s\t]\s*', vids)
e537a6e6
JF
3479
3480 # Special treatment to make sure that the vlans mapped with vnis
3481 # (in single-vxlan context) are not mistaken for regular vlans.
3482 # We need to proactively remove them from the "running_vids"
3483 vlans_mapped_with_vnis = self.get_bridge_vlans_mapped_to_vnis_as_integer_list(ifaceobj)
3484 new_running_vids = []
3485 user_config_vids = self._ranges_to_ints(vids)
3486 for v in running_vids:
3487 if v in user_config_vids:
3488 new_running_vids.append(v)
3489 elif v not in vlans_mapped_with_vnis:
3490 new_running_vids.append(v)
3491 running_vids = new_running_vids
3492 #####################################################################
3493
223ba5af 3494 if not running_vids or not self._compare_vids(vids, running_vids, running_pvid, expand_range=False):
6d94c17b 3495 running_vids = [str(o) for o in running_vids]
d486dd0d
JF
3496 ifaceobjcurr.update_config_with_status(attr_name,
3497 ' '.join(running_vids), 1)
3498 else:
3499 ifaceobjcurr.update_config_with_status(attr_name,
3500 ' '.join(vids), 0)
3501 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
3502 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
3503 (ifaceobj.flags & iface.OLDEST_SIBLING))):
3504 # if the interface has multiple iface sections,
3505 # we check the below only for the oldest sibling
3506 # or the last iface section
3507
3508 # check if it matches the bridge vids
223ba5af 3509 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
d486dd0d 3510 if (bridge_vids and (not running_vids or
223ba5af 3511 not self._compare_vids(bridge_vids, running_vids, running_pvid, expand_range=False))):
d486dd0d
JF
3512 ifaceobjcurr.status = ifaceStatus.ERROR
3513 ifaceobjcurr.status_str = 'bridge vid error'
3514
223ba5af
JF
3515 _query_check_brport_attributes = (
3516 "bridge-pvid",
3517 "bridge-vids",
3518 "bridge-trunk",
3519 "bridge-access",
3520 "bridge-pathcosts",
3521 "bridge-portprios",
3522 "bridge-portmcrouter",
3523 "bridge-learning",
3524 "bridge-portmcfl",
3525 "bridge-unicast-flood",
3526 "bridge-multicast-flood",
3527 "bridge-broadcast-flood",
3528 "bridge-arp-nd-suppress",
3529 "bridge-l2protocol-tunnel"
3530 )
3531
d486dd0d
JF
3532 def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
3533 ifaceobj_getfunc):
223ba5af
JF
3534
3535 ifname = ifaceobj.name
3536
3537 if not self.cache.link_is_bridge_port(ifname):
3538 # Mark all bridge brport attributes as failed
3539 ifaceobjcurr.check_n_update_config_with_status_many(
3540 ifaceobj, self._query_check_brport_attributes, 1
3541 )
d486dd0d 3542 return
223ba5af
JF
3543
3544 bridge_name = self.cache.get_bridge_name_from_port(ifname)
3545 if not bridge_name:
c46af1c9 3546 self.logger.warning("%s: unable to determine bridge name" % ifname)
d486dd0d
JF
3547 return
3548
223ba5af
JF
3549 if self.cache.bridge_is_vlan_aware(bridge_name):
3550 self._query_check_bridge_port_vidinfo(ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
3551
3552 brport_info_slave_data = self.cache.get_link_info_slave_data(ifname)
3553
3554 #
3555 # bridge-portmcfl
3556 #
3557 portmcfl = ifaceobj.get_attr_value_first("bridge-portmcfl")
3558
3559 if portmcfl:
3560 cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE)
3561
3562 ifaceobjcurr.update_config_with_status(
3563 "bridge-portmcfl",
3564 "yes" if cached_value else "no",
3565 utils.get_boolean_from_string(portmcfl) != cached_value
3566 )
3567
3568 #
3569 # bridge-portmcrouter
3570 #
3571 portmcrouter = ifaceobj.get_attr_value_first("bridge-portmcrouter")
3572
3573 if portmcrouter:
3574 cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER)
3575
3576 ifaceobjcurr.update_config_with_status(
3577 "bridge-portmcrouter",
3578 self._ifla_brport_multicast_router_dict_int_to_str.get(cached_value),
3579 self._ifla_brport_multicast_router_dict_to_int.get(portmcrouter) != cached_value
3580 )
3581
3582 #
3583 # bridge-learning
3584 # bridge-unicast-flood
3585 # bridge-multicast-flood
3586 # bridge-broadcast-flood
3587 # bridge-arp-nd-suppress
3588 #
3589 for attr_name, attr_nl in (
3590 ("bridge-learning", Link.IFLA_BRPORT_LEARNING),
3591 ("bridge-unicast-flood", Link.IFLA_BRPORT_UNICAST_FLOOD),
3592 ("bridge-multicast-flood", Link.IFLA_BRPORT_MCAST_FLOOD),
3593 ("bridge-broadcast-flood", Link.IFLA_BRPORT_BCAST_FLOOD),
3594 ("bridge-arp-nd-suppress", Link.IFLA_BRPORT_NEIGH_SUPPRESS),
3595 ):
3596 attribute_value = ifaceobj.get_attr_value_first(attr_name)
3597
3598 if not attribute_value:
3599 continue
3600
3601 cached_value = brport_info_slave_data.get(attr_nl)
3602
3603 ifaceobjcurr.update_config_with_status(
3604 attr_name,
3605 "on" if cached_value else "off",
3606 utils.get_boolean_from_string(attribute_value) != cached_value
3607 )
3608
3609 #
3610 # bridge-pathcosts
3611 # bridge-portprios
3612 #
3613 for attr_name, attr_nl in (
3614 ("bridge-pathcosts", Link.IFLA_BRPORT_COST),
3615 ("bridge-portprios", Link.IFLA_BRPORT_PRIORITY),
3616 ):
3617 attribute_value = ifaceobj.get_attr_value_first(attr_name)
3618
3619 if not attribute_value:
d486dd0d
JF
3620 continue
3621
223ba5af
JF
3622 cached_value = brport_info_slave_data.get(attr_nl)
3623
d486dd0d 3624 try:
223ba5af
JF
3625 ifaceobjcurr.update_config_with_status(
3626 attr_name,
3627 str(cached_value),
3628 int(attribute_value) != cached_value
3629 )
3630 except ValueError as e:
3631 ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1)
3632 raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e)))
d486dd0d
JF
3633
3634 self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
3635
e537a6e6
JF
3636 #
3637 # bridge-vlan-vni-map
3638 #
e537a6e6
JF
3639 cached_vlans, cached_vnis = self.get_vlan_vni_ranges(self.cache.get_vlan_vni(ifaceobj.name))
3640
b2751c6f
JF
3641 for bridge_vlan_vni_map_entry in ifaceobj.get_attr_value("bridge-vlan-vni-map") or []:
3642 fail = False
e537a6e6 3643
b2751c6f
JF
3644 for vlan_vni in bridge_vlan_vni_map_entry.split():
3645 try:
3646 vlans_str, vni_str = vlan_vni.split("=")
3647 except:
3648 fail = True
e537a6e6 3649
b2751c6f 3650 self.__warn_bridge_vlan_vni_map_syntax_error(ifname, vlan_vni)
e537a6e6 3651
b2751c6f
JF
3652 if fail:
3653 # if we already have detected an error on this entry there's
3654 # no point doing anything else than syntax check on the rest
3655 continue
e537a6e6 3656
b2751c6f
JF
3657 vlans_list = self._ranges_to_ints([vlans_str])
3658 vnis_list = self._ranges_to_ints([vni_str])
e537a6e6 3659
b2751c6f
JF
3660 try:
3661 for i, vlan in enumerate(vlans_list):
3662 index = cached_vnis.index(vlan)
3663
3664 if vlan != cached_vnis[index] or vnis_list[i] != cached_vlans[index]:
3665 fail = True
3666 except:
3667 fail = True
3668
3669 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map_entry, fail)
e537a6e6
JF
3670
3671 @staticmethod
3672 def get_vlan_vni_ranges(bridge_vlan_tunnel):
3673 vlans = []
3674 vnis = []
3675
8a78cd07
JF
3676 if not bridge_vlan_tunnel:
3677 return vlans, vnis
3678
e537a6e6
JF
3679 tunnel_vlan_range = None
3680 tunnel_vni_range = None
3681
3682 for tunnel_vlan, tunnel_vni, tunnel_flags in bridge_vlan_tunnel:
3683
3684 if tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
3685 tunnel_vlan_range = tunnel_vlan
3686 tunnel_vni_range = tunnel_vni
3687
3688 elif tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_END:
3689 vlans.extend(range(tunnel_vlan_range, tunnel_vlan + 1))
3690 vnis.extend(range(tunnel_vni_range, tunnel_vni + 1))
3691
3692 else:
3693 vlans.append(tunnel_vlan)
3694 vnis.append(tunnel_vni)
3695
3696 return vlans, vnis
3697
d486dd0d
JF
3698 def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
3699 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3700
3701 if user_config_l2protocol_tunnel:
3702 result = 0
3703 try:
3704 self._query_check_l2protocol_tunnel(ifaceobj.name, user_config_l2protocol_tunnel)
3705 except Exception as e:
3706 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
3707 result = 1
3708 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
3709
223ba5af 3710 def _query_check_l2protocol_tunnel_on_bridge(self, ifname, ifaceobj, ifaceobjcurr):
d486dd0d
JF
3711 """
3712 In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
3713 We need to make sure that all ports comply with the mask given under the bridge
3714 """
3715 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3716
3717 if user_config_l2protocol_tunnel:
3718 if '=' in user_config_l2protocol_tunnel:
3719 try:
3720 config_per_port_dict = self.parse_interface_list_value(user_config_l2protocol_tunnel)
3b01ed76 3721 brport_list = list(config_per_port_dict.keys())
3218f49d 3722 except Exception:
d486dd0d
JF
3723 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
3724 return
3725 else:
3726 config_per_port_dict = {}
223ba5af
JF
3727 brport_list = self.cache.get_slaves(ifname)
3728
d486dd0d
JF
3729 try:
3730 for brport_name in brport_list:
3731 self._query_check_l2protocol_tunnel(
3732 brport_name,
3733 config_per_port_dict.get(brport_name) if config_per_port_dict else user_config_l2protocol_tunnel
3734 )
3735 result = 0
3736 except Exception as e:
3737 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
3738 result = 1
3739 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
3740
3741 def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
223ba5af
JF
3742 cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
3743 cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
d486dd0d
JF
3744
3745 for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
3746 callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
3747
3748 if callable(callback):
3749 if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3750 raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)'
3751 % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
3752
3753 def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
223ba5af
JF
3754 cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
3755 cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
d486dd0d 3756 running_protocols = []
3b01ed76 3757 for protocol_name, callback in list(self.query_check_l2protocol_tunnel_callback.items()):
d486dd0d 3758 if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3b01ed76 3759 running_protocols = list(self.query_check_l2protocol_tunnel_callback.keys())
d486dd0d
JF
3760 running_protocols.remove('all')
3761 break
3762 elif callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3763 running_protocols.append(protocol_name)
3764 if running_protocols:
3765 if brport_ifaceobj:
3766 brport_ifaceobj.update_config('bridge-l2protocol-tunnel', ' '.join(running_protocols))
3767 elif bridge_ifaceobj:
3768 current_config = bridge_ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3769
3770 if current_config:
3771 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s %s=%s' % (current_config, brport_name, ','.join(running_protocols)))
3772 else:
3773 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s=%s' % (brport_name, ','.join(running_protocols)))
3774
3775 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
3776 if self._is_bridge(ifaceobj):
d1265fd5 3777 self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
d486dd0d
JF
3778 else:
3779 self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
3780 ifaceobj_getfunc)
3781
3782 def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
223ba5af 3783 if self.cache.bridge_is_vlan_aware(ifaceobjrunning.name):
d486dd0d
JF
3784 ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
3785 ifaceobjrunning.update_config_dict(self._query_running_attrs(
3786 ifaceobjrunning,
3787 ifaceobj_getfunc,
3788 bridge_vlan_aware=True))
3789 else:
3790 ifaceobjrunning.update_config_dict(self._query_running_attrs(
3791 ifaceobjrunning, None))
3792
3793 def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
3794 if self.systcl_get_net_bridge_stp_user_space() == '1':
3795 return
3796
223ba5af 3797 v = str(self.cache.get_brport_cost(ifaceobjrunning.name))
d486dd0d
JF
3798 if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
3799 ifaceobjrunning.update_config('bridge-pathcosts', v)
3800
223ba5af 3801 v = str(self.cache.get_brport_priority(ifaceobjrunning.name))
d486dd0d
JF
3802 if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
3803 ifaceobjrunning.update_config('bridge-portprios', v)
3804
3805 def _query_running_bridge_port(self, ifaceobjrunning,
3806 ifaceobj_getfunc=None):
3807
223ba5af 3808 bridgename = self.cache.get_bridge_name_from_port(
d486dd0d
JF
3809 ifaceobjrunning.name)
3810 bridge_vids = None
3811 bridge_pvid = None
3812 if not bridgename:
c46af1c9 3813 self.logger.warning('%s: unable to find bridgename'
d486dd0d
JF
3814 %ifaceobjrunning.name)
3815 return
3816
223ba5af 3817 if not self.cache.bridge_is_vlan_aware(bridgename):
d486dd0d
JF
3818 try:
3819 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
3820 except Exception as e:
3821 self.logger.debug('%s: q_query_running_bridge_l2protocol_tunnel: %s' % (ifaceobjrunning.name, str(e)))
3822 return
3823
3824 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, brport_ifaceobj=ifaceobjrunning)
3825
3826 (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
3827 ifaceobjrunning.name)
3828 if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
3829 bridge_port_vids.remove(bridge_port_pvid)
3830
3831 bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
3832 if bridgeifaceobjlist:
3833 bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
3834 bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
3835
3836 if not bridge_port_vids and bridge_port_pvid:
3837 # must be an access port
3838 if bridge_port_pvid != '1':
3839 ifaceobjrunning.update_config('bridge-access',
3840 bridge_port_pvid)
3841 else:
3842 if bridge_port_vids:
3843 if (not bridge_vids or bridge_port_vids != bridge_vids):
3844 ifaceobjrunning.update_config('bridge-vids',
3845 ' '.join(bridge_port_vids))
3846 if bridge_port_pvid and bridge_port_pvid != '1':
3847 if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
3848 ifaceobjrunning.update_config('bridge-pvid',
3849 bridge_port_pvid)
3850
223ba5af 3851 v = utils.get_onff_from_onezero(self.cache.get_brport_learning(ifaceobjrunning.name))
d486dd0d
JF
3852 if v and v != self.get_mod_subattr('bridge-learning', 'default'):
3853 ifaceobjrunning.update_config('bridge-learning', v)
3854
223ba5af 3855 v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(ifaceobjrunning.name))
d486dd0d
JF
3856 if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
3857 ifaceobjrunning.update_config('bridge-unicast-flood', v)
3858
223ba5af 3859 v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(ifaceobjrunning.name))
d486dd0d
JF
3860 if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
3861 ifaceobjrunning.update_config('bridge-multicast-flood', v)
3862
223ba5af
JF
3863 v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(ifaceobjrunning.name))
3864 if v and v != self.get_mod_subattr('bridge-broadcast-flood', 'default'):
3865 ifaceobjrunning.update_config('bridge-broadcast-flood', v)
3866
3867 v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(ifaceobjrunning.name))
d486dd0d
JF
3868 # Display running 'arp-nd-suppress' only on vxlan ports
3869 # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
3870 # otherwise, display on all bridge-ports
3871
3872 bportifaceobj = ifaceobj_getfunc(ifaceobjrunning.name)[0]
3873 if (v and
3874 v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default') and
3875 (not self.arp_nd_suppress_only_on_vxlan or
3876 (self.arp_nd_suppress_only_on_vxlan and
3877 bportifaceobj.link_kind & ifaceLinkKind.VXLAN))):
3878 ifaceobjrunning.update_config('bridge-arp-nd-suppress', v)
3879
3880 self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
3881
3882 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
3883 try:
223ba5af 3884 if self.cache.bridge_exists(ifaceobjrunning.name):
d486dd0d 3885 self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
223ba5af 3886 elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
d486dd0d
JF
3887 self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
3888 except Exception as e:
3889 raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
3890
3891 def _query(self, ifaceobj, **kwargs):
3892 """ add default policy attributes supported by the module """
ecb7cc13
JF
3893
3894 if self.bridge_vxlan_arp_nd_suppress \
3895 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT \
3896 and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
3897 ifaceobj.update_config("bridge-arp-nd-suppress", "on")
3898
d486dd0d
JF
3899 if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
3900 ifaceobj.get_attr_value_first('bridge-stp')):
3901 return
3902 if self.default_stp_on:
3903 ifaceobj.update_config('bridge-stp', 'yes')
3904
ecb7cc13 3905
d486dd0d
JF
3906 _run_ops = {
3907 'pre-up': _up,
3908 'post-down': _down,
3909 'query-checkcurr': _query_check,
3910 'query-running': _query_running,
3911 'query': _query
3912 }
3913
3914 def get_ops(self):
3915 """ returns list of ops supported by this module """
3b01ed76 3916 return list(self._run_ops.keys())
d486dd0d 3917
d486dd0d
JF
3918 def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
3919 """ run bridge configuration on the interface object passed as
3920 argument. Can create bridge interfaces if they dont exist already
3921
3922 Args:
3923 **ifaceobj** (object): iface object
3924
3925 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
3926 'query-running'
3927
3928 Kwargs:
3929 **query_ifaceobj** (object): query check ifaceobject. This is only
3930 valid when op is 'query-checkcurr'. It is an object same as
3931 ifaceobj, but contains running attribute values and its config
3932 status. The modules can use it to return queried running state
3933 of interfaces. status is success if the running state is same
3934 as user required state in ifaceobj. error otherwise.
3935 """
3936 op_handler = self._run_ops.get(operation)
3937 if not op_handler:
3938 return
d486dd0d 3939
223ba5af 3940 if (not self.requirements.bridge_utils_is_installed
d486dd0d 3941 and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
223ba5af 3942 and self.bridge_utils_missing_warning):
d486dd0d
JF
3943 self.logger.warning('%s: missing - bridge operation may not work as expected. '
3944 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
223ba5af
JF
3945 self.bridge_utils_missing_warning = False
3946
3947 # make sure BRIDGE_VXLAN is set if we have a vxlan port
4b16a997 3948 self._re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc)
d486dd0d
JF
3949
3950 if operation == 'query-checkcurr':
3951 op_handler(self, ifaceobj, query_ifaceobj,
3952 ifaceobj_getfunc=ifaceobj_getfunc)
3953 else:
3954 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)