Офисный переключатель интернета

7dbf0781784248a8b3bc988be15f82bc.jpgПровели нам в офис вторую линию интернета. Так как основная (дальше я буду называть её первой) хоть и хороша по-скорости, но ограничена по трафику. Вторая немного медленнее, но безлимитна. Днём вторая линия почти свободна и выдаёт хорошую скорость, поэтому и была выбрана основной для рабочего дня. К вечеру скорость сильно падает из-за нагрузки на канал и приходится переключаться на первую. Так бывает не всегда, но достаточно часто.Потому возник вопрос в переключении линий. Роутером у нас трудится обычный компьютер с FreeBSD на борту. Городить хитрую логику проверки скорости канала смысла не было, к тому же нужна была индикация активного подключения. Настроив переключение каналов на консольные команды с помощью sh скриптов в папке /bin, возникли 2 проблемы:1. Доступ к консоли только у админа, а он не всегда на месте, да и дёргать ради переключения каналов тоже не очень удобно.2. Нет индикации актвного, на данный момент, подключения.Поэтому было принято решение сделать переключатель физическим и снабдить идикаторами.ПроектИз имеющегося на данный момент в наличии железа, а именно кучки AVR Atmega 8A и FT232RL, было решено сделать переключатель, который подключается к USB порту и простым переключением тумблера меняет активный канал на другой. А так же светодиодом показывает тот, который сейчас выбран.Логика работы устройства очень проста: Несколько раз в секунду atmega проверяет состояние входов на порту C и передаёт это состояние через UART в виде символа A для первого канала и B для второго. К UART МК подключен преобразователь на FT232RL, который пересылае этот символ через USB в виртуальный COM порт сервера. На сервере работает простой демон написанный на питоне, который в случае изменения канала, выполняет команду переключения (смена шлюза по-умолчанию и статичных маршрутов, но это уже выходит за рамки данной статьи).Демон запускается вместе с системой, поэтому для него написан rc скрипт.Но обо всём по-порядку.Подготовка Исходя из задачи в пакете DipTrace была сделана схема: 4da2c1f9cfcd44f480d57d4fa1dc58b8.jpgU1 это FT232RL, U3 — Atmega 8.S1 — тумблер переключения, который так же переключает состояние светодиода-индикатора активного канала.Примечение уже после сброки узнал про свинью, которую FTDI подложила всем пользователям их чипов, так что в следующий раз дважды задумаюсь над применением их продукции. Так как отличить оригинал от подделки практически невозможно, то самым лучшим будет не играть в эту лотерею. Но так как в наличии осталось несколько их чипов, то решил делать всё-таки на них. Забегая вперёд скажу, что пляски с драйверами избежать не удалось, но в итоге удалось заставить эти чипы работать. C FreeBSD же проблем не возникло, так как там старый драйвер.По этой схеме была разведена плата под имеющийся корпус: e71dec6f3cb24fff88dcacc81f9a89eb.jpgДалее встал вопрос как делать плату. Можно, конечно, старым дедовским ЛУТом. Но есть способ лучше: заказать у китайцев. На хабре достаточно подробно описывался процесс заказа, и в почта России тоже частенько стала радовать своей работой.DipTrace умеет делать экспорт в формат Gerber, поэтому проблем с заказом не возникло.Сборка Через пару недель получив посылку с платами на почте, можно приступать к сборке. Так как схема очень проста, то и проблем со сброкой не возникло: 92e285f59e3d4c00878d8fe905abdf9e.jpg5ed9f00b1ed14d469a29d9bcf7aab3e5.jpgЗамечу только, что SMD компоненты паял с помощью паяльной пасты и фена. Так удобнее и получается намного качественней чем паяльником. Особенно при пайке такой мелочи как FT232.

Настройка Во FreeBSD есть драйвер для чипов FTDI, поэтому проблем с подключением не возникло. Единственно, что потребовалось включить загрузку модуля ядра. В файле /boot/loader.conf прописать: uftdi_load=«YES» Atmega была прошита с помощью avrdude, пропатченным для работы с ft232, прямо через USB подключение.Исходник прошивки /* * net_switch.c * * Created: 09.09.2014 10:07:41 * Author: exp131 */ #define F_CPU 1000000UL // Частота 1МГц, внутренний кварц #define BAUD 2400 // Скорость UART, 2400 хватает за глаза #define MYUBRR F_CPU/16/BAUD-1

#include #include

// Так как не использую файл с заголовками, то описываю функцию тут void ReportStatus ();

// Обработчик прерывания по таймеру ISR (TIMER0_OVF_vect) { ReportStatus (); // Вызываем отправку состояния переключателя }

// Функция проверки входов 0 и 1, порта С и выдача в UART соответствующего символа void ReportStatus () { cli (); // Отключим прерывания на время передачи unsigned char a; if ((PINC & (1<

//Инициализация void init (void) { // Настройки UART, включена передача и установлена скорость 2400 UCSRB = (1<> 8); UBRRL = (unsigned char)ubrr; // Таймер, задаём максимальный делитель и включаем прерывание по переполнению. TCCR0 = (1<

