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