Раздача интернета с 3G модема в локальную сеть в Linux

от автора

Эта статья — продолжение статьи Беспроводная точка доступа, используя Linux. Тут я опишу, что же необходимо сделать для того, чтобы раздавать интернет с 3G-модема по уже созданной по инструкции из предыдущего топика вайфай-сети.

1) Прежде всего, научить Linux работать с модемом
2) Создать NAT для раздачи интернета
3) Запихнуть всё это дело в автозагрузку
Итак, bash, wvdial и iptables под мышку — и поехали!

Подключение USB 3G-модема

Бывает и так, что в некоторых странах есть свои провайдеры 3G-интернета, которые не предоставляют настроек для подключения, используя Linux, что, в общем-то, и понятно — ‘популярность’ как провайдера, так и Linux даёт о себе знать. Не все конфиги есть ещё на сайтах, тем более — для отдельных программ. Итак, в Латвии, где я и проживаю. есть два провайдера — LMT и Bite. Оба они предоставляют беспроводной интернет через модемы Huawei, залоченные, естественно, на них, ну да не в этом дело. Ну так вот — необходимо обеспечить интернет всюду, где есть 3G, используя модем и сервер. Что же делать?

Прежде всего, воткнуть модем в ноут. USB-модемы определяются в Linux как устройства под адресом /dev/ttyUSB*, где * — порядковый номер устройства, обычно адрес выглядит как /dev/ttyUSB0.

root@localhost:/# ls /dev/ttyUSB*  ls: cannot access /dev/ttyUSB*: No such file or directory

Ой. Что-то он не определяется. А проблема вот такая (обмусоленная уже тысячу раз): модем — это устройство типа “два в одном”. Почему? Он совмещает в одной флешке как собственно модем, так и встроенный накопитель с драйверами модема под Windows (я уже молчу про кардридер). В Linux по умолчанию включается режим диска, а не модема Для того, чтобы включить ещё и режим модема, нужно установить пакет usb-modeswitch. После этого нужно перезагрузиться и опять подключить модем, подождать секунд 10 и опять выполнить команду на вывод списка устройств модема:

root@localhost:/# ls /dev/ttyUSB*  /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 

Когда вывод походит на этот, всё отлично и можно двигаться дальше. У нас есть три устройства. Нам необходимо лишь одно — под номером 0, остальные 2 мы не используем — они не для наших целей. Насколько мне известно, одно из них, скорее всего, используется для отсылки СМС, а второе — для просмотра уровня сигнала сети и прочего.

Теперь — дело за программой, которая подключит нас. Я буду использовать программу wvdial, дополнительно к ней нужно установить пакет ppp, если он ещё не установлен.

apt-get install ppp wvdial

Многие советуют использовать программу wvdialconf для настройки подключения, но в данном случае она нам не поможет. После установки нам нужно отредактировать файл /etc/wvdial.conf. Стираем из него всё содержание, затем разбираемся в формате файла. Я предоставлю рабочие конфиги для провайдера LMT с тарифом OKarte Internets datorā и модемом Huawei E173 и Bite с неизвестным тарифом и модемом Huawei E1550.

[Dialer lmt]  Init1 = AT Init2 = AT&FE0V1X1&D2&C1S0=0 #Init3 = AT+CPIN="1219"  Init4 = AT+CGDCONT=1,"IP","internet.lmt.lv"  Phone = *99#  ISDN = 0 Username = { }  Password = { }  Ask Password = 0  Modem = /dev/ttyUSB0  PPPD Options = noauth crtcts multilink usepeerdns lock defaultroute nobsdcomp nodeflate refuse-pap refuse-eap refuse-chap refuse-mschap +chap  Idle Seconds = 3000  Modem Type = USB Modem  Compuserve = 0  Auto DNS = 1  Dial Command = ATD  Stupid Mode = 1  FlowControl = NOFLOW [Dialer bite]  Init1 = AT Init2 = AT&FE0V1X1&D2&C1S0=0 #Init3 = AT+CPIN="1219" Init4 = AT+CGDCONT=1,"IP","internet"  Phone = *99#  ISDN = 0  Username = { }  Password = { }  Ask Password = 0  Modem = /dev/ttyUSB0  PPPD Options = noauth crtcts multilink usepeerdns lock defaultroute nobsdcomp nodeflate refuse-pap refuse-eap refuse-chap refuse-mschap +chap  Idle Seconds = 3000  Modem Type = USB Modem Compuserve = 0  Auto DNS = 1  Dial Command = ATD  Stupid Mode = 1  FlowControl = NOFLOW

