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