summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/conntrackd/conntrackd.conf.tmpl105
-rw-r--r--debian/control3
-rw-r--r--interface-definitions/service_conntrack-sync.xml.in156
-rw-r--r--op-mode-definitions/conntrack-sync.xml.in36
-rw-r--r--python/vyos/ifconfig/interface.py39
-rw-r--r--python/vyos/template.py14
-rwxr-xr-xsrc/conf_mode/conntrack_sync.py116
-rw-r--r--src/etc/systemd/system/conntrackd.service.d/override.conf8
-rwxr-xr-xsrc/migration-scripts/conntrack-sync/1-to-266
-rwxr-xr-xsrc/op_mode/conntrack_sync.py100
-rwxr-xr-xsrc/validators/ipv43
-rwxr-xr-xsrc/validators/ipv4-multicast3
-rwxr-xr-xsrc/validators/ipv6-multicast3
13 files changed, 642 insertions, 10 deletions
diff --git a/data/templates/conntrackd/conntrackd.conf.tmpl b/data/templates/conntrackd/conntrackd.conf.tmpl
new file mode 100644
index 000000000..6f9fa51b0
--- /dev/null
+++ b/data/templates/conntrackd/conntrackd.conf.tmpl
@@ -0,0 +1,105 @@
+# autogenerated by conntrack_sync.py
+
+# Synchronizer settings
+Sync {
+ Mode FTFW {
+ DisableExternalCache {{ 'on' if disable_external_cache is defined else 'off' }}
+ }
+{% for iface, iface_config in interface.items() %}
+{% if loop.first %}
+{% if iface_config.peer is defined and iface_config.peer is not none %}
+ UDP {
+{% if listen_address is defined and listen_address is not none %}
+ IPv4_address {{ listen_address }}
+{% endif %}
+ IPv4_Destination_Address {{ iface_config.peer }}
+ Port 3780
+{% else %}
+{% set ip_address = iface | get_ipv4 %}
+ Multicast {
+ IPv4_address {{ mcast_group }}
+ Group 3780
+ IPv4_interface {{ ip_address[0] | ip_from_cidr }}
+{% endif %}
+ Interface {{ iface }}
+{% endif %}
+{% endfor %}
+ SndSocketBuffer {{ sync_queue_size | int *1024 *1024 }}
+ RcvSocketBuffer {{ sync_queue_size | int *1024 *1024 }}
+ Checksum on
+ }
+{% if expect_sync is defined and expect_sync is not none %}
+ Options {
+{% if 'all' in expect_sync %}
+ ExpectationSync on
+{% else %}
+ ExpectationSync {
+{% for protocol in expect_sync %}
+ {{ protocol }}
+{% endfor %}
+{% endif %}
+ }
+ }
+{% endif %}
+}
+Helper {
+ Type rpc inet tcp {
+ QueueNum 3
+ Policy rpc {
+ ExpectMax 1
+ ExpectTimeout 300
+ }
+ }
+ Type rpc inet udp {
+ QueueNum 4
+ Policy rpc {
+ ExpectMax 1
+ ExpectTimeout 300
+ }
+ }
+ Type tns inet tcp {
+ QueueNum 5
+ Policy tns {
+ ExpectMax 1
+ ExpectTimeout 300
+ }
+ }
+}
+
+# General settings
+General {
+ HashSize {{ hash_size }}
+ HashLimit {{ table_size | int *2 }}
+ LogFile off
+ Syslog on
+ LockFile /var/lock/conntrack.lock
+ UNIX {
+ Path /run/conntrackd/conntrackd.ctl
+ }
+ NetlinkBufferSize {{ 2 *1024 *1024 }}
+ NetlinkBufferSizeMaxGrowth {{ event_listen_queue_size | int *1024 *1024 }}
+ NetlinkOverrunResync off
+ NetlinkEventsReliable on
+{% if ignore_address is defined or accept_protocol is defined %}
+ Filter From Userspace {
+{% if ignore_address is defined and ignore_address is not none %}
+ Address Ignore {
+{% for address in ignore_address if address | is_ipv4 %}
+ IPv4_address {{ address }}
+{% endfor %}
+{% for address in ignore_address if address | is_ipv6 %}
+ IPv6_address {{ address }}
+{% endfor %}
+ }
+{% endif %}
+{% if accept_protocol is defined and accept_protocol is not none %}
+ Protocol Accept {
+{% for protocol in accept_protocol %}
+ {{ protocol | upper }}
+{% endfor %}
+ }
+{% endif %}
+ }
+{% endif %}
+}
+
diff --git a/debian/control b/debian/control
index 851152d95..de030310e 100644
--- a/debian/control
+++ b/debian/control
@@ -35,6 +35,7 @@ Depends:
bmon,
bsdmainutils,
conntrack,
+ conntrackd,
conserver-client,
conserver-server,
console-data,
@@ -67,6 +68,8 @@ Depends:
lcdproc,
libatomic1,
libndp-tools,
+ libnetfilter-conntrack3,
+ libnfnetlink0,
libpam-radius-auth (>= 1.5.0),
libvyosconfig0,
lldpd,
diff --git a/interface-definitions/service_conntrack-sync.xml.in b/interface-definitions/service_conntrack-sync.xml.in
new file mode 100644
index 000000000..088cd78f6
--- /dev/null
+++ b/interface-definitions/service_conntrack-sync.xml.in
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="conntrack-sync" owner="${vyos_conf_scripts_dir}/conntrack_sync.py">
+ <properties>
+ <help>Connection tracking synchronization</help>
+ <priority>995</priority>
+ </properties>
+ <children>
+ <leafNode name="accept-protocol">
+ <properties>
+ <help>Protocols for which local conntrack entries will be synced</help>
+ <completionHelp>
+ <list>tcp udp icmp sctp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>tcp</format>
+ <description>Sync Transmission Control Protocol conntrack entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>udp</format>
+ <description>Sync User Datagram Protocol conntrack entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>icmp</format>
+ <description>Sync Internet Control Message Protocol conntrack entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sctp</format>
+ <description>Sync Stream Control Transmission Protocol conntrack entries</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(tcp|udp|icmp|sctp)$</regex>
+ </constraint>
+ <constraintErrorMessage>Allowed protocols: tcp udp icmp or sctp</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-external-cache">
+ <properties>
+ <help>Directly injects the flow-states into the in-kernel Connection Tracking System of the backup firewall.</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="event-listen-queue-size">
+ <properties>
+ <help>Queue size for local conntrack events</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Queue size in MB</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>8</defaultValue>
+ </leafNode>
+ <leafNode name="expect-sync">
+ <properties>
+ <help>Protocol for which expect entries need to be synchronized</help>
+ <completionHelp>
+ <list>all ftp sip h323 nfs sqlnet</list>
+ </completionHelp>
+ <constraint>
+ <regex>^(all|ftp|sip|h323|nfs|sqlnet)$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid protocol</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="failover-mechanism">
+ <properties>
+ <help>Failover mechanism to use for conntrack-sync</help>
+ </properties>
+ <children>
+ <node name="vrrp">
+ <properties>
+ <help>VRRP as failover-mechanism to use for conntrack-sync</help>
+ </properties>
+ <children>
+ <leafNode name="sync-group">
+ <properties>
+ <help>VRRP sync group</help>
+ <completionHelp>
+ <path>high-availability vrrp sync-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="ignore-address">
+ <properties>
+ <help>IP addresses for which local conntrack entries will not be synced</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to ignore</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix to ignore</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address to ignore</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 prefix to ignore</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4"/>
+ <validator name="ipv6"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <tagNode name="interface">
+ <properties>
+ <help>Interface to use for syncing conntrack entries</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --bridgeable</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="peer">
+ <properties>
+ <help>IP address of the peer to send the UDP conntrack info too. This disable multicast.</help>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ #include <include/listen-address-ipv4.xml.i>
+ <leafNode name="mcast-group">
+ <properties>
+ <help>Multicast group to use for syncing conntrack entries</help>
+ <constraint>
+ <validator name="ipv4-multicast"/>
+ </constraint>
+ </properties>
+ <defaultValue>225.0.0.50</defaultValue>
+ </leafNode>
+ <leafNode name="sync-queue-size">
+ <properties>
+ <help>Queue size for syncing conntrack entries</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Queue size in MB</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>1</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/conntrack-sync.xml.in b/op-mode-definitions/conntrack-sync.xml.in
new file mode 100644
index 000000000..cd0ec5cc8
--- /dev/null
+++ b/op-mode-definitions/conntrack-sync.xml.in
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="reset">
+ <children>
+ <node name="conntrack-sync">
+ <properties>
+ <help>Reset connection syncing parameters</help>
+ </properties>
+ <children>
+ <leafNode name="external-cache">
+ <properties>
+ <help>Reset external cache and request resync with other systems</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/conntrack_sync.py --reset-cache-external</command>
+ </leafNode>
+ <leafNode name="internal-cache">
+ <properties>
+ <help>Reset internal cache and request resync with other systems</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/conntrack_sync.py --reset-cache-internal</command>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="restart">
+ <children>
+ <leafNode name="conntrack-sync">
+ <properties>
+ <help>Restart connection tracking synchronization service</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/conntrack_sync.py --restart</command>
+ </leafNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index ff05cab0e..a08872509 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -743,28 +743,37 @@ class Interface(Control):
"""
self.set_interface('proxy_arp_pvlan', enable)
- def get_addr(self):
+ def get_addr_v4(self):
"""
- Retrieve assigned IPv4 and IPv6 addresses from given interface.
+ Retrieve assigned IPv4 addresses from given interface.
This is done using the netifaces and ipaddress python modules.
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').get_addrs()
- ['172.16.33.30/24', 'fe80::20c:29ff:fe11:a174/64']
+ >>> Interface('eth0').get_addr_v4()
+ ['172.16.33.30/24']
"""
-
ipv4 = []
- ipv6 = []
-
- if AF_INET in ifaddresses(self.config['ifname']).keys():
+ if AF_INET in ifaddresses(self.config['ifname']):
for v4_addr in ifaddresses(self.config['ifname'])[AF_INET]:
# we need to manually assemble a list of IPv4 address/prefix
prefix = '/' + \
str(IPv4Network('0.0.0.0/' + v4_addr['netmask']).prefixlen)
ipv4.append(v4_addr['addr'] + prefix)
+ return ipv4
- if AF_INET6 in ifaddresses(self.config['ifname']).keys():
+ def get_addr_v6(self):
+ """
+ Retrieve assigned IPv6 addresses from given interface.
+ This is done using the netifaces and ipaddress python modules.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_addr_v6()
+ ['fe80::20c:29ff:fe11:a174/64']
+ """
+ ipv6 = []
+ if AF_INET6 in ifaddresses(self.config['ifname']):
for v6_addr in ifaddresses(self.config['ifname'])[AF_INET6]:
# Note that currently expanded netmasks are not supported. That means
# 2001:db00::0/24 is a valid argument while 2001:db00::0/ffff:ff00:: not.
@@ -777,8 +786,18 @@ class Interface(Control):
# addresses
v6_addr['addr'] = v6_addr['addr'].split('%')[0]
ipv6.append(v6_addr['addr'] + prefix)
+ return ipv6
+
+ def get_addr(self):
+ """
+ Retrieve assigned IPv4 and IPv6 addresses from given interface.
- return ipv4 + ipv6
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_addr()
+ ['172.16.33.30/24', 'fe80::20c:29ff:fe11:a174/64']
+ """
+ return self.get_addr_v4() + self.get_addr_v6()
def add_addr(self, addr):
"""
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 3fbb33acb..e1986b1e4 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -121,6 +121,14 @@ def render(
##################################
# Custom template filters follow #
##################################
+@register_filter('ip_from_cidr')
+def ip_from_cidr(prefix):
+ """ Take an IPv4/IPv6 CIDR host and strip cidr mask.
+ Example:
+ 192.0.2.1/24 -> 192.0.2.1, 2001:db8::1/64 -> 2001:db8::1
+ """
+ from ipaddress import ip_interface
+ return str(ip_interface(prefix).ip)
@register_filter('address_from_cidr')
def address_from_cidr(prefix):
@@ -361,3 +369,9 @@ def natural_sort(iterable):
return [convert(c) for c in re.split('([0-9]+)', str(key))]
return sorted(iterable, key=alphanum_key)
+
+@register_filter('get_ipv4')
+def get_ipv4(interface):
+ """ Get interface IPv4 addresses"""
+ from vyos.ifconfig import Interface
+ return Interface(interface).get_addr_v4()
diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py
new file mode 100755
index 000000000..e0a414745
--- /dev/null
+++ b/src/conf_mode/conntrack_sync.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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.configverify import verify_interface_exists
+from vyos.util import call
+from vyos.util import dict_search
+from vyos.util import read_file
+from vyos.template import render
+from vyos.template import get_ipv4
+from vyos.validate import is_addr_assigned
+from vyos.xml import defaults
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+config_file = '/run/conntrackd/conntrackd.conf'
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['service', 'conntrack-sync']
+ if not conf.exists(base):
+ return None
+
+ conntrack = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ conntrack = dict_merge(default_values, conntrack)
+
+ conntrack['hash_size'] = read_file('/sys/module/nf_conntrack/parameters/hashsize')
+ conntrack['table_size'] = read_file('/proc/sys/net/netfilter/nf_conntrack_max')
+
+ conntrack['vrrp'] = conf.get_config_dict(['high-availability', 'vrrp', 'sync-group'],
+ get_first_key=True)
+
+ return conntrack
+
+def verify(conntrack):
+ if not conntrack:
+ return None
+
+ if 'interface' not in conntrack:
+ raise ConfigError('Interface not defined!')
+
+ for interface in conntrack['interface']:
+ verify_interface_exists(interface)
+ # Interface must not only exist, it must also carry an IP address
+ if len(get_ipv4(interface)) < 1:
+ raise ConfigError(f'Interface {interface} requires an IP address!')
+
+ if 'expect_sync' in conntrack:
+ if len(conntrack['expect_sync']) > 1 and 'all' in conntrack['expect_sync']:
+ raise ConfigError('Cannot configure all with other protocol')
+
+ if 'listen_address' in conntrack:
+ address = conntrack['listen_address']
+ if not is_addr_assigned(address):
+ raise ConfigError(f'Specified listen-address {address} not assigned to any interface!')
+
+ vrrp_group = dict_search('failover_mechanism.vrrp.sync_group', conntrack)
+ if vrrp_group == None:
+ raise ConfigError(f'No VRRP sync-group defined!')
+ if vrrp_group not in conntrack['vrrp']:
+ raise ConfigError(f'VRRP sync-group {vrrp_group} not configured!')
+
+ return None
+
+def generate(conntrack):
+ if not conntrack:
+ if os.path.isfile(config_file):
+ os.unlink(config_file)
+ return None
+
+ render(config_file, 'conntrackd/conntrackd.conf.tmpl', conntrack)
+
+ return None
+
+def apply(conntrack):
+ if not conntrack:
+ call('systemctl stop conntrackd.service')
+ return None
+
+ call('systemctl restart conntrackd.service')
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/etc/systemd/system/conntrackd.service.d/override.conf b/src/etc/systemd/system/conntrackd.service.d/override.conf
new file mode 100644
index 000000000..eb611e0d9
--- /dev/null
+++ b/src/etc/systemd/system/conntrackd.service.d/override.conf
@@ -0,0 +1,8 @@
+[Unit]
+After=
+After=vyos-router.service
+ConditionPathExists=/run/conntrackd/conntrackd.conf
+
+[Service]
+ExecStart=
+ExecStart=/usr/sbin/conntrackd -C /run/conntrackd/conntrackd.conf
diff --git a/src/migration-scripts/conntrack-sync/1-to-2 b/src/migration-scripts/conntrack-sync/1-to-2
new file mode 100755
index 000000000..ebbd8c35a
--- /dev/null
+++ b/src/migration-scripts/conntrack-sync/1-to-2
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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/>.
+
+# VyOS 1.2 crux allowed configuring a lower or upper case loglevel. This
+# is no longer supported as the input data is validated and will lead to
+# an error. If user specifies an upper case logleve, make it lowercase
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['service', 'conntrack-sync']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+else:
+ base_accept_proto = base + ['accept-protocol']
+ if config.exists(base_accept_proto):
+ tmp = config.return_value(base_accept_proto)
+ config.delete(base_accept_proto)
+ for protocol in tmp.split(','):
+ config.set(base_accept_proto, value=protocol, replace=False)
+
+ base_ignore_addr = base + ['ignore-address', 'ipv4']
+ if config.exists(base_ignore_addr):
+ tmp = config.return_values(base_ignore_addr)
+ config.delete(base_ignore_addr)
+ for address in tmp:
+ config.set(base + ['ignore-address'], value=address, replace=False)
+
+ # we no longer support cluster mode
+ base_cluster = base + ['failover-mechanism', 'cluster']
+ if config.exists(base_cluster):
+ config.delete(base_cluster)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py
new file mode 100755
index 000000000..48017c58a
--- /dev/null
+++ b/src/op_mode/conntrack_sync.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 syslog
+
+from argparse import ArgumentParser
+from vyos.configquery import CliShellApiConfigQuery
+from vyos.util import call
+from vyos.util import run
+
+conntrackd_bin = '/usr/sbin/conntrackd'
+conntrackd_config = '/run/conntrackd/conntrackd.conf'
+
+parser = ArgumentParser(description='Conntrack Sync')
+parser.add_argument('--restart', help='Restart connection tracking synchronization service', action='store_true')
+parser.add_argument('--reset-cache-internal', help='Reset internal cache', action='store_true')
+parser.add_argument('--reset-cache-external', help='Reset external cache', action='store_true')
+
+def is_configured():
+ """ Check if conntrack-sync service is configured """
+ config = CliShellApiConfigQuery()
+ if not config.exists(['service', 'conntrack-sync']):
+ print('Service conntrackd-sync not configured!')
+ exit(1)
+
+def send_bulk_update():
+ """ send bulk update of internal-cache to other systems """
+ tmp = run(f'{conntrackd_bin} -c {conntrackd_config} -B')
+ if tmp > 0:
+ print('ERROR: failed to send bulk update to other conntrack-sync systems')
+
+def request_sync():
+ """ request resynchronization with other systems """
+ tmp = run(f'{conntrackd_bin} -c {conntrackd_config} -n')
+ if tmp > 0:
+ print('ERROR: failed to request resynchronization of external cache')
+
+def flush_cache(direction):
+ """ flush conntrackd cache (internal or external) """
+ if direction not in ['internal', 'external']:
+ raise ValueError()
+ tmp = run(f'{conntrackd_bin} -c {conntrackd_config} -f {direction}')
+ if tmp > 0:
+ print('ERROR: failed to clear {direction} cache')
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+ syslog.openlog(ident='conntrack-tools', logoption=syslog.LOG_PID,
+ facility=syslog.LOG_INFO)
+
+ if args.restart:
+ is_configured()
+
+ syslog.syslog('Restarting conntrack sync service...')
+ call('systemctl restart conntrackd.service')
+ # request resynchronization with other systems
+ request_sync()
+ # send bulk update of internal-cache to other systems
+ send_bulk_update()
+
+ elif args.reset_cache_external:
+ is_configured()
+ syslog.syslog('Resetting external cache of conntrack sync service...')
+
+ # flush the external cache
+ flush_cache('external')
+ # request resynchronization with other systems
+ request_sync()
+
+ elif args.reset_cache_internal:
+ is_configured()
+ syslog.syslog('Resetting internal cache of conntrack sync service...')
+ # flush the internal cache
+ flush_cache('internal')
+
+ # request resynchronization of internal cache with kernel conntrack table
+ tmp = run(f'{conntrackd_bin} -c {conntrackd_config} -R')
+ if tmp > 0:
+ print('ERROR: failed to resynchronize internal cache with kernel conntrack table')
+
+ # send bulk update of internal-cache to other systems
+ send_bulk_update()
+
+ else:
+ parser.print_help()
+ exit(1)
diff --git a/src/validators/ipv4 b/src/validators/ipv4
new file mode 100755
index 000000000..53face090
--- /dev/null
+++ b/src/validators/ipv4
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ipaddrcheck --is-ipv4 $1
diff --git a/src/validators/ipv4-multicast b/src/validators/ipv4-multicast
new file mode 100755
index 000000000..e5cbc9532
--- /dev/null
+++ b/src/validators/ipv4-multicast
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ipaddrcheck --is-ipv4-multicast $1
diff --git a/src/validators/ipv6-multicast b/src/validators/ipv6-multicast
new file mode 100755
index 000000000..66cd90c9c
--- /dev/null
+++ b/src/validators/ipv6-multicast
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ipaddrcheck --is-ipv6-multicast $1