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