Skip to content

Commit

Permalink
policy-conf: T439: Add policy local-route PBR
Browse files Browse the repository at this point in the history
  • Loading branch information
sever-sever committed Nov 20, 2020
1 parent 4dc9321 commit 7606fa6
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 0 deletions.
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)

0 comments on commit 7606fa6

Please sign in to comment.