#!/bin/sh
#
# Copyright 2011, 2016, 2017, 2019, 2023 Eric Hameleers, Eindhoven, NL
# Copyright 2011 Patrick Volkerding, Sebeka, Minnesota USA
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Code used from SeTpxe and SeTnet scripts, part of the Slackware installer.
# ---------------------------------------------------------------------------
# PXEserver works as follows:
# - It requires a wired network; wireless PXE boot is impossible.
# - The pxeserver script tries to find a wired interface; you can pass an
# explicit interfacename as parameter to the script (optional).
# - If multiple wired interfaces are detected, a dialog asks the user to
# select the right one.
# - A check is done for DHCP configuration of this wired interface;
# * If DHCP configuration is found then pxeserver will not start its own
# DHCP server and instead will rely on your LAN's DHCP server.
# * If no DHCP config is found, the script will ask permission to start
# its own internal DHCP server.
# Additionally the user will be prompted to configure an IP address for the
# network interface and IP range properties for the internal DHCP server.
# - The script will then start the PXE server, comprising of:
# * dnsmasq providing DNS, DHCP and BOOTP;
# * NFS and RPC daemons;
# - The script will detect if you have an outside network connection on
# another interface and will enable IP forwarding if needed, so that the
# PXE clients will also have network access.
# - Optionally, the script can hide its PXE clients behind a NAT router
# in case external network is not accessible via normal routing.
# - The Live OS booted via pxelinux is configured with additional boot
# parameters:
# * nfsroot=${LOCAL_IPADDR}:/mnt/livemedia
# * luksvol=
# * nop
# * hostname=@DISTRO@
# * tz=$(cat /etc/timezone)
# * locale=${SYSLANG:-"en_US.UTF-8"}
# * kbd=${KBD:-"us"}
# Which shows that the configuration of the Live OS where the PXE server
# runs is largely determining the configuration of the PXE clients.
# - Note that when networkbooting, the hostname of the Live OS will be
# suffixed with the machine's MAC address to make every network-booted
# Live OS unique.
#
# Initialization:
#
DEBUG=${DEBUG:-0}
DIALOG=dialog
# UEFI prefix:
UEFIPREFIX="/EFI/BOOT"
# Number of PXE clients we want to serve with our own DHCP server:
DEF_DHCP_RANGE=${DEF_DHCP_RANGE:-10}
# Optional argument to the script is the name of the interface on which
# the PXE server should run:
INTERFACE="$1"
# This variable will be used to determine if the network default gateway
# is reached through a second NIC in the computer, or not.
GLOBAL_GW_INT=""
# In the above case, the global and local gateways will not be equal.
GLOBAL_GATEWAY=""
LOCAL_GATEWAY=""
# Defining more global variables ahead of time:
LOCAL_IPADDR=""
LOCAL_NETMASK=""
LOCAL_NETWORK=""
# The script optionally configures a NAT gateway:
ENABLE_NAT="no"
# The Slackware setup depends on english language settings because it
# parses program output like that of "fdisk -l". So, we need to override
# the Live user's local language settings here:
SYSLANG=$LANG
export LANG=C
export LC_ALL=C
# Warn the user if the Live Media is inaccessible (however unlikely):
if [ ! -d /mnt/livemedia/@LIVEMAIN@/system ]; then
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "LIVE MEDIA NOT ACCESSIBLE" --msgbox "\
\n\
Before you can install software, complete the following tasks:\n\
\n\
1. Mount your Live media partition on /mnt/livemedia." 16 68
exit 1
fi
TMP=/var/log/setup/tmp
if [ ! -d $TMP ]; then
mkdir -p $TMP
fi
rm -f $TMP/SeT* $TMP/pxe*
echo "on" > $TMP/SeTcolor # turn on color menus
PATH="$PATH:/usr/share/@LIVEMAIN@"
export PATH;
export COLOR=on
# Add some eye candy for BIOS PXE clients:
if [ -d /mnt/livemedia/boot/syslinux ]; then
PXETXTSRC=/mnt/livemedia/boot/syslinux
elif [ -d /mnt/livemedia/boot/extlinux ]; then
PXETXTSRC=/mnt/livemedia/boot/extlinux
else
PXETXTSRC=""
fi
if [ -n "$PXETXTSRC" ]; then
# All the files that are needed for the BIOS PXE boot menu:
for SFILE in f2.txt f3.txt f4.txt message.txt swlogov.png ter-i16v.psf vesamenu.c32
do
ln -sf ${PXETXTSRC}/${SFILE} /var/lib/tftpboot/
done
fi
# For UEFI computers:
if [ ! -f /mnt/livemedia${UEFIPREFIX}/SLACKWARELIVE ]; then
# We boot from a USB stick created with isu2usb.sh:
if ! mount |grep -q 'on /boot/efi' ; then
# USB EFI partition is not yet mounted, let's find where it is:
LIVEPART="$(df -P /mnt/livemedia/ |tail -n1 |cut -d' ' -f1)"
USBDEV="/dev/$(lsblk -no pkname ${LIVEPART=})"
EFIPART="$(blkid -t PARTLABEL="EFI System Partition" ${USBDEV=}* |cut -d: -f1)"
mkdir -p /boot/efi
mount -t vfat -o defaults ${EFIPART} /boot/efi
fi
UEFI_TFTP="/boot/efi"
else
UEFI_TFTP="/mnt/livemedia"
fi
# Allow the boot files to be served by tftp:
mkdir -p /var/lib/tftpboot${UEFIPREFIX}
ln -sf ${UEFI_TFTP}${UEFIPREFIX}/SLACKWARELIVE /var/lib/tftpboot${UEFIPREFIX}/
ln -sf ${UEFI_TFTP}${UEFIPREFIX}/bootx64.efi /var/lib/tftpboot${UEFIPREFIX}/
ln -sf ${UEFI_TFTP}${UEFIPREFIX}/theme /var/lib/tftpboot${UEFIPREFIX}/
#
# Function definitions:
#
# Function to convert the netmask from CIDR format to dot notation.
cidr_cvt() {
# Number of args to shift, 255..255, first non-255 byte, zeroes
set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
[ $1 -gt 1 ] && shift $1 || shift
echo ${1-0}.${2-0}.${3-0}.${4-0}
}
# Function to convert the netmask from dot notation to CIDR format.
mask_cvt ()
{
# Assumes there's no "255." after a non-255 byte in the mask
local x=${1##*255.}
set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*}
x=${1%%$3*}
echo $(( $2 + (${#x}/4) ))
}
# IP Address to integer conversion:
ip_to_int() {
IFS=.
set -f
set -- $1
echo $(($1 << 24 | $2 << 16 | $3 << 8 | $4))
}
# Integer to IP Address conversion:
int_to_ip() {
echo $(($1>>24)).$(($1>>16&0xff)).$(($1>>8&0xff)).$(($1&0xff))
}
# Find out whether the interface is managed by DHCP:
get_dhcpcd_pid() {
# Find the location of the PID file of dhcpcd:
MYDEV="$1"
if [ -s /run/dhcpcd/dhcpcd-${MYDEV}.pid ]; then
echo "/run/dhcpcd/dhcpcd-${MYDEV}.pid"
elif [ -s /run/dhcpcd/dhcpcd-${MYDEV}-4.pid ]; then
echo "/run/dhcpcd/dhcpcd-${MYDEV}-4.pid"
elif [ -s /run/dhcpcd-${MYDEV}.pid ]; then
echo "/run/dhcpcd-${MYDEV}.pid"
elif [ -s /run/dhcpcd-${MYDEV}-4.pid ]; then
echo "/run/dhcpcd-${MYDEV}-4.pid"
elif [ -s /run/${MYDEV}.pid ]; then
echo "/run/${MYDEV}.pid"
else
echo UNKNOWNLOC
fi
}
# Find out whether the interface is managed by DHCP:
get_nm_internal_lease() {
# Find the lease of NetworkManager internal dhcp client:
MYDEV="$1"
if [ -s /var/lib/NetworkManager/intern*-${MYDEV}.lease ]; then
# NM is indeed managing this interface:
echo "$(ls --indicator-style=none /var/lib/NetworkManager/intern*-${MYDEV}.lease)"
else
echo UNKNOWNLOC
fi
}
# The network interface IP configuration routine.
# Will be called if the interface was not configured by DHCP.
# It ends with a configured network interface:
devconfig() {
# Function accepts a parameter; if not given, use the global INTERFACE:
MYIF=${1:-"$INTERFACE"}
# Determine a LAN range we are going to be using for the internal DHCP
# range that does not conflict with existing IP configuration:
if ! ip -f inet -o addr show |grep -v " lo " |grep -qw 192.168 ; then
MYIP="192.168.10.10"
elif ! ip -f inet -o addr show |grep -v " lo " |grep -qw 172.16 ; then
MYIP="172.16.10.10"
else
MYIP="10.16.10.10"
fi
# Main loop IP configuration:
while [ 0 ]; do
cat << EOF > $TMP/tempmsg
You will need to enter the IP address you wish to
assign to interface ${MYIF}. Example: ${MYIP}
What is your IP address?
EOF
if [ "x$LOCAL_IPADDR" = "x" ]; then # assign default
LOCAL_IPADDR=${MYIP}
fi
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "ASSIGN IP ADDRESS" --inputbox "$(cat $TMP/tempmsg)" 12 \
65 $LOCAL_IPADDR 2> $TMP/local
if [ ! $? = 0 ]; then
rm -f $TMP/tempmsg $TMP/local
return
fi
LOCAL_IPADDR="$(cat $TMP/local)"
rm -f $TMP/local
clear
cat << EOF > $TMP/tempmsg
Now we need to know your netmask.
Typically this will be 255.255.255.0
but this can be different depending on
your local setup.
What is your netmask?
EOF
if [ "x$LOCAL_NETMASK" = "x" ]; then # assign default
LOCAL_NETMASK=${NETMASK:-255.255.255.0}
fi
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "ASSIGN NETMASK" --inputbox "$(cat $TMP/tempmsg)" 14 \
65 $LOCAL_NETMASK 2> $TMP/mask
if [ ! $? = 0 ]; then
rm -f $TMP/tempmsg $TMP/mask
return
fi
clear
LOCAL_NETMASK="$(cat $TMP/mask)"
rm $TMP/mask
# GLOBAL_GATEWAY was determined right before calling this function:
if [ "x$GLOBAL_GATEWAY" = "x" ]; then
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --yesno "Do you have a gateway?" 5 30
HAVE_GATEWAY=$?
clear
if [ $HAVE_GATEWAY -eq 0 ]; then
LOCAL_GATEWAY="$(echo $LOCAL_IPADDR | cut -f1-3 -d '.')."
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "ASSIGN GATEWAY ADDRESS" --inputbox \
"\nWhat is the IP address for your gateway?" 9 65 \
$LOCAL_GATEWAY 2> $TMP/gw
if [ ! $? = 0 ]; then
rm -f $TMP/tempmsg $TMP/gw
LOCAL_GATEWAY=""
else
LOCAL_GATEWAY="$(cat $TMP/gw)"
rm -f $TMP/gw
fi
fi
unset HAVE_GATEWAY
clear
elif [ "$GLOBAL_GW_INT" = "$MYIF" ]; then
LOCAL_GATEWAY=$GLOBAL_GATEWAY
fi
cat << EOF > $TMP/tempmsg
This is the proposed network configuration for $MYIF -
If this is OK, then select 'Yes'.
If this is not OK and you want to configure again, select 'No'.
* IP Address: $LOCAL_IPADDR
* Netmask: $LOCAL_NETMASK
* Gateway: ${LOCAL_GATEWAY:-"$GLOBAL_GATEWAY (via $GLOBAL_GW_INT)"}
EOF
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --no-collapse --title "NETWORK CONFIGURATION" \
--yesno "$(cat $TMP/tempmsg)" 14 68
if [ $? -eq 1 ]; then
continue # New round of questions
fi
#echo "Configuring ethernet card..."
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "INITIALIZING NETWORK" --infobox \
"\nConfiguring your network interface $MYIF ..." 5 56
# We don't need this anymore:
dhcpcd -k $MYIF 1>/dev/null 2>&1
rm -f /run/dhcpcd/dhcpcd-${MYIF}.pid 2>/dev/null
rm -f /run/dhcpcd-${MYIF}.pid 2>/dev/null
rm -f /run/${MYIF}.pid 2>/dev/null
# Broadcast and network are derived from IP and netmask:
LOCAL_BROADCAST="$(ipmask $LOCAL_NETMASK $LOCAL_IPADDR | cut -f 1 -d ' ')"
LOCAL_NETWORK="$(ipmask $LOCAL_NETMASK $LOCAL_IPADDR | cut -f 2 -d ' ')"
if [ -x /etc/rc.d/rc.networkmanager 2>/dev/null ]; then
# Use nmcli to reconfigure NetworkManager:
nmcli con add save no con-name pxe-${MYIF} ifname ${MYIF} type ethernet
nmcli con mod pxe-${MYIF} ipv4.addresses ${LOCAL_IPADDR}/$(mask_cvt ${LOCAL_NETMASK}) ipv4.method manual connection.autoconnect no
if [ "x$GLOBAL_GATEWAY" = "x" -a "x$LOCAL_GATEWAY" != "x" ]; then
nmcli con mod pxe-${MYIF} ipv4.gateway $LOCAL_GATEWAY
fi
nmcli con up pxe-${MYIF}
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
else
# Use ifconfig and route commands:
ifconfig $MYIF $LOCAL_IPADDR netmask $LOCAL_NETMASK broadcast $LOCAL_BROADCAST
if [ "x$GLOBAL_GATEWAY" = "x" -a "x$LOCAL_GATEWAY" != "x" ]; then
#echo "Configuring your gateway..."
route add default gw $LOCAL_GATEWAY metric 1
fi
fi
echo $LOCAL_IPADDR > $TMP/SeTIP
echo $LOCAL_NETMASK > $TMP/SeTnetmask
echo $LOCAL_GATEWAY > $TMP/SeTgateway
clear
break
done # end main loop IP configuration
} # end devconfig()
# The PXE Server configuration routine:
pxeconfig() {
# This function accepts a parameter (network interface to configure).
# If no name was passed, we will do our best to find out ourselves.
# Create empty PXE configuration file:
echo "" > $TMP/SeTpxe
# Find out what interface we should be using.
# Did we get one passed as a parameter?
if [ "x$1" = "x" ]; then
# No parameter or it was empty; find out if we have a wired interface:
WIRED_INT=""
IINT=0
for WINT in $(ls --indicator-style=none /sys/class/net |grep -v ^lo); do
if ! grep -q $WINT /proc/net/wireless ; then
WIRED_INT="$WIRED_INT $WINT"
IINT=$(( $IINT + 1 ))
fi
done
if [ $IINT -eq 0 ]; then
# Zero wired interfaces found - exit.
cat <<EOF > $TMP/tempmsg
Could not find a wired network interface. \n\
A PXE Server needs a configured network interface to work.\n\
EOF
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "MISSING NETWORK DEVICE" --msgbox "$(cat $TMP/tempmsg)"
8 68
rm -f $TMP/tempmsg
exit 1
elif [ $IINT -eq 1 ]; then
# Exactly one wired interfaces found - use it.
INTERFACE=$(echo $WIRED_INT) # get rid of the space
else
# Multiple wired interfaces found - let the user select one:
rm -f $TMP/iflist
for WINT in $WIRED_INT ; do
DRIVERTXT="IP=$(ip -f inet -o addr show ${WINT} |tr -s ' ' |head -1 |cut - f4 -d' ' |cut -f1 -d/)"
if cat /sys/class/net/$WINT/device/uevent 1>/dev/null 2>/dev/null ; then
DRIVERTXT="$DRIVERTXT driver=$(grep "DRIVER=" /sys/class/net/$WINT/device/uevent |cut -f2 -d=)"
fi
echo "$WINT \"network interface ($DRIVERTXT)\"" >> $TMP/iflist
done
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "SELECT NETWORK INTERFACE" \
--menu \
"Select an option below using the UP/DOWN keys and SPACE or ENTER.\n\
Alternate keys may also be used: '+', '-', and TAB." 13 72 9 \
--file $TMP/iflist \
2> $TMP/intset
INTERFACE="$(cat $TMP/intset)"
rm $TMP/intset $TMP/iflist
fi
fi # End undefined INTERFACE
#
# We now know what network interface to use.
#
# If our interface is configured by DHCP, it likely has a lease from a
# LAN DHCP server, so we should not activate another DHCP server ourself now:
if [ -s $(get_dhcpcd_pid ${INTERFACE}) -a -n "$(ps -q $(cat $(get_dhcpcd_pid ${INTERFACE})) -o comm= 2>/dev/null)" ]; then
OWNDHCP="no"
elif [ -s $(get_nm_internal_lease ${INTERFACE}) ]; then
OWNDHCP="no"
else
# Assume nothing... we will ask the user for confirmation later!
OWNDHCP="yes"
fi
# If $INTERFACE != $GLOBAL_GW_INT then we are dealing with a dual-nic setup
# and later on we can suggest configuring (NAT) routing:
GLOBAL_GW_INT=$(ip -f inet -o route show default |grep -v linkdown |head -1 |tr -s ' ' |cut -f5 -d' ')
GLOBAL_GATEWAY=$(ip -f inet -o route show default |grep -v linkdown |head -1 |tr -s ' ' |cut -f3 -d' ')
#
# Start the interactive part:
#
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --backtitle "@CDISTRO@ Linux Live (@LIVEDE@) PXE Server." \
--title "WELCOME TO PXE CONFIGURATION" --msgbox "\
We will be asking you a few questions now.\n\
The answers will be used to start a PXE service on this computer \
which does not interfere with other services in your network.\
\n\
The only assumption is, that there is NO PXE service already \
running on your local network at this moment.
\n\
If in doubt, stick with the defaults." 0 0
if [ "$OWNDHCP" = "yes" ]; then
# Be extra safe. Do not start a DHCP server if the user denies it:
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "ENABLE DHCP SERVER" --yesno " \
Network interface ${INTERFACE} did not get an IP address from a DHCP server. \
Slackware's PXE server starting on ${INTERFACE} needs a working DHCP server.\n\
Do you want this computer to start its own DHCP server on ${INTERFACE} \
(you can control what IP addresses are used by the DHCP server)?\n\
Say 'YES' if you are certain your network interface ${INTERFACE} is
not in reach of any DHCP server." 13 68
if [ $? = 0 ]; then
OWNDHCP="yes"
else
OWNDHCP="no"
fi
fi
if [ "$OWNDHCP" == "yes" ]; then
if [ "$INTERFACE" != "$GLOBAL_GW_INT" ]; then
# The default gateway for this computer is on another interface;
$DIALOG --title "ENABLE NAT FIREWALL" --defaultno --yesno " \
This computer's default gateway is network interface ${GLOBAL_GW_INT}. \
The network behind the PXE server's interface ${INTERFACE} seems to be isolated.\n\
Do you want to hide your PXE clients behind a NAT gateway?\n\
This may be helpful if PXE clients cannot reach the external network otherwise.\n\
Say 'NO' if you are not sure which is best." 12 68
if [ $? = 0 ]; then
ENABLE_NAT="yes"
else
ENABLE_NAT="no"
fi
fi
fi
# Assemble the network parameters:
LOCAL_IPADDR=$(ip -f inet -o addr show ${INTERFACE} |tr -s ' ' |head -1 |cut -f4 -d' ' |cut -f1 -d/)
if [ "x$LOCAL_IPADDR" = "x" ]; then # no IP Address was configured?!?
cat <<EOF > $TMP/tempmsg
Next step is to define an IP address for network interface ${INTERFACE}. \n\
A PXE Server needs a configured network interface to work.\n\
EOF
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "UNCONFIGURED NETWORK DEVICE" --msgbox "$(cat $TMP/tempmsg)" 9 68
rm -f $TMP/tempmsg
# Run the static IP configuration routine for $INTERFACE:
devconfig ${INTERFACE}
else
# DHCP configured interface, we assume that the default gaeway is here.
LOCAL_GATEWAY=$GLOBAL_GATEWAY
fi
# OK we have an IP Address, let's continue.
LOCAL_NETMASK=$(ip -f inet -o addr show ${INTERFACE} |tr -s ' ' |head -1 |cut -f4 -d' ' |cut -f2 -d/)
LOCAL_NETMASK=$(cidr_cvt $LOCAL_NETMASK)
LOCAL_BROADCAST=$(ipmask $LOCAL_NETMASK $LOCAL_IPADDR |cut -f 1 -d ' ')
LOCAL_NETWORK=$(ipmask $LOCAL_NETMASK $LOCAL_IPADDR |cut -f 2 -d ' ')
if [ "$OWNDHCP" = "yes" ]; then
# Find out a suitable IP address range for the DHCP server. Involves magic:
I_LOCAL_IPADDR=$(ip_to_int "$LOCAL_IPADDR")
I_LOCAL_NETMASK=$(ip_to_int "$LOCAL_NETMASK")
I_MINLEASE_IP=$(( ($I_LOCAL_IPADDR & $I_LOCAL_NETMASK) + 1 ))
I_MAXLEASE_IP=$(( ($I_LOCAL_IPADDR | ${I_LOCAL_NETMASK}^0xffffffff) - 1 ))
if [ $(( $I_MAXLEASE_IP - $I_LOCAL_IPADDR )) -ge $DEF_DHCP_RANGE ]; then
# Use $DEF_DHCP_RANGE IP addresses in the top of the address range:
I_MINLEASE_IP=$(( $I_MAXLEASE_IP - $(($DEF_DHCP_RANGE - 1)) ))
elif [ $(($I_LOCAL_IPADDR - $I_MINLEASE_IP)) -ge $DEF_DHCP_RANGE ]; then
# Use $DEF_DHCP_RANGE IP addresses in the bottom of the address range:
I_MAXLEASE_IP=$(( $I_MINLEASE_IP + $(($DEF_DHCP_RANGE - 1)) ))
else
# Smaller range available than we wanted, use what we can get:
I_MINLEASE_IP=$(( $I_LOCAL_IPADDR + 1 ))
fi
MINLEASE_IP=$(int_to_ip "$I_MINLEASE_IP")
MAXLEASE_IP=$(int_to_ip "$I_MAXLEASE_IP")
while [ 0 ]; do
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
( $DIALOG --stdout --backtitle "@CDISTRO@ Linux Live (@LIVEDE@) PXE Server." \
--title "DHCP SERVER CONFIGURATION" \
--cancel-label Restart \
--form "\
The PXE Service is going to run on $INTERFACE with these values \
(the defaults should be OK). \n\
You can change the range of IP addresses used by the DHCP server, if \
IP addresses in the proposed range are used by computers in your LAN. \
For instance, your default gateway if you have one. \n\
\n\
Also note that we will not validate any changes you make:" \
18 68 0 \
"IP Address:" 1 1 "$LOCAL_IPADDR" 1 30 0 0 \
"Netmask:" 2 1 "$LOCAL_NETMASK" 2 30 0 0 \
"Gateway:" 3 1 "$LOCAL_GATEWAY" 3 30 0 0 \
"Lowest DHCP Client Address:" 4 1 "$MINLEASE_IP" 4 30 15 0 \
"Highest DHCP Client Address:" 5 1 "$MAXLEASE_IP" 5 30 15 0 \
) > $TMP/tempopts
if [ $? = 0 ]; then
# Remember... busybox ash is no good with arrays :/
local i=0
rm -f $TMP/tempkeys
cat $TMP/tempopts | while read VALUE ; do
if [ $i = 0 ]; then echo "MINLEASE_IP=\"$VALUE\"" >> $TMP/tempkeys
elif [ $i = 1 ]; then echo "MAXLEASE_IP=\"$VALUE\"" >> $TMP/tempkeys
fi
i=$(expr $i + 1)
done
eval $(cat $TMP/tempkeys)
rm $TMP/tempopts
break
fi
done
else
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --backtitle "@CDISTRO@ Linux Live (@LIVEDE@) PXE Server." \
--title "DHCP SERVER CONFIGURATION" --msgbox "\
\n\
PXE server has been configured to use a DHCP server in your network.\n\
\n\
Press ENTER to continue." 14 68
fi # [ "$OWNDHCP" = "yes" ]
echo "DHCP=${OWNDHCP}" >> $TMP/SeTpxe
echo "GLOBAL_GATEWAY=${GLOBAL_GATEWAY}" >> $TMP/SeTpxe
echo "GLOBAL_GW_INT=${GLOBAL_GW_INT}" >> $TMP/SeTpxe
echo "LOCAL_IPADDR=${LOCAL_IPADDR}" >> $TMP/SeTpxe
echo "LOCAL_NETMASK=${LOCAL_NETMASK}" >> $TMP/SeTpxe
echo "LOCAL_GATEWAY=${LOCAL_GATEWAY}" >> $TMP/SeTpxe
echo "LOCAL_BROADCAST=${LOCAL_BROADCAST}" >> $TMP/SeTpxe
echo "LOCAL_NETWORK=${LOCAL_NETWORK}" >> $TMP/SeTpxe
echo "MINLEASE_IP=${MINLEASE_IP}" >> $TMP/SeTpxe
echo "MAXLEASE_IP=${MAXLEASE_IP}" >> $TMP/SeTpxe
# Write out a suitable dnsmasq configuration:
cat <<EOF > ${TMP}/pxe_dnsmasq.conf
# Only listen at our designated interface:
listen-address=$LOCAL_IPADDR
# Write the pid file:
pid-file=${TMP}/pxe_dnsmasq.pid
# Start a TFTP server:
enable-tftp
# Set the root directory for files available via FTP:
tftp-root=/var/lib/tftpboot
# Disable re-use of the DHCP servername and filename fields as extra
# option space. That's to avoid confusing some old or broken DHCP clients.
dhcp-no-override
# Log connections so that we can display them on the console:
log-facility=/var/log/pxe_dnsmasq.log
log-dhcp
# Custom path for the leases file:
dhcp-leasefile=$TMP/pxe_dnsmasq.leases
# Test for the architecture of a netboot client. PXE clients are
# supposed to send their architecture as option 93. (See RFC 4578) .
# The known types are X86PC, PC98, IA64_EFI, Alpha, Arc_x86,
# Intel_Lean_Client, IA32_EFI, BC_EFI, Xscale_EFI and X86-64_EFI
dhcp-match=X86PC, option:client-arch, 0 #BIOS x86
dhcp-match=BC_EFI, option:client-arch, 7 #EFI Byte Code
dhcp-match=X86-64_EFI, option:client-arch, 9 #EFI x86_64
# Craft a nice PXE menu (user has 3 seconds to interrupt in which case the
# network boot sequence will be aborted):
pxe-prompt="PXE booting in 3 seconds...", 3
# Now let's build a boot menu. If there's only one menu item PXE will
# automatically boot into this. If thre are multiple boot selections,
# then user input is expected.
# I found out that UEFI PXE boot with more than one menu item won't work.
# The 'pxe-service' definitions are the PXE alternative to the generic
# 'dhcp-boot' keyword.
# The PXE boot image has to match the client architecture.
# And we enforce that our own TFTP server is being used so that misbehaving
# DHCP servers on the LAN that set 'next-server' are not affecting us:
pxe-service=X86PC, "Boot from network (BIOS)", pxelinux,${LOCAL_IPADDR}
pxe-service=BC_EFI, "Boot from network (UEFI)", ${UEFIPREFIX}/bootx64.efi,${LOCAL_IPADDR}
pxe-service=X86-64_EFI, "Boot from network (UEFI)", ${UEFIPREFIX}/bootx64.efi,${LOCAL_IPADDR}
# A boot service type of 0 is special, and will abort the
# net boot procedure and continue booting from local media.
pxe-service=X86PC, "Boot from local hard disk", 0
# Note:
# The above 'pxe-service' menu does not always work for UEFI-based clients,
# so alternatively you could implement a combination of 'dhcp-match' and
# 'dhcp-boot' to provide a boot image. Here is a commented-out example:
#dhcp-match=set:BC_EFI,option:client-arch,7
#dhcp-match=set:X86-64_EFI,option:client-arch,9
#dhcp-match=set:X86_EFI,option:client-arch,6
#dhcp-match=set:X86PC,option:client-arch,0
#dhcp-boot=tag:X86-64_EFI,"${UEFIPREFIX}/bootx64.efi,${LOCAL_IPADDR}"
#dhcp-boot=tag:BC_EFI,"${UEFIPREFIX}/bootx64.efi,${LOCAL_IPADDR}"
#dhcp-boot=tag:X86_EFI,"${UEFIPREFIX}/bootia32.efi,${LOCAL_IPADDR}"
#dhcp-boot=tag:X86PC,"pxelinux.0,${LOCAL_IPADDR}"
EOF
if [ -n "$LOCAL_GATEWAY" -a "$INTERFACE" = "$GLOBAL_GW_INT" ]; then
# The default gw can be reached through our $INTERFACE.
cat <<EOF >> ${TMP}/pxe_dnsmasq.conf
# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
# The two options below are equivalent:
#dhcp-option=option:router,${LOCAL_GATEWAY}
dhcp-option=3,${LOCAL_GATEWAY}
EOF
else
# The default gw is reached through a second interface on the computer.
# We need to build a router, a bridge or a NAT firewall between the two.
cat <<EOF >> ${TMP}/pxe_dnsmasq.conf
# Apply the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
# And we want to let our PXE clients use $INTERFACE as the default gw.
#dhcp-option=option:router,${LOCAL_IPADDR}
EOF
fi
if [ "$OWNDHCP" = "yes" ]; then
cat <<EOF >> ${TMP}/pxe_dnsmasq.conf
# dnsmasq functions as a normal DHCP server, providing IP leases.
dhcp-range=${MINLEASE_IP},${MAXLEASE_IP},${LOCAL_NETMASK},1h
EOF
else
cat <<EOF >> ${TMP}/pxe_dnsmasq.conf
# There is an existing DHCP server on this LAN, so dnsmasq functions
# as a proxy DHCP server providing boot information but no IP leases.
# Any ip in the subnet will do, so you may just put your server NIC ip here.
dhcp-range=${LOCAL_IPADDR},proxy
EOF
fi
# Create the pxelinux configuration file for BIOS boot:
KBD=$(sed -n "s%^ */usr/bin/loadkeys *%%p" /etc/rc.d/rc.keymap 2>/dev/null)
cat <<EOF > /var/lib/tftpboot/pxelinux.cfg/default
prompt 0
timeout 300
ui vesamenu.c32
default pxelive
menu background swlogov.png
menu title @CDISTRO@ Linux Live PXE boot menu
menu clear
F1 pxemessage.txt #00000000
F2 f2.txt #00000000
F3 f3.txt #00000000
F4 f4.txt #00000000
menu hshift 1
menu vshift 9
menu width 55
menu margin 1
menu rows 10
menu helpmsgrow 14
menu helpmsgendrow 18
menu cmdlinerow 18
menu tabmsgrow 19
menu timeoutrow 20
menu color screen 37;40 #00000000 #00000000 none
menu color border 34;40 #00000000 #00000000 none
menu color title 1;36;44 #ffb9556b #30002d1f none
menu color unsel 37;44 #ff354172 #007591ff none
menu color hotkey 1;37;44 #ffad37b7 #00000000 none
menu color sel 7;37;40 #ffffffff #00000000 none
menu color hotsel 1;7;37;40 #ffe649f3 #00000000 none
menu color scrollbar 30;44 #00000000 #00000000 none
menu color tabmsg 31;40 #ffA32222 #00000000 none
menu color cmdmark 1;36;40 #ffff0000 #00000000 none
menu color cmdline 37;40 #ffffffff #ff000000 none
menu color pwdborder 30;47 #ffff0000 #00000000 std
menu color pwdheader 31;47 #ffff0000 #00000000 std
menu color pwdentry 30;47 #ffff0000 #00000000 std
menu color timeout_msg 37;40 #ff809aef #00000000 none
menu color timeout 1;37;40 #ffb72f9f #00000000 none
menu color help 37;40 #ff354172 #00000000 none
label pxelive
menu label Boot @CDISTRO@ Linux Live (@LIVEDE@) from network
kernel /generic
append initrd=/initrd.img @KAPPEND@ load_ramdisk=1 prompt_ramdisk=0 rw printk.time=0 nfsroot=${LOCAL_IPADDR}:/mnt/livemedia luksvol= nop hostname=@DISTRO@ tz=$(cat /etc/timezone) locale=${SYSLANG:-"en_US.UTF-8"} kbd=${KBD:-"us"}
EOF
# And a Grub configuration for UEFI boot:
cat <<EOF > /var/lib/tftpboot${UEFIPREFIX}/grub.cfg
# PXE boot menu for UEFI based systems:
set default=0
set timeout=200
# EFI video support:
insmod efi_gop
insmod efi_uga
# (U)EFI requirement: must support all_video:
insmod all_video
# Load the network modules first, so that we can use \$prefix;
insmod net
insmod efinet
insmod tftp
insmod gzio
insmod part_gpt
insmod ext2
# Determine whether we can show a graphical themed menu:
insmod font
if loadfont \$prefix/theme/dejavusansmono12.pf2 ; then
loadfont \$prefix/theme/dejavusansmono24.pf2
loadfont \$prefix/theme/dejavusansmono20.pf2
loadfont \$prefix/theme/dejavusansmono10.pf2
loadfont \$prefix/theme/dejavusansmono5.pf2
set font="DejaVu Sans Mono Regular 12"
set gfxmode=1024x768,800x600,640x480,auto
export gfxmode
insmod gfxterm
insmod gfxmenu
terminal_output gfxterm
insmod gettext
insmod png
set theme=\$prefix/theme/liveslak.txt
export theme
fi
set gfxpayload=keep
menuentry 'Boot @CDISTRO@ Linux Live (@LIVEDE@) from network' --class slackware --class gnu-linux --class gnu --class os {
echo "Loading @CDISTRO@ kernel"
linux generic @KAPPEND@ load_ramdisk=1 prompt_ramdisk=0 rw printk.time=0 nfsroot=${LOCAL_IPADDR}:/mnt/livemedia luksvol= nop hostname=@DISTRO@ tz=$(cat /etc/timezone) locale=${SYSLANG:-"en_US.UTF-8"} kbd=${KBD:-"us"}
initrd initrd.img
echo "Booting @CDISTRO@ kernel"
}
EOF
} # end of pxeconfig()
# -------------------------------------------------------- #
# Above was just initialization and function definitions. #
# Let's make use of all that. #
# ---------------------------------------------------------#
# Main loop:
while [ 0 ]; do
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "@CDISTRO@ Linux Live PXE Server (@LIVEDE@ @SL_VERSION@)" \
--menu \
"Welcome to @CDISTRO@ Linux Live PXE Server.\n\
Select an option below using the UP/DOWN keys and SPACE or ENTER.\n\
Alternate keys may also be used: '+', '-', and TAB." 11 72 7 \
"NETWORK" "Configure your network parameters" \
"ACTIVATE" "Activate the @CDISTRO@ PXE Server" \
"EXIT" "Exit @CDISTRO@ PXE Setup" 2> $TMP/hdset
if [ ! $? = 0 ]; then
rm -f $TMP/hdset $TMP/SeT*
exit
fi
MAINSELECT="`cat $TMP/hdset`"
rm $TMP/hdset
# Start checking what to do. Some modules may reset MAINSELECT to run the
# next item in line.
if [ "$MAINSELECT" = "NETWORK" ]; then
# Set up our network. We may not know anything yet in which case
# the variable $INTERFACE will be empty.
pxeconfig $INTERFACE
if [ -r $TMP/SeTpxe ]; then
MAINSELECT="ACTIVATE"
fi
fi
if [ "$MAINSELECT" = "ACTIVATE" ]; then
if [ ! -r $TMP/SeTpxe ]; then
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "CANNOT START PXE SERVER YET" --msgbox "\
\n\
Before you can start the PXE Server, complete the following task:\n\
\n\
(*) Set up your computer's network parameters.\n\
\n\
Press ENTER to return to the main menu." 14 68
continue
else
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --title "READY TO START PXE SERVER" --msgbox "\
\n\
Ready to start the PXE Server!\n\
The PXE server log will be displayed in the next screen.
\n\
Press ENTER to start or ESCAPE to return to the main menu." 14 68
if [ $? -ne 0 ]; then
# User did not press ENTER, let's return to main menu:
continue
fi
fi
# Time to start the BOOTP/TFTP/NFS servers:
echo > /var/log/pxe_dnsmasq.log
dnsmasq -C ${TMP}/pxe_dnsmasq.conf -z -i ${INTERFACE}
if ! grep -q "^/mnt/livemedia" /etc/exports ; then
# Without 'fsid' nfsd refuses to export the filesystem if it RAM based;
# the number '14' could be any unique low-range number:
cat <<EOT >> /etc/exports
/mnt/livemedia ${LOCAL_NETWORK}/${LOCAL_NETMASK}(ro,sync,insecure,no_subtree_check,root_squash,fsid=14)
EOT
fi
sh /etc/rc.d/rc.nfsd restart
if [ "$INTERFACE" != "$GLOBAL_GW_INT" ]; then
# The default gateway for this computer is on another interface;
# we need to enable forwarding:
OLDROUTING=$(cat /proc/sys/net/ipv4/ip_forward)
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ "${ENABLE_NAT}" == "yes" ]; then
# Add NAT firewall rule:
iptables -t nat -A POSTROUTING -o ${GLOBAL_GW_INT} -j MASQUERADE
iptables -A FORWARD -p ALL -i ${GLOBAL_GW_INT} -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -i ${GLOBAL_GW_INT} -j ACCEPT
else
if [ -z "$(pidof routed)" ]; then
# Also start the route daemon:
/usr/sbin/routed -g -s /var/log/routed_pxeserver.log
fi
fi
else
OLDROUTING=""
fi
if [ $DEBUG -ne 0 ]; then read -p "Press ENTER to continue: " JUNK ; fi
$DIALOG --backtitle "@CDISTRO@ Linux Live PXE Server." \
--title "PXE Client activity log" \
--ok-label "EXIT" \
--tailbox /var/log/pxe_dnsmasq.log 20 68
# Time to kill the BOOTP/TFTP/NFS servers and revert network settings:
if [ "${ENABLE_NAT}" == "yes" ]; then
# Remove NAT firewall rule:
iptables -D FORWARD -m state --state ESTABLISHED,RELATED -i ${GLOBAL_GW_INT} -j ACCEPT
iptables -D FORWARD -p ALL -i ${GLOBAL_GW_INT} -j ACCEPT
iptables -t nat -D POSTROUTING -o ${GLOBAL_GW_INT} -j MASQUERADE
fi
if [ -n "$OLDROUTING" ]; then
echo $OLDROUTING > /proc/sys/net/ipv4/ip_forward
fi
kill -TERM $(cat ${TMP}/pxe_dnsmasq.pid)
sh /etc/rc.d/rc.nfsd stop
sed -i -e "s%^/mnt/livemedia.*%#&%" /etc/exports
fi
if [ "$MAINSELECT" = "EXIT" ]; then
clear
if [ -x /etc/rc.d/rc.networkmanager 2>/dev/null ]; then
# Use nmcli to remove the NetworkManager connection:
nmcli con down pxe-${INTERFACE}
nmcli con del pxe-${INTERFACE}
else
# Manually bring the interface down:
dhcpcd -k ${INTERFACE} 2>/dev/null
ip link set dev ${INTERFACE} down
ip address flush dev ${INTERFACE}
fi
break
fi
done # end of main loop
# end @CDISTRO@ Linux Live PXE Server script