diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/flow_accounting_conf.py | 34 | ||||
| -rwxr-xr-x | src/system/uacctd_stop.py | 67 | 
2 files changed, 100 insertions, 1 deletions
| diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index 71acd69fa..f29fc94fb 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -28,6 +28,7 @@ from vyos.ifconfig import Section  from vyos.template import render  from vyos.utils.process import call  from vyos.utils.process import cmd +from vyos.utils.process import run  from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag @@ -116,6 +117,30 @@ def _nftables_config(configured_ifaces, direction, length=None):          cmd(command, raising=ConfigError) +def _nftables_trigger_setup(operation: str) -> None: +    """Add a dummy rule to unlock the main pmacct loop with a packet-trigger + +    Args: +        operation (str): 'add' or 'delete' a trigger +    """ +    # check if a chain exists +    table_exists = False +    if run('nft -snj list table ip pmacct') == 0: +        table_exists = True + +    if operation == 'delete' and table_exists: +        nft_cmd: str = 'nft delete table ip pmacct' +        cmd(nft_cmd, raising=ConfigError) +    if operation == 'add' and not table_exists: +        nft_cmds: list[str] = [ +            'nft add table ip pmacct', +            'nft add chain ip pmacct pmacct_out { type filter hook output priority raw - 50 \\; policy accept \\; }', +            'nft add rule ip pmacct pmacct_out oif lo ip daddr 127.0.254.0 counter log group 2 snaplen 1 queue-threshold 0 comment NFLOG_TRIGGER' +        ] +        for nft_cmd in nft_cmds: +            cmd(nft_cmd, raising=ConfigError) + +  def get_config(config=None):      if config:          conf = config @@ -252,7 +277,6 @@ def generate(flow_config):      call('systemctl daemon-reload')  def apply(flow_config): -    action = 'restart'      # Check if flow-accounting was removed and define command      if not flow_config:          _nftables_config([], 'ingress') @@ -262,6 +286,10 @@ def apply(flow_config):          call(f'systemctl stop {systemd_service}')          if os.path.exists(uacctd_conf_path):              os.unlink(uacctd_conf_path) + +        # must be done after systemctl +        _nftables_trigger_setup('delete') +          return      # Start/reload flow-accounting daemon @@ -277,6 +305,10 @@ def apply(flow_config):          else:              _nftables_config([], 'egress') +    # add a trigger for signal processing +    _nftables_trigger_setup('add') + +  if __name__ == '__main__':      try:          config = get_config() diff --git a/src/system/uacctd_stop.py b/src/system/uacctd_stop.py new file mode 100755 index 000000000..7fbac0566 --- /dev/null +++ b/src/system/uacctd_stop.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# Control pmacct daemons in a tricky way. +# Pmacct has signal processing in a main loop, together with packet +# processing. Because of this, while it is waiting for packets, it cannot +# handle the control signal. We need to start the systemctl command and then +# send some packets to pmacct to wake it up + +from argparse import ArgumentParser +from socket import socket +from sys import exit +from time import sleep + +from psutil import Process + + +def stop_process(pid: int, timeout: int) -> None: +    """Send a signal to uacctd +    and then send packets to special address predefined in a firewall +    to unlock main loop in uacctd and finish the process properly + +    Args: +        pid (int): uacctd PID +        timeout (int): seconds to wait for a process end +    """ +    # find a process +    uacctd = Process(pid) +    uacctd.terminate() + +    # create a socket +    trigger = socket() + +    first_cycle: bool = True +    while uacctd.is_running() and timeout: +        trigger.sendto(b'WAKEUP', ('127.0.254.0', 0)) +        # do not sleep during first attempt +        if not first_cycle: +            sleep(1) +            timeout -= 1 +        first_cycle = False + + +if __name__ == '__main__': +    parser = ArgumentParser() +    parser.add_argument('process_id', +                        type=int, +                        help='PID file of uacctd core process') +    parser.add_argument('timeout', +                        type=int, +                        help='time to wait for process end') +    args = parser.parse_args() +    stop_process(args.process_id, args.timeout) +    exit() | 
