lxcfw-dhcp-handler 4.71 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
#!/usr/bin/bash

###################################################################################################################
#
# This file is part of lxcfw.
#
# Copyright © 2017-2018 Stefan Göbel < lxcfw ʇɐ subtype ˙ de >.
#
# lxcfw 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 3 of the License, or (at your option) any
# later version.
#
# lxcfw 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 lxcfw. If not, see
# <http://www.gnu.org/licenses/>.
#
###################################################################################################################
#
# Monitor the pipe /run/lxcfw/dhcp/fifo for lease messages from the dnsmasq DHCP server and add/remove/modify
# firewall rules accordingly. This script should be started by its systemd service (lxcfw-dhcp-handler.service).
#
# The lines read from the FIFO must be the arguments dnsmasq passes to its --dhcp-script:
#
#     <add|old|del|arp-add|arp-del|init|tftp> <MAC address> <IP address> <hostname (if known)>
#
# The script must be run as root (or someone else who can modify the netfilter rule set, and read from the pipe).

set -o errexit
set -o nounset

# lock [<directory>] ==============================================================================================
#
# Try to get a lock, using a lock file in the specified directory (defaults to /run/lock/lxcfw-dnsmasq-handler/ if
# unset or empty). A trap will be installed to remove the lock directory on exit. Return error on failure, else
# success.
#
lock() {

   local _dir="${1:-/run/lock/lxcfw-dhcp-handler}"

   mkdir -p -m 0700 "$_dir"                                                      # Directory for the lock file.
   >"$_dir/lock"                                                                 # Create the lock file.
   exec 666<"$_dir/lock"                                                         # Use fd 666 to read from it.

   if ! flock -x -n 666 ; then                                                   # Try to flock.
      printf 'Error: Already running.\n' >&2
      return 1
   fi

   trap "rm -rf $_dir" EXIT                                                      # Remove the directory on exit.

}

# process <start|stop> <MAC address> <IP address> <hostname> ======================================================
#
# If all required parameters are present, call lxcfw-hook with the appropriate arguments to set up the firewall
# rules for the container. Arguments have to pass a pretty basic check for validity, this is done to prevent crap
# being passed to the hook script, checks are not thorough, though.
#
process() {

   declare -g snat_minip
   declare -g snat_maxip
   declare -g snat_nmask
   declare -g if_ext

   local _act="$1"
   local _mac="$2"
   local _ipa="$3"
   local _hst="$4"
   local _arg=()

   if [[ -z "$_mac" || -z "$_ipa" || -z "$_hst" ]] ; then
      return 0
   fi

   if [[ ! "$_mac" =~ ^([[:alnum:]]{2}:){5}[[:alnum:]]{2}$   ]] ; then return 1 ; fi
   if [[ ! "$_ipa" =~ ^[0-9a-f][0-9a-f:.]+[0-9a-f]$          ]] ; then return 2 ; fi
   if [[ ! "$_hst" =~ ^[[:alnum:]][[:alnum:].-]+[[:alnum:]]$ ]] ; then return 3 ; fi

   lxcfw-hook "$_hst" "$_act" "@ip0=$_ipa" "@hw0=$_mac"

}

# main ============================================================================================================
#
# Watch the FIFO for incoming data, indefinitely. Takes no parameters, all paths are hard coded for now, there is
# nothing to configure.
#
dhcp_handler_main() {

   local _conf='/etc/lxcfw/lxcfw.conf'
   local _fifo='/run/lxcfw/dhcp/fifo'
   local _func='/usr/local/share/lxcfw/functions'
   local _line=''

   lock || exit 1

   source "$_conf"
   source "$_func"

   while true ; do

      if read _line <"$_fifo" ; then

         local _action='' _hwaddr='' _ipaddr='' _host='' _remains=''
         read _action _hwaddr _ipaddr _host _remains <<<"$_line"

         case "$_action" in
            add|old) process 'start' "$_hwaddr" "$_ipaddr" "$_host" ;;
                del) process 'stop'  "$_hwaddr" "$_ipaddr" "$_host" ;;
         esac

      else

         printf 'Warning: Error reading from pipe.\n' >&2

      fi

   done

}

# Run the main function: ==========================================================================================
#
if [[ "$0" == "$BASH_SOURCE" ]] ; then
   dhcp_handler_main "$@"
fi

# :indentSize=3:tabSize=3:noTabs=true:mode=shellscript:maxLineLen=115: ############################################