summaryrefslogtreecommitdiff
path: root/src/op_mode
diff options
context:
space:
mode:
authorNataliia S. <81954790+natali-rs1985@users.noreply.github.com>2025-04-01 17:30:12 +0300
committerGitHub <noreply@github.com>2025-04-01 15:30:12 +0100
commit07a3b0f5ae87a2ab400390c9fa0ca632d7815e15 (patch)
treedd6c549c9694e14d31c4c47f0649f187d36863b3 /src/op_mode
parentaf6751ecdf44bde0c6dfb397e5ad128561f93112 (diff)
downloadvyos-1x-07a3b0f5ae87a2ab400390c9fa0ca632d7815e15.tar.gz
vyos-1x-07a3b0f5ae87a2ab400390c9fa0ca632d7815e15.zip
T7283: VPP add static NAT support (#24)
* T7283: VPP add static NAT support Add static mapping NAT implementation ``` set vpp nat44 static rule 10 outbound-interface 'eth0' set vpp nat44 static rule 10 inbound-interface 'eth1' set vpp nat44 static rule 10 destination address 192.168.122.10 # optional, if not set outbound interface ip address is used set vpp nat44 static rule 10 destination port 6545 # optional set vpp nat44 static rule 10 protocol tcp|udp|icmp|all # optional, defaults to "all" set vpp nat44 static rule 10 translation address 100.64.0.10 set vpp nat44 static rule 10 translation port 64010 # optional ``` * Improve help strings (Daniil Baturin) --------- Co-authored-by: Daniil Baturin <daniil@baturin.org>
Diffstat (limited to 'src/op_mode')
-rw-r--r--src/op_mode/show_vpp_nat44.py183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/op_mode/show_vpp_nat44.py b/src/op_mode/show_vpp_nat44.py
new file mode 100644
index 000000000..1aae8164e
--- /dev/null
+++ b/src/op_mode/show_vpp_nat44.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2025 VyOS Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import json
+import sys
+from tabulate import tabulate
+
+import vyos.opmode
+from vyos.configquery import ConfigTreeQuery
+
+from vyos.vpp import VPPControl
+
+
+protocol_map = {
+ 0: 'all',
+ 1: 'icmp',
+ 6: 'tcp',
+ 17: 'udp',
+}
+
+
+def _verify(func):
+ """Decorator checks if config for VPP NAT44 exists"""
+ from functools import wraps
+
+ @wraps(func)
+ def _wrapper(*args, **kwargs):
+ config = ConfigTreeQuery()
+ base = 'vpp nat44'
+ if not config.exists(base):
+ raise vyos.opmode.UnconfiguredSubsystem(f'{base} is not configured')
+
+ return func(*args, **kwargs)
+
+ return _wrapper
+
+
+def _get_raw_output_sessions(vpp_api):
+ users: list[dict] = vpp_api.nat44_user_dump()
+ sessions_list: list[dict] = []
+ for user in users:
+ ip_address = str(user._asdict().get('ip_address'))
+ user_sessions_dump = vpp_api.nat44_user_session_v3_dump(ip_address=ip_address)
+ user_sessions = [
+ json.loads(json.dumps(session._asdict(), default=str))
+ for session in user_sessions_dump
+ ]
+ sessions_list.extend(user_sessions)
+ return sorted(sessions_list, key=lambda x: x["inside_ip_address"])
+
+
+def _get_formatted_output_sessions(sessions_list):
+ print('NAT44 ED sessions:')
+ print(f'--------------- {len(sessions_list)} sessions ---------------')
+ for session in sessions_list:
+ in_ip_addr = session.get('inside_ip_address')
+ in_port = session.get('inside_port')
+ out_ip_addr = session.get('outside_ip_address')
+ out_port = session.get('outside_port')
+ protocol = protocol_map[session.get('protocol')].upper()
+ last_heard = session.get('last_heard')
+ time_since_last_heard = session.get('time_since_last_heard')
+ total_bytes = session.get('total_bytes')
+ total_pkts = session.get('total_pkts')
+ ext_host_address = session.get('ext_host_address')
+ ext_host_port = session.get('ext_host_port')
+ is_timed_out = session.get('is_timed_out')
+
+ print(f' i2o {in_ip_addr} proto {protocol} port {in_port}')
+ print(f' o2i {out_ip_addr} proto {protocol} port {out_port}')
+ print(f' external host {ext_host_address}:{ext_host_port}')
+ print(
+ f' i2o flow: match: saddr {in_ip_addr} sport {in_port} daddr {ext_host_address} dport {ext_host_port} proto {protocol} rewrite: saddr {out_ip_addr}'
+ + (
+ f' sport {out_port}'
+ if protocol != 'ICMP'
+ else f' daddr {ext_host_address} icmp-id {ext_host_port}'
+ )
+ )
+ print(
+ f' o2i flow: match: saddr {ext_host_address} sport {ext_host_port} daddr {out_ip_addr} dport {out_port} proto {protocol} rewrite: '
+ + (
+ f'daddr {in_ip_addr} dport {in_port}'
+ if protocol != 'ICMP'
+ else f' saddr {ext_host_address} daddr {in_ip_addr} icmp-id {ext_host_port}'
+ )
+ )
+ print(f' last heard {last_heard}')
+ print(f' time since last heard {time_since_last_heard}')
+ print(f' total packets {total_pkts}, total bytes {total_bytes}')
+ if is_timed_out:
+ print(' session timed out')
+ print('\n')
+
+
+def _get_raw_output_static_rules(vpp_api):
+ nat_static_dump = vpp_api.nat44_static_mapping_dump()
+ rules_list = [
+ json.loads(json.dumps(rule._asdict(), default=str)) for rule in nat_static_dump
+ ]
+ return rules_list
+
+
+def _get_formatted_output_rules(vpp, rules_list):
+ data_entries = []
+ for rule in rules_list:
+ dest_address = rule.get('external_ip_address')
+ dest_port = rule.get('external_port') or ''
+ trans_address = rule.get('local_ip_address')
+ trans_port = rule.get('local_port') or ''
+ protocol = protocol_map[rule.get('protocol', 0)]
+ dest_sh_if_index = rule.get('external_sw_if_index')
+
+ vpp_if_name = vpp.get_interface_name(dest_sh_if_index)
+ if vpp_if_name:
+ dest_address = vpp_if_name
+
+ values = [dest_address, dest_port, trans_address, trans_port, protocol]
+ data_entries.append(values)
+ headers = [
+ 'Des_address/interface',
+ 'Dest_port',
+ 'Trans_address',
+ 'Trans_port',
+ 'Protocol',
+ ]
+ out = sorted(data_entries, key=lambda x: x[2])
+ return tabulate(out, headers=headers, tablefmt='simple')
+
+
+@_verify
+def show_sessions(raw: bool):
+ vpp = VPPControl()
+ sessions_list: list[dict] = _get_raw_output_sessions(vpp.api)
+
+ if raw:
+ return sessions_list
+
+ else:
+ return _get_formatted_output_sessions(sessions_list)
+
+
+@_verify
+def show_summary(raw: bool):
+ vpp = VPPControl()
+ return vpp.cli_cmd('show nat44 summary').reply
+
+
+@_verify
+def show_static(raw: bool):
+ vpp = VPPControl()
+ rules_list: list[dict] = _get_raw_output_static_rules(vpp.api)
+
+ if raw:
+ return rules_list
+
+ else:
+ return _get_formatted_output_rules(vpp, rules_list)
+
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except (ValueError, vyos.opmode.Error) as e:
+ print(e)
+ sys.exit(1)