summaryrefslogtreecommitdiff
path: root/data
diff options
context:
space:
mode:
Diffstat (limited to 'data')
-rw-r--r--data/configd-include.json4
-rw-r--r--data/templates/firewall/nftables-nat.tmpl33
-rw-r--r--data/templates/firewall/nftables-nat66.tmpl85
-rw-r--r--data/templates/frr/bgp.frr.tmpl50
-rw-r--r--data/templates/frr/ospf.frr.tmpl131
-rw-r--r--data/templates/frr/ospfv3.frr.tmpl41
-rw-r--r--data/templates/openvpn/server.conf.tmpl6
-rw-r--r--data/templates/proxy-ndp/ndppd.conf.tmpl44
-rw-r--r--data/templates/ssh/override.conf.tmpl1
9 files changed, 375 insertions, 20 deletions
diff --git a/data/configd-include.json b/data/configd-include.json
index eb1dd13f9..751d8e012 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -28,11 +28,15 @@
"ipsec-settings.py",
"lldp.py",
"nat.py",
+"nat66.py",
"ntp.py",
"policy-local-route.py",
+"protocols_bgp.py",
"protocols_igmp.py",
"protocols_isis.py",
"protocols_mpls.py",
+"protocols_ospf.py",
+"protocols_ospfv3.py",
"protocols_pim.py",
"protocols_rip.py",
"protocols_static_multicast.py",
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
index 770a24a95..499733225 100644
--- a/data/templates/firewall/nftables-nat.tmpl
+++ b/data/templates/firewall/nftables-nat.tmpl
@@ -21,18 +21,34 @@
{% set comment = 'DST-NAT-' + rule %}
{% set base_log = '[NAT-DST-' + rule %}
{% set interface = ' iifname "' + config.inbound_interface + '"' if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
-{% set trns_addr = 'dnat to ' + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{# support 1:1 network translation #}
+{% if config.translation.address | is_ip_network %}
+{% set trns_addr = 'dnat ip prefix to ip daddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %}
+{# we can now clear out the src_addr part as it's already covered in aboves map #}
+{% set src_addr = '' %}
+{% else %}
+{% set trns_addr = 'dnat to ' + config.translation.address %}
+{% endif %}
+{% endif %}
{% elif chain == 'POSTROUTING' %}
{% set comment = 'SRC-NAT-' + rule %}
{% set base_log = '[NAT-SRC-' + rule %}
{% set interface = ' oifname "' + config.outbound_interface + '"' if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %}
-{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
-{% set trns_addr = config.translation.address %}
-{% if config.translation.port is defined and config.translation.port is not none %}
-{% set trns_addr = trns_addr + ' to ' %}
+{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{% if config.translation.address == 'masquerade' %}
+{% set trns_addr = config.translation.address %}
+{% if config.translation.port is defined and config.translation.port is not none %}
+{% set trns_addr = trns_addr + ' to ' %}
+{% endif %}
+{# support 1:1 network translation #}
+{% elif config.translation.address | is_ip_network %}
+{% set trns_addr = 'snat ip prefix to ip saddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %}
+{# we can now clear out the src_addr part as it's already covered in aboves map #}
+{% set src_addr = '' %}
+{% else %}
+{% set trns_addr = 'snat to ' + config.translation.address %}
{% endif %}
-{% else %}
-{% set trns_addr = 'snat to ' + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
{% endif %}
{% endif %}
{% set trns_port = ':' + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %}
@@ -102,7 +118,7 @@
{% endmacro %}
# Start with clean NAT table
-flush table nat
+flush table ip nat
{% if helper_functions == 'remove' %}
{# NAT if going to be disabled - remove rules and targets from nftables #}
{% set base_command = 'delete rule ip raw' %}
@@ -132,7 +148,6 @@ add rule ip raw NAT_CONNTRACK counter accept
{{ nat_rule(rule, config, 'PREROUTING') }}
{% endfor %}
{% endif %}
-
#
# Source NAT rules build up here
#
diff --git a/data/templates/firewall/nftables-nat66.tmpl b/data/templates/firewall/nftables-nat66.tmpl
new file mode 100644
index 000000000..b1a8f7a16
--- /dev/null
+++ b/data/templates/firewall/nftables-nat66.tmpl
@@ -0,0 +1,85 @@
+#!/usr/sbin/nft -f
+
+{% macro nptv6_rule(rule,config, chain) %}
+{% set src_prefix = "ip6 saddr " + config.source.prefix if config.source is defined and config.source.prefix is defined and config.source.prefix is not none %}
+{% set dest_address = "ip6 daddr " + config.destination.address if config.destination is defined and config.destination.address is defined and config.destination.address is not none %}
+{% if chain == "PREROUTING" %}
+{% set interface = " iifname \"" + config.inbound_interface + "\"" if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
+{% if config.translation.address | is_ip_network %}
+{# support 1:1 network translation #}
+{% set dnat_type = "dnat prefix to " %}
+{% else %}
+{% set dnat_type = "dnat to " %}
+{% endif %}
+{% set trns_address = dnat_type + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{% elif chain == "POSTROUTING" %}
+{% set interface = " oifname \"" + config.outbound_interface + "\"" if config.outbound_interface is defined else '' %}
+{% set trns_prefix = "snat prefix to " + config.translation.prefix if config.translation is defined and config.translation.prefix is defined and config.translation.prefix is not none %}
+{% endif %}
+{% set comment = "NPT-NAT-" + rule %}
+{% if rule.log %}
+{% set base_log = "[NPT-DST-" + rule %}
+{% set log = base_log + "]" %}
+{% endif %}
+{% set output = "add rule ip6 nat " + chain + interface %}
+{# Count packets #}
+{% set output = output + " counter" %}
+{# Special handling of log option, we must repeat the entire rule before the #}
+{# NAT translation options are added, this is essential #}
+{% if log %}
+{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %}
+{% endif %}
+{% if src_prefix %}
+{% set output = output + " " + src_prefix %}
+{% endif %}
+{% if dest_address %}
+{% set output = output + " " + dest_address %}
+{% endif %}
+{% if trns_prefix %}
+{% set output = output + " " + trns_prefix %}
+{% endif %}
+{% if trns_address %}
+{% set output = output + " " + trns_address %}
+{% endif %}
+{% if comment %}
+{% set output = output + " comment \"" + comment + "\"" %}
+{% endif %}
+{{ log_output if log_output }}
+{{ output }}
+{% endmacro %}
+
+# Start with clean NAT table
+flush table ip6 nat
+{% if helper_functions == 'remove' %}
+{# NAT if going to be disabled - remove rules and targets from nftables #}
+{% set base_command = "delete rule ip6 raw" %}
+{{base_command}} PREROUTING handle {{ pre_ct_conntrack }}
+{{base_command}} OUTPUT handle {{ out_ct_conntrack }}
+
+delete chain ip6 raw NAT_CONNTRACK
+
+{% elif helper_functions == 'add' %}
+{# NAT if enabled - add targets to nftables #}
+add chain ip6 raw NAT_CONNTRACK
+add rule ip6 raw NAT_CONNTRACK counter accept
+{% set base_command = "add rule ip6 raw" %}
+{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
+{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
+{% endif %}
+
+#
+# Destination NAT66 rules build up here
+#
+{% if destination is defined and destination.rule is defined and destination.rule is not none %}
+{% for rule, config in destination.rule.items() if config.disable is not defined %}
+{{ nptv6_rule(rule, config, 'PREROUTING') }}
+{% endfor %}
+{% endif %}
+#
+# Source NAT66 rules build up here
+#
+{% if source is defined and source.rule is defined and source.rule is not none %}
+{% for rule, config in source.rule.items() if config.disable is not defined %}
+{{ nptv6_rule(rule, config, 'POSTROUTING') }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl
index 74a9b8c30..523fe75e3 100644
--- a/data/templates/frr/bgp.frr.tmpl
+++ b/data/templates/frr/bgp.frr.tmpl
@@ -52,6 +52,22 @@
{% if config.update_source is defined and config.update_source is not none %}
neighbor {{ neighbor }} update-source {{ config.update_source }}
{% endif %}
+{% if config.interface is defined and config.interface is not none %}
+{% if config.interface.peer_group is defined and config.interface.peer_group is not none %}
+ neighbor {{ neighbor }} interface peer-group {{ config.interface.peer_group }}
+{% endif %}
+{% if config.interface.remote_as is defined and config.interface.remote_as is not none %}
+ neighbor {{ neighbor }} interface remote-as {{ config.interface.remote_as }}
+{% endif %}
+{% if config.interface.v6only is defined and config.interface.v6only is not none %}
+{% if config.interface.v6only.peer_group is defined and config.interface.v6only.peer_group is not none %}
+ neighbor {{ neighbor }} interface v6only peer-group {{ config.interface.v6only.peer_group }}
+{% endif %}
+{% if config.interface.v6only.remote_as is defined and config.interface.v6only.remote_as is not none %}
+ neighbor {{ neighbor }} interface v6only remote-as {{ config.interface.v6only.remote_as }}
+{% endif %}
+{% endif %}
+{% endif %}
!
{% if config.address_family is defined and config.address_family is not none %}
{% for af in config.address_family %}
@@ -84,14 +100,16 @@
{% if config.address_family[af].distribute_list is defined and config.address_family[af].distribute_list is not none %}
{% if config.address_family[af].distribute_list.export is defined and config.address_family[af].distribute_list.export is not none %}
neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} out
-{% elif config.address_family[af].distribute_list.import is defined and config.address_family[af].distribute_list.import is not none %}
- neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} in
+{% endif %}
+{% if config.address_family[af].distribute_list.import is defined and config.address_family[af].distribute_list.import is not none %}
+ neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.import }} in
{% endif %}
{% endif %}
{% if config.address_family[af].filter_list is defined and config.address_family[af].filter_list is not none %}
{% if config.address_family[af].filter_list.export is defined and config.address_family[af].filter_list.export is not none %}
neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.export }} out
-{% elif config.address_family[af].filter_list.import is defined and config.address_family[af].filter_list.import is not none %}
+{% endif %}
+{% if config.address_family[af].filter_list.import is defined and config.address_family[af].filter_list.import is not none %}
neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.import }} in
{% endif %}
{% endif %}
@@ -108,14 +126,16 @@
{% if config.address_family[af].route_map is defined and config.address_family[af].route_map is not none %}
{% if config.address_family[af].route_map.export is defined and config.address_family[af].route_map.export is not none %}
neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.export }} out
-{% elif config.address_family[af].route_map.import is defined and config.address_family[af].route_map.import is not none %}
+{% endif %}
+{% if config.address_family[af].route_map.import is defined and config.address_family[af].route_map.import is not none %}
neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.import }} in
{% endif %}
{% endif %}
{% if config.address_family[af].prefix_list is defined and config.address_family[af].prefix_list is not none %}
{% if config.address_family[af].prefix_list.export is defined and config.address_family[af].prefix_list.export is not none %}
neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.export }} out
-{% elif config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %}
+{% endif %}
+{% if config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %}
neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.import }} in
{% endif %}
{% endif %}
@@ -198,11 +218,21 @@ router bgp {{ asn }}
{% endif %}
!
{% if neighbor is defined and neighbor is not none %}
-{% for n, config in neighbor.items() %}
-{{ bgp_neighbor(n, config) }}
+{% for peer, config in neighbor.items() %}
+{{ bgp_neighbor(peer, config) }}
{% endfor %}
{% endif %}
!
+{% if listen is defined %}
+{% if listen.limit is defined and listen.limit is not none %}
+ bgp listen limit {{ listen.limit }}
+{% endif %}
+{% for prefix, options in listen.range.items() %}
+{% if options.peer_group is defined and options.peer_group is not none %}
+ bgp listen range {{ prefix }} peer-group {{ options.peer_group }}
+{% endif %}
+{% endfor %}
+{% endif %}
{% if parameters is defined %}
{% if parameters.always_compare_med is defined %}
bgp always-compare-med
@@ -282,8 +312,8 @@ router bgp {{ asn }}
{% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %}
timers bgp {{ timers.keepalive }} {{ timers.holdtime }}
{% endif %}
- !
+!
{% if route_map is defined and route_map is not none %}
- ip protocol bgp route-map {{ route_map }}
+ip protocol bgp route-map {{ route_map }}
{% endif %}
- !
+!
diff --git a/data/templates/frr/ospf.frr.tmpl b/data/templates/frr/ospf.frr.tmpl
new file mode 100644
index 000000000..07699290c
--- /dev/null
+++ b/data/templates/frr/ospf.frr.tmpl
@@ -0,0 +1,131 @@
+!
+router ospf
+{% if access_list is defined and access_list is not none %}
+{% for acl, acl_config in access_list.items() %}
+{% for protocol in acl_config.export if acl_config.export is defined %}
+ distribute-list {{ acl }} out {{ protocol }}
+{% endfor %}
+{% endfor %}
+{% endif %}
+{% if area is defined and area is not none %}
+{% for area_id, area_config in area.items() %}
+{% if area_config.area_type is defined and area_config.area_type is not none %}
+{% for type, type_config in area_config.area_type.items() if type != 'normal' %}
+ area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }}
+{% if type_config.default_cost is defined and type_config.default_cost is not none %}
+ area {{ area_id }} default-cost {{ type_config.default_cost }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if area_config.authentication is defined and area_config.authentication is not none %}
+ area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication == 'md5' }}
+{% endif %}
+{% for network in area_config.network if area_config.network is defined %}
+ network {{ network }} area {{ area_id }}
+{% endfor %}
+{% if area_config.range is defined and area_config.range is not none %}
+{% for range, range_config in area_config.range.items() %}
+{% if range_config.cost is defined and range_config.cost is not none %}
+ area {{ area_id }} range {{ range }} cost {{ range_config.cost }}
+{% endif %}
+{% if range_config.not_advertise is defined %}
+ area {{ area_id }} range {{ range }} not-advertise
+{% endif %}
+{% if range_config.substitute is defined and range_config.substitute is not none %}
+ area {{ area_id }} range {{ range }} substitute {{ range_config.substitute }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if area_config.shortcut is defined and area_config.shortcut is not none %}
+ area {{ area_id }} shortcut {{ area_config.shortcut }}
+{% endif %}
+{% if area_config.virtual_link is defined and area_config.virtual_link is not none %}
+{% for link, link_config in area_config.virtual_link.items() %}
+{% if link_config.authentication is defined and link_config.authentication is not none %}
+{% if link_config.authentication.plaintext_password is defined and link_config.authentication.plaintext_password is not none %}
+ area {{ area_id }} virtual-link {{ link }} authentication-key {{ link_config.authentication.plaintext_password }}
+{% elif link_config.authentication.md5 is defined and link_config.authentication.md5.key_id is defined and link_config.authentication.md5.key_id is not none %}
+{% for key, key_config in link_config.authentication.md5.key_id.items() %}
+ area {{ area_id }} virtual-link {{ link }} message-digest-key {{ key }} md5 {{ key_config.md5_key }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if link_config.dead_interval is defined and link_config.dead_interval is not none %}
+{# The following values are default values #}
+ area {{ area_id }} virtual-link {{ link }} hello-interval {{ link_config.hello_interval }} retransmit-interval {{ link_config.retransmit_interval }} transmit-delay {{ link_config.retransmit_interval }} dead-interval {{ link_config.dead_interval }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if auto_cost is defined and auto_cost.reference_bandwidth is defined and auto_cost.reference_bandwidth is not none %}
+ auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }}
+{% endif %}
+{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %}
+ default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }}
+{% endif %}
+{% if default_metric is defined and default_metric is not none %}
+ default-metric {{ default_metric }}
+{% endif %}
+{% if distance is defined and distance is not none %}
+{% if distance.global is defined and distance.global is not none %}
+ distance {{ distance.global }}
+{% endif %}
+{% if distance.ospf is defined and distance.ospf is not none %}
+ distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is defined }}
+{% endif %}
+{% endif %}
+{% if log_adjacency_changes is defined %}
+ log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }}
+{% endif %}
+{% if max_metric is defined and max_metric.router_lsa is defined and max_metric.router_lsa is not none %}
+{% if max_metric.router_lsa.administrative is defined %}
+ max-metric router-lsa administrative
+{% endif %}
+{% if max_metric.router_lsa.on_shutdown is defined and max_metric.router_lsa.on_shutdown is not none %}
+ max-metric router-lsa on-shutdown {{ max_metric.router_lsa.on_shutdown }}
+{% endif %}
+{% if max_metric.router_lsa.on_startup is defined and max_metric.router_lsa.on_startup is not none %}
+ max-metric router-lsa on-startup {{ max_metric.router_lsa.on_startup }}
+{% endif %}
+{% endif %}
+{% if mpls_te is defined and mpls_te.enable is defined %}
+ mpls-te on
+ mpls-te router-address {{ mpls_te.router_address }}
+{% endif %}
+{% if neighbor is defined and neighbor is not none%}
+{% for address, address_config in neighbor.items() %}
+ neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is defined }}
+{% endfor %}
+{% endif %}
+{% if parameters is defined and parameters is not none %}
+{% if parameters.abr_type is defined and parameters.abr_type is not none %}
+ ospf abr-type {{ parameters.abr_type }}
+{% endif %}
+{% if parameters.router_id is defined and parameters.router_id is not none %}
+ ospf router-id {{ parameters.router_id }}
+{% endif %}
+{% endif %}
+{% for interface in passive_interface if passive_interface is defined %}
+ passive-interface {{ interface }}
+{% endfor %}
+{% for interface in passive_interface_exclude if passive_interface_exclude is defined %}
+ no passive-interface {{ interface }}
+{% endfor %}
+{% if redistribute is defined and redistribute is not none %}
+{% for protocol, options in redistribute.items() %}
+ redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }}
+{% endfor %}
+{% endif %}
+{% if refresh is defined and refresh.timers is defined and refresh.timers is not none %}
+ refresh timer {{ refresh.timers }}
+{% endif %}
+{% if timers is defined and timers.throttle is defined and timers.throttle.spf is defined and timers.throttle.spf is not none %}
+{# Timer values have default values #}
+ timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }}
+{% endif %}
+!
+{% if route_map is defined and route_map is not none %}
+ip protocol ospf route-map {{ route_map }}
+{% endif %}
+!
diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospfv3.frr.tmpl
new file mode 100644
index 000000000..c63ef80dc
--- /dev/null
+++ b/data/templates/frr/ospfv3.frr.tmpl
@@ -0,0 +1,41 @@
+!
+router ospf6
+{% if area is defined and area is not none %}
+{% for area_id, area_config in area.items() %}
+{% if area_config.interface is defined and area_config.interface is not none %}
+{% for interface in area_config.interface %}
+ interface {{ interface }} area {{ area_id }}
+{% endfor %}
+{% endif %}
+{% if area_config.range is defined and area_config.range is not none %}
+{% for prefix, prefix_config in area_config.range.items() %}
+ area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }}
+{% endfor %}
+{% endif %}
+{% if area_config.export_list is defined and area_config.export_list is not none %}
+ area {{ area_id }} export-list {{ area_config.export_list }}
+{% endif %}
+{% if area_config.import_list is defined and area_config.import_list is not none %}
+ area {{ area_id }} import-list {{ area_config.import_list }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if distance is defined and distance is not none %}
+{% if distance.global is defined and distance.global is not none %}
+ distance {{ distance.global }}
+{% endif %}
+{% if distance.ospfv3 is defined and distance.ospfv3 is not none %}
+ distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }}
+{% endif %}
+{% endif %}
+{% if parameters is defined and parameters is not none %}
+{% if parameters.router_id is defined and parameters.router_id is not none %}
+ ospf6 router-id {{ parameters.router_id }}
+{% endif %}
+{% endif %}
+{% if redistribute is defined and redistribute is not none %}
+{% for protocol, options in redistribute.items() %}
+ redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }}
+{% endfor %}
+{% endif %}
+!
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index b3b0c936a..79288e40f 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -281,6 +281,10 @@ compat-names
# Custom options added by user (not validated)
#
{% for option in openvpn_option %}
-{{ option }}
+{% for argument in option.split('--') %}
+{% if argument is defined and argument != '' %}
+--{{ argument }}
+{% endif %}
+{% endfor %}
{% endfor %}
{% endif %}
diff --git a/data/templates/proxy-ndp/ndppd.conf.tmpl b/data/templates/proxy-ndp/ndppd.conf.tmpl
new file mode 100644
index 000000000..0137d8135
--- /dev/null
+++ b/data/templates/proxy-ndp/ndppd.conf.tmpl
@@ -0,0 +1,44 @@
+########################################################
+#
+# autogenerated by nat66.py
+#
+# The configuration file must define one upstream
+# interface.
+#
+# For some services, such as nat66, because it runs
+# stateless, it needs to rely on NDP Proxy to respond
+# to NDP requests.
+#
+# When using nat66 source rules, NDP Proxy needs
+# to be enabled
+#
+########################################################
+
+{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %}
+{% if source is defined and source.rule is defined and source.rule is not none %}
+{% for rule, config in source.rule.items() if config.disable is not defined %}
+{% if config.outbound_interface is defined %}
+{% if config.outbound_interface not in global.ndppd_interfaces %}
+{% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %}
+{% endif %}
+{% if config.translation.prefix is defined %}
+{% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.prefix}] %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% for interface in global.ndppd_interfaces %}
+proxy {{ interface }} {
+ router yes
+ timeout 500
+ ttl 30000
+{% for map in global.ndppd_prefixs %}
+{% if map.interface == interface %}
+ rule {{ map.rule }} {
+ static
+ }
+{% endif %}
+{% endfor %}
+}
+{% endfor %}
diff --git a/data/templates/ssh/override.conf.tmpl b/data/templates/ssh/override.conf.tmpl
index 0abde6248..5f8f35e89 100644
--- a/data/templates/ssh/override.conf.tmpl
+++ b/data/templates/ssh/override.conf.tmpl
@@ -8,5 +8,6 @@ ConditionPathExists={{config_file}}
ExecStart=
ExecStart={{vrf_command}}/usr/sbin/sshd -f {{config_file}} -D $SSHD_OPTS
Restart=always
+RestartPreventExitStatus=
RestartSec=10
RuntimeDirectoryPreserve=yes