Вкратце — файл разделён на секции. Каждая из секций отвечает за одну комбинацию модем-провайдер. Начало секции обозначается меткой [Dialer xxx], где ххх — это название метки, по которой мы будем указывать, какие именно настройки нужны для подключения. Если нам потребуются настройки LMT, мы наберём команду wvdial lmt, и будут использоваться настройки из секции [Dialer lmt] — суть понятна. Из этих настроек нам нужно обратить внимание на следующие:

InitX = AT-BLABLABLA

— AT-команды после InitX — те команды, которые wvdial отсылает модему перед тем, как поднять подключение.

#Init3 = AT+CPIN="1219"

— Эта настройка, если убрать # в начале, будет посылать модему команду ввода пин-кода. Если честно, желательно её отключить — у меня эта команда по непонятным причинам не работала корректно. Легче просто подключить модем один раз к компьютеру с Windows и отключить ввод пин-кода при подключении, используя программу, поставляемую с модемом.

Init4 = AT+CGDCONT=1,"IP","internet" 

— Здесь прописывается адрес APN, который предоставляет провайдер. Нужно обратить внимание на две последних отделённых кавычками части. Первая — IP — указывает IP-адрес для подключения, если настройки провайдера подразумевают то, что используется IP-адрес APN. Если же используется буквенный адрес вида “internet” или “internet.lmt.lv”, в первой части нужно оставить “IP”, а во второй — прописать буквенный адрес, как это сделано в примере.

Phone = *99# 

— Ну тут всё стандартно — этот номер телефона используют практически все провайдеры, и менять его в большинстве случаев не понадобится.

Username = { }  Password = { } 

Имя пользователя и пароль для подключения к интернету. Если их нужно оставить пустыми, оставьте там скобочки вида { }. Если нет — просто поставьте там имя и пароль, без скобочек.

Modem = /dev/ttyUSB0

Имя устройства, которое нам нужно использовать. В 99% случаев оно будет именно таким.

Остальные параметры могут быть другими в случае других модемов, но для вышеперечисленных двух комбинаций модем-провайдер всё работает без проблем.

Ещё раз расскажу о том, как правильно запускать подключение вручную. Достаточно одной команды — wvdial xxx, где ххх — это название провайдера из конфигурационного файла (для меня это либо lmt, либо bite.) Однако — при запуске wvdial ”занимает собой” всю консоль, не давая возможности запустить что-либо ещё. Кроме того — если вы запустите wvdial в окне SSH и тут же разорвёте сессию, то и wvdial завершится. Нужно либо постоянно держать сессию открытой, либо использовать screen, который в данном случае решает сразу две проблемы довольно эффективно — что и советую.
Что в идеале нужно? Также научиться просто и легко запускать эти программы. В использовании мной описанной схемы есть свои нюансы:

1) Соединение нужно каждый раз запускать вручную.
— Достаточно немного изменить конфигурационные файлы системы, а именно — тот же /etc/network/interfaces:

auto ppp0  iface ppp0 inet wvdial  provider lmt #Поднимать интерфейс ppp0 автоматически #Для подключения вызывать команду wvdial с аргументом lmt. Естественно, аргумент будет меняться. 

Для меня этот способ не подходит — он рассчитан на то, что провайдер не меняется, но большая вероятность, что это понадобится кому-то ещё. Да и не особо-то надёжно это работает, по моему опыту, лучше настроить udev. Для себя же я не нашёл подходящих решений — для этого надо было бы определять принадлежность вставленной сим-карты тому или иному провайдеру, а решение с использованием этого становится очень сложным.
Ну а если всё же надо быть постоянно подключённым, даже если что-то глючит и модем отключается от сети? Ну тогда поможет следующий скрипт. Он смотрит, есть ли wvdial в списке процессов, а если нет, то делает ifup ppp0, что в совокупности с вышеупомянутыми настройками в interfaces должно вызывать wvdial заново:

