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