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