Волей судеб понадобилось в ArchLinux поднять несколько VLAN. Привычным движением открываю файл
/etc/rc.conf
на редактирование, прописываю туда:
eth2v0001=“eth2.0001 192.168.0.254 netmask 255.255.255.0”
добавляю eth2v0001
в INTERFACES
:
INTERFACES=(… eth2v0001 …)
и поднимаю интерфейс: /etc/rc.d/network ifup eth2v0001
и… получаю ругань что такого интерфейса не существует.
В чем проблема?.. Иду на
http://wiki.archlinux.org и делаю поиск по слову VLAN, нахожу всего одну
статью:
High Performance Firewall/Nat with iptables and VLANs and iproute2
в которой упоминается программа vconfig
из одноименного пакета - именно она нужна для настройки VLAN
на сетевых интерфейсах.
Ладно, с этим всё хорошо, но неужели ручками настраивать все VLAN которые потребуются?
Почесав макушку, иду смотреть содержимое файла /etc/rc.d/network
и нахожу, что для VLAN там
нет вообще ничего.
“Если гора не идет к Магомеду, то Магомед идет к горе”, - подумал я, и краем глаза посмотрел как
процедура сделана в Debian Linux. Там в директории /etc/network/if-pre-up.d
лежит скрипт vlan
который занимается конфигурированием VLAN. В директории /etc/network/if-post-down.d
скрипт vlan
занимается подчисткой следов после своей работы - сиречь удаляет созданные VLAN.
Поразмыслив, решаю допиливать скрипт настройки сети в Arch.
Для начала ставим нужные пакеты: pacman -S vconfig iproute
Создаём файл /etc/rc.d/network_vlan
со следующим содержимым:
#!/bin/bash
. /etc/rc.conf
. /etc/rc.d/functions
# wireless settings
[ -f /etc/conf.d/wireless ] && . /etc/conf.d/wireless
# ethernet bonding settings
[ -f /etc/conf.d/bonding ] && . /etc/conf.d/bonding
# bridge settings
[ -f /etc/conf.d/bridges ] && . /etc/conf.d/bridges
# dhcpcd settings
[ -f /etc/conf.d/dhcpcd ] && . /etc/conf.d/dhcpcd
# Code taked from Debian distribution
vlan_up()
{
local iface
local VLANID
local IF_VLAN_RAW_DEVICE
iface=$1
case "$iface" in
vlan0*)
vconfig set_name_type VLAN_PLUS_VID > /dev/null 2>&1
VLANID=`echo $iface|sed "s/vlan0*//"`
;;
vlan*)
vconfig set_name_type VLAN_PLUS_VID_NO_PAD > /dev/null 2>&1
VLANID=`echo $iface|sed "s/vlan0*//"`
;;
eth*.0*)
vconfig set_name_type DEV_PLUS_VID > /dev/null 2>&1
VLANID=`echo $iface|sed "s/eth[0-9][0-9]*<br/>.0*//g"`
IF_VLAN_RAW_DEVICE=`echo $iface|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
;;
eth*.*)
vconfig set_name_type DEV_PLUS_VID_NO_PAD > /dev/null 2>&1
VLANID=`echo $iface|sed "s/eth[0-9][0-9]*<br/>.0*//g"`
IF_VLAN_RAW_DEVICE=`echo $iface|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
;;
*) return 0
;;
esac
if [ -n "$IF_VLAN_RAW_DEVICE" ]
then
if [ ! -x /usr/sbin/vconfig ]; then
return 0
fi
if ! ip link show dev "$IF_VLAN_RAW_DEVICE" > /dev/null; then
#echo "$IF_VLAN_RAW_DEVICE does not exist, unable to create $iface"
return 1
fi
ip link set up dev $IF_VLAN_RAW_DEVICE > /dev/null 2>&1
vconfig add $IF_VLAN_RAW_DEVICE $VLANID > /dev/null 2>&1
fi
return 0
}
vlan_down()
{
local iface
local IF_VLAN_RAW_DEVICE
iface=$1
case "$iface" in
eth*.0*)
IF_VLAN_RAW_DEVICE=`echo $IFACE|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
;;
eth*.*)
IF_VLAN_RAW_DEVICE=`echo $IFACE|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
;;
*) return 0
;;
esac
if [ -z "$IF_VLAN_RAW_DEVICE" ]; then
return 0
fi
if [ ! -x /usr/sbin/vconfig ]; then
return 0
fi
vconfig rem $face > /dev/null 2>&1
}
ifup()
{
if [ "$1" = "" ]; then
echo "usage: $0 ifup <interface_name>"
return 1
fi
wi_up $1 || return 1
eval ifcfg="<br/>$${1}"
if [ "$ifcfg" = "dhcp" ]; then
# remove the .pid file if it exists
/bin/rm -f /var/run/dhcpcd-${1}.pid >/dev/null 2>&1
/bin/rm -f /var/run/dhcpcd-${1}.cache >/dev/null 2>&1
/sbin/dhcpcd $DHCPCD_ARGS ${1}
else
iface=`echo $ifcfg | awk '{print $1}'`
vlan_up $iface || return 1
/sbin/ifconfig $ifcfg
fi
return $?
}
wi_up()
{
eval iwcfg="<br/>$wlan_${1}"
[ "$iwcfg" = "" ] && return 0
/usr/sbin/iwconfig $iwcfg
[[ -z "$WIRELESS_TIMEOUT" ]] && WIRELESS_TIMEOUT=2
sleep $WIRELESS_TIMEOUT
bssid=`iwgetid $1 -ra`
if [[ "$bssid" = "00:00:00:00:00:00" ]]; then
printhl "Could not associate $1 - try increasing WIRELESS_TIMEOUT and check network is WEP or has no security"
return 1
fi
return 0
}
ifdown()
{
if [ "$1" = "" ]; then
echo "usage: $0 ifdown <interface_name>"
return 1
fi
eval ifcfg="<br/>$${1}"
if [ "$ifcfg" = "dhcp" ]; then
if [ -f /var/run/dhcpcd-${1}.pid ]; then
/bin/kill $(cat /var/run/dhcpcd-${1}.pid)
fi
fi
# Always bring the interface itself down
/sbin/ifconfig ${1} down >/dev/null 2>&1
iface=`echo $ifcfg | awk '{print $1}'`
vlan_down $iface || return 1
return 1
}
iflist()
{
local iface
for ifline in ${INTERFACES[@]}; do
if [ "$ifline" = "${ifline#!}" ]; then
printf " $ifline:<br/>t"
else
printf "$ifline:<br/>t"
fi
eval real_ifline=<br/>$${ifline#!}
iface=`echo $real_ifline | awk '{print $1}'`
if [ x"$iface" != x"dhcp" ]; then
is_run=`ifconfig $iface 2>/dev/null | grep 'inet addr'`
if [ -z "$is_run" ]; then
run='( )'
else
run='(*)'
fi
else
run='(?)'
fi
echo $real_ifline $run
done
}
rtup()
{
if [ "$1" = "" ]; then
echo "usage: $0 rtup <route_name>"
return 1
fi
eval routecfg="<br/>$${1}"
if grep -q :: <<< $routecfg; then
/sbin/route -A inet6 add $routecfg
else
/sbin/route add $routecfg
fi
return $?
}
rtdown()
{
if [ "$1" = "" ]; then
echo "usage: $0 rtdown <route_name>"
return 1
fi
eval routecfg="<br/>$${1}"
if grep -q :: <<< $routecfg; then
/sbin/route -A inet6 del $routecfg
else
/sbin/route del $routecfg
fi
return $?
}
rtlist()
{
for rtline in ${ROUTES[@]}; do
if [ "$rtline" = "${rtline#!}" ]; then
printf " $rtline:<br/>t"
else
printf "$rtline:<br/>t"
fi
eval real_rtline=<br/>$${rtline#!}
echo $real_rtline
done
}
bond_up()
{
for ifline in ${BOND_INTERFACES[@]}; do
if [ "$ifline" = "${ifline#!}" ]; then
eval bondcfg="<br/>$bond_${ifline}"
/sbin/ifenslave $ifline $bondcfg || error=1
fi
done
}
bridge_up()
{
for br in ${BRIDGE_INTERFACES[@]}; do
if [ "$br" = "${br#!}" ]; then
# if the bridge already exists, remove it
if [ "$(/sbin/ifconfig $br 2>/dev/null)" ]; then
/sbin/ifconfig $br down
/usr/sbin/brctl delbr $br
fi
/usr/sbin/brctl addbr $br
eval brifs="<br/>$bridge_${br}"
for brif in $brifs; do
if [ "$brif" = "${brif#!}" ]; then
/usr/sbin/brctl addif $br $brif || error=1
fi
done
fi
done
}
bridge_down()
{
for br in ${BRIDGE_INTERFACES[@]}; do
if [ "$br" = "${br#!}" ]; then
/usr/sbin/brctl delbr $br
fi
done
}
case "$1" in
start)
if ! ck_daemon network; then
echo "Network is already running. Try 'network restart'"
exit
fi
stat_busy "Starting Network"
error=0
# bring up bridge interfaces
bridge_up
# bring up ethernet interfaces
for ifline in ${INTERFACES[@]}; do
if [ "$ifline" = "${ifline#!}" ]; then
ifup $ifline || error=1
fi
done
# bring up bond interfaces
bond_up
# bring up routes
for rtline in "${ROUTES[@]}"; do
if [ "$rtline" = "${rtline#!}" ]; then
rtup $rtline || error=1
fi
done
if [ $error -eq 0 ]; then
add_daemon network
stat_done
else
stat_fail
fi
;;
stop)
#if ck_daemon network; then
# echo "Network is not running. Try 'network start'"
# exit
#fi
stat_busy "Stopping Network"
rm_daemon network
error=0
for rtline in "${ROUTES[@]}"; do
if [ "$rtline" = "${rtline#!}" ]; then
rtdown $rtline || error=1
fi
done
for ifline in ${INTERFACES[@]}; do
if [ "$ifline" = "${ifline#!}" ]; then
ifdown $ifline || error=1
fi
done
# bring down bridge interfaces
bridge_down
if [ $error -eq 0 ]; then
stat_done
else
stat_fail
fi
;;
restart)
$0 stop
/bin/sleep 2
$0 start
;;
hotplug_ifup|ifup|ifdown|iflist|rtup|rtdown|rtlist)
$1 $2
;;
*)
echo "usage: $0 {start|stop|restart}"
echo " $0 {ifup|ifdown|iflist|rtup|rtdown|rtlist}"
esac
# vim: set ts=2 noet:
Он основан на оригинальном скрипте network
из пакета initscripts
. Кроме поддержки VLAN
там подправлен немного вывод iflist
который добавляет в конце каждый строки с интерфейсом:
(*)
- интерфейс поднят( )
- интерфейс не поднят(?)
- dhcp
Т.к. это скрипт основан на оригинальном, имеет смысл предоставить diff между оригиналом и новым файлом:
--- network 2008-09-19 07:26:34.000000000 +1100
@@ -12,6 +12,83 @@
# dhcpcd settings
[ -f /etc/conf.d/dhcpcd ] && . /etc/conf.d/dhcpcd
+# Code taked from Debian distribution
+vlan_up()
+{
+ local iface
+ local VLANID
+ local IF_VLAN_RAW_DEVICE
+ iface=$1
+
+ case "$iface" in
+ vlan0*)
+ vconfig set_name_type VLAN_PLUS_VID
+ VLANID=`echo $iface|sed "s/vlan0*//"`
+ ;;
+ vlan*)
+ vconfig set_name_type VLAN_PLUS_VID_NO_PAD
+ VLANID=`echo $iface|sed "s/vlan0*//"`
+ ;;
+ eth*.0*)
+ vconfig set_name_type DEV_PLUS_VID
+ VLANID=`echo $iface|sed "s/eth[0-9][0-9]*<br/>.0*//g"`
+ IF_VLAN_RAW_DEVICE=`echo $iface|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
+ ;;
+ eth*.*)
+ vconfig set_name_type DEV_PLUS_VID_NO_PAD
+ VLANID=`echo $iface|sed "s/eth[0-9][0-9]*<br/>.0*//g"`
+ IF_VLAN_RAW_DEVICE=`echo $iface|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
+ ;;
+ *) return 0
+ ;;
+ esac
+
+ if [ -n "$IF_VLAN_RAW_DEVICE" ]
+ then
+ if [ ! -x /usr/sbin/vconfig ]; then
+ return 0
+ fi
+
+ if ! ip link show dev "$IF_VLAN_RAW_DEVICE" > /dev/null; then
+ echo "$IF_VLAN_RAW_DEVICE does not exist, unable to create $iface"
+ return 1
+ fi
+ ip link set up dev $IF_VLAN_RAW_DEVICE
+ vconfig add $IF_VLAN_RAW_DEVICE $VLANID
+ fi
+
+ return 0
+}
+
+vlan_down()
+{
+ local iface
+ local IF_VLAN_RAW_DEVICE
+
+ iface=$1
+
+ case "$iface" in
+ eth*.0*)
+ IF_VLAN_RAW_DEVICE=`echo $IFACE|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
+ ;;
+ eth*.*)
+ IF_VLAN_RAW_DEVICE=`echo $IFACE|sed "s/<br/>(eth[0-9][0-9]*<br/>)<br/>..*/<br/>1/"`
+ ;;
+ *) return 0
+ ;;
+ esac
+
+ if [ -z "$IF_VLAN_RAW_DEVICE" ]; then
+ return 0
+ fi
+
+ if [ ! -x /usr/sbin/vconfig ]; then
+ return 0
+ fi
+
+ vconfig rem $face
+}
+
ifup()
{
if [ "$1" = "" ]; then
@@ -28,6 +105,9 @@
/bin/rm -f /var/run/dhcpcd-${1}.cache >/dev/null 2>&1
/sbin/dhcpcd $DHCPCD_ARGS ${1}
else
+ iface=`echo $ifcfg | awk '{print $1}'`
+ vlan_up $iface || return 1
+
/sbin/ifconfig $ifcfg
fi
return $?
@@ -63,12 +143,17 @@
fi
fi
# Always bring the interface itself down
1. /sbin/ifconfig ${1} down >/dev/null 2>&1
1. return $?
+ /sbin/ifconfig ${1} down >/dev/null 2>&1 || return 1
+
+ iface=`echo $ifcfg | awk '{print $1}'`
+ vlan_down $iface || return 1
+
+ return 1
}
iflist()
{
+ local iface
for ifline in ${INTERFACES[@]}; do
if [ "$ifline" = "${ifline#!}" ]; then
printf " $ifline:<br/>t"
@@ -76,7 +161,20 @@
printf "$ifline:<br/>t"
fi
eval real_ifline=<br/>$${ifline#!}
1. echo $real_ifline
+
+ iface=`echo $real_ifline | awk '{print $1}'`
+ if [ x"$iface" != x"dhcp" ]; then
+ is_run=`ifconfig $iface | grep 'inet addr'`
+ if [ -z "$is_run" ]; then
+ run='( )'
+ else
+ run='(*)'
+ fi
+ else
+ run='(?)'
+ fi
+
+ echo $real_ifline $run
done
}
Можно скачать файлом: network_vlan.diff
В заключении немного об именовании интерфейсов и переменных в /etc/rc.conf
.
Имя интерфейса с VLAN должно быть одним из:
eth#.#
- если сразу после точки идет цифра отличная от нуля (ex.: eth2.1, eth2.815)eth#.####
- если сразу после точки идет ноль (0), тогда обязательно VLAN ID должен быть расширен до 4 цифр (ex.: eth2.0001, eth2.0814). Что будет если вы этого не сделаете? Попробуйте сами, а найдете решение как обойти - дайте знать ;)
Есть ещё варианта интерсейсов vlan#
и vlan####
, но я с ними не сталкивался и ничего сказать не могу.
Кто может дополнить - с радостью выслушаю :)
Внутри конфига /etc/rc.conf
я выработал следующие правила для именования переменных с параметрами интерфейсов:
eth#
- для основного интерфейсаeth#a#
- для алиасов (eth2:1)eth#v#
илиeth#v####
- для VLAN (eth2.1)
Примеры: eth2=“eth2 192.168.0.1 netmask 255.255.255.0” eth2a1=“eth2:1 192.168.1.130 netmask 255.255.255.255” eth2v814=“eth2.814 192.168.2.130 netmask 255.255.255.255”
Вот в общем и всё. Сейчас ещё патч запщу на http://bugs.archlinux.org.
UPD:
Хех… я не один такой оказывается: FS#10420 - VLAN support for Arch default network script
там же есть вариант патча для ethX.Y Сейчас своё добавлю ;)
UPD2:
Да, возможно придется добавить модуль ядра 8021q
в строчку MODULES
в /etc/rc.conf