Засунуть себе в cron

#!/bin/bash # (c)2009 John de Graaff, rewritten by CRImier # This script checks if wvdial is running. # If it's not, it brings ppp0 up and down. # It is assumed that ifup ppp0 starts wvdial if test "$(pidof wvdial)" != "" ;  then 	exit 0 else 	logger "wvdial not running. Better restart ppp0." 	/sbin/ifdown ppp0 	sleep 2 	/sbin/ifup ppp0 	logger "ppp0 restarted." 	exit 0

2) При включении ноутбука, если модем был подключен во время загрузки системы, иной раз случаются зависания, которые выражаются в следующем — при попытке подключения, используя wvdial, выходят строчки вида

--> Cannot open /dev/ttyUSB0: Device or resource busy 

, и подключиться не удаётся. Лечится на один раз просто — нужно лишь вынуть и воткнуть модем, а затем запустить соединение вручную, но вы же понимаете, что при отсутствии физического доступа к компьютеру эта задача усложняется до невозможности.
— Пока что я не могу предоставить нормального решения, поскольку сам ещё не занялся этим. Предполагают, что это из-за того, что программа usb-modeswitch не отрабатывает корректно, если модем вставлен в компьютер при запуске системы. Видимо, нужно покопаться с udev или указать какие-либо особые параметры для usb-modeswitch.

3) В условиях плохого приёма соединение часто обрубается
— Всё довольно просто. Дело в том, что у портов ЮСБ есть ограничение на отдаваемый ток, при превышении которого, насколько я помню, порт отрубается. Видимо, в условиях плохого приёма сигнала сети модем пытается повысить мощность приёмника и передатчика, и случается так, что модем начинает потреблять больший ток, чем выдерживает порт — порт отключается, модем выключается, соедниение отрубается насовсем. Посоветовать могу лишь, к примеру, купить отдельный адаптер питания для модема и впаять его в кабель.

После того, как интернет появился на нашем сервере, остаётся лишь настроить раздачу интернета с модема по Wi-Fi сети.

NAT

Если у компьютера есть два сетевых интерфейса, это ещё не означает, что из коробки можно спокойно раздавать интернет с одного на другой. Однако — не всё так сложно, чаще всего требуется всего пара настроек. Конечно, эти настройки сложно запомнить, не вникая в суть каждой строчки, но ведь для этого есть эта статья! Я нашёл наиболее подходящий для этой ситуации и безглючный скрипт, не могу не дать ссылку на него, поскольку найденный на нём скрипт самый короткий и ясный из тех, что я встречал — остальные умудряются растянуть пару правил iptables на несколько страниц… Прежде всего, посмотрю, что в нём надо бы изменить под мои нужды:

Найденный скрипт
Спойлер:

#!/bin/sh  PATH=/usr/sbin:/sbin:/bin:/usr/bin  # # delete all existing rules. # iptables -F iptables -t nat -F iptables -t mangle -F iptables -X  # Always accept loopback traffic iptables -A INPUT -i lo -j ACCEPT   # Allow established connections, and those not coming from the outside iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -m state --state NEW -i ! eth1 -j ACCEPT iptables -A FORWARD -i eth1 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT  # Allow outgoing connections from the LAN side. iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT  # Masquerade. iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE  # Don't forward from the outside to the inside. iptables -A FORWARD -i eth1 -o eth1 -j REJECT  # Enable routing. echo 1 > /proc/sys/net/ipv4/ip_forward

