Одна из интересных проблем, которые возникают при построении как домашних, так и корпоративных сетей разбитых на подсети — это построение сетевого окружения. В корпоративных сетях сделать сетевое окружение проще благодаря тому, что все подсети сходятся на одном сервере-рутере, который и выдаёт сетевое окружение.
Всё намного сложнее в домашних сетях: здесь многое определяется застройкой и часто нет возможности разродится звездообразной конфигурацией. Вот и ходят пользователи к друг другу через всякие сканы, NetView-веры — а что делать. Также часто невозможно всё сделать правильно из-за разнородности материального положения сетей: одна сеть разжилась сервером с DHCP, WINS, обозревателем на самбе, а в другой сети живут вообще без серверов, или, например, нет человека, хоть немного разбирающегося в сетевой «механике»: помогать такой сети никто не собирается — чай не райсобес, но соединиться «по-правильному» хотелось бы. Что делать?
А ответ в данном случае достаточно прост: надо научиться строить добавлять пользователей чужих сетей в сетевое окружение (сетевой обозреватель) и в службу WINS. Классическое построение сети предполагает наличие в таких сетях реплицирующихся WINS-серверов (samba4wins или WINS на Windows) и главного обозревателя сети (PDC) на самбе с включенным режимом enhanced browsing и настроенным remote browse sync. Однако, по описанным выше причинам, классика здесь неприменима 🙂
Однако, сама самба имеет встроенный нереплицируемый WINS и умеет выполнять роль PDC — всё в одном флаконе, удобно. Т.е. дело за малым — научить самбу добавлять новых пользователей из чужих сетей в сетевое окружение и WINS (без WINS-а тупой пользователь будет тыкать мышкой в хост, а IP адреса-то его никто знать не будет 🙂 ).
Вобщем, всё получилось, результат патча на скриншоте.
За основу была взята самба 3.0.24-6etch4. Выяснить, имя и группу произвольного хоста в сети можно с помощью команды nmblookup -A <ip>, т.е. взяв за основу исходники nmblookup -A, можно построить нетбиос сканер.
Ф-ция на которой базируется nmblookup -A — node_status_query — находится в файле libsmb/namequery.c. В этой ф-ции надо подправить таймаут ожидания ответа с 2сек на 0.15сек, т.к. если реальный хост будет так «быстро» отвечать, то врядли он может кого-то заинтересовать 🙂
NODE_STATUS_STRUCT *node_status_query(int fd,struct nmb_name *name,
struct in_addr to_ip, int *num_names,
struct node_status_extra *extra)
{
BOOL found=False;
int retries = 2;
int retry_time = 2000; // вот тут надо установить 150 млС
struct timeval tval;
struct packet_struct p;
struct packet_struct *p2;
struct nmb_packet *nmb = &p.packet.nmb;
NODE_STATUS_STRUCT *ret;
Со сканером всё вроде понятно, теперь надо придумать, как полученные данные вписать сетевое окружение. Частенько можно встретить разные «замечательные» способы с исправлениями browse.dat, который формирует nmbd, а показывает smbd, но постараемя быть выше этого: стоит попробовать заставить nmbd сформировать правильный список сразу.
Списки пользователей nmbd формирует и хранит в памяти: nmbd только записывает файл browse.dat, но никогда не читает, т.е. надо найти точку внедрения в код. Внимание сразу же привлекает файл nmbd_incomingdgrams.c, а в нём ф-ция process_host_announce — это то, что доктор прописал. Из ф-ции видно, что хост добавляется след.образом:
- 1. вначале в памяти ищется рабочая группа, которой принадлежит хост
work = find_workgroup_on_subnet(subrec, work_name);
- 2. если такой группы ещё нет, то создаём её
// откат, если нулевой указатель if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL) goto done;
- 3. по аналогии с группой, ищем имя хоста и если его нет, создаём в списке обозревателя новую запись
if((servrec = find_server_in_workgroup( work, announce_name))==NULL) { create_server_on_workgroup(work, announce_name, servertype|SV_TYPE_LOCAL_LIST_ONLY,ttl, comment); } else { servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY; // обновление типа хоста // обновляем время жизни записи, в своём коде впишем сюда "вечный" TTL update_server_ttl( servrec, ttl); fstrcpy(servrec->serv.comment,comment); // обновляем комментарий }
Т.е. из этого кода видно, как можно добавить сервер в сетевое окружение: надо знать имя рабочей группы, имя хоста и коментарий, который можно сформировать и самим. Чуть ниже идёт код удаления хоста из списка.
if(!is_myname(announce_name) && (work != NULL) &&
((servrec = find_server_in_workgroup( work, announce_name))!=NULL))
{
remove_server_from_workgroup( work, servrec);
}
Этого всего уже достаточно для того, чтобы самим формировать список обозревателя сети, теперь надо освоить работу со службой WINS: работа с WINS через файл wins.dat представляется несколько ущербной, т.к. этот файл читается только при запуске nmbd, а в процессе работы только записывается. На самом деле записи WINS хранятся в tdb-файлах мини-БД, встроенной в самбу. К тому же придётся написать свою ф-цию ip >-> name, обратную работе WINS для удаления старых записей имён в списке обозревателя. После недолгих «фтыканий» находим ф-цию
add_name_to_subnet(wins_server_subnet,announce_name,nb_type,(uint16)NB_ACTIVE,
ttl,REGISTER_NAME,ip_nums, ip_list);
Здесь announce_name — имя, регистрируемое в WINS, nb_type — тип нетбиос имени, далее идёт статус записи, время жизни, описание имени (доменное, винс и т.п.), ip_nums — кол-во адресов в массиве ip_list, которые относятся к имени. Удалять имя из WINS — вредно, поэтому на это заморачиваться не будем, ведём только накопительную деятельность 🙂 Регистрировать будем 2 типа записей: nb_type=0x00 и nb_type=0x20. Осталось разобраться с мини-БД самбы для реализации ip >-> name, и можно приступать к написанию патча на самбу.
Каталог TDB достаточно документирован для того, чтобы сообразить с первого подхода, как написать поиск имени по IP, поэтому код поиска по связанному списку приводится без предисловия: он прост.
BOOL find_name_by_ip(uint32 ip,fstring ntb_name)
{
TDB_DATA key, data, newkey;
struct name_record *namerec=NULL;
for(key=tdb_firstkey(wins_tdb); // находим первый элемент в списке
key.dptr; // проверяем, а не хвост ли это? он вообще указвает на что-либо?
newkey=tdb_nextkey(wins_tdb,key),safe_free(key.dptr),key=newkey)
// а текущий элемент указывает на следующий элемент
{
data=tdb_fetch(wins_tdb,key);
if (data.dsize==0) continue;
namerec=wins_record_to_name_record(key,data); // перевод записи в удобоваримый формат
SAFE_FREE(data.dptr);
if (!namerec) continue;
if (namerec->data.num_ips)
{
DEBUG(9,("find_name_by_ip: %s ip=%s\n",namerec->name.name,inet_ntoa(*(namerec->data.ip)) ));
if (*((uint32*)namerec->data.ip)==ip)
{
pull_ascii_nstring(ntb_name,sizeof(fstring),namerec->name.name);
SAFE_FREE(namerec->data.ip);\n SAFE_FREE(namerec);
return True;
}
}
SAFE_FREE(namerec->data.ip);
SAFE_FREE(namerec);
}
return False;
}
Всё, все составляющие для дополнения самбы готовы, процесс сканирования будет жить в отдельном треде и передавать данные в бесконечный рабочий цикл nmbd, т.к. ф-ции, которыми будем пользоваться — нереентерантны. В дополнение к скану снабдим самбу полезным свойством «нерегистрации» левых групп типа FRILAN вместо FREELAN, или S2LANE вместо S2LAN, которые создают тупые пользователи. Для этого немного изменим ф-цию create_workgroup в файле nmbd_workgroupdb.c дополнив её проверкой check_workgroup, которая говорит о полезности или вредности группы в списке.
DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\\n",
name, subrec->subnet_name));
if (!check_workgroup((char*)name)) return NULL;
if ((work = create_workgroup(name, ttl)))
{
add_workgroup(subrec, work);
Также сделаем свой комментарий (адрес хоста) для найденного или анонсировавшего себя хоста.
void create_comment(char* name, uint32 ip, fstring cm_text)
{
if (name[0]>0x20)
{
snprintf(cm_text,MAX_SERVER_STRING_LENGTH,"%s",
inet_ntoa(*((struct in_addr*)&ip)) );
}
}
Из текста патча, приведённого ниже, будет видно, что в секции [global] появляются два новых параметра scan net и allow groups. В параметр scan net вписываются все сети, которые надо сканировать в формате сеть/маска, сети записываются через пробел или запятую, например:
scan net = 192.168.15.0/24 192.168.12.0/255.255.255.0 192.168.14.0/24
Если параметр scan net пуст, то фича сканирования не будет активизирована и самба будет работать, как «нормальная» самба без патча.
Разрешённые группы записываются в параметре allow groups через пробел или запятую, например
allow groups = OPENLAN FREELAN S2LAN LIFESTREAM
Если список пуст или параметр не задан, то доступны все сканированные или анонсировавшие себя группы.
И хотя фича сканирование задумывалась для дополнения «своего» списка самбы хостами из других подсетей, оказалось, что очень удобно сканировать и свою сеть: решаются проблемы «могильных камней» и становятся однотипными комментарии. Крайне не рекомендуется включать scan net с выключенным своим WINS, надеюсь, что не надо объяснять почему 🙂
Собственно и всё. Скачать патчи для samba-3.0.24 можно здесь, а для samba-3.0.28a — здесь.
Вот пример конфигурационного файла /etc/samba/smb.conf
[global]
workgroup = S2LAN
dos charset = CP1251
server string = S2LAN-14 Router
obey pam restrictions = Yes
domain logons = No
lm announce = Yes
dns proxy = No
ldap ssl = no
os level = 124
domain master = Yes
local master = Yes
preferred master = Yes
wins support = yes
scan net = 192.168.15.0/24 192.168.12.0/24 192.168.14.0/24
allow groups = S2LAN VGU
share modes = Yes
security = share
encrypt passwords = yes
load printers = No
show add printer wizard = No