Простейшая конфигурация ser.cfg
Довольно неплохая идея - это первоначальный запуск сервера SER с фалом конфигурации ser.cfg, который содержит только самые базовые настройки, чтобы убедиться в работоспособности самого SIP сервера.
Что должно содержаться в этом файле ser.cfg:
- Настройка SIP сервера для Вашей внутренней сети.
- Она должна позволять подключиться Вашим IP телефонам в локальной сети к серверу SER.
- Должна позволять Вам совершать вызовы между IP телефонами в локальной сети.
Чего НЕ должно быть в этом файле ser.cfg:
- Не должно быть никакой авторизации IP телефонов и какой-либо работы с базами данных. Все это будет добавлено позже.
- Не должен содержать в себе поддержку вызовов в публичную телефонную сеть, только телефоны в пределах локальной сети могут совершать вызовы между собой.
Листинг файла ser.cfg
Приведенный ниже пример - это простейший файл конфигурации ser.cfg удовлетворяющий вышеописанным требованиям. Вы можете также получить простейший файл конфигурации с сайта http://www.ONsip.org.
Листинг содержит описания производимых действий.
debug=3 # Уровень отладочной информации для SER, варьируя значение директивы debug, # Вы определяете объем отладочной информации. Номинальное значение - 3, # его обычно хватает, чтобы получить достаточную информацию при возникновении ошибок. # Директива debug определяет, какое количество информации будет поступать в syslog. # Чем больше число, тем более подробный лог #ведет сервер SER. # Самый высокий уровень отладки - 9. # Когда уровень отладки установлен больше, чем 3 - логи сервера SER становятся очень # подробными, и время запуска может стать намного больше. fork=no # Директива fork сообщает серверу SER выполняться в фоновом режиме (как демон) или нет. # Когда у Вас все готово для работы сервера SER, как системное приложение, # Вы должны установить эту директиву в yes. Пока мы будем работать с сервером SER # как с обычным процессом. log_stderror=yes # Так как мы запускаем SER, как обычный процесс (не в фоне), мы должны установить # директиву log_stderror в yes, чтобы иметь возможность видеть сообщения на экране. listen=192.0.2.13 # ЗДЕСЬ ДОЛЖЕН БЫТЬ ВАШ IP АДРЕС # Эта директива указывает маршрутизатору SER, использовать для приема SIP трафика указанный IP адрес. # Ваш сервер будет принимать SIP сообщения __только__ на указанный в этой директиве IP адрес. # Если Вы не пропустите это директиву, тогда SER будет принимать SIP сообщения, # которые адресованны на все IP адреса сервера. # # Обратите внимание: Когда вы запустите SER, то он сообщит обо всех интерфейсах, которые он слушает. port=5060 # В дополнение к заданному IP адресу для приема SIP сообщений, Вы также можете определить номер порта. # Порт, используемый по умолчанию у большинства SIP маршрутизаторов, имеет номер - 5060. # Если Вы пропустите эту директиву, то SER будет использовать порт с номером 5060. children=4 # Директива children указывает серверу SER сколько дочерних процессов он может создать при его запуске. # Оптимальное значение - 4, однако, при использовании в производственной среде, # Вам, вероятно, потребуется увеличить это число. dns=no rev_dns=no # Этими директивами мы указываем серверу SER не делать попыток поиска его IP адреса в DNS. # Используя эти две строки в файле конфигурации ser.cfg, мы подавляем любые предупреждения в том случае, # если IP адрес вашего сервера не найден на вашем DNS сервере. fifo="/tmp/ser_fifo" # Директива fifo определяет местоположение файлов для FIFO сервера SER. # FIFO - похоже на файловая систему proc операционной системы Linux и может использоваться # для просмотра текущих действий, производимых SIP прокси сервером. # FIFO также может использоваться, если Вы хотите вставить новое SIP сообщение непосредственно # в SIP прокси сервер из внешнего приложения. Хороший пример - это утилита serctl, # которая обычно находиться в директории /usr/local/sbin/. С ее помощью Вы можете управлять # пользователями, "пинговать" SIP URI и даже отправлять email SIP пользователям. # Утилита serctl производит все эти действия, внутри себя, используя буфер FIFO маршрутизатора SER. # # FIFO может быть расположен где-нибудь на диске, однако, учетная запись под которой выполняется SER, # должна быть в состоянии создавать файлы FIFO в указанном каталоге. loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" # Тут мы подключаем внешние модули, которые необходимы для функционирования нашей # простейшей конфигурации SIP прокси сервера. Модули SER могут располагаться где угодно # на диске, но, по умолчанию, они располагаются в директории /usr/local/lib/ser/modules. # Загрузка модулей столь же проста, как это и видно в вышеприведенных директивах # loadmodule. # # Вы можете задается вопросом, как Вы можете узнать какие модули Вам нужны, а какие - нет, # однако, однозначного способа это узнать - нет. Вообще модули, которые указаны в этом # примере, будут необходимы для любой конфигурации SIP прокси сервера. # Когда Вам потребуется расширить функциональные возможности, например, Вы захотите # использовать базу данных MySQL, тогда Вам понадобиться загрузить соответствующий модуль, # в этом случае - это модуль mysql.so. # # Все модули сервера SER поставляются вместе с его исходными кодами и расположены в # каталоге <ser-source>/modules. Для каждого модуля имеется README файл, который # описывает все параметры, которые он может получать из файла конфигурации ser.cfg. # Очень рекомендуется, потратить некоторое время на ознакомление с описанием модулей, # идущих в поставке вместе с сервером SER. Этот документ пытается объяснить работу самых # важных модулей SER и методы их использования, но он ни в коем случае не заменяет # информацию из файлов README, этих модулей. # # Некоторые модули имеют параметры, которые должны быть установлены в файле ser.cfg # для того, чтобы модуль мог функционировать должным образом. # Однако, в большинстве случаев, для других модулей не требуется указывать каких-либо # параметров. В следующем разделе мы укажем параметры, которые используются модулями # в нашей простейшей конфигурации. modparam("usrloc", "db_mode", 0) # Модуль Usrloc отвечает за хранение и отслеживанием местоположениями SIP клиента при его # регистрации на сервере. Другими словами, когда SIP клиент регистрируется на SIP прокси # сервере, SER сохранит контактную информацию в таблице, также известную как # "Адрес Записи" Address Of Record (AOR). Эта таблица просматривается, когда другой SIP # клиент производит вызов. Местоположение этой таблицы может меняться в зависимости от # значения параметра db_mode модуля usrloc . # # Для некоторых SIP прокси устанавливают параметр db_mode в ноль, для указания того, # что мы не хотим сохранять регистрационные данные в базе данных. # Вместо этого, мы будем хранить эту информацию только в памяти. # # В дальнейших примерах мы покажем, как еще можно сохранять записи AOR. modparam("rr", "enable_full_lr", 1) # Параметр rr называется enable_full_lr - это обходное решение для старых SIP клиентов, # которые должным образом не обрабатывают SIP заголовки record-route. В SIP RFC3261 # говорится, что для включения режима свободной маршрутизации (loose routing) # нужно указать ;lr в SIP заголовке record-route - что является правильным способом # сказать другим SIP прокси серверам, что наш SIP сервер является маршрутизатором # со свободным выбором маршрутов. Однако, некоторые старые и/или неправильно работающие # SIP клиенты некорректно удаляют ;lr тег, потому что они не видят значения связанного с # этим параметром (т.е, такого: lr=true). # # Поэтому, устанавливая параметр enable_full_lr в единицу, SER будет записывать # все теги ;lr как: ;lr=on , чтобы избежать вышеописанной проблемы. route { # Далее следует логика обработки SIP сообщений. # В этой строке определяется основной блок маршрутизации. Блок маршрутизации должен # начинаться и заканчиваться фигурной скобкой (соответственно открывающей и # закрывающей блок). # # Главный маршрутизационный блок - это то место, куда направляются все принятые SIP сообщения. # Из этого блока Вы можете вызывать другие маршрутизационные блоки, проверять сообщения # на соответствие определенным условиям, отвергать сообщения, передать сообщение дальше по цепочке, # и главное, делать с ними все что Вам нужно, в зависимости от Ваших потребностей. # # Вот обзор основных производимых действий на сообщениями: # # 1. Сообщение попадает в главный маршрутизационный блок, и мы производим некоторые проверки. # # 2.Определяем, адресовано ли это сообщение нам, если нет, то пересылаем его по адресу назначения (route[1]). # # 3. Если оно адресовано нам, то мы отдельно обрабатываем SIP сообщение REGISTER # (сообщение с телефона, который хочет себя зарегистрировать). Блок Route[2] обрабатывает SIP сообщения # REGISTER, сохраняя местоположение телефона в сети, в то время как блок Route[1] - обрабатывает # все остальные SIP сообщения, по умолчанию. # # Стоит отметить: Может показаться, что тут представлена не совсем четкая функциональность # в обработке сообщений, но эта структура будет использоваться как основа для дальнейших # более сложных конфигураций ser.cfg, которые будут приведены далее. # # Детальное описание действий приведено ниже. # ------------------------------------------------------------------------ # Sanity Check Section # Секция проверки параметров. # ------------------------------------------------------------------------ if (!mf_process_maxfwd_header("10")) { # mf_process_maxfwd_header - это проверка, которую Вы всегда должны включать, как первое действие # в Вашем основном маршрутизационном блоке. Эта функция модуля mf.so. Используется для того, # чтобы сохранять количество прохождений SIP сообщения через сервер SER. Ошибки в файле ser.cfg # могут приводить к отправке SIP сообщения по неправильному адресу, что может вызвать зацикливание. # Также, другие SIP прокси сервера, с которыми взаимодействует SER, также могут сделать то же самое. # # Основная задача этого правила - в том, что, если эта функция возвращает 'true', тогда Вам необходимо # прекратить обработку этого проблематичного SIP сообщения, чтобы избежать бесконечного цикла. sl_send_reply("483", "Too Many Hops"); # В случае обнаружения зацикливания, серверу SER необходимо сообщить SIP клиенту о факте # обнаружении ошибки. Функция Sl_send_reply() производит эти действия. sl_send_reply () - это функция # модуля sl.so, и все что она делает - это отправляет stateless SIP сообщение SIP клиенту. # Это означает, что маршрутизатор SER отправит это сообщение и забудет о нем. # Он не будет пытаться повторно его отправить в случае, если оно не достигло получателя, # и не будет ждать ответное сообщение. # # Вы можете определить свое соответствующее сообщение об ошибке, как показано в функции. # Это сообщение должно состоять из комбинации кода ошибки SIP сообщения и текстовым сообщением # о ее причине. Многие IP телефоны отображают полученное текстовое сообщение пользователю. break; # Оператор break; указывает маршрутизатору SER прекратить обработку SIP сообщения # и выйти из маршрутизационного блока, в котором это сообщение обрабатывается в данный момент. # Так как мы вызываем break; в основном маршрутизационном блоке, то SER полностью прекратит # обрабатывать текущее SIP сообщение. }; if (msg:len > max_len) { # msg:len - это функция ядра сервера SER, которая возвращает размер в байтах текущего SIP сообщения. # Эту проверку , как и mf_process_maxfwd_header(), нужно вызывать в начале главного маршрутизационного # блока во всех файлах ser.cfg. # # Эта проверка просто проверяет длину SIP сообщения на предмет максимально разрешенного размера. # Если SIP сообщение является слишком длинным, то мы прекращаем его обрабатывать, т.к. мы обнаружили # потенциальную опасность переполнение буфера. sl_send_reply("513", "Message Overflow"); break; }; # ------------------------------------------------------------------------ # Record Route Section # Секция записи маршрутов. # ------------------------------------------------------------------------ if (method!="REGISTER") { # Тут мы пытаемся определить, является ли полученное SIP сообщение, сообщением REGISTER. # Если это не сообщение REGISTER, тогда мы должны добавить маршрутизационную запись в это # сообщение для гарантии того, чтобы предыдущий и последующие SIP прокси сервер, с которыми # взаимодействует наш сервер, сохранили наш SIP сервер на пути следования сообщений # и информировали бы его о всех изменениях статусов SIP диалога. Сделав это, мы гарантируем то, # что наш SER имеет большие шансы обработать все SIP сообщения сеанса связи. # # Ключевое слово 'method' предоставляется ядром сервера SER и позволяет нам узнать, с каким типом # SIP сообщением мы имеем дело record_route(); # Функция Record_route() просто добавляет заголовочное поле Record-Route в текущее SIP сообщение. # Эти заголовки будут вставлены перед всеми остальными полями Record-Route, которые могут уже # присутствовать в SIP сообщении. Другие SIP сервера или клиенты будут использовать эти заголовочные # поля для того, чтобы узнать, как и куда отправлять ответное или новое сообщение SIP диалога. }; # ------------------------------------------------------------------------ # Loose Route Section # Секция свободной маршрутизации. # ------------------------------------------------------------------------ if (loose_route()) { # loose_route () проверяет, должно ли текущее обрабатываемое SIP сообщение быть свободно # маршрутизировано или нет. Если да, то сервер SER должен просто переправить это сообщение # следующему адресату согласно тому, что определено в самом верхнем заголовочном поле Record-Route. # # Во всех Ваших файлах ser.cfg Вы должны вызывать loose_route () после функции record_route(). # # Это - основное правило для режима свободной маршрутизации. # Обратитесь к RFC3261 за описанием принципов организации свободной маршрутизации.
Заметка для заинтересованных читателей:
В RFC3261 определяется, что SIP прокси сервер, как говориться, является сервером со "свободной маршрутизацией", если он следует процедурам, определенными в этой спецификации, по обработке полей заголовков "Route" SIP сообщений. Эти процедуры отделяют запрашиваемый адрес (указанный в заголовочном поле Request-URI) от набора прокси серверов, которые должно пройти это сообщение (которые указываются в заголовочных полях Route). Прокси сервера, которые удовлетворяют этим условиям также называются "свободными маршрутизаторами" ("loose router").
SER - является свободным маршрутизатором, следовательно Вам не надо производить каких-либо действий, кроме вызова функции loose_route(), для обеспечения этих функциональных возможностей.
Замечание по SIP диалогам (от Jan Janak):
Когда приходит SIP сообщения, Вы можете классифицировать на те, которые требуют создагие диалога и на те, которые не нуждаются в создании диалога. Сообщения, которые передаются в пределах одного диалога (ACK, BYE, NOTIFY) обычно не требует обработки на сервере и, таким образом, должны быть переданы далее по цепочке к адресату в начале вашего алгоритма обработки SIP сообщений (в вашем файле ser.cfg), сразу после функции loose_route.
SIP cообщения, которые создают диалоги, могут быть далее классифицированы как те, которые адресованы нашему серверу (они имеют либо имя домена либо IP адрес сервера в заголовочном поле Request-URI), и те, которые адресованы не нам. Сообщения, которые не содержат имя домена или IP адрес нашего прокси сервера в заголовке Request-URI, должны быть немедленно переданы далее или блокированы.
route(1); # Если проверка на предмет свободную маршрутизации возвращает 'true', тогда мы должны передать # сообщение далее без дальнейшей его обработки. Для этого, мы передаем управление блоку # маршрутизации с номером один (т.е. route[1]). Этот маршрутизационный блок описывается позже, # но этого пока достаточно, т.к. это обработчик SIP сообщений, по умолчанию. break; # Так как использовался механизм свободной маршрутизации, мы не можем далее обрабатывать сообщение, # следовательно, мы должны использовать команду break, чтобы выйти из основного маршрутизационного блока. }; # ------------------------------------------------------------------------ # Call Type Processing Section # Секция, обрабатывающая различные типы вызовов. # ------------------------------------------------------------------------ if (uri!=myself) { # Теперь мы подошли к той части файла конфигурации SIP сервера, где мы обрабатываем SIP сообщения # не принадлежащие какому-либо SIP диалогу. Другими словами, мы обрабатываем SIP сообщения, # которые инициируют новый диалог, или мы обрабатываем SIP сообщения, которые не адресованы # нашему прокси серверу (но мы транслируем или проксируем эти сообщения). # # Ключевое слово 'uri' определяется ядром сервера SER, и оно является кратким синонимом для reuest-URI # или R-URI. Ключевое слово 'myself' также определяется ядром сервера SER и является синонимом # для имени прокси сервера SER. # # Таким образом, в этой строке проверяется, содержится ли в поле R-URI адрес нашего SIP прокси сервера. # С другой точки зрения, если это выражение возвращает TRUE, тогда SIP сообщение адресовано # не нашему SIP серверу, а скорее всего некому другому адресату, вроде SIP телефона третьих лиц. route(1); # Переправляем SIP сообщение адресату без дальнейшей обработки. break; # Прекращаем обработку текущего сообщения, т.к. мы уже отправили его адресату на предыдущей строке. }; if (method=="ACK") { # SIP сообщения ACK обрабатываются отдельно, следовательно нужно узнать, обрабатываем ли мы ACK сообщение. route(1); # Пересылаем SIP сообщение адресату, без дальнейшей обработки. break; # Прекращаем обработку, т.к. SIP сообщение ACK было полностью обработано. } if (method=="REGISTER") { # SIP сообщения REGISTER обрабатываются отдельно, таким образом, сначала нужно узнать, # является ли сообщение запросом на регистрацию. route(2); # Все запросы на регистрацию попадают в блок маршрутизации route[2] для их обработки. # Мы сделали это для разгрузки основного блока маршрутизации. Поступая так, # мы создаем читаемый и легко управляемый файл конфигурации ser.cfg . break; # Прекращаем обработку, т.к. SIP сообщение REGISTER было полностью обработано. }; lookup("aliases"); # Функция Lookup(aliases) пытается отыскать любые псевдонимы для запрашиваемого URI. # Псевдоним - это другой способ сослаться на какого-либо SIP адресата. # Например, если у Вас есть SIP пользователь с номером 4075559938 и, # бесплатный для звонящих абонентов, номер 8885551192, при поступлении вызова на который, # вызывается абонент с номером 4075559938, тогда Вы можете создать для номера 4075559938 # псевдоним, который содержит в себе этот бесплатный номер. # Когда кто-то совершает вызов на бесплатный номер, будет вызываться SIP клиент с номером 4075559938. # # Функция Lookup(aliases) не особо необходима для этого простого примера конфигурации, однако, # утилитой командной строки - serctl, которая идет в поставке вместе с сервером SER, требует, # чтобы существовала строка с именем этой функцией для правильного функционирования данной утилиты. if (uri!=myself) { # Если был найден псевдоним, и запрашиваемый URI содержит адрес не нашего сервера, тогда мы передаем SIP # сообщение дальше, используя для этого наш блок маршрутизации, по умолчанию - route[1]. # После передачи этого сообщения, мы прекращаем его обрабатывать, используя команду break. route(1); break; }; if (!lookup("location")) { # lookup(location) пытается отыскать AOR для запрашиваемого URI. Другими словами, # она пытается определить, где физически расположен вызываемый абонент. # Это производиться путем поиска адреса абонента в таблице местоположений, # которую формирует и обновляет функция save(). Если AOR была найдена, # мы можем завершить обработку вызова, иначе мы должны возвратить ошибку # вызывающему абоненту, с указанием причины, по которой мы не можем закончить # обработку вызова. sl_send_reply("404", "User Not Found"); # Если невозможно определить адрес вызываемой стороны, тогда сервер SER отправляет # ответ с сообщением об ошибке: "404 User Not Found". # Это сообщение не зависит от состояния диалога и не является частью какого-либо из них. break; # Так как невозможно найти вызываемого пользователя, # то мы прекращаем обрабатывать сообщение. }; route(1); # Если текущее SIP сообщение не является запросом на регистрацию, то мы передаем его # адресату без дальнейшей обработки. Это делается путем передачи управления # блоку маршрутизации route[1], обработчику SIP сообщений по умолчанию. } # ------------------------------------------------------------------------ # Default Message Handler # Обработчик SIP сообщений, по умолчанию. # ------------------------------------------------------------------------ route[1] { # Route[1] - обработчик SIP сообщений, по умолчанию, который вызывается в файле ser.cfg, # когда необходимо переслать SIP сообщение. Заметьте, что в определении блока маршрутизации # используются квадратные скобки, но при вызове этого блока Вы должны использовать круглые скобки. if (!t_relay()) { # t_relay() - это функция модуля tm.so, и, возможно, одна из самых важных функций для любого файла # конфигурации ser.cfg. Эта функция отвечает за отправку SIP сообщения тому, кому оно было адресовано # и отвечает за повторную его отправку и прием ответов на это сообщение. Если это сообщение # не может быть доставлено, тогда функция t_relay() возвратит состояние ошибки. # # См. раздел: ((SER how it work|Что такое SIP и как он работает)), на предмет информации о транзакциях # и диалогах в SIP протоколе. sl_reply_error(); # Если функция t_relay () не может доставить SIP сообщение, тогда будет использоваться # функция sl_reply_error (), для оповещения SIP клиента, о произошедшей ошибке. }; } # ------------------------------------------------------------------------ # REGISTER Message Handler # Обработчик SIP сообщения REGISTER. # ------------------------------------------------------------------------ route[2] { # Route[2] - блок обработки сообщений с запросами на регистрацию SIP клиентов. # В этом месте мы сохраняем данные о том, как можно "дозвониться" до SIP клиента. if (!save("location")) { # Все SIP сообщения REGISTER не проверяются и всегда корректно обрабатываются, # потому что у нас нет никакой проверки на предмет авторизации на сервере. # Функция save() отвечает за сохранение регистрационной информации о SIP клиенте # в адресной таблице местоположений. Однако, мы выбрали, что параметр # usrloc db_mode установлен в ноль. Это означает, что функция save() будет сохранять # эту регистрационную информацию только в оперативной памяти, а не на диске. sl_reply_error(); # Если сервер SER не может сохранить контактную информацию о SIP клиенте, # тогда мы сообщаем клиенту о произошедшей ошибке и возвращаем управление # главному маршрутизационному блоку. }; }
Использование простейшей конфигурации ser.cfg.
Теперь, когда мы создали нашу первую конфигурацию SIP прокси сервера, пришло время запустить сервер и настроить два SIP телефона так, чтобы мы могли совершать телефонные вызовами между этими двумя зарегистрированными клиентами.
Для запуска сервера SER, не как фоновый процесс, а как обычный процесс, Вам надо открыть свой любимый терминал, например, bash.
И выполнить следующую команду:
/usr/local/sbin/ser D E
Параметр D говорит серверу SER не переходить в фоновый режим (т.е., не выполняться, как демон), а параметр E указывает ему, что все ошибки должны протоколироваться на стандартное устройство stderr.
Сервер SER должен запуститься и сообщить, какие TCP и UDP сокеты он слушает на IP адресе Вашего компьютера. IP адрес не должен быть 127.0.0.1, т.к. этот IP - адрес интерфейса loopback, и в этом случае SIP телефоны будут не в состоянии соединиться с Вашим SIP прокси сервером.
Стоит отметить, что, если SER собран без параметра mode=debug, т.к. это, возможно, работает не для всех архитектур. Тогда сервер SER будет сообщать только базовую информацию о себе, и более ничего сообщать не будет.
Теперь, когда SER уже запущен, Вы должны сконфигурировать два или более SIP телефона. Неплохой пример SIP телефона - Grandstream BT100, т.к. он недорог и неплохо подходит для тестирования SIP прокси сервера.
При конфигурировании SIP телефонов, Вы должны использовать следующие параметры:
SIP Proxy: | IP адрес Вашего сервера SER. |
SIP Port: | 5060 |
Outbound Proxy: | Оставьте это поле пустым. |
User name: | Тут введите уникальное числовое значение. Как хороший пример, можно использовать 1000 для первого телефона и 1001 - для второго. Каждый телефон должен иметь уникальное имя пользователя, хотя у Вас может иметься больше одного SIP телефона с тем же самым именем. |
Authentication ID: | Если в Вашем SIP телефоне имеется это поле, то Вы можете указать в нем тоже самое значение, что и в поле User Name или оставить его пустым. |
Password: | На данный момент Вы можете оставить это поле пустым. Если Вы укажете какое-либо значение в поле, то оно будет проигнорировано, т.к., на данный момент, SIP прокси сервер не проверяет авторизацию. |
Теперь, после перезагрузки, Ваши SIP телефоны могут совершать телефонные вызовы друг к другу. Набираемые номера, для совершения вызовов, - любые значения, которые Вы задали в поле User Name. Итак, пусть, например, наш SIP телефон с номером 1000 может совершить вызов телефона с номером 1001, а SIP телефон c номером 1001, может вызвать телефон с номером 1000.
Использованные материалы: http://siprouter.onsip.org/doc/gettingstarted/ch06.html