Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

policy-conf: T439: Add policy local-route PBR #614

Merged
merged 1 commit into from Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -80,6 +80,7 @@ interface_definitions: $(BUILD_DIR) $(obj)
rm -rf $(TMPL_DIR)/protocols/nbgp
rm -rf $(TMPL_DIR)/protocols/isis
rm -f $(TMPL_DIR)/protocols/static/node.def
rm -f $(TMPL_DIR)/policy/node.def
rm -f $(TMPL_DIR)/system/node.def
rm -f $(TMPL_DIR)/vpn/node.def
rm -f $(TMPL_DIR)/vpn/ipsec/node.def
Expand Down
1 change: 1 addition & 0 deletions data/configd-include.json
Expand Up @@ -29,6 +29,7 @@
"lldp.py",
"nat.py",
"ntp.py",
"policy-local-route.py",
"protocols_igmp.py",
"protocols_isis.py",
"protocols_mpls.py",
Expand Down
67 changes: 67 additions & 0 deletions interface-definitions/policy-local-route.xml.in
@@ -0,0 +1,67 @@
<?xml version="1.0"?>
<!-- Policy local-route -->
<interfaceDefinition>
<node name="policy">
<children>
<node name="local-route" owner="${vyos_conf_scripts_dir}/policy-local-route.py">
<properties>
<help>IPv4 policy route of local traffic</help>
</properties>
<children>
<tagNode name="rule">
<properties>
<help>Policy local-route rule set number</help>
<valueHelp>
<!-- table main with prio 32766 -->
<format>&lt;1-32765&gt;</format>
<description>Local-route rule number (1-219)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-32765"/>
</constraint>
</properties>
<children>
<node name="set">
<properties>
<help>Packet modifications</help>
</properties>
<children>
<leafNode name="table">
<properties>
<help>Routing table to forward packet with</help>
<valueHelp>
<format>&lt;1-200&gt;</format>
<description>Table number</description>
</valueHelp>
<completionHelp>
<list>main</list>
</completionHelp>
</properties>
</leafNode>
</children>
</node>
<leafNode name="source">
<properties>
<help>Source address or prefix</help>
<valueHelp>
<format>ipv4</format>
<description>Address to match against</description>
</valueHelp>
<valueHelp>
<format>ipv4net</format>
<description>Prefix to match against</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
</children>
</tagNode>
</children>
</node>
</children>
</node>
</interfaceDefinition>
61 changes: 61 additions & 0 deletions smoketest/scripts/cli/test_policy_local-route.py
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
#
# Copyright (C) 2020 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
import unittest

from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running

class PolicyLocalRouteTest(unittest.TestCase):
def setUp(self):
self.session = ConfigSession(os.getpid())
self._sources = ['203.0.113.1', '203.0.113.2']

def tearDown(self):
# Delete all policies
self.session.delete(['policy', 'local-route'])
self.session.commit()
del self.session

# Test set table for some sources
def test_table_id(self):
base = ['policy', 'local-route']
rule = '50'
table = '23'
for src in self._sources:
self.session.set(base + ['rule', rule, 'set', 'table', table])
self.session.set(base + ['rule', rule, 'source', src])

self.session.commit()

# Check generated configuration

# Expected values
original = """
50: from 203.0.113.1 lookup 23
50: from 203.0.113.2 lookup 23
"""
tmp = cmd('ip rule show prio 50')
original = original.split()
tmp = tmp.split()

self.assertEqual(tmp, original)

if __name__ == '__main__':
unittest.main()
110 changes: 110 additions & 0 deletions src/conf_mode/policy-local-route.py
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
#
# Copyright (C) 2020 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 sys import exit

from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configdict import leaf_node_changed
from vyos.template import render
from vyos.util import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()


def get_config(config=None):

if config:
conf = config
else:
conf = Config()
base = ['policy', 'local-route']
pbr = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)

# delete policy local-route
dict = {}
tmp = node_changed(conf, ['policy', 'local-route', 'rule'])
if tmp:
for rule in (tmp or []):
src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
if src:
dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)
pbr.update(dict)

# delete policy local-route rule x source x.x.x.x
if 'rule' in pbr:
for rule in pbr['rule']:
src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
if src:
dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)
pbr.update(dict)

return pbr

def verify(pbr):
# bail out early - looks like removal from running config
if not pbr:
return None

if 'rule' in pbr:
for rule in pbr['rule']:
if 'source' not in pbr['rule'][rule]:
raise ConfigError('Source address required!')
else:
if 'set' not in pbr['rule'][rule] or 'table' not in pbr['rule'][rule]['set']:
raise ConfigError('Table set is required!')

return None

def generate(pbr):
if not pbr:
return None

return None

def apply(pbr):
if not pbr:
return None

# Delete old rule if needed
if 'rule_remove' in pbr:
for rule in pbr['rule_remove']:
for src in pbr['rule_remove'][rule]['source']:
call(f'ip rule del prio {rule} from {src}')

# Generate new config
if 'rule' in pbr:
for rule in pbr['rule']:
table = pbr['rule'][rule]['set']['table']
if pbr['rule'][rule]['source']:
for src in pbr['rule'][rule]['source']:
call(f'ip rule add prio {rule} from {src} lookup {table}')

return None

if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)