#!/bin/bash ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2007-2022 IPFire Team # # # # 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 . # # # ############################################################################### eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings) eval $(/usr/local/bin/readhash /var/ipfire/dns/settings) ip2bin() { local address="${1}" local IFS='.' local octet local n=0 for octet in ${address}; do # Shift n (( n <<= 8 )) # Apply the octet (( n |= octet )) done echo "${n}" } bin2ip() { local n="${1}" local IFS='.' local address=() for i in {3..0}; do address+=( $(( n >> (8 * i) & 0xff )) ) done echo "${address[*]}" } network_get_intfs() { local zone="${1}" case "${zone^^}" in RED) # For PPPoE, the RED interface is called ppp0 (unless we use QMI) if [ "${RED_TYPE}" = "PPPOE" ] && [ "${RED_DRIVER}" != "qmi_wwan" ]; then echo "ppp0" return 0 # Otherwise we return RED_DEV elif [ -n "${RED_DEV}" ]; then echo "${RED_DEV}" return 0 fi ;; GREEN) if [ -n "${GREEN_DEV}" ]; then echo "${GREEN_DEV}" return 0 fi ;; ORANGE) if [ -n "${ORANGE_DEV}" ]; then echo "${ORANGE_DEV}" return 0 fi ;; BLUE) if [ -n "${BLUE_DEV}" ]; then echo "${BLUE_DEV}" return 0 fi ;; IPSEC) local VARS=( id status x1 x2 type x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 interface_mode rest ) while IFS="," read -r "${VARS[@]}"; do # Check if the connection is enabled [ "${status}" = "on" ] || continue # Check if this a net-to-net connection [ "${type}" = "net" ] || continue # Determine the interface name case "${interface_mode}" in gre|vti) echo "${interface_mode}${id}" ;; esac done < /var/ipfire/vpn/config return 0 ;; WIREGUARD|WG) echo "wg+" return 0 ;; OPENVPN|OVPN) # OpenVPN is using all tun devices echo "tun+" return 0 ;; esac # Not found return 1 } network_get_address() { local network="${1}" # Return everything before the slash echo "${network%%/*}" } network_get_prefix() { local network="${1}" # Consider everything after the / the prefix local prefix="${network##*/}" # If the prefix is valid, return it if network_prefix_is_valid "${prefix}"; then echo "${prefix}" # Otherwise it might be a subnet mask else network_netmask_to_prefix "${prefix}" fi } network_get_netmask() { local network="${1}" # Consider everything after the / the netmask local netmask="${network##*/}" # If we have a prefix, we need to convert if network_prefix_is_valid "${netmask}"; then network_prefix_to_netmask "${netmask}" # Otherwise return what we got else echo "${netmask}" fi } network_prefix_is_valid() { local prefix="${1}" # The prefix must be numbers only if ! [[ "${prefix}" =~ ^[0-9]+$ ]]; then return 1 fi # Must be a number between 0 and 32 (inclusive) [ "${prefix}" -ge 0 -a "${prefix}" -le 32 ] } network_prefix_to_netmask() { local prefix="${1}" # Set n with all bits set local n=0xffffffff # Shift (( n <<= (32 - prefix) )) # Convert back bin2ip "${n}" } network_netmask_to_prefix() { local netmask="${1}" local prefix=0 # Convert to binary local n="$(ip2bin "${netmask}")" while [ "${n}" -gt 0 ]; do # If the highest bit is not set, we are done [ "$(( n & (1 << 31) ))" -eq 0 ] && break # Increment prefix & shift n (( prefix++ )) (( n <<= 1 )) done echo "${prefix}" } network_address_in_network() { local address="${1}" local network="${2}" # Split the network into its address & mask local netaddr="$(network_get_address "${network}")" local netmask="$(network_get_netmask "${network}")" # Abort if we could not parse the network if [ -z "${netaddr}" -o -z "${netmask}" ]; then return 1 fi # Convert everything to binary address="$(ip2bin "${address}")" netaddr="$(ip2bin "${netaddr}")" netmask="$(ip2bin "${netmask}")" # Ensure the network address is the first address (( netaddr &= netmask )) # Compute broadcast local broadcast=$(( netaddr | (~netmask & 0xffffffff) )) # Return true if address is in the network [ "${address}" -ge "${netaddr}" -a "${address}" -le "${broadcast}" ] } # Takes a network and list of IP addresses and will return the first IP address # that is in the given network. first_address_in_network() { local network="${1}" shift local addr for addr in $@; do if network_address_in_network "${addr}" "${network}"; then echo "${addr}" return 0 fi done return 1 } # Returns the first of IPFire's own IP addresses that is in any of the given networks ipfire_address_in_networks() { local addresses=() local var for var in GREEN_ADDRESS BLUE_ADDRESS ORANGE_ADDRESS; do if [ -n "${!var}" ]; then addresses+=( "${!var}" ) fi done local network for network in $@; do # Find and end after the first match if first_address_in_network "${network}" "${addresses[@]}"; then return 0 fi done # Nothing found return 1 } dhcpcd_get_pid() { # This function returns the pid of a dhcpcd by a given # network device, if a pidfile exists. local device="$1" local pidfile="/var/run/dhcpcd/${device}.pid" # Check if a pid file exists. if [ -f "${pidfile}" ] ; then # Get the pid from the file. local pid="$(<"${pidfile}")" echo "${pid}" fi } dhcpcd_is_running() { # This functions checks if a dhcpcd is running by a given pid. local pid="$1" # Check if a dhcpcd is running. if [ -n "${pid}" -a -d "/proc/${pid}" ]; then # Return "0" (True) if a dhcpcd is running. return 0 fi # Return 1 (False) no dhcpcd is running. return 1 } dhcpcd_start() { # This function will start a dhcpcd on a speciefied device. local device="$1" shift local dhcp_start=() boot_mesg -n "Starting dhcpcd on the ${device} interface..." # Check if a dhcpcd is already running. local pid="$(dhcpcd_get_pid "${device}")" if dhcpcd_is_running "${pid}"; then boot_mesg "dhcpcd already running!" ${WARNING} echo_warning exit 2 fi # Check if a DHCP hostname has been set. if [ -n "${RED_DHCP_HOSTNAME}" ]; then dhcp_start+=( "-h" "${RED_DHCP_HOSTNAME}" ) fi # Tell dhcpcd to use the configured MTU if [ -n "${RED_DHCP_FORCE_MTU}" ]; then dhcp_start+=( "--static" "mtu=${RED_DHCP_FORCE_MTU}" ) fi # Append any further command line options dhcp_start+=( $@ ) # Start dhcpcd. /sbin/dhcpcd "${dhcp_start[@]}" ${device} >/dev/null 2>&1 ret="$?" if [ "${ret}" -eq 0 ]; then . /var/ipfire/dhcpc/dhcpcd-"${device}".info if [ $ip_address ]; then echo "" echo_ok boot_mesg " DHCP Assigned Settings for ${device}:" boot_mesg_flush boot_mesg " IP Address: $ip_address" boot_mesg_flush if [ -n "${RED_DHCP_HOSTNAME}" ]; then boot_mesg " Hostname: $RED_DHCP_HOSTNAME" boot_mesg_flush fi boot_mesg " Subnet Mask: $subnet_mask" boot_mesg_flush boot_mesg " Default Gateway: $routers" boot_mesg_flush boot_mesg " DNS Server: $domain_name_servers" boot_mesg_flush else echo "" echo_ok boot_mesg "DHCP for ${device} still running..." boot_mesg_flush fi else echo "" $(exit "${ret}") evaluate_retval fi } dhcpcd_stop() { # This function stops a previously started dhcpcd on a given device. local device="$1" local dhcp_stop="-k" local leaseinfo="/var/ipfire/dhcpc/dhcpcd-${device}.info" boot_mesg -n "Stopping dhcpcd on the ${device} interface..." # Check if a dhcpcd is running. local pid="$(dhcpcd_get_pid "${device}")" if ! dhcpcd_is_running "${pid}"; then boot_mesg " Not running." ${WARNING} echo_warning exit 1 fi # Stop dhcpcd. /sbin/dhcpcd ${dhcp_stop} ${device} &> /dev/null ret="$?" # Wait until dhcpd has stopped. while [ -d "/proc/${pid}" ]; do sleep 1 # repeat stop if dhcp was still running /sbin/dhcpcd ${dhcp_stop} ${device} &> /dev/null done # Display console message, depended on the exit code # of the stopped dhcpcd. if [ "${ret}" -eq 0 ]; then boot_mesg echo_ok elif [ "${ret}" -eq 1 ]; then boot_mesg "failed to stop dhcpcd!" ${WARNING} echo_warning else boot_mesg echo_failure fi } # QMI stuff qmi_find_device() { local intf="${1}" local _intf local path for path in /dev/cdc-*; do if [ -c "${path}" ]; then _intf="$(qmi_find_interface "${path}")" # Check if the interface matches if [ "${intf}" = "${_intf}" ]; then echo "${path}" return 0 fi fi done # Nothing found return 1 } qmi_find_interface() { local device="${1}" qmicli --device="${device}" --device-open-proxy --get-wwan-iface } qmi_enable_rawip_mode() { local intf="${1}" # Shut down the device first ip link set "${intf}" down &>/dev/null echo "Y" > "/sys/class/net/${intf}/qmi/raw_ip" } qmi_configure_apn() { local device="${1}" # APN settings local apn="${2}" local auth="${3}" local username="${4}" local password="${5}" local args=( # We only support IPv4 right now "ip-type=4" ) # Set APN if [ -n "${apn}" ]; then args+=( "apn=${apn}" ) fi # Set auth case "${auth}" in PAP|CHAP) args+=( "auth=${auth}" ) ;; esac # Set username if [ -n "${username}" ]; then args+=( "username=${username}" ) fi # Set password if [ -n "${password}" ]; then args+=( "password=${password}" ) fi local _args local arg for arg in ${args[@]}; do if [ -n "${_args}" ]; then _args="${_args}," fi _args="${_args}${arg}" done qmicli --device="${device}" --device-open-proxy \ --wds-start-network="${_args}" \ --client-no-release-cid &>/dev/null } qmi_reset() { local device="${1}" qmicli --device="${device}" --device-open-proxy \ --wds-reset &>/dev/null } # Assigns a "static" MAC address qmi_assign_address() { local intf="${1}" # Find the device local device="$(qmi_find_device "${intf}")" # Switch off the raw_ip mode to be able to proper # assign the generated MAC address. echo "N" > "/sys/class/net/${intf}/qmi/raw_ip" local address # Generate a "random" MAC address using the device number printf -v address "02:ff:ff:ff:ff:%02x" "${device:12}" # Change the MAC address ip link set "${intf}" address "${address}" }