-
Notifications
You must be signed in to change notification settings - Fork 335
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Currently VyOS has protocol igmp option to enable IGMP querier and reports through FRR's pimd. I would like to add protocol mld for IPv6 as well since FRR's IPv6 multicast functionality has significantly improved. Enabling MLD querier and reports on a VyOS router will allow us to turn on MLD snooping on layer-3 switches.
- Loading branch information
Showing
5 changed files
with
263 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
! | ||
{% for iface in old_ifaces %} | ||
interface {{ iface }} | ||
{% for group in old_ifaces[iface].gr_join %} | ||
{% if old_ifaces[iface].gr_join[group] %} | ||
{% for source in old_ifaces[iface].gr_join[group] %} | ||
no ipv6 mld join {{ group }} {{ source }} | ||
{% endfor %} | ||
{% else %} | ||
no ipv6 mld join {{ group }} | ||
{% endif %} | ||
{% endfor %} | ||
no ipv6 mld | ||
! | ||
{% endfor %} | ||
{% for interface, interface_config in ifaces.items() %} | ||
interface {{ interface }} | ||
{% if interface_config.version %} | ||
ipv6 mld version {{ interface_config.version }} | ||
{% else %} | ||
{# IGMP default version 3 #} | ||
ipv6 mld | ||
{% endif %} | ||
{% if interface_config.query_interval %} | ||
ipv6 mld query-interval {{ interface_config.query_interval }} | ||
{% endif %} | ||
{% if interface_config.query_max_resp_time %} | ||
ipv6 mld query-max-response-time {{ interface_config.query_max_resp_time }} | ||
{% endif %} | ||
{% for group in interface_config.gr_join %} | ||
{% if ifaces[iface].gr_join[group] %} | ||
{% for source in ifaces[iface].gr_join[group] %} | ||
ipv6 mld join {{ group }} {{ source }} | ||
{% endfor %} | ||
{% else %} | ||
ipv6 mld join {{ group }} | ||
{% endif %} | ||
{% endfor %} | ||
! | ||
{% endfor %} | ||
! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?xml version="1.0"?> | ||
<!-- Multicast Listener Discovery (MLD) configuration --> | ||
<interfaceDefinition> | ||
<node name="protocols"> | ||
<children> | ||
<node name="mld" owner="${vyos_conf_scripts_dir}/protocols_mld.py"> | ||
<properties> | ||
<help>Multicast Listener Discovery (MLD)</help> | ||
</properties> | ||
<children> | ||
<tagNode name="interface"> | ||
<properties> | ||
<help>MLD interface</help> | ||
<completionHelp> | ||
<script>${vyos_completion_dir}/list_interfaces</script> | ||
</completionHelp> | ||
</properties> | ||
<children> | ||
<tagNode name="join"> | ||
<properties> | ||
<help>MLD join multicast group</help> | ||
<valueHelp> | ||
<format>ipv6</format> | ||
<description>Multicast group address</description> | ||
</valueHelp> | ||
<constraint> | ||
<validator name="ipv6-address"/> | ||
</constraint> | ||
</properties> | ||
<children> | ||
<leafNode name="source"> | ||
<properties> | ||
<help>Source address</help> | ||
<valueHelp> | ||
<format>ipv6</format> | ||
<description>Source address</description> | ||
</valueHelp> | ||
<constraint> | ||
<validator name="ipv6-address"/> | ||
</constraint> | ||
<multi/> | ||
</properties> | ||
</leafNode> | ||
</children> | ||
</tagNode> | ||
<leafNode name="version"> | ||
<properties> | ||
<help>MLD version</help> | ||
<completionHelp> | ||
<list>1 2</list> | ||
</completionHelp> | ||
<valueHelp> | ||
<format>1</format> | ||
<description>MLD version 1</description> | ||
</valueHelp> | ||
<valueHelp> | ||
<format>2</format> | ||
<description>MLD version 2</description> | ||
</valueHelp> | ||
<constraint> | ||
<validator name="numeric" argument="--range 1-2"/> | ||
</constraint> | ||
</properties> | ||
</leafNode> | ||
<leafNode name="query-interval"> | ||
<properties> | ||
<help>MLD query interval</help> | ||
<valueHelp> | ||
<format>u32:1-65535</format> | ||
<description>Query interval in seconds</description> | ||
</valueHelp> | ||
<constraint> | ||
<validator name="numeric" argument="--range 1-65535"/> | ||
</constraint> | ||
</properties> | ||
</leafNode> | ||
<leafNode name="query-max-response-time"> | ||
<properties> | ||
<help>MLD max query response time</help> | ||
<valueHelp> | ||
<format>u32:1-65535</format> | ||
<description>Query response value in deci-seconds</description> | ||
</valueHelp> | ||
<constraint> | ||
<validator name="numeric" argument="--range 1-65535"/> | ||
</constraint> | ||
</properties> | ||
</leafNode> | ||
</children> | ||
</tagNode> | ||
</children> | ||
</node> | ||
</children> | ||
</node> | ||
</interfaceDefinition> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# Copyright (C) 2020-2023 VyOS maintainers and contributors | ||
# | ||
# This program is free software; you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 2 or later as | ||
# published by the Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
import os | ||
|
||
from ipaddress import IPv6Address | ||
from sys import exit | ||
|
||
from vyos import ConfigError | ||
from vyos.config import Config | ||
from vyos.utils.process import process_named_running | ||
from vyos.utils.process import call | ||
from vyos.template import render | ||
from signal import SIGTERM | ||
|
||
from vyos import airbag | ||
airbag.enable() | ||
|
||
# Required to use the full path to pim6d, in another case daemon will not be started | ||
pimd_cmd = f'/usr/lib/frr/pim6d -d -F traditional --daemon -A 127.0.0.1' | ||
|
||
config_file = r'/tmp/mld.frr' | ||
|
||
def get_config(config=None): | ||
if config: | ||
conf = config | ||
else: | ||
conf = Config() | ||
mld_conf = { | ||
'mld_conf' : False, | ||
'old_ifaces' : {}, | ||
'ifaces' : {} | ||
} | ||
if not (conf.exists('protocols mld') or conf.exists_effective('protocols mld')): | ||
return None | ||
|
||
if conf.exists('protocols mld'): | ||
mld_conf['mld_conf'] = True | ||
|
||
conf.set_level('protocols mld') | ||
|
||
# # Get interfaces | ||
for iface in conf.list_effective_nodes('interface'): | ||
mld_conf['old_ifaces'].update({ | ||
iface : { | ||
'version' : conf.return_effective_value('interface {0} version'.format(iface)), | ||
'query_interval' : conf.return_effective_value('interface {0} query-interval'.format(iface)), | ||
'query_max_resp_time' : conf.return_effective_value('interface {0} query-max-response-time'.format(iface)), | ||
'gr_join' : {} | ||
} | ||
}) | ||
for gr_join in conf.list_effective_nodes('interface {0} join'.format(iface)): | ||
mld_conf['old_ifaces'][iface]['gr_join'][gr_join] = conf.return_effective_values('interface {0} join {1} source'.format(iface, gr_join)) | ||
|
||
for iface in conf.list_nodes('interface'): | ||
mld_conf['ifaces'].update({ | ||
iface : { | ||
'version' : conf.return_value('interface {0} version'.format(iface)), | ||
'query_interval' : conf.return_value('interface {0} query-interval'.format(iface)), | ||
'query_max_resp_time' : conf.return_value('interface {0} query-max-response-time'.format(iface)), | ||
'gr_join' : {} | ||
} | ||
}) | ||
for gr_join in conf.list_nodes('interface {0} join'.format(iface)): | ||
mld_conf['ifaces'][iface]['gr_join'][gr_join] = conf.return_values('interface {0} join {1} source'.format(iface, gr_join)) | ||
|
||
return mld_conf | ||
|
||
def verify(mld): | ||
if mld is None: | ||
return None | ||
|
||
if mld['mld_conf']: | ||
# Check interfaces | ||
if not mld['ifaces']: | ||
raise ConfigError(f"MLD require defined interfaces!") | ||
# Check, is this multicast group | ||
for intfc in mld['ifaces']: | ||
for gr_addr in mld['ifaces'][intfc]['gr_join']: | ||
if not IPv6Address(gr_addr).is_multicast: | ||
raise ConfigError(gr_addr + " not a multicast group") | ||
|
||
def generate(mld): | ||
if mld is None: | ||
return None | ||
|
||
render(config_file, 'frr/mld.frr.j2', mld) | ||
return None | ||
|
||
def apply(mld): | ||
if mld is None: | ||
return None | ||
pim_pid = process_named_running('pim6d') | ||
if mld['mld_conf']: | ||
if not pim_pid: | ||
call(pimd_cmd) | ||
if os.path.exists(config_file): | ||
call(f'vtysh -d pim6d -f {config_file}') | ||
os.remove(config_file) | ||
elif pim_pid: | ||
os.kill(int(pim_pid), SIGTERM) | ||
return None | ||
|
||
if __name__ == '__main__': | ||
try: | ||
c = get_config() | ||
verify(c) | ||
generate(c) | ||
apply(c) | ||
except ConfigError as e: | ||
print(e) | ||
exit(1) |