Хм-хм. Этот скрипт уже староват — iptables ругается на одну из команд и не хочет выполнять, да и тот путь, которым в статье скрипт пытаются поместить в автозагрузку, тоже работает не всегда на моей практике. Более того, есть проблема — этот скрипт отлично подходит для ситуации, когда ничего не собирается меняться. Если бы так и было, я бы поставил iptables-persistence и на этом закончил бы статью. А вот я собираюсь иногда получать интернет по интерфейсу ppp0, иногда — по eth0, а иногда — вообще по wlan1, причём менять интерфейс хочу одной консольной командой. Так, eth1 в примере — внешний интерфейс, а eth0 — внутренний. Заменим их переменными, чтобы при необходимости можно было поменять одну строчку, а не редактировать весь текст. Также я хочу, чтобы при перезапуске компьютера последний выбранный внешний интерфейс сохранялся. Что тогда? Нужно всё поменять!

Задачи:
  1. Принимать первый аргумент командной строки в качестве названия внешнего интерфейса, проверяя подлинность имени, используя команду ifconfig;
  2. Добавить сохранение выбранного интерфейса в какой-нибудь файл в /etc и сделать ключ выбора последнего интерфейса, а лучше — при отсутствии имени интерфейса как аргумента.
  3. Запихнуть это всё красиво в автозагрузку и в $PATH.

Что же вышло в итоге?

#!/bin/bash #NAT script from www.debian-administration.org, modified by CRImier # Exit status 0 if operation is correct # Exit status 1 if trying to use last interface used when running for the first time # Exit status 2 if interface doesn't exist EIF='' IIF='wlan0' PATH=/usr/sbin:/sbin:/bin:/usr/bin LOGFILE=/etc/nat-if.conf touch $LOGFILE  # #Checking command-line arguments and setting $EIF variable according to them #  if [ $1 == "" ] #If there's no arguments, just use previous settings. then 	EIF=`cat $LOGFILE` 	if [ $EIF == "" ] #Just check for an empty file! 	then 		echo "Please, specify interface name for first usage using 'firewall interface', e.g. 'firewall eth0'" 		exit 1 	fi elif [ $1 == "help" ] #Output help message then 	echo "NAT script" 	echo "(c) www.debian-administration.org, modified by CRImier" 	echo "Usage: 'firewall interface', 'firewall info' or simply 'firewall' to use last interface firewall was set on." 	echo "Argument is external interface name, internal interface name is hard-coded in the script" 	exit 0 elif [ $1 == "info" ] #Print interface firewall is set on then  	cat $LOGFILE 	exit 0 else 	ifconfig $1 &>/dev/null 	if [ $? == 0 ] 	then #Interface name must be correct as ifconfig gives 0 exit code 		EIF=$1 		echo $EIF > $LOGFILE  	else  		echo "Incorrect interface name" 		exit 2 	fi fi  # #$EIF is set correctly, let's apply the rules: #  iptables -F  iptables -t nat -F iptables -t mangle -F iptables -X iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i $EIF -o $IIF -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i $IIF -o $EIF -j ACCEPT iptables -t nat -A POSTROUTING -o $EIF -j MASQUERADE iptables -A FORWARD -i $EIF -o $IIF -j REJECT echo 1 > /proc/sys/net/ipv4/ip_forward echo "Firewall started."

Комментарии писал на английском — так привычнее. Если будут просьбы — могу и перевести.

Ну и не забываем обязательную часть:

chmod +x /etc/init.d/user-autorun

Окей, скрипт у нас готов. Как можно понять, вариантов вызова четыре — firewall (используется последний интерфейс), firewall наш_интерфейс, firewall info (выводит текущий интерфейс, на котором настроен NAT) или firewall help. Осталась лишь автозагрузка и $PATH.

echo $PATH  >/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Для того, чтобы вызывать скрипт командой firewall, не указывая местоположение, нужно запихнуть его в одну из папок, указанных в PATH. Я предпочитаю /usr/local/bin по религиозным соображениям. Полный путь к скрипту будет /usr/local/bin/firewall, а вот вызвать из консоли его всегда можно будет просто командой firewall.

Автозагрузка

А теперь — автозагрузка, с ней посложнее. Я сразу опишу создание скрипта автозагрузки, в который можно будет запихнуть всё, что угодно. Он будет стартовать вместе с системой, нооо…

