#!/bin/sh
###############################################################################
# #
# IPFire.org - A linux based firewall #
# Copyright (C) 2009 Michael Tremer & Christian Schmidt #
# #
# 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 3 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, see . #
# #
###############################################################################
HOME_DIR=${HOME_DIR-/lib/network}
CONFIG_DIR=/etc/network
HOOKS_DIR=${HOME_DIR}/hooks
LOG_DIR=/var/log/network
CONNECTIONS_FILE=/var/log/network/connections.db
CONFIG_ZONES=${CONFIG_DIR}/zones
CONFIG_PORTS=${CONFIG_DIR}/ports
CONFIG_HOOKS=${CONFIG_DIR}/hooks
CONFIG_PPP=${CONFIG_DIR}/ppp
CONFIG_UUIDS=${CONFIG_DIR}/uuids
# Create config directories
for dir in ${CONFIG_ZONES} ${CONFIG_PORTS} ${CONFIG_HOOKS} ${CONFIG_PPP} ${CONFIG_UUIDS}; do
[ -d "${dir}" ] && continue
mkdir -p "${dir}"
done
COMMON_DEVICE=port+
EXIT_OK=0
EXIT_ERROR=1
EXIT_CONF_ERROR=2
VALID_ZONES="blue green orange red grey"
[ -n "${DEBUG}" ] || DEBUG=
[ -n "${VERBOSE}" ] || VERBOSE=
function is_mac() {
[[ $1 =~ ^[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]$ ]]
}
function is_uuid() {
local string=${1}
# Length must be 37 characters
if [ ${#string} -eq 36 ] \
&& [ "${string:8:1}" = "-" ] \
&& [ "${string:13:1}" = "-" ] \
&& [ "${string:18:1}" = "-" ] \
&& [ "${string:23:1}" = "-" ]; then
return ${EXIT_OK}
fi
return ${EXIT_ERROR}
}
function get_device_by_mac() {
local mac=${1}
local device
for device in /sys/class/net/*; do
[ -d "${device}" ] || continue
if [ "$(cat $device/address)" = "$mac" ]; then
device=${device##*/}
# Skip virtual devices
if [ -e "/proc/net/vlan/$device" ]; then
continue
fi
# Skip zones
if zone_exists ${device}; then
continue
fi
echo ${device}
return 0
fi
done
return 1
}
function get_device_by_mac_and_vid() {
local mac=$1
local vid=$2
local i
local VID
local DEVICE
if [ -e "/proc/net/vlan/config" ]; then
grep '|' /proc/net/vlan/config | sed "s/|//g" | \
while read DEVICE VID PARENT; do
if [ "${vid}" = "${VID}" ] && [ "$(macify ${PARENT})" = "${mac}" ]; then
echo "${DEVICE}"
return 0
fi
done
fi
return 1
}
function get_device() {
if [ ${#@} -gt 1 ]; then
get_device_by_mac_and_vid $@
else
get_device_by_mac $@
fi
}
function get_mac_by_device() {
local device
device=$1
if [ -d "/sys/class/net/$device" ]; then
cat /sys/class/net/$device/address
return 0
fi
return 1
}
function get_mac() {
get_mac_by_device $@
}
function devicify() {
local device=${1}
local mac
[ -n "${device}" ] || return 1
if is_mac ${device}; then
mac=${device}
device=$(get_device_by_mac ${device})
fi
if [ -n "${device}" ]; then
echo ${device}
return 0
else
echo "devicify: Could not find device of $@" >&2
return 1
fi
}
function macify() {
local input=${1}
local mac
if is_mac ${input}; then
mac=${input}
else
mac=$(get_mac_by_device ${input})
fi
echo ${mac}
}
function device_exists() {
[ -n "${1}" ] || return ${EXIT_ERROR}
local device=$(devicify ${1})
[ -n "${device}" ] || return ${EXIT_ERROR}
ip link show ${device} &>/dev/null
}
function device_is_bonding() {
[ -d "/sys/class/net/${1}/bonding" ]
}
function device_is_bonded() {
local dev
for dev in /sys/class/net/*; do
# Skip crappy files
[ -d "${dev}" ] || continue
# Continue if not a bonding device
device_is_bonding "${dev##*/}" || continue
if grep -q "\<${1}\>" ${dev}/bonding/slaves; then
return 0
fi
done
return 1
}
function device_is_bridge() {
[ -d "/sys/class/net/${1}/bridge" ]
}
function device_is_up() {
ip link show $(devicify ${1}) 2>/dev/null | grep -qE "<.*UP.*>"
}
function device_is_vlan() {
if [ ! -e "/proc/net/vlan/config" ]; then
return 1
fi
grep -q "^${1}" /proc/net/vlan/config
}
function device_is_ppp() {
# XXX need something better
[ "${1:0:3}" = "ppp" ]
}
function device_is_loopback() {
local device=$(devicify ${1})
[ "${device}" = "lo" ]
}
function device_is_real() {
local device=${1}
device_is_loopback ${device} && \
return ${EXIT_ERROR}
device_is_bonding ${device} && \
return ${EXIT_ERROR}
device_is_bridge ${device} && \
return ${EXIT_ERROR}
device_is_ppp ${device} && \
return ${EXIT_ERROR}
device_is_vlan ${device} && \
return ${EXIT_ERROR}
return ${EXIT_OK}
}
function device_type() {
local device=$(devicify ${1})
if device_is_vlan ${device}; then
echo "vlan"
elif device_is_bonding ${device}; then
echo "bonding"
elif device_is_bridge ${device}; then
echo "bridge"
elif device_is_ppp ${device}; then
echo "ppp"
elif device_is_loopback ${device}; then
echo "loopback"
elif device_is_real ${device}; then
echo "real"
else
echo "unknown"
fi
}
function device_has_vlans() {
if [ ! -e "/proc/net/vlan/config" ]; then
return 1
fi
grep -q "${1}$" /proc/net/vlan/config
}
function device_has_carrier() {
local device=$(devicify ${1})
[ "$(/dev/null); do
[ -d "${zone}" ] && echo ${zone}
done
}
function zone_is_red() {
local zone=${1}
[ "${zone#red}" != "${zone}" ]
}
function _run_hooks() {
local action
local type
while [ $# -gt 0 ]; do
case "${1}" in
--type=*)
type=${1#--type=}
;;
*)
action="${1}"
shift; break
;;
esac
shift
done
local dir=${1}; shift
local failed
local hook
local hooks
if [ -z "${action}" ] || [ -z "${dir}" ]; then
echo "Not enough parameters given." >&2
return 1
fi
for hook in $(find ${dir}); do
# Skip dirs
[ -d "${hook}" ] && continue
(
. ${hook}
# Skip hooks that are not of the given type
if [ -n "${type}" ] && [ "$(hook_type ${HOOK})" != "${type}" ]; then
continue
fi
if [ -n "${HOOK}" ]; then
hook_run ${HOOK} --config=${hook} $@ ${action}
RET=$?
else
echo -e "${FAILURE}Unable to process ${hook}. Either"
echo -e "${FAILURE}the HOOK variable was not set,"
echo -e "${FAILURE}or the specified hook cannot be executed."
message=""
log_failure_msg
fi
exit ${RET}
) || failed=1
done
return ${failed}
}
function hooks_run_all() {
_run_hooks $@
}
function hooks_run_ports() {
_run_hooks --type="port" $@
}
function hooks_run_zones() {
_run_hooks --type="zone" $@
}
function hook_type() {
local hook=${1}
(
eval $(${HOOKS_DIR}/${hook} info)
echo "${HOOK_TYPE}"
)
}
function hook_list() {
local type=${1}
local hook
for hook in ${HOOKS_DIR}/*; do
[ -x "${hook}" ] || continue
hook=${hook##*/}
[[ ${hook} =~ helper$ ]] && continue
if [ -n "${type}" ] && [ "$(hook_type ${hook})" != "${type}" ]; then
continue
fi
echo "${hook}"
done
}
function config_get_hook() {
local config=${1}
if [ ! -e "${config}" ]; then
log_failure_msg "Config file \"${config}\" does not exist."
return ${EXIT_ERROR}
fi
( . ${config}; echo ${HOOK} )
}
function hook_run() {
local hook=${1}
shift
if ! hook_exists ${hook}; then
log_failure_msg "Hook ${hook} cannot be found or is not executeable."
return ${EXIT_ERROR}
fi
[ -n "${DEBUG}" ] && echo "Running hook: ${hook} $@"
DEBUG=${DEBUG} VERBOSE=${VERBOSE} ${HOOKS_DIR}/${hook} $@
return $?
}
function hook_run_multiple() {
local zone
local config
local hook
local hook_type2
local type
while [ "$#" -gt "0" ]; do
case "${1}" in
--type=*)
type=${1#--type=}
;;
*)
zone=${1}
break
;;
esac
shift
done
if ! zone_exists ${zone}; then
return ${EXIT_ERROR}
fi
for config in $(find ${CONFIG_ZONES}/${zone} 2>/dev/null); do
hook=$(config_get_hook ${config})
if [ -n "${type}" ]; then
hook_type2=$(hook_type ${hook})
if [ "${type}" != "${hook_type2}" ]; then
continue
fi
fi
hook_run ${hook} $@
done
}
function zone_run() {
local zone=${1}
shift
if ! zone_exists ${zone}; then
log_failure_msg "Zone ${zone} does not exist."
exit ${EXIT_ERROR}
fi
decho "Running zone: ${zone} $@"
DEBUG=${DEBUG} VERBOSE=${VERBOSE} ${HOME_DIR}/zone --zone=${zone} $@
}
function zone_valid_name() {
local zone=${1}
local match
local i
for i in ${VALID_ZONES}; do
match="${match}|${i}[0-9]{1,5}"
done
[[ ${zone} =~ ${match:1:${#match}} ]]
}
function isset() {
local key=${1}
[ -n "${!key}" ] && return
if [[ ${key} =~ port|zone ]]; then
echo "ERROR: The --${key} flag is not set." >&2
else
echo "ERROR: The \"${key}\" variable is not set properly." >&2
fi
return 1
}
# Test if device is attached to the given bridge
function zone_has_device_attached () {
local zone=${1}
local device=${2}
[ -d "/sys/class/net/${zone}/brif/${device}" ]
}
function device_has_ipv4() {
local device=${1}
local ip=${2}
ip addr show ${device} | grep inet | fgrep -q ${ip}
}
function check_config() {
local failed
local i
for i in $@; do
isset ${i} || failed=1
done
if [ "${failed}" = "1" ]; then
echo "Exiting..."
exit ${EXIT_ERROR}
fi
}
function mac_generate() {
local mac="00"
while [ "${#mac}" -lt 15 ]; do
mac="${mac}:$(cut -c 1-2 /proc/sys/kernel/random/uuid)"
done
echo "${mac}"
}
function connection() {
local action
local dns
local interface
local iplocal
local ipremote
local name
local status
local weight
local zone
while [ $# -gt 0 ]; do
case "${1}" in
--up)
action="up"
;;
--down)
action="down"
;;
--starting)
action="starting"
;;
--stopping)
action="stopping"
;;
--name=*)
name=${1#--name=}
;;
--zone=*)
zone=${1#--zone=}
zone_is_red ${zone} || return 0
;;
--interface=*)
interface=${1#--interface=}
;;
--iplocal=*)
iplocal=${1#--iplocal=}
;;
--ipremote=*)
ipremote=${1#--ipremote=}
;;
--weight=*)
weight=${1#--weight=}
;;
--dns=*)
dns=${1#--dns=}
;;
esac
shift
done
if [ ! -e "${CONNECTIONS_FILE}" ]; then
sqlite3 -batch ${CONNECTIONS_FILE} <