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