int main (void) { // Тут инициализируемся init (); while (1) // и запускаем основной цикл { } } В качестве базы для демона нашёл в Сети класс на питоне. К сожалению ссылка на источник не сохранилась, просто приведу тут код.

Код класса демона #!/usr/bin/env python

import sys, os, time, atexit from signal import SIGTERM

class Daemon: def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile def demonize (self): try: pid = os.fork () if pid > 0: sys.exit (0) except OSError, e: sys.stderr.write («fork #1 failed: %d (%s)\n» % (e.errno, e.strerror)) sys.exit (1) os.chdir (»/») os.setsid () os.umask (0) sys.stdout.flush () sys.stderr.flush () si = file (self.stdin, 'r') so = file (self.stdout, 'a+') se = file (self.stderr, 'a+', 0) os.dup2(si.fileno (), sys.stdin.fileno ()) os.dup2(so.fileno (), sys.stdout.fileno ()) os.dup2(se.fileno (), sys.stderr.fileno ()) atexit.register (self.delpid) pid = str (os.getpid ()) file (self.pidfile, 'w+').write (»%s\n» % pid) def delpid (self): os.remove (self.pidfile) def start (self): try: pf = file (self.pidfile, 'r') pid = int (pf.read ().strip ()) pf.close () except IOError: pid = None if pid: message = «Pidfile %s already exists. Deamon already running?\n» sys.stderr.write (message % self.pidfile) sys.exit (1) self.demonize () self.run () def stop (self): try: pf = file (self.pidfile, 'r') pid = int (pf.read ().strip ()) pf.close () except IOError: pid = None if not pid: message = «Pidfile %s does not exists. Daemon is not running?\n» sys.stderr.write (message % self.pidfile) return try: while 1: os.kill (pid, SIGTERM) time.sleep (0.1) except OSError, err: err = str (err) if err.find («No such process») > 0: if os.path.exists (self.pidfile): os.remove (self.pidfile) else: print str (err) sys.exit (1) def restart (self): self.stop () self.start () def run (self): »« Need to be overriden »« На базе этого класса был написан простой демон который слушает указанный в конфиге порт и выполняет, в зависимости от состояния преключателя, либо команду А, либо команду В.Конфиг [global] port=/dev/cuaU1 rate=2400 log=/var/log/net_switch.log cmdA=/bin/vist cmdB=/bin/unico А вот и код самого демона: Код демона #!/usr/bin/env python

import sys, os, time, serial, ConfigParser from daemon import Daemon

class NetSwitch (Daemon):

def run (self): file (self.logfile, 'a+').write («Net switch started\n») while True: ser = serial.Serial (self.port, self.rate, timeout=1) x = ser.read () if not x == self.state: self.state = x if x == 'A': os.system (self.cmdA) else: os.system (self.cmdB) file (self.logfile, 'a+').write («State changed %s\n» % x) time.sleep (0.2) def loadConfig (self, configPath): try: config = ConfigParser.RawConfigParser () config.read (configPath) self.port = config.get ('global', 'port') self.rate = config.getint ('global', 'rate') self.logfile = config.get ('global','log') self.cmdA = config.get ('global', 'cmdA') self.cmdB = config.get ('global', 'cmdB') self.state = 'A' return True except: return False if __name__ == »__main__»: daemon = NetSwitch ('/var/run/net_switch.pid') if len (sys.argv) == 2: if 'start' == sys.argv[1]: print «Usage: %s start path_to_config» % sys.argv[0] elif 'stop' == sys.argv[1]: daemon.stop () elif 'restart' == sys.argv[1]: daemon.restart () else: print «Unknown command» sys.exit (2) sys.exit (0) elif len (sys.argv) == 3: if 'start' == sys.argv[1]: configPath = sys.argv[2] if daemon.loadConfig (configPath): daemon.start () else: print «Unable to load config file\n» else: print «Usage %s start path_to_config» % sys.argv[0] else: print «Usage: %s start|stop|restart» % sys.argv[0] sys.exit (2) И в заключении, для того, чтобы демон стартовал вместе с системой, был написан rc скрипт: RC скрипт для запуска #!/bin/sh

. /etc/rc.subr

name=net_switch rcvar=`set_rcvar`

#reading the config load_rc_config $name : ${net_switch_enable:=«NO»} : ${net_switch_config:=»/usr/local/etc/net_switch/config.conf»}

pidfile=»/var/run/net_switch.pid» command=»/usr/local/sbin/${name}.py»

start_cmd=«start_cmd» stop_cmd=«stop_cmd» restart_cmd=«restart_cmd»

start_cmd () { ${command} start ${net_switch_config} }

stop_cmd () { ${command} stop }

restart_cmd () { ${command} restart }

run_rc_command »$1» Для того, чтобы демон мог стартовать вместе с загрузкой системы, нужно в /etc/rc.conf добавить строку: net_switch_enable=«YES» Заключение В результате получилось забавное, но функциональное устройство. Теперь любой сотрудник находящийся ближе к серверу может переключить активный канал в случае каких-либо проблем. Так же светодиоды показывают какой из каналов на данный момент активен. Он разного цвета (зеленый и красный), так что из далека видно каким подключением пользуемся.И в заключении хочу сказать, что у меня ещё остались платы под это устройство, так как при заказе меньше 10 цена всё равно не меняется, так что заказал сразу 10 штук. Удобно на случай если какая-то плата будет загублена в результате кривых рук при монтаже. Если кого заинтересовал этот девайс и есть желание собрать что-то подобное — пишите в личку, готов поделиться платами.

© Habrahabr.ru