#!/bin/bash set -e INTERFACE_FILE="/config/interface.conf" WHITELIST_FILE="/config/whitelist.conf" NETWORK_FILE="/config/network.conf" DHCPD_CONF="/etc/dhcp/dhcpd.conf" if [ ! -f "$INTERFACE_FILE" ]; then echo "Error: Interface configuration file not found: $INTERFACE_FILE" exit 1 fi if [ ! -f "$WHITELIST_FILE" ]; then echo "Error: Whitelist configuration file not found: $WHITELIST_FILE" exit 1 fi INTERFACE=$(cat "$INTERFACE_FILE" | tr -d '\n\r ') echo "Using interface: $INTERFACE" # Wait for interface to be available for i in {1..30}; do if ip link show "$INTERFACE" &>/dev/null; then break fi echo "Waiting for interface $INTERFACE..." sleep 1 done if ! ip link show "$INTERFACE" &>/dev/null; then echo "Error: Interface $INTERFACE not found" exit 1 fi # Load network configuration if available if [ -f "$NETWORK_FILE" ]; then echo "Loading network configuration from $NETWORK_FILE" source "$NETWORK_FILE" else echo "No network configuration file found, using defaults" fi # Set defaults for unset variables RANGE_START_OFFSET=${RANGE_START_OFFSET:-10} RANGE_END_OFFSET=${RANGE_END_OFFSET:-100} LEASE_TIME=${LEASE_TIME:-43200} MAX_LEASE_TIME=${MAX_LEASE_TIME:-86400} # Get interface IP and network info INTERFACE_IP=$(ip -4 addr show "$INTERFACE" | grep "inet " | awk '{print $2}' | cut -d'/' -f1) INTERFACE_CIDR=$(ip -4 addr show "$INTERFACE" | grep "inet " | awk '{print $2}' | cut -d'/' -f2) # Check if subnet is manually configured if [ -n "$SUBNET" ] && [ -n "$NETMASK" ]; then echo "Using manual network configuration: $SUBNET/$NETMASK" INTERFACE_NETWORK="$SUBNET" # Calculate broadcast from subnet and netmask if [ "$NETMASK" = "255.255.255.0" ]; then BROADCAST="${SUBNET%.*}.255" elif [ "$NETMASK" = "255.255.0.0" ]; then BROADCAST="${SUBNET%.*.*}.255.255" elif [ "$NETMASK" = "255.0.0.0" ]; then BROADCAST="${SUBNET%.*.*.*}.255.255.255" else BROADCAST="${SUBNET%.*}.255" fi else echo "Auto-detecting network configuration from interface" # For test environment, handle Docker's internal network properly if [ "$INTERFACE" = "eth0" ] && [ -n "$INTERFACE_IP" ]; then # Extract network base from the actual IP INTERFACE_NETWORK=$(echo "$INTERFACE_IP" | sed 's/\.[0-9]*$/\.0/') else # Fallback for physical interfaces INTERFACE_NETWORK=$(ip -4 addr show "$INTERFACE" | grep "inet " | awk '{print $2}' | cut -d'/' -f1 | sed 's/\.[0-9]*$/\.0/') fi # Convert CIDR to netmask and calculate broadcast case $INTERFACE_CIDR in 24) NETMASK="255.255.255.0" BROADCAST="${INTERFACE_NETWORK%.*}.255" ;; 16) NETMASK="255.255.0.0" BROADCAST="${INTERFACE_NETWORK%.*.*}.255.255" ;; 8) NETMASK="255.0.0.0" BROADCAST="${INTERFACE_NETWORK%.*.*.*}.255.255.255" ;; *) NETMASK="255.255.255.0" BROADCAST="${INTERFACE_NETWORK%.*}.255" ;; esac fi echo "Interface IP: $INTERFACE_IP" echo "Network: $INTERFACE_NETWORK/$NETMASK" echo "DHCP Range: ${INTERFACE_NETWORK%.*}.$RANGE_START_OFFSET - ${INTERFACE_NETWORK%.*}.$RANGE_END_OFFSET" echo "Lease Time: $LEASE_TIME seconds" # Generate dhcpd.conf from template cp /etc/dhcp/dhcpd.conf.template "$DHCPD_CONF" # Generate host entries for whitelisted MACs HOST_ENTRIES="" HOST_NUM=1 echo "Reading whitelist from $WHITELIST_FILE" # Check if file exists and is readable if [ ! -f "$WHITELIST_FILE" ]; then echo "ERROR: Whitelist file does not exist!" exit 1 fi if [ ! -r "$WHITELIST_FILE" ]; then echo "ERROR: Whitelist file is not readable!" exit 1 fi echo "File size: $(stat -c%s "$WHITELIST_FILE") bytes" echo "File contents (hex dump):" hexdump -C "$WHITELIST_FILE" | head -5 echo "File contents (raw):" cat "$WHITELIST_FILE" echo "" echo "--- Processing MACs ---" # Read file line by line without subshell while IFS= read -r MAC || [ -n "$MAC" ]; do # Remove any whitespace, carriage returns, and convert to lowercase ORIG_MAC="$MAC" MAC=$(echo "$MAC" | tr -d '\r\n\t ' | tr '[:upper:]' '[:lower:]') echo "Processing: original='$ORIG_MAC' cleaned='$MAC'" if [ -n "$MAC" ]; then # Generate IP based on host number (starting from .10) LAST_OCTET=$((10 + HOST_NUM - 1)) HOST_IP="${INTERFACE_NETWORK%.*}.$LAST_OCTET" NEW_ENTRY=" host client$HOST_NUM { hardware ethernet $MAC; fixed-address $HOST_IP; }" HOST_ENTRIES="${HOST_ENTRIES}${NEW_ENTRY}" echo "Added whitelist entry: $MAC -> $HOST_IP" HOST_NUM=$((HOST_NUM + 1)) fi done < "$WHITELIST_FILE" if [ -z "$HOST_ENTRIES" ]; then echo "Warning: No MAC addresses found in whitelist!" else echo "Total whitelisted MACs: $((HOST_NUM - 1))" fi # Replace placeholders in config sed -i "s#__SUBNET__#$INTERFACE_NETWORK#g" "$DHCPD_CONF" sed -i "s#__NETMASK__#$NETMASK#g" "$DHCPD_CONF" sed -i "s#__RANGE_START__#${INTERFACE_NETWORK%.*}.$RANGE_START_OFFSET#g" "$DHCPD_CONF" sed -i "s#__RANGE_END__#${INTERFACE_NETWORK%.*}.$RANGE_END_OFFSET#g" "$DHCPD_CONF" sed -i "s#__BROADCAST__#$BROADCAST#g" "$DHCPD_CONF" sed -i "s#__LEASE_TIME__#$LEASE_TIME#g" "$DHCPD_CONF" sed -i "s#__MAX_LEASE_TIME__#$MAX_LEASE_TIME#g" "$DHCPD_CONF" # Use a different approach for multiline HOST_ENTRIES awk -v entries="$HOST_ENTRIES" '{gsub(/__HOST_ENTRIES__/, entries); print}' "$DHCPD_CONF" > "$DHCPD_CONF.tmp" && mv "$DHCPD_CONF.tmp" "$DHCPD_CONF" echo "Starting DHCP server on interface $INTERFACE..." echo "Configuration:" cat "$DHCPD_CONF" # Start DHCP server exec dhcpd -4 -f -d -cf "$DHCPD_CONF" "$INTERFACE"