Нельзя просто так взять и создать файл автозагрузки. Есть одна проблема — Debian с какого-то времени пересмотрел свои требования к файлам автозагрузки. Файл мало просто создать, его нужно ещё по-особому отформатировать:

  1. Первая проблема — это LSB headers. Это заголовок файла автозагрузки. Нужен он потому, что компоненты автозагрузки должны выполняться в определённом порядке, поскольку часть из них зависят друг от друга. Предположим, у вас есть два скрипта в автозагрузке — один из них должен будет монтировать сетевую папку, а второй — делать в неё резервную копию файлов. Естественно, что сначала нужно выполнить первый, а потом — второй, поскольку второй зависит от первого. Для указания таких зависимостей и используются заголовки загрузочного файла. Впрочем, будет достаточно того заголовка, который я выложу в образце файла автозагрузки.
  2. Вторая проблема — любой скрипт в автозагрузке при запуске системы вызывается командой /etc/init.d/script start, а при выключении компьютера — командой /etc/init.d/script stop. Нужно добавить условия для обработки этих случаев.

Я сделал просто — взял за основу скрипт из имеющихся в /etc/init.d/ — уж они-то должны быть созданы по правилам, потом изучил этот скрипт и вырезал из него всё ненужное. Осталось два места, которые нужно изменить — место для команд, которые выполняются при запуске системы, и место для команд, которые выполняются при выключении компьютера. Впрочем, сейчас всё увидите:

#!/bin/sh  ### BEGIN INIT INFO # Provides:          firewall # Required-Start:    $network $local_fs $remote_fs # Required-Stop:     $network $local_fs $remote_fs # Default-Start:     2 3 4 5 # Default-Stop:      0 1 6 # X-Interactive:     false # Short-Description: Start user autorun events ### END INIT INFO  case "$1" in 	start) 		echo "Starting user autorun events" 		/usr/local/bin/firewall  		#Место для команд, которые должны выполниться при запуске системы 		;; 	stop) 		echo "Stopping user autorun events" 		#Место для команд, которые должны выполниться при завершении работы системы 		#Останавливать NAT нет необходимости 		;; 	*) 		echo "Usage: /etc/init.d/user-autorun {start|stop}" 		exit 1 		;; esac  exit 0

Опять же, дать права на исполнение:

chmod +x /etc/init.d/user-autorun

В файле автозагрузки лучше указывать полный путь к исполняемому файлу, поскольку иначе при загрузке иногда возникают проблемы вида “firewall: command not found”.

Этот файл кладём в папку /etc/init.d/. Полный путь к нашему файлу автозагрузки — /etc/init.d/user-autorun. Осталось лишь указать системе, что нужно выполнять этот файл при загрузке:

update-rc.d user-autorun defaults

Эта команда заодно и проверяет, соответствует ли заголовок скрипта нужному, поэтому — если с этим будут проблемы, ничего в автозагрузку не поставится и придётся разбираться с ошибками. Всё, скрипт автозагрузки готов к работе и будет выполняться каждый раз при запуске системы, запуская скрипт маршрутизации. Конечно, в данном решении есть свои недостатки, вроде невозможности как-либо контролировать доступ пользователей к Интернету, кроме отключения-включения самого скрипта, но для случая переносного сервера плюс один и огромный — это просто работает, без вмешательства и стабильно, а альтернативные системы при надобности я ещё успею рассмотреть.
Удачной настройки!


Следующая статья, скорее всего, будет про написание простого веб-интерфейса на Python, используя web.py. Через этот интерфейс можно будет управлять NAT (правда, не превышая возможностей написанного скрипта), включать/выключать wvdial, отсылать смс и просматривать состояние модема… А также делать всё, до чего дойдут руки. Пока что пишу скрипт для взаимодействия с модемом и продумываю интерфейс таким образом, чтобы его было легко использовать даже на мобильных устройствах. Также в запасе есть почти готовая статья по настройке параметров энергосбережения ноутбука, используя cpufreqd. Стоит ли выкладывать её, будет ли актуально?

Аргументированная критика и дополнения к статье категорически приветствуются.

ссылка на оригинал статьи http://habrahabr.ru/post/188886/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *