Мы решили поделиться частью наших трудов в более удобном для разработки, изучения и использования формате — залили их на гитхаб.
Пока там репозитории для четырех наших проектов:
Looking Glass для BIRD
Предотвращение спама в phpbb3
Определение номеров и направлений фродовых вызовов
и практическая часть статьи Asterisk REST Interface (ARI) — реализация на asterisk конференции при помощи ARI и NodeJS.
Метки статьи: ‘php’
Кратко о BIRD:
The BIRD project aims to develop a fully functional dynamic IP routing daemon. - Both IPv4 and IPv6 - Multiple routing tables - BGP - RIP - OSPF - Static routes - Inter-table protocol - Command-line interface - Soft reconfiguration - Powerful language for route filtering WWW: http://bird.network.cz/
Пользователи FreeBSD могут найти демоны BIRD в портах: IPv4 версия: /usr/ports/net/bird IPv6 версия: /usr/ports/net/bird6
С момента размещения статьи Используем BIRD для создания «пограничного» маршрутизатора размещенную zaikini, я все мечтал найти время и уделить его изучению и пробам BIRD.
О демоне BIRD народ хорошо отзывается, да и многие IX`ы держат свои Route Server`а именно на этом демоне.
И вот наконец мне предоставилась такая возможность. Для одного проекта понадобилось автоматом генерить blackhole маршруты для BGP и тут я вспомнил что как раз давно хотел попробовать BIRD.
Поднял на тестовом стенде из тройки виртуальных машин:
- FreeBSD 9.1 + BIRD 1.3.11
- FreeBSD 8.4 + BIRD 1.3.11
- FreeBSD 7.2 + BIRD 1.3.2
- FreeBSD 7.2 + BIRD 1.3.11
Погонял BGP и OSPF в различных конфигурациях и схемах — понравилось. Даже больше скажу — очень понравилось. Есть конечно в BIRD некоторые вещи пока не совсем мне понятные или как это сказать не до конца логичные, НО в целом абалденный демон. Респект и уважуха разработчикам BIRD.
Итоговый «приговор» по BIRD: must have.
До сего момента мы работали на Quagga, но она иногда реально достает своими непонятными приколами, когда после реконфига чего либо помогает тока рестарт демонов дабы эти изменения наконец сказались.
Рестарт bgpd и о чудо ! Все непонятки устраняются и все работает как и положено. Но сейчас не об этом речь 🙂
Вообщем пересели мы на BIRD. Что дальше ? А дальше, как это водится, понадобился Looking Glass для BIRD`а, не всегда ж есть желание/возможность лазать в консоль 🙂
Погуглил… нагуглил только sileht/bird-lg и ulg—universal-looking-glass — оба написаны на python.
Попытки найти нечто подобное лукинг глассу для Cisco/Quagga/Juniper и на PHP мне не удалось.
«Ну что ж… Толи лыжи, толи … получается что нету… значит надо написать самому.» — сказал я и приступил к реализации :).
Через некоторое время первая версия LG на PHP была готова и протестирована.
Пока из функционала было реализовано самое необходимое — возможность посмотреть маршрут, пингать и трасернуть.
Потом я подумал о том, что скорее всего я не один такой, кто не хочет/может пользоваться LG на питоне, так же о (похоже) единственно существующем варианте LG для BIRD`а, а так же о том что сам пользуюсь бесплатным ПО, которое пишут и выкладывают другие и … и решил что нужно так же поделиться с общественностью своей разработкой.
Итак представляю вам свой труд и новый проект в рамках проекта Subnets.ru: BIRD Looking Glass на PHP.
Для работы LG вам необходимо:
- установленный и запущенный BIRD
- web сервер (аля apache)
- PHP5: модуль для apache и CLI версия
Более детально написано в README и ошибках, которые, если чего то не хватает, вам будет выдавать web интерфейс LG после его установки.
Я не смотрю на свои возможности через «розовые очки», я больше сис.админ чем программист, но все же кое что напрограммить и я могу 🙂
Лично я свою первоочередную задачу (наличие хоть какого LG для BIRD на PHP) решил. Возможно сей «продукт» поможет и вам.
Это явно не последняя версия этого LG, как снова дойдут руки — буду допиливать функционал далее, благо дело мыслей много, а желаний (как это обычно и бывает) ещё больше :).
В LG встроен механизм проверки наличия новой версии, поэтому если таковая появится, то те кто установит и будет использовать узнают об этом зайдя на главную страницу своего LG.
Надо ли пояснять, что для функционирования сего механизма сервер с LG должен иметь доступ к Инету, а если быть точнее то к URL`у http://bird-lg.subnets.ru/.
Работа LG проверена под OS FreeBSD, если у вас другая ось то вы как раз проверите сами.
З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА ! Уважайте чужой труд.
Автор: Николаев Дмитрий (virus (at) subnets.ru)
Предыстория
Однажды возник вопрос о том, как оперативно получать информацию о состоянии сети, когда находишься не у рабочего места.
Путем нехитрых размышлений был найден единственный выход: отсылка SMS-сообщений системой мониторинга о ключевых событиях.
Для реализации этого проекта были куплены GSM модем Siemens MC35i и 1-портовый асинхронный сервер Moxa NPort-5110, обеспечивающий преобразование интерфейса RS-232 в Ethernet и конечно же сервер под FreeBSD 🙂
Moxa NPort-5110 был приобретен для цели отсылки SMS-сообщений из любого сегмента сети, а не только с машины, к которой подключен GSM-шлюз.
Теория с примерами
Итак, конфигурация схемы следующая:
GSM модем включен в Moxa NPort-5110 кабелем RS-232.
Moxa имеет адрес 10.100.0.2:4001. Для отправки сообщения на латинице нужно выполнить следующую последовательность команд на модеме:
telnet 10.100.0.2 4001 AT+CPIN=7256 OK AT+CMGF=1 OK AT+CMGS="+79101234567" >test,test,testCTRL+Z +CMGS
где CTRL+Z — комбинация клавиш, нажатие которой означает конец сообщения.
С отправкой сообщения «Ахтунг!» на кириллице на номер +79101234567 все сильно сложнее, т.к. сообщение должно уходить пакетом в формате PDU в кодировке UCS2 (юникодная кодировка, поддерживающая в том числе кириллицу), а потому рассмотрим этот случай подробнее.
Пример:
telnet 10.100.0.2 4001 AT+CPIN=7256 OK AT+CMGF=0 OK AT+CMGS=28 >0011000B919701214365F70018C10E0410044504420443043D04330021CTRL+Z +CMGS
Разберем поподробнее эту «кашу»:
AT+CMGF=0 - установка модема в PDU-режим AT+CMGS=28 - длина строки пакета /2 -1 0011000B919701214365F70018C10E0410044504420443043D04330021 - сам PDU пакет
Формат PDU-пакета представляет собой 16-ричную последовательность, передающуюся человекочитаемой строкой (не ASCII-представления самих 16-ричных чисел). Оригинал описания (сохраненная копия) формата PDU пакета (на английском).
Перевод и коментарии:
00 Длина и номер SMS-центра провайдера. у нас — дефолтный из настроек GSM
11 Сообщение SMS-Submit (сохраненная копия)
0B Длина телефонного номера получателя (количество цифр в нем — в нашем случае 11)
91 Тип телефонного номера получателя (у нас получается «международный тип с планом нумерации Е.164/E.163) (сохраненная копия)
6 байт телефонный номер получателя (кодировка описана ниже)
00 Протокол (00 — SMS) (сохраненная копия)
18 Кодирование данных (08=UCS2,00-latin 7 bit etc. Старший байт — не сохранять в истории получателя) (сохраненная копия)
С1 Доставка актуальна 1 неделю (как рассчитывается) (сохраненная копия)
0E Длина сообщения («байт». длина символов кодированной UCS2-строки/2)
xx байт Кодированное сообщение (текст в UCS2, 16-ричное представление)
по-порядку подробней:
- первый байт на некоторых телефонах передавать не следует (работа возможна только через SMS-центр провайдера, телефон другого не умеет)
- номер получателя (алгоритм создавали наркаманы). кодируется «как есть» путем переставления местами соседних цифр, «добивается» до длины в 12 символов символами «F». например номер доблестной советской милиции (02 кто забыл) кодируется как 20FFFFFFFFFF, а номер из нашего примера (+79101234567) — как 9701214365F7. З.Ы. Не забывайте что длина SMS-сообщения ограничена, в кодировке UCS2 70-ю символами, на латинице — 160-ю символами.
Кодирование отправки SMS сообщений на PHP
<?
//Массив с параметрами GSM шлюза
$init=array('ip'=>"10.100.0.2",'port'=>'4001','pin'=>'7256');
// Отправка СМС :)
send_sms("Это тест","9101234567");
//Функция отсылки СМС
//$mess - сообщение
//$mob - номер мобильного в формате ХХХУУУУУУУ
//$debug - 1 - выводить отладочную инфу, 0 - нет
function send_sms($mess,$mob,$debug=0){ global $init; //Если сообщение и номер мобильника не пустые. //Проверку правильности формата мобильника возлагается на вас ;-) if ($mess && $mob){ $len=strlen($mess); $mob="7".$mob; $ooo=""; if (preg_match('/^([x0Ax0Dx20-x7F]+)$/',$mess,$tmp)){ if ($len<=160){ $len=0; print "8-bit encoded SMS<br>"; }else{ print "Длина сообщения в 8-битной кодировке [$len] больше 160 символов!<br>n"; return -1; } }else{ if ($len<=70){ $mess=cp1251_2ucs2($mess); $ret.="00";//it is only an indicator of the length of the SMSC information supplied (0) $ret.="11"; //First octet of the SMS-SUBMIT message. $ret.="00"; //TP-Message-Reference. The "00" value here lets the phone set the message reference number itself. $ret.="0B"; // Address-Length. Length of phone number (11) $ret.="91"; //Type-of-Address. (91 indicates international format of the phone number). // Начало кодирования номера мобильного if ((strlen($mob)/2)%2){ $mob.="F"; } for ($i=0;$i<strlen($mob);$i+=2){ $ret.=$mob[$i+1].$mob[$i]; } // Закончили взрывать мозг :) $ret.="00"; //TP-PID. Protocol identifier $ret.="18"; //TP-DCS. Data coding scheme. 18 - don't save at history, 08 - save $ret.="C1"; //TP-Validity-Period. C1 means 1 week $ret.=sprintf("%02X",strlen($mess)/2); //TP-User-Data-Length. Length of message. $ret.=$mess; //TP-User-Data $ret.=chr(26); //end of TP-User-Data $len=sprintf ("%s",(strlen($ret)-3)/2); $mess=$ret; print "16-bit encoded SMS<br>"; }else{ print "Длина ссобщения в 16-битной кодировке [$len] больше 70 символов!<br>n"; return -1; } } // Создаем сокет $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!$socket) { err($socket); }else{ // Устанавливаем опцию таймаута в 5 секунд для сокета. // Проверка на удачную установку лежит на вас :) socket_set_option($socket,SOL_SOCKET,SO_SNDTIMEO,array("sec"=>5,"usec"=>0)); // Устанавливаем соединение на сокет $result = @socket_connect($socket, $init['ip'], $init['port']); if (!$result) { err($socket); } else { // Инициализируем GSM шлюз $out=sms_init($socket,$len,$debug); if ($len){ // Сообщение на кириллице $out.=raw($socket,"AT+CMGS=$len",">"); }else{ // Сообщение на латинице $out.=raw($socket,sprintf("AT+CMGS="+%s"",$mob),">"); $mess.=chr(26); } $out.=raw($socket,$mess,"CMGS",$debug); if ($debug){ printf ("<pre>[ %s ]</pre>",$out); } } @socket_close($socket); } } return $out;
}
// Функция начальной инициализации модема
// $socket - ресурс сокета
// $type - 0 - сообщение на латинице, 1 - на кириллице
// $debug - 1 - выводить отладочную инфу, 0 - нет
function sms_init($socket,$type,$debug=0){
global $init;
$out=raw($socket,"AT","OK",$debug);
$out.=raw($socket,sprintf("AT+CPIN=%s",$init['pin']),'(ERROR)|(OK)',$debug);
$out.=raw($socket,sprintf("AT+CMGF=%d",$type?0:1),"OK$",$debug);
if ($debug){
print "<pre><font color="green">$out</font></pre><br>";
}
return $out;
}
// Функция посылки комманды на GSM шлюз
// $socket - ресурс сокета
// $write - команда, передаваемая GSM шлюзу
// $delim - последовательность, означающая окончание чтения ответа с GSM шлюза
// $debug - 1 - выводить отладочную инфу, 0 - нет
function raw($socket,$write,$delim,$debug=0){
$ooo='';
$write.="\r\n";
if (@socket_write($socket,$write,strlen($write))===false){
err($socket);
}else{
while ($out = @socket_read($socket, 2, PHP_NORMAL_READ)) {
if ($out===false){
err($socket);
}else{
if ($debug){
print $out;
flush();
}
$ooo=sprintf("%s%s",$ooo,$out);
}
if (preg_match("/".$delim."/i",$ooo,$tmp)){
if ($debug){
print_r($tmp);
flush();
}
break;
}
}
}
usleep(100000);
return $ooo;
}
// Функция вывода ошибки при работе с сокетом
// $soc - ресурс сокета
function err($soc){
printf ("<script>alert ('Ошибка: %s');</script><font color=red><b>%s: [%s]</b></font><br>",
socket_strerror(socket_last_error($soc)),"Ошибка",socket_strerror(socket_last_error($soc)));
}
// Функция кодирования кириллицы из CP1251 в UCS2
// $str - строка для перекодирования
function cp1251_2ucs2($str){
for ($i=0;$i<strlen($str);$i++){
if (ord($str[$i]) < 127){
$results = sprintf("%04X",ord($str[$i]));
}elseif (ord($str[$i])==184){ //ё
$results="0451";
}elseif (ord($str[$i])==168){ //Ё
$results="0401";
}else{
$results = sprintf("%04X",(ord($str[$i])-192+1040));
}
$ucs2 .= $results;
}
return $ucs2;
}
// Функция вывода отладочной инфы
function debug($array){
ob_start("get_vars");
print_r($array);
ob_end_flush();
}
// Функция output_callback для функции debug()
function get_vars($buffer){
return sprintf ("<div style="padding: 3px; border: 2px #88bbbb solid; color: #00dd00; background-color: #bbffbb; font-weight: bold; text-align: left;"><pre>%s</pre></div>",
htmlspecialchars($buffer));
}
?>
Декодирование принятых SMS сообщений на PHP
Отлично, с отсылкой СМС разобрались, осталось чтение сообщений (как обычно, с 8-битной кодировкой проблем нет, есть только с нашей кириллицей).
Команды для чтения SMS с GSM модемов:
function ucs2_2cp1251($str){ for ($i=0;$i<strlen($str);$i+=4){ $char=hexdec($str[$i].$str[$i+1].$str[$i+2].$str[$i+3]); if ($char>126){ if ($char==1105){ $char=184; //ё }elseif($char==1025){ $char=168; //Ё }elseif ($char>=848){ $char-=848; } }elseif(!$char){ $char=32; } $ret.=chr($char); } return $ret; } ?>
Источники: 1. О виджетах и гаджетах 2. SMS and PDU format 3. IXBT.ru 4. Связывание мобильника и FreeBSD через bluetooth (c п.1 по п.6 включительно) Ссылки:
З.Ы. Этот материал можно использовать и при отсылке СМС через обычный мобильник. Разница в том, что вместо сетевого сокета нужно использовать устройство FreeBSD /dev/ttyU0 (при подключении через USB кабель) либо /dev/ttypf (выполнив перед этим команду rfcomm_sppd -a MAC_адрес_телефона -t /dev/ttypf &). О связывании мобильника и FreeBSD через bluetooth смотри выше "Источники". З.З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА !
Автор: Панфилов Алексей (lehis (at) subnets.ru)
Многие ищут/спрашивают функции для проверок IP-адресов на вхождение в подсеть, итак:
Perl
#!/usr/bin/perl # Пример вызова и проверки IP-адреса 192.168.0.4 # на принадлежность к сети 192.168.0.0 с маской 255.255.255.248 # if (ip_vs_net("192.168.0.4","192.168.0.0","255.255.255.248")){ print "Адрес принадлежит сети\n"; }else{ print "Адрес не входит в подсеть\n"; } #Подпрограмма перевода IP в число sub aton{ my $addr=shift; (my $a0, my $a1, my $a2, my $a3)=split('\.',$addr); return $a3+($a2<<8)+($a1<<16)+($a0<<24); } #Подпрограмма перевода числа в IP sub ntoa{ my $ip=shift; return sprintf("%d.%d.%d.%d", (($ip&0xFF000000)>>24),(($ip&0xFF0000)>>16),(($ip&0xFF00)>>8),($ip&0xFF)); }
#Подпрограмма проверки вхождения IP в подсеть
sub ip_vs_net{
my $ip=shift;
my $network=shift;
my $mask=shift;
if((aton($ip)&aton($mask))==aton($network)){
return 1;
}else{
return 0;
}
}
PHP
<?
// Пример вызова и проверки IP-адреса 192.168.0.4
// на принадлежность к сети 192.168.0.0 с маской 255.255.255.248
if (ip_vs_net("192.168.0.4","192.168.0.0","255.255.255.248")){
print "Адрес принадлежит сети<BR>";
}else{
print "Адрес не входит в подсеть<BR>";
}
function ip_vs_net($ip,$network,$mask){
if (((ip2long($ip))&(ip2long($mask)))==ip2long($network)){
return 1;
}else{
return 0;
}
}
?> Функции которые переводят из CIDR в обычную маску и наоборот <? function cidr_2_mask($mask){ return long2ip(pow(2,32) - pow(2, (32-$mask))); } function mask_2_cidr($mask){ $a=strpos(decbin(ip2long($mask)),"0"); if (!$a){$a=32;} return $a; } ?> Пример: <? printf("%s",cidr_2_mask("24")); //Напечает 255.255.255.0 printf("%s",mask_2_cidr("255.255.255.0")); //Напечает 24 ?>
Ссылки:
З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА !