№2(27) февраль 2005 подписной индекс 81655 www.samag.ru
Системная интеграция – общие концепции Автоматическая установка операционной системы и программного обеспечения SOCKS – универсальный прокси-сервер Строим виртуальную сеть с TINC Создание релиза FreeBSD IP-роуминг: вводный курс Почтовый сервер на базе Postfix Режем спам. Дополнительные методы
№2(27) февраль 2005
Техника оптимизации под Linux Считаем трафик на FreeBSD: ng_ipacct + Perl+ MySQL
оглавление ТЕНДЕНЦИИ
2 FreeBSD tips: NAT по старинке Сергей Супрунов amsand@rambler.ru
СОБЫТИЯ
3
РЕПОРТАЖ
Всегда на связи, 4 или IP-роуминг: вводный курс Сергей Яремчук grinder@ua.fm
АДМИНИСТРИРОВАНИЕ
Владимир Чижиков stepan-razin@newmail.ru
6
Кто купил Corel Linux? Обзор возможностей настольного дистрибутива Xandros.
Валентин Синицын val@linuxcenter.ru
12
Создание релиза FreeBSD Андрей Елсуков bu7cher@yandex.ru
skif@owe.com.ua
Автоматическая установка ОС и сопутствующего программного обеспечения Иван Коробко ikorobko@prosv.ru
Почтовый сервер на базе Postfix
Крис Касперски
stranger03@mail.ru
26
Режем спам. Дополнительные методы Денис Назаров pheonix@sysattack.com
val@linuxcenter.ru
Евгений Воякин evgy@mail.ru
30
HARDWARE
Владимир Мешков ubob@mail.ru
34
Unformat для NTFS Крис Касперски
grinder@ua.fm
39
kk@sendmail.ru
BUGTRAQ №2, февраль 2005
80
ОБРАЗОВАНИЕ
Строим виртуальную сеть с TINC Сергей Яремчук
75
Запись дисков CD-R/RW в Linux Часть 4
Универсальный прокси-сервер Валентин Синицын
68
20 Zend Studio 4.0 – новая версия, новые возможности
Защита от вирусов и нежелательной почты минимальными средствами.
Геннадий Дмитриев
60
16 Техника оптимизации под Linux kk@sendmail.ru
amsand@rambler.ru
50
ПРОГРАММИРОВАНИЕ
Конструктивный Dialog Сергей Супрунов
46
Сага о биллинге, или Считаем трафик на FreeBSD (ng_ipacct + perl+ MySQL) Часть 1
Системная интеграция – комплексный подход к самостоятельному решению проблемы Роман Марков
44
88 11, 15 1
тенденции Пополнение серверной линейки HP
Сага о патентах на ПО в Европе
Пресс-конференция компании HP, посвященная представлению новых серверных продуктов HP ProLiant на базе процессоров AMD Opteron, прошла 21 февраля в Москве в отеле «Балчуг». Специалистами компании была анонсирована обновленная линейка процессоров AMD Opteron x52, презентованы новые модели 2-х и 4-х процессорных серверов HP ProLiant DL385 и DL585 на процессорах AMD, и также новые серверные модули на базе процессоров AMD в форм-факторе блейд-серверов BL25p и BL35p. Новые процессоры AMD Opteron, изготовленные по процессу 90 нм, модели 152, 252 и 852, были представлены Сергеем Мелеховым, ведущим инженером по внедрению продукции AMD. Набор привычных технологических новшеств в стиле «дальше, выше и быстрее», которые характеризуют продукцию AMD, возглавляют технологии Direct Connect, HiperTransport и AMD PowerNow!. Старшие модели этого ряда специально ориентированы на использование в многопроцессорных системах. Наиболее интересными представляются результаты применения AMD PowerNow!, которые позволяют процессорам AMD существенно превосходить конкурентные аналоги по энергосбережению. Но, бесспорно, самое важное свойство новых изделий – это так называемая совместимость с двуядерными процессорами (Dual-Core), выпуск которых намечен на середину 2005 года. Использование этих процессоров в новых серверах HP ряда DL и BL, о которых рассказали менеджеры соответствующих направлений компании HP Игорь Слепцов и Сергей Члек, делает эти модели очень привлекательными, поскольку позволит без аппаратных переделок произвести апгрейд на двуядерные процессоры AMD Opteron и практически удвоить тем самым вычислительные мощности. Новые серверы HP ProLiant DL385 и DL585, соответственно 2U/2P и 4U/4P (для новичков: размер по вертикали/число процессоров) являются очередными отлично выполненными изделиями компьютерного hi-end и вполне могут повторить успех предыдущей серверной пары на процессорах Intel (DL380 в настоящее время лидер продаж). Производителем гарантируется полная совместимость с широким рядом операционных систем и прикладных программ. Серверы BL25p и BL35p, оба двухпроцессорные, выполнены в форм-факторе «блейд». Серверы такого типа сейчас фавориты внимания. Стратегия их внедрения, так называемые «экосистемы», очень спорна. Но именно в этом классе серверов, в силу высокой интеграции, максимально выгодно могут быть использованы свойства процессоров AMD Opteron, пониженное тепловыделение и в недалеком будущем двуядерные процессоры. И уже сейчас эти блейдсерверы вместе с моделями DL показывают максимальную производительность в своем классе. Хотя, что тут удивительного! Если в Cray на основе AMD Opteron создают суперкомпьютеры, то применение этих процессоров в серверах стандартной архитектуры вместе с высокими технологиями HP позволяет добиваться замечательных результатов.
Продолжается затянувшаяся история с принятием законопроекта о патентах на программное обеспечение в Европе. На OSDL Linux Summit в очередной раз эту проблему поднимают видные деятели Open Source (среди них Торвальдс, Белендорф и Капор), открыто критикуя инициативу ЕС. И уже 2 февраля выясняется, что директива CIID (о компьютерных разработках) будет переписана – таковы итоги заседания юридического комитета Европарламента (JURI). Несмотря на этот успех, Мартин Финк, Linux-специалист из Hewlett-Packard, предостерегает всех противников патентов на ПО, советуя им смириться с тем, что данного шага все равно не избежать, и к этому надо привыкать. В середине месяца при поддержке FFII в Брюсселе проходит акция протеста против принятия закона о патентах на ПО, в которой принимает участие около 300 представителей различных европейских стран. Позже Хартмут Пилч, возглавляющий FFII, высказывает сомнение в том, что Европейская комиссия воспользуется возможностью подготовить качественную замену отвергнутому законопроекту.
Алексей Барабанов
2
Новости «огненного лиса» Разработчик Firefox Бен Гуджер сообщает о планах по будущим релизам браузера. Firefox 1.1 Alpha появится в марте, Beta – через месяц, а выпуск финальной версии 1.1, изначально ожидавшийся к началу весны, перенесен на июнь. Ориентировочным временем выхода Firefox 2 остается декабрь 2005 года. Поисковая система Yahoo решила последовать примеру Amazon.com и выпустила свою панель для Firefox, с помощью которой пользователи могут искать в сети, не заходя на сайт. Компания Ask Jeeves проявила заинтересованность в возможном создании собственного браузера на базе Mozilla Firefox, а также выразила готовность открыть свои технологии поиска из Ask Jeeves Desktop Search. 16 февраля Mozilla Foundation публикует анонс о том, что Firefox скачали уже более 25 миллионов раз, а 24 числа сообщается о выходе Firefox 1.0.1 с исправлениями (в частности, в безопасности браузера).
Linux-активность Novell Novell не перестает вести активную политику по отношению к Linux. В начале месяца компания анонсирует бетаверсию Novell Client для Linux, затем совместно с IBM объявляет о намерении продвигать свой корпоративный дистрибутив SLES на платформе Power, а позже представляет решение на базе SUSE Linux для обеспечения сетевой безопасности – Security Manager. Но не останавливается и на этом: Novell открывает 200 тысяч строк кода проекта NetMail, на которых базируется новое серверное Open Source ПО компании для работы с электронной почтой, календарями и контактами, получившее название Hula. Linux-инициативы Novell добрались и до азиатских регионов: на Тайване открыто два центра для сертификации дистрибутива SUSE Linux, а в Корее подписан договор с KT, по которому местный оператор сотовой связи будет заниматься продажами SLES.
Составил Дмитрий Шурупов по материалам www.nixp.ru
события Форум по открытому коду в России 27-29 апреля 2005 г. В конце апреля впервые состоится Open Source Forum Russia – крупнейшее на сегодняшний день мероприятие, целиком посвященное технологиям разработки программного обеспечения с открытым исходным кодом. Форум, включающий в себя конференцию и выставку, пройдет 27-29 апреля 2005 года в Москве, в гостинице «Рэдиссон САС Славянская». Организаторы – ассоциация РУССОФТ, агентство Форт-Росс и компания Линукс Инк. Чем особенно интересно данное мероприятие? ! Многие широко известные в мире гуру открытого кода и главы компаний, занимающихся открытым кодом, уже подтвердили свои выступления в программе. Это Jon maddog (Linux International), Richard Seibt (Novell), David Axmark (MySQL), Larry Wall (Perl), Alex Pinchev (RedHat) и другие. ! Во время форума будет постоянно работать выставка, два раздела которой будут посвящены демонстрации наиболее интересных решений (в разделе Linux City – как различные предприятия и организации полностью функционируют на Linux, и в разделе OpenSourceLive – лучшие приложения и решения на открытом коде, отобранные к демонстрации на конкурсной основе). ! Форум поддержан Министерством ИТ и связи и Министерством экономического развития и торговли. Вопросам использования открытого кода в государственных структурах будет посвящен один из дней конференции. Программный комитет включает представителей всемирно известных вендоров, лидеров разработки программного обеспечения, представителей ведущих отраслевых министерств и крупнейших ИТ-ассоциаций как РУССОФТ и ITAA. Форум по открытому коду в России получил поддержку Всемирного Альянса ИТ-ассоциаций (WITSA). Программный комитет отбирает темы и выступления для широкого обсуждения и профессиональных дискуссий: ! управление проектами при разработке ПО с открытым кодом; ! безопасность программных продуктов и технологий на базе ПО с открытым кодом; ! обучение персонала ведению разработок в среде ПО с открытым кодом; ! особенности разработки и оказания коммерческих услуг по разработке ПО с открытым кодом; ! сравнение характеристик ПО с открытым кодом и закрытого ПО. Подробную информацию об участии в выставке и конференции вы можете найти на сайте по адресу: http:// www.opensource-forum.ru. Ждем вашей регистрации на сайте или по электронной почте: info@fort-ross.ru.
Конкурс Стартовал первый конкурс разработчиков открытого программного обеспечения, проводимый проектом OpenNet.ru совместно с ассоциацией РУССОФТ, компанией LinuxCenter и журналом «Системный администратор». Трех победителей ждет бесплатное участие в специализированном форуме
№2, февраль 2005
«Open Source Forum Russia» и возможность внеконкурсного показа своих разработок в демозоне OpenSourceLive. Заявки на участие в конкурсе принимаются до 15 марта 2005 года по адресу http://www.opennet.ru/konkurs. С 15 марта по 10 апреля будет проходить голосование, а уже 11 апреля будут подведены итоги и опубликованы результаты работы. В качестве претендентов могут выступать специалисты, участвующие в разработке программ, распространяемых с открытыми исходными текстами; авторы статей и переводов документации; энтузиасты, поддерживающие пакеты программ; эксперты, предоставляющие консультации в веб-форумах, новостных конференциях и почтовых рассылках. Кроме бесплатного участия в московском форуме, победители конкурса ОpenNet.ru получат комплект призов от компании LinuxCenter. От редакции журнала «Системный администратор» обладатели призовых мест получат в подарок бесплатную подписку на издание на 2005 год. Для одного из участников, не вошедшего в тройку лидеров, на основании решения администрации OpenNet.ru будет предоставлен сертификат на покупку книг на сумму 3000 руб. Планируется, что проводимое в рамках OpenNet.ru мероприятие получит продолжение в серии конкурсов, направленных на поддержание и стимулирование русскоязычных разработчиков открытых программ.
Международная специализированная выставка-конференция Infosecurity Russia 7-9 сентября 2005 г. Выставочное объединение «Рестэк» совместно с компанией Reed Exhibitions объявляют о проведении международной специализированной выставки-конференции Infosecurity Russia. Выставка пройдет 7-9 сентября 2005 года на одной площадке с LinuxWorld Russia и StorageExpo в выставочном комплексе «Гостиный двор» (Москва, ул. Ильинка, д. 4). В рамках выставки Infosecurity 2005 предусмотрена насыщенная деловая программа, которая включает в себя конференцию, «круглые столы», семинары и презентации для специалистов отрасли. На выставке Infosecurity 2005 будут представлены продукты и решения мировых лидеров отрасли информационной безопасности, таких как Cisco Systems, CPS, HewlettPackard, McAfee (Associates), Nortel, PatchLink Corporation, RedHat, SafeBoot, Sun Microsystems, Symantec, Veritas, а также ведущих российских фирм, таких как АМТ Груп, ДиалогНаука, Информзащита, Инфосистемы Джет, Корпорация ЮНИ, Компьюлинк, Лаборатория Касперского, ЛАНИТ, ЛанКрипто, МОО «АЗИ», Элвис Плюс, Aladdin, Digital Security, InfoWatch, Positive Technologies, Protection Technologies, Rainbow Technologies, SecurIT и других. Посетить экспозицию выставки Infosecurity 2005, а также послушать любой из докладов экспертов абсолютно бесплатно может каждый специалист по информационной безопасности, предварительно зарегистрировавшийся на официальном веб-сайте выставки www.infosecuritymoscow.com с марта 2005 года. Следите за обновлениями на сайте!
3
репортаж Школа-cеминар «Информационные технологии в образовании: Технологии Linux» C 26 по 28 января на базе Московского Государственного Педагогического Университета (МПГУ) прошла школа-семинар «Информационные технологии в образовании: Технологии Linux». Организаторами мероприятия выступили Всемирный Распределенный Университет – World Distributed University (WDU) и Центр компетенции Linux корпорации IBM. Журнал «Системный администратор» выступил информационным спонсором этого события. В работе школы приняли участие более 100 преподавателей информатики, студентов старших курсов, аспирантов и IT-специалистов, прибывших в МПГУ со всех уголков России и даже бывших союзных республик: Азербайджана и Украины.
Рабочий день семинара традиционно делился на две части. В первой половине дня проходили пленарные заседания и «круглые столы», затрагивающие актуальные темы: «Технологии Linux», «Специальность ИТО», «Защита информации и информационная безопасность». С докладами на этих секциях выступали специалисты-практики, а также приглашенные эксперты из министерств РФ.
использовать среду KDE, создавать текстовые документы, электронные таблицы, презентации и даже векторные диаграммы в открытом офисном пакете OpenOffice.org и редактировать растровые изображения в The GIMP.
По окончании работы школы слушатели, успешно усвоившие программу практических занятий и подтвердившие свои знания в ходе квалификационного экзамена, получили профессиональный сертификат «Пользователь Linux», удостоверенный печатью WDU. Кроме этого, участники семинара получили самозагружаемые компакт-диски (LiveCD) со специализированной версией дистрибутива Knoppix RE, содержащие тезисы наиболее интересных докладов, и другие полезные материалы.
Семинар «Технологии Linux» – первое и пока что единственное мероприятие подобного рода. Вспоминая известную пословицу «первый блин – комом», не будем чересчур строго относиться к организационным моментам, но отметим исключительную важность таких «педагогических» встреч для широкого продвижения идей Linux и Open Source в массы.
Живым интересом со стороны слушателей-педагогов пользовались «истории успеха» – сообщения о применении Linux в образовательном процессе в высшей школе, а также вопросы совместного использования Windows и Linux. Вторая половина дня отводилась для практических занятий. Вниманию участников школы были предложены две программы, разработанные Мельниковым В.В. (Академия наук Крыма) и Синицыным В.Е. (LinuxCenter.ru) и рассчитанные на разный уровень начальной подготовки. В ходе этих занятий слушатели приобрели базовые навыки, необходимые для повседневной работы в Linux. Они научились
4
Валентин Синицын Фото Павла Заклякова
администрирование
СИСТЕМНАЯ ИНТЕГРАЦИЯ – КОМПЛЕКСНЫЙ ПОДХОД К САМОСТОЯТЕЛЬНОМУ РЕШЕНИЮ ПРОБЛЕМЫ
РОМАН МАРКОВ «Системная интеграция». К сожалению, не все IT-специалисты четко понимают смысл этого выражения. Сегодня мы попробуем разъяснить значение этого популярного в области информационных технологий термина, а также дадим рекомендации по самостоятельному осуществлению этой задачи силами собственного IT-отдела. Проект системной интеграции в разрезе информационно-технического обеспечения предприятия – это комплекс действий, направленных на автоматизацию рабочих процессов и увеличение эффективности труда сотрудников при помощи вычислительной техники, а также на повышение безопасности корпоративной сети.
6
При этом понятие «интеграция» подразумевает реализацию единых механизмов взаимодействия IT-ресурсов и пользователей, комплексные меры безопасности на уровне всей информационной структуры предприятия, создание административной политики работы в сети, регламент функционирования отделов технической поддержки, системных администраторов и руководителей IT-структур. Помимо этого, важным условием для успешного внедрения проекта является реализация всех этапов автоматизации одной организацией (структурным подразделением), имеющей опыт создания и сдачи подобных проектов «под ключ».
администрирование С чего начать? Попробуем описать общие концепции такого проекта, а также типичные ошибки и трудности, с которыми придется столкнуться на различных этапах работы. Автор статьи является руководителем отдела системной интеграции IT-компании и принимает прямое участие в таких проектах. Описанные ситуации основаны на реальных событиях, происходящих в процессе взаимодействия с участниками проекта. Как было сказано выше, «системная интеграция» – глобальный процесс, поэтому участниками проекта в той или иной мере являются все сотрудники фирмы. В связи с этим и появляется необходимость в создании административных правил для всех подразделений предприятия. Впрочем, обо всем по порядку. Опишем порядок как административных, так и технических мер. Существуют два варианта развития проекта: ! Вы хотите построить на своем предприятии всю IT-структуру «с нуля» и «под ключ». ! У вас уже есть функционирующая сетевая инфраструктура, которую необходимо упорядочить. Как правило, первый вариант всегда проще, хотя и в этом случае обязательна разработка плана и концепций. В противном случае в итоге вы получите вариант №2 и начало новой попытки системной интеграции. Рассмотрим первый вариант как основу построения корпоративных систем, а затем на его базе проанализируем возможные пути модификации уже существующей инфраструктуры.
Кабельная система – все «с нуля» Как «театр начинается с вешалки», так и любая IT-система начинается с физических коммуникаций. Проще говоря, с проводов (или беспроводных коммуникаций). Именно грамотное проектирование с учетом дальнейшего роста компании, а также качественная разводка кабельных систем и определяет дальнейшие возможности масштабирования существующей сети. Старайтесь сразу строить предварительные расчеты и планы таким образом, чтобы свести все коммуникации (локальная сеть и телефония) в единый центр коммутации. Как правило, это коммутационный шкаф-стойка для установки в нем патч-панелей и оборудования. Именно здесь рекомендуется сконцентрировать все проводные коммуникации для их дальнейшей коммутации и распределения. Кажущиеся излишними трудозатраты по организации описанной схемы с лихвой оправдаются дальнейшей экономией времени при реорганизации фирмы (переезды отделов в другие помещения, добавление рабочих мест и т. п.). Статистика показывает, что в средних и крупных компаниях примерно 30% служащих раз в год меняют свое рабочее место в связи с описанными выше событиями. И именно по этой причине всегда необходимо планировать кабельную сеть таким образом, чтобы количество розеток было большим, чем нужно для удовлетворения сегодняшнего спроса. При масштабировании сети такой избыток дает возможность пользователям и гостям беспроблемно мигрировать внутри офиса. Всегда необходимо помнить, что и в глобальных коммуникациях (это могут быть как соединения между помещениями, так и между зданиями) рекомендуется предусмот-
№2, февраль 2005
реть «лишнюю пару» – дополнительный провод (или несколько – все зависит от задач), который не будет использоваться сразу, однако при необходимости легко вводится в общую схему коммутации. Это поможет в будущем избежать проблем с потерей скорости при резком увеличении количества рабочих мест в соседних зданиях/помещениях. Телефонная сеть вообще является камнем преткновения при расширении компании. В случае с масштабированием компьютерной сети всегда можно выйти из положения (пусть и ценой потери скорости передачи данных) установкой дополнительного сетевого коммутатора в помещение, где сгруппировались новые рабочие места. К сожалению, со стандартной телефонией такой фокус не проходит и при необходимости увеличить количество телефонов в определенном помещении приходится тянуть туда новые провода. В этом случае и можно будет использовать «лишний провод», который был заблаговременно оставлен при первичной прокладке сети. Причем задействовать витую пару, которая была запланирована под локальную сеть с некоторой избыточностью, оказывается гораздо удобнее, так как по стандартной витой паре можно прокинуть сразу несколько телефонных линий. Постарайтесь при начальном монтаже максимально облегчить себе дальнейшую жизнь. Справедливость поговорки «Два переезда равны одному пожару» много раз проверена на практике, поэтому попробуйте приложить все усилия, чтобы спланировать это еще на этапе проектирования кабельных систем. Помимо этого, не стоит забывать об уже прочно держащих позиции технологиях беспроводных сетей. В последние годы оборудование для построения беспроводных сетей наконец-то стало удовлетворять тем требованиям, которые возникают при работе пользователей с современными сетевыми приложениями, так что их применение в помещениях с прямой видимостью (или с незначительными перекрытиями) является прекрасной альтернативой проводным системам. Однако следует помнить, что стремиться построить полностью беспроводную сеть не стоит – соединения между помещениями/этажами лучше сделать на кабельной основе.
Оборудование После того, как все кабельные системы спроектированы и построены, можно приступать к закупке нового оборудования и модификации устаревшего. Тут следует руководствоваться многократно описанными принципами соответствия поставленных задач и необходимой для этого техники. Прежде всего необходимо напомнить основную мысль, которую, к сожалению, очень часто игнорируют: не экономьте на узлах, которые являются критичными по отношению к работоспособности всей системы. Практика показывает, что возможность сэкономить хотя бы сотню долларов часто приводит к тому, что закрываются глаза на обратную сторону медали – итоговую надежность системы. Как правило, руководители, принимающие решения в области финансирования, не в состоянии оперировать техническими понятиями. Зато они прекрасно знают и понимают значимость таких терминов, как «убытки в результате простоя предприятия», «цейтнот при формировании результатов», «налоговая и финансово-экономическая отчетность». И в
7
администрирование техническом обосновании приобретения надежной техники должны бросаться в глаза не «гигагерцы и гигабайты», а аргументы, понятные именно этим людям. Например, фраза о том, что «скорость формирования товарного отчета увеличится в 6-10 раз, а скорость формирования складских документов повысится в 4 раза» гораздо эффективнее, нежели «время реакции дисковой системы при операциях чтения-записи улучшается в 5 раз». Всегда помните – для руководства важны итоговые результаты, а не технические подробности. Научитесь формулировать свои мысли и идеи так, чтобы они были понятны людям, принимающим решения о финансировании проекта. Тогда вы получите большие возможности для реализации задуманного. Также немаловажным является приведение расчетов, показывающих экономическую эффективность модернизации. Спрогнозировав возможные перемещения рабочих мест сотрудников, увеличение их численности, появление новых задач, рост рабочих информационных баз, вы сможете заранее просчитать будущие затраты на необходимую модернизацию ресурсов, и сравнив их с возможностью сразу приобрести необходимую технику, вывести экономическую выгоду. В частности, попытка сэкономить на серверных системах обычно приводит либо к необходимости новой модификации уже через полгода, либо вообще к падению операционной системы, потере рабочих данных и, соответственно, незапланированным простоям – от невозможности работы одного отдела до остановки работы всего предприятия. Если рассматривать требования к таким системам, то очевидна необходимость применения в серверах надежных дисковых систем в случае больших вычислительных нагрузок – многопроцессорных ресурсов, резервных систем электропитания и защиты от перегрузок в электрических сетях. Не стоит пользоваться принципом: «система может и на IDE-диске стоять – там никаких нагрузок нет». Сэкономив 50-100 у.е. сейчас, ваша фирма получит неработоспособную систему через год. Причем произойдет это как всегда «в самый неподходящий момент». Запомните – не бывает «подходящих моментов» для выхода из строя информационной системы. И именно вы несете за это ответственность. Если при обсуждении расходов вы натыкаетесь на стену непонимания и экономить на критически важных узлах принуждают люди, принимающие решения о финансировании, – не забывайте принять меры для того, чтобы не оказаться крайним. В данном случае рекомендуется написать на имя руководителя служебную записку примерно следующего содержания: Äèðåêòîðó ÎÎÎ «Ðîãà è êîïûòà» Ïóïêèíó Âàñèëèþ Äîðìèäîíòîâè÷ó îò ñèñòåìíîãî àäìèíèñòðàòîðà Ôåäüêèíà Ïåòðà Èâàíîâè÷à Ñëóæåáíàÿ çàïèñêà Äîâîæó äî Âàøåãî ñâåäåíèÿ, ÷òî èñïîëüçîâàíèå â ïðèîáðåòàåìûõ ñåðâåðàõ æåñòêèõ äèñêîâ òèïà IDE, à òàêæå îòñóòñòâèå èñòî÷íèêîâ áåñïåðåáîéíîãî ïèòàíèÿ ìîùíîñòüþ íå ìåíåå 1000 VA ìîãóò ïðèâåñòè ê íåïîëàäêàì â ðàáîòå èíôîðìàöèîííîé ñèñòåìû âïëîòü äî ïîëíîãî âûõîäà èç ñòðîÿ è ïîòåðå õðàíèìûõ äàííûõ. Ïîäïèñü Îçíàêîìëåí: <ïîäïèñü äèðåêòîðà>
8
Ну и последнее замечание, касаемо физического построения сетей. Документируйте свои действия, маркируйте коммуникации. Даже если вы не владеете профессиональными программами для проектирования и документирования информационных сетей – программу Microsoft Visio никто не отменял. Она представляет собой довольно неплохой инструмент для инженерного планирования начального уровня. Маркировка кабелей и кабель-каналов – вообще обязательное действие. Помимо этого необходимо маркировать и серверы в стойках. Когда в стойке смонтировано несколько серверов от одного производителя, вряд ли удастся навскидку сказать, какой из них за что отвечает. Крепите на них таблички с DNS-именем и IP-адресом.
Если что-то уже есть… Если вы модернизируете уже существующую информационную систему, то все советы, приведенные выше, остаются в силе. Помимо этого, при наличии старых коммуникаций необходимо проверить их на целостность и качество передачи. Простейший способ – посмотреть статистику передачи по сетевым интерфейсам после проведения множественных операций передачи/приема данных. Если скорость передачи недостаточна, а в статистике появляются потерянные пакеты – стоит задуматься о смене кабельной системы или отдельных ее частей. Однако такой способ проверки является довольно спорным и отражает действительность только при грамотной настройке приемника/передатчика. В противном случае, например, плохо настроенное антивирусное ПО, может отрицательно повлиять на упомянутые характеристики, хотя сама кабельная система окажется идеальной. Гораздо лучше применять тестирование кабельной системы на физическом уровне при помощи приборов, измеряющих такие характеристики, как сигнал/ шум, сопротивление изоляции и др. Это дорогостоящее оборудование, однако вовсе необязательно его приобретать. Для тестирования в сомнительных случаях можно обратиться к организациям, оказывающим подобные услуги. В любом случае при сложной кабельной разводке такое исследование обойдется дешевле смены всей кабельной системы. Точно так же необходимо подвергнуть тестированию и ресурсы серверов, и при необходимости – рабочих станций. Недостаточно просто переустановить операционную систему на сервере. Необходимо убедиться в том, что нет дефектов в оборудовании – дисковой системы, памяти и т. д.
Информационная среда Убедившись в исправности физических коммуникаций и оборудования, следует переходить к следующему этапу – внедрению информационной среды. На этом этапе необходимо будет установить операционные системы на серверы и рабочие станции, организовать их взаимодействие, настроить программное обеспечение под конкретных пользователей. Разумеется, что выбор операционных систем и распределение задач должны происходить еще до того, как будет закуплено оборудование. Тут все стандартно – подбор соответствующего оборудования происходит исходя из поставленных задач.
администрирование Перейдем к принципам настройки серверного программного обеспечения, вопросам защиты от вторжений, а также настройке рабочих станций для взаимодействия с серверами и между собой. Акцентируем ваше внимание, что в статье не приводится инструкций по настройке конкретного программного обеспечения или оборудования, а даются общие рекомендации, следуя которым вы получите максимально защищенную IT-структуру. Также не указывается никаких предпочтений при выборе операционных систем для реализации проекта. Автор ни в коей мере не претендует на непогрешимость указанных методов, а всего лишь делится своим опытом реализации подобных проектов. Практика показала, что после сдачи таких сетей у заказчиков не возникает необходимости полной реорганизации, а масштабируемость позволяет легко вводить в эксплуатацию новые сервера, рабочие места и программное обеспечение. Прежде всего необходима логическая структура, которая будет объединять все учетные записи для пользователей, компьютеров и серверов в одно целое. Это «домен». Обращаем внимание, что понятие «домен» вовсе не подразумевает обязательное использование продуктов Microsoft, как многие ошибочно считают. «Домен» в переводе означает «область», «район». То есть в нашем случае домен – это логическое объединение для централизованного управления. Домен отличается от рабочей группы тем, что данные о всех структурных единицах, в него входящих, хранятся в единой центральной базе данных, что сильно упрощает администрирование системы в целом. Внутри домена необходимо спланировать разделение упомянутых структурных единиц на группы. Это упростит дальнейшее делегирование прав на пользование ресурсами. Помните, что основой построения защищенной среды является принцип «что не разрешено, то запрещено» и ни в коем случае не наоборот! Действительно, по умолчанию при начальном вводе систему в эксплуатацию никто (кроме администратора, разумеется) не должен иметь доступа никуда. Абсолютно! После этого можно делегировать группам и структурным единицам определенные права. Упомянутый и кажущийся очень простым принцип на самом деле подразумевает под собой систему глобальной безопасности. Просто необходимо научиться корректно его реализовывать.
Связь с внешним миром и безопасность Построив систему в виде локальной сети и проверив ее работоспособность (взаимодействие клиент-сервер, клиент-клиент, действие запретов и разрешений), приступаем к организации связи с внешним миром. Проще говоря, подключаемся к сети Интернет и другим филиалам компании. Тут действует тот же универсальный принцип, о котором мы уже писали. Поэтому перед тем, как подключить кабель от внешнего мира к вашей локальной сети, необходимо убедиться в том, что на устройстве маршрутизации запрещен проход любого трафика во всех направлениях. Только после этого можно аккуратно добавлять настройки, которые будут частично разрешать прохождение необходимого трафика. Любые запросы, не отвечающие разрешающим пра-
№2, февраль 2005
вилам, должны отбрасываться. Причем чаще всего рекомендуется применять для защиты «режим молчания». Запрещающие правила систем маршрутизации при поступлении соответствующего пакета отправляют ответ о том, что запрошенный ресурс запрещает подключение к нему. При соблюдении режима молчания такой пакет игнорируется, имитируя выключенное или несуществующее устройство. Для настройки таких систем необходим специалист, хорошо представляющий работу стека протокола IP. Для большей надежности рекомендуется использовать аппаратные маршрутизаторы/межсетевые экраны. Абсолютным лидером в этой области является компания Cisco Systems. Однако их продукты отличаются очень высокой стоимостью (проверенная надежность не бывает дешевой). В последнее время на рынок вышли многочисленные компании, предлагающие недорогую альтернативу подобным продуктам. Практически все производители сетевого оборудования имеют в ассортименте решения для построения защитных систем эконом-класса (маршрутизаторы со встроенными пакетными фильтрами, VPN-маршрутизаторы). Наиболее надежным окажется решение с использованием каскадного комплекса защиты. Например, последовательного использования аппаратного маршрутизатора и шлюзового сервера с работающими на нем средствами маршрутизации и протоколирования. Следующие шаги также неразрывно связаны с доступом во внешний мир. Это использование антивирусных мониторов и сканеров корпоративного уровня, а также обязательное создание внутреннего почтового сервера, отвечающего за перенаправление всей электронной почты компании. Акцентируем внимание на словосочетании «корпоративный уровень». Помните, что в системе IT-безопасности компании не должно быть звеньев, хоть каким-то образом зависящих от решения пользователя. Понятие «корпоративный антивирусный монитор и сканер» означает, что это программное обеспечение настраивается и устанавливается администратором системы, автоматически проверяет наличие обновлений, распространяется на всю сеть, а пользователь не в состоянии ни отключить, ни удалить продукт со своей рабочей станции. Такой антивирусный продукт при грамотной настройке не будет спрашивать пользователя о необходимости обновить свои базы, не станет уточнять, что делать с зараженным файлом, не позволит вмешаться в свою работу. При этом корпоративная почтовая система просканирует всю входящую и исходящую корреспонденцию еще до того, как пользователь получит возможность принять ее, отбросит зараженные письма, заблокирует подозрительные и явно запрещенные вложения, проверит легитимность отправителя и отсутствие его в международных черных списках. И только после этого доставит письмо пользователю. Как правило, на почтовые системы устанавливают антивирус другого производителя, нежели используется в режиме монитора внутри локальной сети. Таким образом достигается страховка от несвоевременного выхода обновлений какого-либо из них. Неопознанное на почтовом сервере зараженное письмо будет поймано монитором на локальной машине пользователя и наоборот. Для еще большей надежности можно применять третий способ защиты – ска-
9
администрирование нирование межсетевого трафика на промежуточном шлюзе. Однако стоит помнить, что нельзя использовать одновременно несколько таких средств в едином логическом пространстве (например, сервере или рабочей станции). Так, одновременная установка двух антивирусных мониторов разных производителей на один компьютер когда-то породила известную в IT-мире байку про дуэль антивирусов. Проверка может быть только последовательной. Не стоит также забывать о контроле активности пользователей в сети Интернет, ведении статистики посещаемости и отчетов по трафику интернет-ресурсов. Так, периодически просматривая указанную статистику, можно обнаружить «нездоровую» активность в определенном направлении и предотвратить утечку информации или распространение вредоносных программ. Помните, что «вы не можете управлять тем, что не можете подсчитать». Как правило, при грамотной настройке дальнейшая работа администратора сети заключается в постоянном контроле. Всего и вся. Осознание того, что полностью контролируешь процесс, приносит уверенность в дальнейшей работе. И напротив – даже малое непонимание происходящего в итоге приведет к полному падению системы. Контролируйте свою систему и спите спокойно. И тогда бытующее мнение о том, что системные специалисты работают круглосуточно и не спят ночами, канет наконец-то в лету. В таком дерганом режиме работают те, кто не спланировал работу заранее.
Проблемы при модернизации существующих систем Перестроение уже работающей структуры также отвечает описанным требованиям. Однако при таком развитии событий появляются некоторые трудности. Во-первых, необходимо составить подробный план перехода и проанализировать, с какими именно трудностями можно столкнуться при реализации каждого этапа. Во-вторых, осуществить внедрение в исключительно короткие сроки. Это, пожалуй, самое трудновыполнимое условие, однако поверьте, что при наличии необходимых профессиональных навыков и опыта оно осуществимо. В противном случае стоит задуматься о возможности привлечения дополнительных специалистов на время проекта. Чтобы не быть голословными, скажем, что полная реорганизация информационной сети компании (замена всех кабельных систем, располагающихся в 2 зданиях, со 100 рабочими станциями и 3 серверами, довольно большое количество сетевых приложений, включая устаревшие DOS-программы) заняла у нас 3.5 дня. Выпавшие на эти дни государственные праздники оказались как нельзя кстати, и уже в начале второй половины рабочего дня сотрудники приступили к своим обязанностям. В последующие 2 дня специалисты выслушивали пожелания сотрудников и исправляли незначительные неточности. Хотелось бы сказать еще об одной довольно серьезной проблеме, часто препятствующей успешной реализации проекта. Это человеческий фактор. А точнее – нежелание определенных индивидуумов оказывать содействие специалистам, а иногда и саботаж с их стороны. За этим стоят разные причины, одной из которых может быть нежелание обучаться работе с новыми программными продуктами и
10
технологиями. Если к этому присовокупить то, что часто такие люди занимают одни из ведущих или необходимых постов в компании и к их словам бездумно прислушиваются руководители, не желая при этом проанализировать ситуацию, – процесс может быть крайне затруднен. На практике встречались разные случаи. Например, в одной крупной строительной фирме бухгалтер со скандалом врывалась к директору и жаловалась на то, что системный администратор умышленно мешает ей работать. На самом деле администратор после внедрения домена Windows 2000 хотел добиться работы в единой операционной среде на всем предприятии и заменить технически устаревшую операционную систему Windows 98 на Windows 2000, тем более что ресурсы компьютера это позволяли. А бухгалтер заявила, что не будет работать в другой системе. В итоге техническому специалисту было в приказном порядке запрещено что-либо менять на компьютере бухгалтера. Помимо этого претензия заключалась в том, что «не надо мне никаких нортонов, пусть стоит касперский». В другой организации бухгалтера постоянно провоцировали сбои и ошибки в работе системы учета, так как не хотели принимать решение руководства о переходе на новую информационную систему бухгалтерского учета взамен старой, которая уже не соответствовала изменениям в законодательстве. Сговорившись, они умышленно искажали результаты работы программы. К счастью руководство оказалось более лояльным по отношению к техническим специалистам и, уволив из бухгалтерии двух человек, издало приказ о «содействии сотрудников техническому персоналу для перехода на новую систему учета». Эти случаи далеко не единичны и многим знакомы не понаслышке. К сожалению, тут два выхода: либо убедить руководство в необходимости модернизации и создания административных приказов и инструкций, либо отказаться от проекта. В противном случае можно получить «частично работающую систему», что не является приемлемым в любых случаях.
Подводя итоги В заключение можно сказать, что компании, решившиеся на реализацию проекта системной интеграции своими силами, должны быть уверены в том, что опыт сотрудников собственного IT-отдела позволит осуществить внедрение от начала и до конца. То есть не прекратить его, а именно завершить. В противном случае может оказаться, что изза отсутствия навыков в некоторых областях весь проект может претерпеть неудачу. Как следствие придется прибегать к помощи компании-системного интегратора, которая, скорее всего, заново будет просчитывать весь процесс и потребует сменить не соответствующее требованиям оборудование. Что, разумеется, приведет к дополнительным (и часто немалым) расходам. Поэтому именно на руководителя IT-структуры ложится основная ответственность по выбору пути системной интеграции. И глобальное понимание этим человеком всей структуры и бизнес-процессов предприятия, а также профессионального потенциала своих подчиненных поможет в итоге принять правильное решение, сделав процесс максимально эффективным, рациональным, а значит, и экономически выгодным.
bugtraq Отказ в обслуживании при обработке BGP-пакетов в Cisco IOS Программа: Cisco IOS все версии. Опасность: Высокая. Описание: Уязвимость существует во всех версиях Cisco OIS при использовании Border Gateway Protocol (BGP) и устройствах с включенным bgp log-neighbor-changes. Удаленный пользователь может послать устройству специально сформированный BGP-пакет, что вызовет перезагрузку системы. Большое количество таких пакетов приведет к отказу в обслуживании. URL производителя: http://www.cisco.com. Решение: Установите обновления с сайта производителя для соответствующей версии IOS.
Отказ в обслуживании при обработке IPv6-пакетов в Cisco IOS Программа: Cisco IOS с включенным IPv6 (все версии). Опасность: Высокая. Описание: Уязвимость обнаружена при обработке IPv6 во всех версиях Cisco IOS. Злонамеренный пользователь может с помощью специально сформированного IPv6-пакета вызвать перезагрузку системы. Большое количество таких пакетов приведет к отказу в обслуживании устройства. URL производителя: http://www.cisco.com. Решение: Установите обновления с сайта производителя для соответствующей версии IOS или запретите использование IPv6-протокола.
Отказ в обслуживании при обработке MPLS-пакетов в Cisco IOS Программа: Cisco IOS 12.1T, 12.2, 12.2T, 12.3 и 12.3T. Опасность: Высокая. Описание: Уязвимость существует при обработке Multi Protocol Label Switching (MPLS)-пакетов во всех версиях Cisco IOS, которые поддерживают MPLS, даже если MPLS запрещено на интерфейсе. Удаленный пользователь может послать специально сформированный пакет и вызвать перезагрузку системы. Большое количество таких пакетов приведет к отказу в обслуживании. URL производителя: http://www.cisco.com. Решение: Установите обновления с сайта производителя для соответствующей версии IOS.
Отказ в обслуживании в Squid при обработке WCCP-сообщений Программа: Squid 2.5.STABLE7 и более ранние версии. Опасность: Высокая. Описание: Уязвимость существует при обработке WCCPсообщений. Удаленный пользователь может создать специально сформированное длинное WCCP-сообщение и вызвать переполнение буфера в функции recvfrom(), что приведет к аварийному завершению работы прокси-сервера. URL производителя: http://www.squid-cache.org. Решение: Установите обновление от производителя: http:// w w w . s q u i d - c a c h e . o r g / Ve r s i o n s / v 2 / 2 . 5 / b u g s / s q u i d 2.5.STABLE7-wccp_buffer_overflow.patch.
№2, февраль 2005
Обход встроенных механизмов защиты от переполнения буфера в куче и технологии DEP в Windows XP SP2 Программа: Windows XP. Опасность: Высокая. Описание: Positive Technologies сообщает о возможности обхода встроенных механизмов защиты от переполнения кучи и выполнения кода в области данных в Windows XP Service Pack 2. Несколько уязвимостей обнаружено в механизме проверки маркера («cookie»), введенного для определения целостности заголовка блока памяти. Проверочный маркер проверяется только тогда, когда выделяется свободный блок, однако такая проверка не выполняется при его освобождении. В результате возможно изменить размер блока и поместить его в произвольный freelist. При работе с lookaside-списками не реализовано никаких проверок целостности заголовка – нет даже проверки достоверности значения проверочного маркера. В результате этого, теоретически, появляется возможность перезаписать до 1016 байт в любой области памяти. Подробнее об уязвимости и практических примерах ее эксплуатации можно прочитать тут: http://www.securitylab.ru/ 52238.html. URL производителя: www.microsoft.com. Решение: Способов устранения обнаруженной уязвимости не существует в настоящее время. В качестве временного решения, компания Positive Technologies выпустила бесплатную утилиту, которая для определенных приложений устанавливает глобальный флаг, который будет запрещать использование ассоциативных списков. Скачать программу можно отсюда: http://www.ptsecurity.ru/ ptmshorp.asp.
Множественные уязвимости в PostgreSQL Программа: PostgreSQL 7.x, 8.x. Опасность: Высокая. Описание: Ошибка в опции LOAD позволяет непривилегированному пользователю загрузить произвольные библиотеки. Удачная эксплуатация этой уязвимости может позволить выполнить произвольный код с повышенными привилегиями. Выполнение такого кода возможно лишь на Windows и UNIX ELF-системах (Linux). Злоумышленник может обойти ограничения на выполнение функций и выполнить произвольные функции на системе. Неизвестная уязвимость в contrib/intagg. Переполнение буфера при выполнении plpgsql с большим количеством параметров. URL производителя: http://postgresql.org Решение: Установите обновление: http://www.master. postgresql.org/download/mirrors-ftp.
Составил Александр Антипов
11
администрирование
КТО КУПИЛ COREL LINUX? ОБЗОР ВОЗМОЖНОСТЕЙ НАСТОЛЬНОГО ДИСТРИБУТИВА XANDROS
ВАЛЕНТИН СИНИЦЫН Историческая справка Компания Xandros была основана в мае 2001 года в Канаде с целью создания и продвижения на рынок недорогой, дружественной пользователю настольной операционной системы на базе Linux, которая составила бы конкуренцию Microsoft Windows – «Complete Linux Desktop Solution». В августе того же года компания приобрела Corel Linux OS, некогда популярную и весьма удачную систему, оставшуюся в результате известных событий в истории Corel «не у дел». Именно она легла в основу выпущенного некоторое время спустя Xandros Desktop 1.0. Будучи наследником Corel Linux, Xandros Desktop OS ведет свою родословную от проекта Debian, разработки которого и используются по сей день для поддержания кодовой базы. Таким образом, Xandros, по сути, является коммерческой разновидностью Debian, подобной Libranet GNU/Linux и Linspire (с последней системой Xandros некоторое время «жил под одной крышей» и развивался параллельно, в память о чем сохранился имущественный иск, предъявленный Linspire к Xandros около года назад), а значит, он совместим с пакетами DEB, репозитариями apt и прочими «прелестями цивилизации». В настоящий момент модельный ряд Xandros насчитывает несколько продуктов. Это Xandros Desktop 3 Standard/ Deluxe Edition, ориентированный на домашенго пользователя, корпоративный рабочий стол Xandros Business Desktop OS Version 2.5 Business Edition и его разновидность Xandros Desktop OS PowerTerm Edition, а также серверный дистрибутив – Xandros Desktop Management Server. С возможностями каждого из них можно ознакомиться по адресу: http:// www.xandros.com/products/products.html. В том что касается бесплатных и оценочных версий, корпорация Xandros придерживается несколько запутанной стратегии. Долгое время они попросту отсутствовали. Однако аккурат 1 апреля 2004 года компания переменила свое мнение, неожиданно предоставив для свободной загрузки самый дорогой (на тот момент) из своих продуктов – Xandros Business Desktop OS Version 2.5. Было это розыгрышем или нет, но спустя несколько дней Xandros, не объяснив мотивов своих действий, прекратила распространение ISO-образа. Однако уже в середине лета компания анонсировала еще одну community-инициативу, получившую название Xandros Desktop OS Version 2 Open Circulation Edition – OCE (http:// www.xandros.com/products/home/desktopoc/dsk_oc_intro.html). Суть ее сводилась к следующему. Из состава дистрибутива удалили коммерческие приложения, в первую очередь – эмулятор CrossOver Office и офисный пакет StarOffice 7, ограничили скорость записи компакт-дисков во встроенном файловом менеджере (об этом далее), лишили поддержки профессиональных консультантов и... отпустили в свободное плавание. Систему было разрешено использовать в некоммерческих целях и даже распространять, беря за это номи-
12
нальную плату. Иными словами, с точки зрения домашнего пользователя, Xandros OCE оказался ничем не хуже Fedora Core или (не побоюсь этого высказывания) оригинального Debian, поскольку вносить изменения в код системы или зарабатывать с ее помощью деньги многие из них и не помышляли. Удачно составленная лицензия на продукт не могла не привлечь внимание дистрибьютеров, в частности ЛинуксЦентра (www.linuxcenter.ru), благодаря усилиям которых приобрести Xandros OCE в России стало не сложнее, чем обычный свободный дистрибутив. Впрочем, граждане с тонким кошельком и толстым каналом всегда могут загрузить ISOобраз системы через сети BitTorrent. В момент написания данной статьи последней версией Xandros является третья, отличающаяся от своих предшественниц встроенной функцией записи DVD и персональным брандмауэром. Однако в связи с ее недоступностью для большей части читателей мы решили сфокусировать свое внимание на второй версии дистрибутива, распространяющейся в форме редакции OCE. О планах выпуска Open Circualtion Edition для Xandros 3 компания ничего не сообщает, однако можно предположить, что это все же случится, ведь сворачивать данную инициативу пока никто не собирается.
Установка Программа-инсталлятор системы является предметом гордости компании Xandros, и не без оснований. Как утверждает реклама, дистрибутив можно установить в пять щелчков мышью. Это вполне соответствует действительности: процедура установки проходит в пять этапов. После стандартного приветствия и ознакомления с лицензией пользователю предлагается сделать выбор между автоматическим (Express Install) и ручным (Custom Install) режимом установки. Предпочитающие делать все «дешево и быстро» могут выбрать первый вариант, создать пару учетных записей и приступить непосредственно к копированию файлов, как это обычно и происходит в других настольных дистрибутивах. Однако Xandros предоставляет более богатые возможности для настройки в ручном режиме. Во-первых, это менеджер разделов, вещь сама по себе не удивительная и распространенная повсеместно: от Red Hat до Slackware. Однако его разновидность, входящая в состав Xandros, обладает рядом особенностей. Это в первую очередь встроенный навигатор по файловой системе в стиле Konqueror, поддерживающий ext2, ext3, ReiserFS и даже NTFS. Если в других случаях вам приходилось «играть в русскую рулетку», вспоминая, какой из разделов жесткого диска подлежит сносу, а какой – содержит бесценные данные, то сейчас достаточно просто нажать на кнопку и посмотреть его содержимое. Как правило, структура каталогов и имена находящихся в них файлов говорят простому пользователю (да, впрочем, и не очень простому пользователю тоже) куда
администрирование больше, чем аббревиатуры типа «hda3» или словосочетания вроде «третий основной раздел». Для уже созданных разделов можно назначить точку монтирования, которая выбирается из выпадающего списка (в соответствии с наукой, содержащаяся на разделе файловая система – ext2, ext3 или ReiserFS – может быть смонтирована в /, /usr/local, /var или /opt). Если вы пожелаете установить Xandros на непустой раздел, инсталлятор предложит вам сохранить имеющиеся на нем каталоги /root и /home. Естественно, это произойдет лишь в том случае, если раздел ранее принадлежал какой-либо UNIX-системе. Среди элементов управления в окне менеджера разделов присутствует и кнопка «Add», которая, по логике вещей, должна создавать новые партиции, однако по не очень понятным мне причинам она перманентно отключена. Другая уникальная в разрезе рассмотренных ранее настольных дистрибутивов возможность Xandros – это функция выбора пакетов. Начинающий администратор может установить одну из заранее определенных конфигураций: «Minimal Desktop» (минимальный набор, всего 655 Мб), «Standard Desktop» (стандартный набор – 980 Мб), «Complete Desktop» (полный набор, 1156 Мб) и «Custom Desktop» (установка по выбору пользователя). Все доступные пакеты сгруппированы по категориям, а их выбор осуществляется путем выставления «галочки» возле имени. Отрадно, что в этом списке присутствуют и средства разработки. Как правило, производители настольных дистрибутивов по ряду причин предпочитают не включать столь мощный инструмент в базовый комплект поставки. Разобравшись с программным обеспечением, можно переходить к вводу параметров сети (Static IP/DHCP, шлюз, DNS). Здесь все вполне стандартно. Далее инсталлятор предложит ввести пароль администратора (Administrator), он же – root. При этом в нижней части окна будут расположены две «галочки», одна из которых – «Enforce strong passwords» («Требовать сильные пароли») – сейчас представляет для нас особый интерес. Если она находится во включенном состоянии, то отделаться паролем «123456», который, не пикнув, проглатывали Linare, Linspire и Lycoris Desktop/LX, уже не удастся. Второй флажок – «Make user home folders private» («Сделать домашние каталоги пользователей личными») – просто запрещает автоматический доступ к домашним каталогам пользователей по сети. Завершив эти манипуляции, можно приступать к созданию непривилегированных учетных записей. Рекомендуется сделать хотя бы одну из них, и использовать ее для повседневных надобностей. При включенной опции «Enforce strong passwords» к паролям простых пользователей предъявляются те же требования, что и к паролю администратора. Однако пустой пароль инсталлятор вполне устраивает. В вопросах установки дискового загрузчика Xandros также проявил неординарную проницательность и учтивость. Инсталлятор корректно определил уже установленные на компьютере операционные системы: Mandrakelinux и Windows 2000 и добавил их в свое меню, сохранив, таким образом, время, которое я традиционно тратил на восстановление содержимого MBR после деятельности менее аккуратных «собратьев». Правда, «покопаться с паяльником» все же пришлось: включенная в состав Xandros вер-
№2, февраль 2005
сия LILO наотрез отказалась дружить с моим ноутбуком, намертво зависая при загрузке. Пожалуй, этой проблемы можно было бы избежать, если знать о ней заранее: я бы просто попросил программу не устанавливать свой загрузчик в MBR или вообще никуда – Xandros предоставляет и такую возможность. После всех описанных выше манипуляций программа откроет окно «Summary». Проверьте и, если вас устраивают все введенные параметры, нажмите кнопку Finish, чтобы начать копирование файлов. Инсталлятор будет держать вас в курсе происходящего, постоянно сообщая о своих текущих занятиях («устанавливаю базовую систему», «настраиваю Samba» ) в строке статуса.
Первый запуск После того как инсталлятор закончит работу, вам будет предложено перезагрузить компьютер (в вопросах подобного рода Xandros не скупится – соответствующие приглашения выводятся на экран шрифтом такого размера, что разрешения 1024x768 едва хватает на 3-4 строчки текста). Выбрав нужный пункт в загрузочном меню и подождав некоторое время (как и все настольные дистрибутивы, Xandros тяжеловат на подъем), вы увидите менеджер входа в систему, выглядящий на фоне своих аналогов из Linspire или Lycoris Desktop/LX несколько аскетично: выпадающее меню с именем учетной записи, поле для ввода пароля и пара кнопок. Как видите, ничего лишнего. После авторизации появляется стандартный стартовый экран KDE, а затем – мастер первого запуска («First Run Wizard»). Выбрав тип мыши («левая» или «правая»), пользователь попадает на поистине интересную вкладку: «Regional Settings» («Региональные настройки»). Здесь можно указать системную локаль, язык рабочего стола, кодировку и раскладку клавиатуры для текущего пользователя. Для каждого из этих параметров (кроме, к сожалению, языка рабочего стола) доступны «русифицированные» варианты ответа. Лично я остановился на следующем наборе: локаль – ru_RU, кодировка – KOI8-R, раскладка – Russian. После этого Xandros сразу же заговорил по-русски, весьма неплохо, хотя и с «акцентом». Шрифты, используемые системой для отображения кириллицы, выглядят ничуть не хуже латинских, благо в стандартную поставку Xandros (в том числе, Xandros OCE) входит некоторое число коммерческих фонтов от Bitstream. Правда, моноширинный кириллический шрифт, используемый в эмуляторе терминала, почему-то странно двоится, однако этот огрех можно считать мелочью на фоне того, что творится с поддержкой русского языка в иных конкурирующих продуктах (любознательного читателя мы отсылаем за подробностями к более ранним статьям данного цикла [1, 2, 3]). Для нормальной работы с системой следует доустановить язык US. English. Это можно сделать с помощью контекстного меню стандартного переключателя раскладок KDE, «обитающего» в системном лотке. Несмотря на несомненные достижения в области «лингвистики», Xandros был и остается иностранцем, поэтому пользоваться родным языком в нем приходится с некоторой долей осторожности. Некоторые подводные камни мы обсудим чуть позже. Вслед за региональными настройками вам будет предложено выбрать часовой пояс и установить принтеры (под-
13
администрирование держиваются как локальные, так и сетевые, например, управляемые компьютерами на базе Windows или подключенные напрямую через HP JetDirect). После ненавязчивой просьбы о регистрации Мастер первого запуска попрощается с вами, сказав напоследок, где его можно будет найти, если он вдруг понадобится. Принимая во внимание частые мучения пользователей, вспоминающих, «где же было такое вот окошко», эта информация кажется весьма и весьма нелишней. Закрыв мастер, можно начинать знакомиться с рабочим окружением (рис. 1). По умолчанию на каждого непривилегированного пользователя автоматически заводится два виртуальных рабочих стола: «синий» и «зеленый». В процессе повседневной работы их число легко можно увеличить, щелкнув правой кнопкой мыши по апплету «Virtual Desktop» в панели и выбрав пункт меню «Configure Virtual Desktops». Простой эксперимент показывает, что столы с номерами три и четыре имеют соответственно желтый и фиолетовый цвета, а в дальнейшем эта закономерность портится, и все становится одинаково темно-синим.
Xandros File Manager Файловый менеджер является еще одним козырем в колоде Xandros. Так или иначе о нем упоминают все рекламные проспекты, а встроенная возможность записи CD (в версии 3.0 – еще и DVD) выставляется как едва ли не главное преимущество этой ОС над конкурентами. В общем, данный продукт вполне заслуживает пристального рассмотрения.
Ðèñóíîê 2. Îêíî Xandros File Manager ñ ìåíåäæåðîì ïðîåêòîâ. Ïóíêò «Removable Disc» â ñàìîì íèçó äåðåâà ñîîòâåòñòâóåò ïîäêëþ÷åííîìó USB-íîñèòåëþ
Ðèñóíîê 1. Ðàáî÷èé ñòîë íåïðèâèëåãèðîâàííîãî ïîëüçîâàòåëÿ Xandros
В свою очередь рабочий стол администратора имеет красный цвет, так что разобраться, какими полномочиями вы обладаете в системе, не составляет труда – достаточно беглого взгляда на экран. Кнопка «Пуск» в Xandros называется «Launch», рядом с ней располагается панель быстрого запуска, на которой находятся пиктограммы эмулятора терминала (только для root), веб-браузера, файлового менеджера, встроенной справки и очистки рабочего стола. Крайняя правая кнопка возле системного лотка (tray) служит для смены пользователя («Switch user»). При ее нажатии открывается диалог, предлагающий заблокировать текущую сессию и запустить X-сервер на новой консоли. Переключение между ними осуществляется как стандартным способом (<Ctrl>+<Alt>+<Fn>, где n = 7, 8, 9), так и при помощи все того же диалогового окна. Однако первое, что бросается в глаза при обзоре рабочего стола, – это отсутствие пиктограммы «Мой компьютер», столь привычной пользователям Windows. Как мы увидим ниже, сама концепция «My Computer» в Xandros живет и здравствует, а почему разработчики решили не помещать средство доступа к ней в привычное место – остается только догадываться.
14
В основе Xandros File Manager (XFM) лежит Konqueror, что весьма логично – это обеспечивает высокую степень интеграции с KDE. В левой части окна XFM находится дерево My Linux (рис. 2), того самого аналога «Моего компьютера», который почему-то забыли поместить на рабочий стол. Первой среди его ветвей является My Home (домашний каталог), за нею следуют принтеры, сетевые ресурсы Microsoft (компьютеры с Xandros добавляются сюда автоматически) и NFS, разделы Windows, которые имеют привычные имена: C,D,..., а также менеджер проектов записи CD (CD Writer) и подключенные съемные носители. Легко заметить, что доступа к корневой файловой системе и «посторонним» разделам Linux дерево не предоставляет, однако для этого можно воспользоваться адресной строкой. Введя в ней что-нибудь вроде «/», вы увидите соответствующее запросу дерево каталогов, а в My Linux появится новый, скрытый доселе пункт: «All File Systems». Видимо, разработчики не без оснований полагают, что рядовому пользователю шастать за пределами своего домашнего каталога не стоит, вот и решили предостеречься от излишне любопытных. Кстати, если вы все же решите полазить по дереву каталогов в одиночку, без помощи KDE, имейте в виду, что все «чужие» файловые системы (в том числе разделы Windows и съемные носители: CD-ROM, USB Flash и т. д.) Xandros подключает не в /mnt, как можно было бы того ожидать, а в /disks. Как уже вскользь упоминалось выше, XFM обладает собственными функциями для записи CD, причем, на мой взгляд, реализует их удачнее, чем Microsoft Windows XP. В основе этой возможности лежит понятие проекта – т.е. набора файлов, предназначенных для записи на CD. Существующие проекты хранятся в «папке» CD Writer в My Linux. Там же находятся инструменты, позволяющие создать новый проект и начать запись. Чтобы добавить к проекту файл(ы) или целые каталоги, можно воспользоваться пунктом контекстного меню «Add
администрирование to CD Prject», ссылкой «Add all to CD Project...» в окне с содержимым каталога или простым перетаскиванием (dragn-drop). Опыт работы показывает, что для файлов или проектов с русскими именами применим только последний метод. Очевидно, что подобным образом можно добавлять файлы, находящиеся в различных каталогах или даже на различных носителях. Сведения о текущем состоянии проекта можно получить, щелкнув по его имени в ветви «CD Writer» (рис. 2). В нижней части появившегося окна расположена линейка, хорошо знакомая пользователям Ahead Nero и отражающая текущий процент заполнения диска. XFM умеет работать с rewritable-носителями (т.е. очищать их) и может создавать аудио-CD. К сожалению, разработчики не предусмотрели поддержку мультисессионных дисков, т.е. «записать» на диск получится, а вот «дописать» – уже нет. В заключение отметим, что в бесплатном Xandros OCE максимальная скорость записи в XFM ограничена 4x (600 Кб/сек).
Впечатления от работы В стандартную поставку Xandros входит весьма неплохой комплект программ, способный удовлетворить все нужды среднестатистического офисного служащего. Немного удивляет отстуствие полноценного графического редактора, например, GIMP, однако для простых рисунков неплохо подойдет и KPaint. В качестве браузера по умолчанию используется Opera 7.5 (в версии OCE – adware), ее же предполагается применять для работы с электронной почтой. Ни Mozilla, ни Firefox, ни тем более Evolution в состав Xandros, как ни странно, не входят. Вообще у разработчиков наблюдается странная неприязнь к приложениям на базе GTK. Они практически отсутствуют – за исключением, пожалуй, MP3-проигрывателя XMMS, который искусно замаскирован под общий стиль оформления (Plastik) при помощи соответствующей «шкурки». Архитектурно такой подход оправдан – рабочий стол получается более интегрированным, однако, как ловко подметил кто-то из экспертов: «И в мире GTK, и в мире Qt есть приложения, не имеющие аналогов». Вряд ли ориентированность на другую интерфейсную библиотеку стала причиной нелюбви к GIMP – вероятнее всего, он просто не влез на диск. Для исправления сложившейся ситуации можно использовать Xandros Networks (http://www.xandros.com/products/home/xn/ xn_intro.html) – продуманную систему для установки и обновления пакетов, бесплатно доступную всем зарегистрированным пользователям. Xandros Networks может работать как с собственным программным репозитарием, так и с банком программ Debian, компакт-дисками и любыми другими хранилищами, совместимыми с apt. Если автоматическое разрешение зависимостей для вас не играет принципиальной роли, с помощью Xandros Networks можно установить или удалить любой пакет в формате DEB и RPM. Помимо свежего ПО через сеть Xandros Networks распространяются новости, уведомления и патчи. Таким образом, эта программа представляет собой мощный инструмент для сопровождения и управления системой, но, к сожалению, ее детальное рассмотрение выходит за рамки данной статьи.
№2, февраль 2005
Попробуем подвести итог вышесказанному. Xandros, несомненно, один из самых мощных игроков на рынке настольного Linux, а неплохая поддержка русского языка и наличие бесплатной версии делает его привлекательным и для отечественных пользователей. Неопределенность относительно будущего Open Circualtion Edition несколько настораживает, однако если вы твердо решили установить Linux на своем рабочем столе, я бы посоветовал вам иметь Xandros в виду. При своей доступности и легкости внедрения он может составить достойную конкуренцию распространенным некоммерческим дистрибутивам Linux. В заключение цикла статей о настольном Linux я хочу привести небольшую сводную таблицу рассмотренных нами дистрибутивов, чтобы вам было легче сравнивать их между собой. Предварительно необходимо сделать несколько замечаний. В графе «Стоимость» указан диапазон цен для различных моделей продуктовой линейки. Наличие средств разработки и запуска Windows-приложений определялось по принципу: «хотя бы в одной редакции продукта в стандартной комплектации». Это означает, что доступность соответствующих пакетов в сетях поддержки (например, CNR Warehouse) в расчет не принималась. Качество поддержки русского языка оценивалось по пятибалльной шкале, где 1 соответствует некоему абстрактному дистрибутиву, в принципе не способному отображать ничего, кроме латиницы, а 5 – полностью русифицированной системе (ввод и вывод кириллицы на экран и на печать, переведенные системные сообщения, интерфейс программ и т. д.).
Пока верстался номер, 14 февраля в Сети появились сведения о том, что компания Xandros выпустила в свет третью версию Open Circulation Edition, восстановив таким образом соответствие между бесплатной и платной версиями. В состав продукта вошли веб-браузер Mozilla Firefox, клиент электронной почты Mozilla Thunderbird и интернет-телефон Skype. Как и его предшественник, Xandros Desktop 3 Open Circulation Edition будет доступен для свободной загрузки через BitTorrent. Вероятно, к выходу этого номера из печати, дистрибутив можно будет приобрести и в российских интернетмагазинах.
Литература: 1. Синицын В. Заметки о Linare. – журнал «Системный администратор», №11, ноябрь 2004 г. 2. Синицын В. Linspire одним глазком. – журнал «Системный администратор», №12, декабрь 2004 г. 3. Синицын В. Linux из Редмонда: обзор Lycoris Desktop/ LX. – журнал «Системный администратор», №1, январь 2005 г.
15
администрирование
СОЗДАНИЕ РЕЛИЗА FreeBSD
АНДРЕЙ ЕЛСУКОВ В этой статье я хочу обобщить свой опыт по сборке релиза операционной системы FreeBSD. Создание релиза – не такая уж сложная задача, но достаточно длительная и при некоторых обстоятельствах может продолжаться дольше, чем ей необходимо. Да и к тому же описание этого процесса на русском языке мне найти не удалось. Надеюсь, что статья поможет желающим собрать свой релиз и обойти те проблемы, с которыми столкнулся когда-то я.
Для чего это нужно? Я вижу несколько причин, для чего может понадобиться создать релиз ОС FreeBSD в «домашних» условиях: ! не всегда есть время, средства, возможность покупать свежий релиз системы, чтобы иметь его на CD для более быстрой установки «свежей» системы; ! хочется иметь под рукой загрузочный инсталляционный диск системы со специфическими настройками: установочный диск STABLE-ветки; диск с обновленными драйверами, например для RAID-контроллеров; с определенным набором скомпилированных пакетов; с архивами исходных файлов некоторых программ для компиляции из «портов»; ! хочется всё знать и уметь. Кто-то, возможно, предложит свои причины, для меня достаточно и этих.
Что нам необходимо для начала? Как это ни банально, нужна машина под управлением ОС FreeBSD. Причём желательно, чтобы версия системы была из той же ветки, что и версия будущего релиза (почему «желательно» – расскажу позднее). Если ваша машина подключена к Интернету и проблемы с ценой трафика для вас
16
не существует, то следующие компоненты для вас не важны, если же нет, то идём по пунктам.
CVS-репозитарий Эта «свалка файлов» в архиве на данный момент у меня занимает порядка 500 Мб. Чтобы получить репозитарий – проще всего воспользоваться CVSup. У меня по крону раз в сутки запускается следующая команда для обновления репозитария: # cvsup –g –L 2 /mnt/cvs/cvs-supfile
где конфигурационный файл CVSup выглядит так: *default *default *default *default *default *default
host=cvsup2.FreeBSD.org base=/var/db prefix=/mnt/cvs/ncvs release=cvs delete use-rel-suffix compress
src-all ports-all doc-all cvsroot-all
Лучше всего, если вы достанете архив репозитария, пусть даже старый у своего знакомого или ещё где-то – сэкономите кучу времени и трафика. Поддержание репозитария в «свежем» состоянии уже не так накладно, так что, сделав один раз, использую и сейчас.
Архивы исходных файлов для сборки портов Не пугайтесь, дистрибутивы для всех портов нам не понадобятся. Но и перечислять каждый порт довольно утомительное занятие, да и от релиза к релизу этот список может различаться, поэтому я немного обобщу:
администрирование ! для сборки проекта документации понадобятся исходные файлы для портов, список которых можно посмотреть в файле src/release/Makefile.inc.docports; ! для создания ISO-образов дисков нужен порт ports/sys utils/cdrtools; ! для создания полноценного miniinst-диска нужны исходные файлы Perl и X.org, либо XFree (в зависимости от версии системы). В общем-то, вот и всё. Недостающие компоненты можно будет скачать во время сборки (если есть доступ к Интеренету, то они скачаются автоматически).
Как это происходит? Весь процесс сборки я условно разделил на несколько этапов. Отчасти моя классификация перекрывает ту, что описана в мануале release(7). Итак, по порядку.
Подготовка к сборке Кроме вышеперечисленного я отнёс сюда следующие действия: ! получение исходных файлов системы для будущего релиза; ! выбор раздела, на котором достаточно свободного места. К слову сказать, для сборки релиза может понадобиться от двух до четырёх с небольшим гигабайт свободного места.
Сборка мира Собственно, сборка мира точно такая же, как и при обновлении системы, подробности – в handbook.
Создание chroot-окружения Весь процесс сборки релиза, сразу после компиляции мира, происходит в chroot-окружении. Сначала туда инсталлируется собранный мир, затем там происходит дальнейшая компиляция системных библиотек, ядер, портов, проекта документации и т. п.
Создание дистрибутива Это заключительная стадия, включает в себя компиляцию указанных ядер, создание структуры каталогов дистрибутива, размещение архивов бинарных файлов и установочных скриптов, создание каталога для инсталляции с FTP, создание ISO-образов дисков.
которых может фатально сказаться на дальнейшей сборке релиза, например Perl. Так что, если не хотите терять время, лучше на первый раз закомментировать все опции в make.conf или переименовать этот файл. Возможно, наоборот, вы хотите использовать какие-то специфические опции, например дополнительную оптимизацию, – дело ваше.
Переменные окружения Часть переменных окружения описана в мануале release(7). Часть можно найти в make-скриптах, используемых при сборке (в папке src/release). Хочу обратить внимание на некоторые из них: ! DOC_LANG – в ней можно перечислить через пробел языки, для которых вы хотите собрать документацию, например, DOC_LANG=”en_US.ISO8859-1 ru_RU.KOI8R”. Если вы не укажете языки, то будут собираться все возможные, а это довольно долго. ! NOPORTREADMES – при включении в дистрибутив дерева портов не будут генерироваться README.html с описанием портов, что существенно ускорит процесс сборки релиза. ! RELEASENOUPDATE – если при сборке у вас возникнет ошибка, эта переменная совместно с целью make rerelease освободит вас от повторного обновления исходных файлов системы из CVS-репозитария. ! BUILDNAME – имя релиза. Если не зададите, то получите в результате что-то подобное FreeBSD-4.11-20050131STABLE. Ещё рекомендую задавать имя в стандартном виде, как это делается в оригинальных дистрибутивах, например 5.3-STABLE. Не нужно называть релиз своим именем, если не хотите в дальнейшем иметь проблемы, например, при установке или обновлении портов. ! MAKE_ISOS – если задать, то при завершении всех этапов сборки автоматически будут созданы ISO-образы дисков дистрибутива. Советую не спешить с указанием этой переменной, создать образы никогда не поздно. ! hostname – это не переменная окружения, но я отнёс её сюда. Когда вы соберёте релиз, при его установке, в выводе команды uname, будет фигурировать имя хоста, на котором происходила сборка. Из эстетических соображений на время сборки можно сменить имя хоста командой hostname.
«distfiles» для портов
Рекомендую прочитать Makefile в каталоге src/release. Из него можно почерпнуть довольно много полезной информации. Так же обязательно прочитать мануал release(7) и желательно просмотреть все файлы, на которые он ссылается.
Как я уже говорил, для сборки релиза понадобятся дистрибутивы некоторых портов. Если у вас уже есть скачанные дистрибутивы, вы можете указать скриптам их местоположение, и они не будут скачиваться снова. Для этого используется переменная окружения RELEASEDISTFILES. Если вы пользуетесь системой портов и в вашем дереве есть каталог ports/distfiles, то переменную можно не определять, этот каталог будет скопирован в chroot-окружение (наверное, стоит задуматься о том, сколько места он занимает).
make.conf
Когда что-то пошло не так
При сборке мира используются установки из /etc/make.conf текущей системы. Рекомендую просмотреть их и подумать – нужны ли они вам. К примеру, некоторые настройки могут запрещать сборку отдельных частей системы, отсутствие
Если с машины, где происходит сборка релиза, нет прямого доступа к Интернету, то есть большая вероятность того, что на каком-нибудь этапе сборка прервётся с ошибкой. Хотя практика показывает, что они могут возникать и при
На что следует обратить внимание?
№2, февраль 2005
17
администрирование наличии доступа в Интернет. Ошибки, препятствующие сборке релиза, могут быть разными. Начиная от простых – нехватки каких-нибудь дистрибутивов для сборки из портов и заканчивая различными ошибками при компиляции.
Не нужно начинать всё сначала Чтобы не начинать всё заново, расскажу, как с «минимальными потерями» продолжить сборку релиза. Итак, предположим, что процесс сборки прервался по какой-то причине. Решив проблему, вы хотите продолжить сборку. Для этого нужно указать утилите make цель «rerelease». И если нет необходимости в обновлении исходных файлов системы из репозитария, желательно определить переменную RELEASE NOUPDATE. Теперь, предположим, что вы хотите повторить какой-то этап сборки, например пересобрать ISO-образы системы. Предыдущая схема здесь не сработает. Если ISO-образы уже были созданы, то повторного создания таким методом не добиться, даже если предварительно их удалить. По той простой причине, что при завершении этапов сборки (они описаны в мануале release(7)) скриптами сохраняются «пометки» об их завершении. Пометки – это обычные файлы, созданные командой touch. Имена файлов соответствуют названию завершившегося этапа. Файлы находятся в каталоге ${CHROOTDIR}/usr/obj/usr/src/release. Для того чтобы вновь пересобрать ISO-образы, нужно удалить файл «iso.1» и уже тогда запускать make rerelease.
Не хватает дистрибутивов для установки порта Если есть доступ в Интернет, то возникновение такой ошибки маловероятно. Если же доступа нет, то можно вручную скачать дистрибутив для установки порта с машины, где есть доступ, и поместить его в папку ${CHROOTDIR}/usr/ ports/distfiles, конечно, нужно не забывать, что некоторые дистрибутивы могут располагаться в отдельных каталогах. Чтобы узнать, должен ли дистрибутив порта располагаться в отдельном каталоге, нужно выполнить в каталоге порта команду grep DIST_SUBDIR Makefile. После этого можно продолжить сборку, как это описано выше.
Ошибки компиляции Однозначный ответ на вопрос: что делать при ошибках компиляции? – пожалуй, дать довольно сложно. Опять же, могу направить к чтению handbook, в котором есть несколько советов на эту тему. Приведу несколько примеров ошибок и возможных путей их решения: ! При сборке порта выводится ошибка о нехватке какойлибо библиотеки, вместо установки её как зависимости. Возможно, у вас отсутствует индексный файл портов – ports/INDEX или ports/INDEX-5 (для 5-й ветки FreeBSD), ports/INDEX-6 (для 6-й ветки FreeBSD). Можно скачать индексный файл с сайта – http://www.freebsd.org/ports/ INDEX.bz2, INDEX-5.bz2 или INDEX-6.bz2. Или просто перейти в ${CHROOTDIR} и собрать требуемый порт руками, устанавливая каждую зависимость, а затем продолжить сборку. Переходить в ${CHROOTDIR} нужно с помощью команды chroot, чтобы порты устанавливались и регистрировались в окружении той системы, релиз которой вы собираете.
18
! Какой-то определённый порт не собирается, даже если выполнить предыдущие рекомендации. Некоторые порты во время сборки определяют версию системы и в зависимости от неё выбирают соответствующие параметры сборки приложения, либо по версии системы выбирается, какие зависимости имеет порт. Система портов определяет версию через переменные ядра. Поэтому, хоть сборка порта и происходит в chroot-окружении, где все бинарные файлы и исходные коды от нужной версии ОС, для системы портов версия ОС будет той, в которой вы собираете релиз. Это следует помнить, так как версия системы влияет и на выбор индексного файла, и на то, какой файл будет скачиваться при выполнении make fetchindex, и какой файл будет генерироваться при выполнении make index. Это ограничение можно обойти, если определить переменные OSVERSION и OSREL. Формат OSVERSION можно посмотреть в переменной ядра kern.osreldate, OSREL определяется по первым цифрам вывода «uname -r». Подробнее всё это можно узнать, просмотрев скрипты в каталоге ports/Mk. Эти переменные можно поместить в ${CHROOTDIR}/etc/ make.conf, тогда они будут использоваться при каждом запуске make в chroot-окружении.
Прочие ошибки Искать способы исправления других ошибок вам придётся, видимо, самим. Можно попытаться обратиться в один из списков рассылки или форум, посвящённый FreeBSD. Расскажу только об одной ошибке, не относящейся к предыдущим пунктам. Я столкнулся с ней, когда попытался собрать релиз 4-й ветки, находясь в 5-й. Ошибка возникает, когда система пытается создать загрузочную область и образы загрузочных дискет. В 5-й версии было убрано псевдоустройство vn(4) и утилита vnconfig(8), их функционал перенесён в драйвер md(4) и mdconfig(8). Да, и ещё в 5-й версии используется devfs. Итак, как решить проблему по созданию релиза 4-й версии FreeBSD в окружении 5-й? Не скажу, что способ правильный, но он рабочий, по пунктам: ! Нужно определить в make.conf переменные OSVERSION и OSREL в значение, которое они должны иметь в создаваемом релизе. ! Собираем мир, устанавливаем его и после завершения этапа release.2 прерываем сборку. Добавляем аналогичные опции в ${CHROOTDIR}/etc/make.conf. Это необходимо для корректной сборки портов. Продолжаем сборку релиза. ! После завершения этапа release.7 сборка должна прерваться с ошибкой в скрипте doFS.sh. Теперь нужно статически собрать утилиту mdconfig, чтобы исключить все зависимости от разделяемых библиотек. Для этого понадобятся исходные файлы текущей системы (той, в которой происходит сборка релиза), переходим в каталог src/sbin/mdconfig и запускаем команду «make -DNO SHARED depend all». В каталоге obj/usr/src/sbin/mdconfig будет статически скомпилированная утилита mdconfig. Копируем её в ${CHROOTDIR}/sbin/. Монтируем файловую систему devfs – «mount_devfs devfs ${CHROOTDIR}/ dev». Теперь можно продолжить сборку релиза.
администрирование Я не проверял, будет ли этот способ работать при сборке релиза 5-й версии FreeBSD, находясь в окружении 4-й, но не вижу причин, по которым сборка может не удастся. Различие только в том, что собирать нужно будет не mdconfig, а vnconfig и вместо монтирования devfs создавать файлы устройств с помощью скрипта /dev/MAKEDEV.
Приступим к сборке Процесс сборки релиза опишу именно так, как делаю его я. Начальные данные: ! Рабочий сервер, у которого есть доступ в Интернет, на нём находятся CVS-репозитарий и каталог distfiles с дистрибутивами для сборки портов. ! Ещё один сервер, на котором я ставлю эксперименты, он находится внутри локальной сети без доступа в Интернет. ! Доступ к CVS-репозитарию осуществляется по NFS. ! Собирать буду FreeBSD 5.3-STABLE, т.е. с CVS-тэгом RELENG_5.
Перед сборкой ISO-образов в 5-й версии FreeBSD необходимо создать пакеты xorg и perl5. Если делать всё «по правилам», то для создания пакетов должен использоваться отдельный сервер, так как других лишних серверов у меня в наличии нет, я использую этот же. Технология такая: ! Создаётся каталог ${CHROOTDIR}/usr/ports/packages. ! В chroot-окружении создаются необходимые пакеты вместе со всеми зависимостями при помощи make packagerecursive. ! По имеющимся пакетам создаётся файл INDEX. ! Каталог packages копируется в ${CHROOTDIR}/R/cdrom/ disc1/. По первым двум пунктам вроде бы вопросов быть не должно, а вот о создании файла INDEX расскажу подробнее. Я использовал для его создания свой скрипт, поэтому не заметил, как в версии 5.3 появился скрипт src/release/ scripts/mkpkgindex.sh. Мой скрипт выглядит так:
Подготовка #!/usr/local/bin/perl # # # # # # # # #
cd /usr/home mkdir release mount_nfs –o rdonly server:/usr/home/cvs /mnt cvs –Rd /mnt/ncvs co –Pr RELENG_5 src cd src/ mv /etc/make.conf /etc/make.conf.old make –j8 buildworld cd release/ vim start.sh
После сборки мира я создаю скрипт примерно со следующим содержанием: make –j8 CHROOTDIR=/usr/home/release BUILDNAME=5.3-STABLE ↵ CVSROOT=/mnt/ncvs RELEASETAG=RELENG_5 ↵ DOC_LANG=”en_US.ISO8859-1 ru_RU.KOI8-R” ↵ NOPORTREADMES=yes RELEASENOUPDATE=yes release
foreach(`ls All/`) { chop; s/\.t.z$//; `grep -E "^$_" ../INDEX-5 >> INDEX`; }
Итак, для минимального дистрибутива нужны пакеты perl и xorg, создаём их и копируем на «будущий» диск: # # # # # # # # # # #
mkdir /usr/home/release/usr/ports/packages cp mkindex.pl /usr/ports/packages chroot /usr/home/release cd /usr/ports/lang/perl5.8 && make package-recursive cd /usr/ports/x11/xorg && make package-recursive cd /usr/ports/x11/xorg-manpages && make package-recursive cd /usr/ports/packages ./mkindex.pl rm mkindex.pl cp –PRv /usr/ports/packages /R/cdrom/disc1/ exit
-j8 –максимальное количество make-процессов, которое может работать параллельно (сервер у меня двухпроцессорный, со SCSI RAID-5); RELEASENOUPDATE – это на будущее (чтобы не забыть), для цели «release» эта переменная не используется. NFS-каталог server:/usr/ports/distfiles можно примонтировать сейчас в /usr/ports/distfiles или позже.
Теперь можно создавать ISO-образы, исправляем файл start.sh, добавив переменную MAKE_ISOS=yes, и запускаем start.sh. Установится порт cdrtools и создадутся два образа диска. На этом всё.
Сборка релиза
Заключение
Теперь запускаем сборку релиза: sh start.sh. После получения исходных файлов системы и дерева портов из CVS можно прервать сборку и примонтировать distfiles:
В завершение хотелось бы упомянуть о возможностях создания нестандартного дистрибутива, имеющихся в сборочных скриптах релиза системы. В первую очередь это настройка sysinstall при помощи конфигурационного файла install.cfg. На эту тему в Интернете можно найти множество статей, ну и конечно же не надо забывать про мануал sysinstall(8). Также есть возможность применения сторонних патчей к исходным файлам системы во время создания релиза. Обратите внимание на переменные LOCAL_PATCHES, PATCH_FLAGS и LOCAL_SCRIPT в мануале release(7). Ну и если вам необходимо что-то действительно необычное, обратите внимание на сборочные скрипты таких проектов, как Frenzy, FreeSBIE, LiveBSD.
# mount_nfs –o rdonly server:/usr/ports/distfiles ↵ /usr/home/release/usr/ports/distfiles # vim start.sh
В файле start.sh исправляем цель release на rerelease и запускаем sh start.sh. Теперь можно заняться своими делами, но переодически посматривать за процессом сборки. Если каких-то портов не будет хватать, я запускаю на сервере make fetch-recursive для нужного порта и после завершения снова запускаю start.sh. Когда процесс закончится, вы увидите надпись «make rerelease for i386 finished …».
№2, февраль 2005
19
администрирование
КОНСТРУКТИВНЫЙ DIALOG
СЕРГЕЙ СУПРУНОВ Думаю, вам приходилось сталкиваться с работой утилиты dialog, входящей в состав большинства UNIX-систем (включая FreeBSD и практически все дистрибутивы Linux). Для Linux она часто используется в конфигурационных сценариях, во FreeBSD ее можно встретить при установке некоторых приложений из коллекции портов (например, oops, php). Она позволяет создавать простейшие диалоговые окна для взаимодействия с пользователем, используя псевдографику (как в sysinstall). Конечно, для «ортодоксального» системного администратора любое отступление от командной строки – кощунство, однако интерактивность в ряде случаев может быть весьма полезной.
20
Нужно заметить, что версии этой утилиты для Linux и для FreeBSD несколько отличаются. Данная статья будет посвящена версии, входящей в состав FreeBSD по умолчанию (конкретно – FreeBSD 5.3), а в завершение я скажу несколько слов об особенностях dialog в Linux. Если в командной строке набрать «dialog» без параметров, то будет выведена справка по использованию утилиты. Нужно заметить, что богатством функций она не блещет. Так, с ее помощью можно задать пользователю вопрос, требующий ответа «Да» или «Нет», отобразить содержимое файла или результат работы какой-нибудь команды, запросить ввод строки, предложить выбрать одну или
администрирование несколько альтернатив из списка, создать меню, отобразить список некоторых значений в виде дерева. Dialog не позволяет комбинировать несколько элементов в одном окне (например, группу полей ввода), то есть если вы решите использовать его для разработки сценария настройки сетевых интерфейсов, то такие параметры, как IP-адрес, маска, шлюз по умолчанию, придется запрашивать поочередно в отдельных окнах. Тем не менее на базе этой утилиты можно строить достаточно «продвинутые» сценарии. Синтаксис команды, с вашего позволения, дублировать не буду. Для разминки сразу напишем сценарий, позволяющий управлять сервером Apache – запускать его, останавливать, выполнять «мягкий» и «жесткий» перезапуск (graceful и restart соответственно). Обычно для этого используется утилита apachectl, однако если Apache работает достаточно стабильно, то ее ключи начинают забываться. Следующий пример демонстрирует решение поставленной задачи:
strerr (имеющий дескриптор 2) в файл с помощью такой конструкции: 2> apctl.tmp. В дальнейшем сохраняем содержимое этого файла в переменной $COMMAND и дополняем полным именем утилиты apachectl. Таким образом, в переменной $COMMAND оказывается команда, которую нужно исполнить. Теперь уточним у пользователя, действительно ли он хочет выполнить эту команду, для чего используем еще один диалог типа «Да/Нет». Результат взаимодействия с пользователем в данном случае можно получить из переменной $?, хранящей код возврата последней команды. Если была выбрана кнопка «Yes», код возврата будет 0 (успешное завершение).
Ïðèìåð 1: apctl.sh - Ñöåíàðèé óïðàâëåíèÿ ñåðâåðîì Apache #!/bin/sh # apctl.sh dialog --title "Apachectl interface" \ --menu " Äàííûé ñöåíàðèé óïðàâëÿåò ñåðâåðîì Apache. Âûáåðèòå äåéñòâèå èç ïðåäëîæåííûõ íèæå:" 17 50 8 \ start "Çàïóñê ñåðâåðà Apache" \ stop "Îñòàíîâ ñåðâåðà Apache" \ restart "'Æåñòêèé' ïåðåçàïóñê" \ graceful "'Ìÿãêèé' ïåðåçàïóñê" \ status "Ñòàòóñ ñåðâåðà" \ fullstatus "Ïîäðîáíûé ñòàòóñ" \ configtest "Òåñò êîíôèãóðàöèîííîãî ôàéëà" \ help "Âûâîä ñïðàâêè" 2> apctl.tmp COMMAND=`cat apctl.tmp` COMMAND="/usr/local/sbin/apachectl $COMMAND" dialog --title "Apachectl interface" \ --yesno " ÂÍÈÌÀÍÈÅ! Áóäåò âûïîëíåíà ñëåäóþùàÿ êîìàíäà: $COMMAND Ïðîäîëæèòü?" 10 50 if [ $? = 0 ]; then $COMMAND fi rm apctl.tmp
Рассмотрим подробнее, что происходит. Основная работа выполняется утилитой dialog, которой передаются параметры для построения в данном случае диалога «Меню», позволяющего выбрать один из вариантов среди предложенных. Синтаксис достаточно прост. Ключ --title задает заголовок окна, далее ключ --menu указывает тип диалога и требует следующих параметров: ! Сопроводительный текст. ! Высота окна (в символах). ! Ширина окна (в символах). ! Количество пунктов меню. ! Пары «Ключ» – «Пояснение» для каждого пункта меню. В результате мы получим окно, изображенное на рис. 1. Ключ выбранного пункта меню выводится в стандартный поток сообщений об ошибках (stderr), и, чтобы можно было его в дальнейшем использовать, мы перенаправляем
№2, февраль 2005
Ðèñóíîê 1
Ну и прежде чем завершить работу, сценарий apctl.sh должен удалить созданный им временный файл apctl.tmp. Учитывая, что этот файл создается в том же каталоге, из которого был вызван сценарий, вероятность, что несколько пользователей запустят его одновременно в одной и той же папке, достаточно низка. Тем не менее если она все же существует, то имеет смысл имя временного файла формировать динамически, как будет показано ниже. Перейдем к более серьезному примеру: напишем сценарий, позволяющий формировать файл .htaccess для управления поведением сервера Apache в конкретных каталогах. Как вы сейчас увидите, работа эта неблагодарная, и создать файл тонкой настройки вручную намного проще. Тем не менее если вы выделяете своим пользователям место под домашнюю страничку и предоставляете им терминальный доступ к домашнему каталогу, то подобным сценарием можно несколько упростить жизнь не только хозяевам странички, но и службе технической поддержки. К тому же в данном примере мы встретимся почти со всеми типами диалоговых окон, что очень хорошо для усвоения материала. Чтобы не завязнуть в возможностях .htaccess и не уйти от основной темы статьи, ограничимся двумя простейшими опциями: обработкой ошибки 404 «Файл не найден» (опция ErrorDocument 404) и включением SSI-обработки для файлов с определенными расширениями (опция AddHandler server-parsed). Опция ErrorDocument может принимать в качестве второго параметра (первый в нашем случае равен «404» – номер обрабатываемой ошибки) либо просто текстовую строку, которая будет использоваться в стандартной странице ошибки, либо URL файла, возвращаемого пользователю при запросе несуществующей страницы. Причем файл может располагаться как локально на этом же сервере, так и
21
администрирование на другой, удаленной машине. В первом случае указывается относительный или абсолютный (от корня веб-сервера) путь к файлу, во втором – полный URL ресурса, включая протокол: ErrorDocument 404 “Çàïðîøåííàÿ ñòðàíèöà íå ñóùåñòâóåò ErrorDocument 404 /_srv_/error404.html ErrorDocument 404 http://my.server.ru/errors/error404.htm
С опцией AddHandler все еще проще – она первым параметром принимает тип обработчика (в нашем случае это server-parsed, говорящий, что будет обрабатываться SSI), а далее – список расширений файлов, которые должны подпадать под действие этого обработчика: AddHandler server-parsed .shtml .shtm
Как обычно, сначала приведу весь сценарий полностью: Ïðèìåð 2: mkhtac.sh – Ñöåíàðèé äëÿ ñîçäàíèÿ .htaccess #!/bin/sh # mkhtac.sh
OFF \ REDIR "Ïåðåíàïðàâëåíèå íà óäàëåííûé ↵ ðåñóðñ" OFF 2> $TF ERR_MSG=`grep "MSG" $TF` ERR_FILE=`grep "FILE" $TF` ERR_REDIR=`grep "REDIR" $TF` QUOT="" if [ "$ERR_MSG" != "" ]; then QUOT="\"" TEXT=" Ââåäèòå òåêñò ñîîáùåíèÿ îá îøèáêå 404:" fi if [ "$ERR_FILE" != "" ]; then TEXT=" Ââåäèòå àáñîëþòíîå èìÿ ôàéëà (îò êîðíÿ âåá-ñåðâåðà) èëè èìÿ îòíîñèòåëüíî òåêóùåãî êàòàëîãà: Ïðèìåð: /errors/err404.html" fi if [ "$ERR_REDIR" != "" ]; then TEXT=" Ââåäèòå ïîëíûé URL ðåñóðñà (âêëþ÷àÿ íàèìåíîâàíèå ïðîòîêîëà): Ïðèìåð: http://my.server.ru/errors/err404.html" fi #==[Dialog 3b]== dialog --title "Make .htaccess: ErrorDocument" \ --inputbox "$TEXT" 10 70 2> $TF
# Ñîçäàíèå âñïîìîãàòåëüíûõ ôàéëîâ TF=`mktemp –t dlg` TF_TREE=`mktemp –t tree` # Çàïèñü â ôàéë äåðåâà êàòàëîãîâ find www -type d > $TF_TREE # Ðåãèñòðàöèÿ îáðàáîò÷èêà ñèãíàëîâ trap "rm -f $TF $TF_TREE" 0 2 9 15 #==[Dialog 0]== dialog --title "Make .htaccess: Confirmation" \ --yesno " Äàííûé ñöåíàðèé ïîìîæåò âàì ñîçäàòü ôàéë .htaccess.
fi
if [ "$SSI" != "" ]; then #==[Dialog 4a]== dialog --title "Make .htaccess: SSI parse" \ --checklist " Îòìåòüòå, êàêèå ôàéëû äîëæíû îáðàáàòûâàòüñÿ ïàðñåðîì SSI:" 15 40 4 \ ".shtml" "ôàéëû shtml" ON \ ".shtm" "ôàéëû shtm" ON \ ".html" "ôàéëû html" OFF \ ".htm" "ôàéëû htm" OFF 2> $TF
Ïðîäîëæèòü?" 10 40 if [ $? != 0 ]; then exit fi #==[Dialog 1]== dialog --title "Make .htaccess: Select dir" \ --ftree $TF_TREE "/" " Óêàæèòå êàòàëîã, â êîòîðîì äîëæåí áûòü ñîçäàí ôàéë .htaccess:" 19 40 10 2> $TF if [ $? != 0 ]; then exit fi HTDIR=`cat $TF` #==[Dialog 2]== dialog --title "Make .htaccess: Select options" \ --checklist " Óêàæèòå îïöèè, êîòîðûå ñëåäóåò äîáàâèòü â ôîðìèðóåìûé .htaccess:" 15 50 2 \ ErrorDocument "Îáðàáîòêà îøèáêè 404" OFF \ AddHandler "Âêëþ÷åíèå SSI" OFF 2> $TF if [ $? != 0 ]; then exit fi ERROR=`grep "ErrorDocument" $TF` SSI=`grep "AddHandler" $TF` if [ "$ERROR" != "" ]; then #==[Dialog 3a]== dialog --title "Make .htaccess: ErrorDocument" \ --radiolist " Âûáåðèòå îäèí èç ñïîñîáîâ îáðàáîòêè îøèáêè 404:" 15 60 3 \ MSG "Òåêñòîâîå ñîîáùåíèå îá îøèáêå" ON \ FILE "Ïåðåíàïðàâëåíèå íà ëîêàëüíûé ôàéë" ↵
22
ERRMSG=`cat $TF` ERR2HT="ErrorDocument 404 $QUOT$ERRMSG" echo $ERR2HT
fi
EXTLST=`cat $TF | tr "\"" "\0"` SSI2HT="AddHandler server-parsed $EXTLST" echo $SSI2HT
# Çàïèñü ðåçóëüòàòîâ â ôàéë .htaccess HTFILE=$HTDIR/.htaccess touch $HTFILE [ "$ERR2HT" != "" ] && echo $ERR2HT >> $HTFILE [ "$SSI2HT" != "" ] && echo $SSI2HT >> $HTFILE #==========================[Dialog 5]== dialog --title "Make .htaccess: Finish" \ --msgbox " Ðàáîòà çàâåðøåíà. Ñåé÷àñ ñîçäàííûé ôàéë áóäåò âûâåäåí íà ýêðàí, ÷òîáû âû ìîãëè îçíàêîìèòüñÿ ñ ðåçóëüòàòîì. Äàëüíåéøèå èçìåíåíèÿ â ñîçäàííûé ôàéë ìîæíî áóäåò âûïîëíèòü âðó÷íóþ." 15 50 #==[Dialog 6]== dialog --title "Make .htaccess: Result" \ --textbox $HTFILE 15 50
В первых строках сценарий подготавливает два временных файла. Они создаются утилитой mktemp с префиксами dlg и tree соответственно и по умолчанию будут размещены в каталоге /tmp. Первый из них ($TF) служит для перехвата вывода утилиты dialog, во второй ($TF_TREE) заносится результат работы команды find, которая возвращает имена всех каталогов, найденных в папке www. Благодаря тому, что mktemp создает файлы со случайными именами, можно не беспокоиться о том, что скрипт, запущенный одним пользовате-
администрирование лем, удалит временные файлы, с которыми в этот же момент времени работает другой экземпляр сценария. Командой trap регистрируем обработчик для сигналов 0 (нормальное завершение), 2 (SIGINT – прерывание, например, по Ctrl-C), 9 (SIGKILL – безусловное завершение) и 15 (SIGTERM – программное прерывание). При поступлении одного из этих сигналов будет выполнена команда rm, удаляющая созданные сценарием временные файлы. Фрагмент сценария, помеченный как [Dialog 0], выводит окно типа «Да/Нет» с вопросом о продолжении, и при ответе «Нет» работа сценария завершается. [Dialog 1] выводит дерево каталогов, корнем которого является в нашем случае папка www, и пользователь может выбрать в нем каталог, в котором следует создать файл .htaccess. Результат представлен на рис. 2, выбор пользователя заносится в переменную $HTDIR.
нейшем используется в следующем диалоге ([Dialog 3b]), поясняя пользователю, что же он должен ввести.
Ðèñóíîê 4
Значение, введенное в [Dialog 3b] (рис. 5), заносится в переменную $ERRMSG, и вместе с именем самой опции в переменной $ERR2HT формируется строка, которая в дальнейшем будет записана в файл.
Ðèñóíîê 2
[Dialog 2] (см. рис. 3) выводит список флажков (checklist), предлагая пользователю отметить те опции, которые он желает иметь в создаваемом файле .htaccess. Результат выбора раскладывается по переменным $ERROR и $SSI с помощью утилиты grep. Если та или иная опция не упоминается во временном файле, то соответствующая переменная останется пустой, что и используется при определении последующих действий.
Ðèñóíîê 5
Следующий диалог ([Dialog 4a]) предлагает пользователю отметить типы файлов, которые будут обрабатываться SSI-парсером (рис. 6). Поскольку отмеченные пункты при выводе в stderr (перенаправляемый в файл) заключаются в кавычки, то при их извлечении кавычки нужно удалить, для чего используется утилита tr.
Ðèñóíîê 3
Так, если была выбрана опция ErrorDocument, то на экране появится диалоговое окно, изображенное на рис. 4. За его формирование отвечает вызов dialog, помеченный, как [Dialog 3a]. Здесь реализуется список зависимых кнопок (radiolist), позволяющий выбрать один из предложенных вариантов. В зависимости от выбора пользователя в переменную $TEXT заносится сообщение, которое в даль-
№2, февраль 2005
Ðèñóíîê 6
После всех этих мучений наконец-то создается файл .htaccess, и в него записываются сформированные строки (если они не пустые). [Dialog 5] поздравляет пользователя с этим знаменательным моментом (рис. 7), а [Dialog 6] выводит на экран полученный результат, демонстрируя рабо-
23
администрирование ту диалога типа «textbox», который выводит содержимое указанного в качестве параметра файла.
Linux работать не будет, и придется запрашивать папку для создания .htaccess другим способом (например, с помощью поля ввода).
Ðèñóíîê 7
Вернемся к свойствам утилиты dialog. Цветовую гамму окон и некоторые другие параметры (например, отображение тени) можно изменить. Для этого используется конфигурационный файл, шаблон которого можно создать такой командой: $ dialog --create-rc <file>
В результате в файл <file> будет записана конфигурация по умолчанию. Полученный файл следует либо переименовать в .dialogrc и поместить в домашнюю папку пользователя (в результате он будет распространяться на все диалоги, запущенные этим пользователем), либо указать к нему путь в переменной окружения DIALOGRC. Редактирование конфигурационного файла сложностей вызвать не должно, поскольку каждая строчка в нем достаточно хорошо прокомментирована.
Ðèñóíîê 9
Ряд параметров (цвет элементов окна, наличие тени и т. д.) в Linux-версии можно изменить не только в конфигурационном файле, но и в командной строке с помощью соответствующих ключей. Кстати говоря, Linux-версию утилиты dialog можно установить и на FreeBSD. Она доступна в коллекции портов под именем cdialog (/usr/ports/devel/cdialog). Помимо консольной версии существует Xdialog, выполняющий аналогичные функции в графической среде. В том же Knoppix его работу можно наблюдать, например, при вызове сценария настройки сети (рис. 10). А на рис. 11 еще раз показан «Календарь», но уже в исполнении Xdialog.
Ðèñóíîê 10
Ðèñóíîê 8
Ну и как было обещано – несколько слов об особенностях dialog в Linux. Так как «стационарного» Linux у меня под рукой нет, я воспользовался LiveCD-дистрибутивом Knoppix. В отличие от FreeBSD-версии в Linux dialog имеет ряд дополнительных опций и возможностей. Так, с его помощью можно реализовать диалоги выбора даты (--calendar, рис. 8), выбора файла (--fselect, рис. 9), вывести индикатор выполнения процесса, именуемый в народе «термометром» (--gauge), запросить ввод пароля (элемент --passwordbox), можно добавлять в диалоговые окна дополнительные кнопки и т. д. Зато в нем нет «деревьев» (элементы --tree и --ftree в FreeBSD-версии), так что рассмотренный нами пример 2 на
24
Ðèñóíîê 11
В завершение статьи отмечу также, что библиотеку, лежащую в основе утилиты dialog, можно использовать и непосредственно в своих программах на C/C++. Существуют также интерфейсы к данной библиотеке для Perl, Python и Ruby (их можно установить из коллекции портов: devel/ p5-Dialog, devel/py24-dialog и devel/ruby-dialogs соответственно). Дополнительную информацию, как обычно, можно получить на страницах справочного руководства man dialog(1) и dialog(3).
bugtraq Множественные уязвимости в AWStats Программа: AWStats 6.3 final и более ранние версии. Опасность: Высокая. Описание: Уязвимость позволяет удаленному пользователю выполнить некоторые perl-директивы, вызвать отказ в обслуживании и получить доступ к важной информации пользователей. Уязвимость обнаружена в плагине rawlog при обработке переменных loadplugin и pluginmode. Удаленный пользователь может выполнить произвольный perl-сценарий с привилегиями веб-сервера или вызвать отказ в обслуживании. Пример: http://www.lan.server/cgi-bin/awstats-6.4/awstats.pl?↵ ↵ &PluginMode=:print+getpwent
Удаленный пользователь может выполнить произвольный плагин. Уязвимость существует из-за недостаточной проверки данных перед вызовом функции require(). Пример: http://server/cgi-bin/awstats-6.4/awstats.pl? ↵ &loadplugin=../../../../usr/libdata/perl/5.00503/blib
Удаленный пользователь может получить доступ к важной информации, вызвав один из отладочных сценариев. Пример: http://www.lan.server/cgi-bin/awstats-6.4/awstats.pl?debug=1 http://www.lan.server/cgi-bin/awstats-6.4/awstats.pl?debug=2
URL производителя: http://awstats.sourceforge.net. Решение: Установите обновление с сайта производителя.
Выполнение произвольного кода в Symantec Norton Anti-Virus Программа: Norton AntiVirus для Microsoft Exchange 2.1, версии до build 2.18.85; Symantec Norton Antivirus 2004 для Windows и Macintosh; Symantec Norton Antivirus 9.0 для Macintosh. Опасность: Высокая. Описание: Уязвимость обнаружена в DEC2EXE при обработке UPX-файлов. Удаленный пользователь может создать специальным образом UPX-файл, вызвать переполнение буфера и выполнить произвольный код на уязвимой системе. URL производителя: http://www.symantec.com. Решение: Установите обновление: http://www.symantec.com/ techsupp.
Уязвимость форматной строки в ngIRCd Программа: ngIRCd 0.8.2 и более ранние версии. Опасность: Высокая. Описание: Обнаружена уязвимость форматной строки, которая позволяет удаленному пользователю выполнить произвольный код на уязвимой системе. Уязвимость обнаружена в функции Log_Resolver() файла log.c. Если ngIRCd был скомпилирован с поддержкой Ident, syslog и DEBUG, удаленный пользователь может послать строку специально сформированных данных ident в ответ на ident-запрос и выполнить произвольный код на системе. URL производителя: http://arthur.ath.cx/~alex/ngircd. Решение: Способов устранения уязвимости не существует в настоящее время.
Переполнение буфера в fd_set-структуре во многих приложениях Программа: gnugk 2.2.0, jabber 1.4.1, bnc 2.8.4, socks5 1.0r1, citadel 6.27, dante 1.1, rinetd 0.62, bld 0.3, 3proxy 0.4. Опасность: Высокая. Описание: Структура fd_set на всех POSIX-совместимых платформах определяется как bitmask-массив с номером сокета в качестве индекса массива. Структура fd_set используется в функции select() и нескольких специальных макросах (FD_SET, FD_CLR, FD_ISSET, FD_CLEAR) для работы с дескрипторами ввода/вывода. Ни FD_SET ни функция select() не контролируют, чтобы номер сокета был больше значения FD_SETSIZE. Удаленный пользователь может перезаписать память за fd_set-структурой и выполнить произвольный код или вызвать отказ в обслуживании. Решение: Установите обновления от производителей. Источник: http://www.security.nnov.ru/advisiories/sockets.asp.
Php-инклудинг и XSS в SquirrelMail Программа: SquirrelMail версии до 1.4.4. Опасность: Высокая. Описание: Некоторые переменные в сценарии src/web mail.php позволяют удаленному пользователю с помощью специально сформированных параметров произвести phpинклудинг и выполнить произвольные команды на системе с привилегиями веб-сервера. Удаленный пользователь может с помощью специально сформированного URL произвести XSS-нападение и получить доступ к важным данным пользователей. Уязвимость существует в сценарии src/webmail.php. Отсутствие инициализации переменных в файле functions/ prefs.php позволяет удаленному пользователю инклудинг и выполнение произвольных php-сценариев при включенной опции register_globals в конфигурационном файле php.ini. URL производителя: http://www.squirrelmail.org. Решение: Установите обновление: http://www.squirrel mail.org/download.php
Удаленное выполнение произвольного кода в BrightStor ARCserve Backup Программа: BrightStor ARCserve Backup версии до 11.1. Опасность: Высокая. Описание: BrightStor ARCserve посылает пробные UDP-сообщения на широковещательный адрес для выявления других BrightStor-серверов в сети. Служба Discovery слушает на 41524 UDP-порту пробные запросы. Переполнение буфера существует в функции recvfrom() при обработке пробных UDP-пакетов. Функция получает 4096 байт, которые копируются в буфер меньшего размера. UDP-сообщение, размером более 1000 байт, вызовет переполнение буфера и даст возможность удаленному пользователю выполнить произвольный код на уязвимой системе с привилегиями Local SYSTEM. URL производителя: http://supportconnectw.ca.com/public/ enews/BrightStor/brigcurrent.asp. Решение: Установите обновления от производителя.
Составил Александр Антипов
№2, февраль 2005
25
администрирование
ПОЧТОВЫЙ СЕРВЕР НА БАЗЕ POSTFIX ЗАЩИТА ОТ ВИРУСОВ И НЕЖЕЛАТЕЛЬНОЙ ПОЧТЫ МИНИМАЛЬНЫМИ СРЕДСТВАМИ
ГЕННАДИЙ ДМИТРИЕВ В январском номере журнала за прошлый год вышла моя статья [1], посвященная настройке почтовых фильтров на базе MTA Sendmail. Тогда я не предполагал, что она может вызвать такой широкий интерес и привести к целому ряду публикаций на совершенно разные темы. Очень большое количество откликов со стороны читателей натолкнуло меня на мысль поделиться своими новыми изысканиями в области системного администрирования.
чтобы она выполняла функции фильтрации почты как на вирусы, так и на нежелательную корреспонденцию. В этой статье я не буду подробно описывать всю систему целиком, материалов на похожие темы предостаточно. Я лишь укажу на некоторые интересные моменты в данной связке и покажу, какими минимальными средствами можно получить вполне рабочую систему. Итак, начнем.
Немного теории
Со времен выхода первой статьи [1], где я рассказывал о связке Sendmail и Kaspersky Antivirus for FreeBSD, ничего не изменилось. Однако для привязки Kaspersky Antivirus в RHEL 3.0 нужно будет использовать отдельную утилиту, обеспечивающую шлюз для обмена данными между Postfix и антивирусной программой. Но все по порядку. Первое, с чего мы начнем – это создадим отдельную группу и пользователей, от имени которых будут работать наши демоны. Они пригодятся нам в дальнейшем, когда мы будем устанавливать и настраивать спам-фильтры.
Среди многочисленных систем передачи информации в глобальной сети Интернет немаловажную роль играет система передачи электронных сообщений от одного респондента к другому. Она включает в себя клиентов доставки, протоколы передачи данных и процедуры согласования между агентами. Один из основных агентов, отвечающий за доставку сообщения от одного клиента к другому, получил название MTA (Mail Transfer Agent). На сегодняшний день этих агентов насчитывается уже более десяти. В данной статье речь пойдет об MTA Postfix. Некоторое время назад я сменил работу, перейдя в одну из лучших фирм Петербурга на должность ведущего инженера. Системный администратор, занимавшийся управлением сетевой инфраструктурой фирмы, был очень занят серьезным проектом и меня попросили помочь в решении небольшой проблемы, связанной с постоянным падением почтового сервера. Таким образом я получил в свои руки связку из Red Hat Enterprise Linux (RHEL) 3.0 и как-то настроенного Postfix. При всем моем скептическом отношении к Linux меня в данной ситуации подогревал жуткий интерес, какими минимальными средствами можно заставить корректно работать данную систему. При этом требовалось,
26
Kaspersky
# groupadd filter –g 551 # mkdir /var/spool/filter /var/spool/filter/spamd # useradd -u 542 -g 551 -d /var/spool/filter/spamd ↵ -s /sbin/nologin avpclient # useradd -u 543 -g 551 -d /nonexistent ↵ -s /nonexistent avpdaemon # chown avpclient:filter /var/spool/filter/spamd # cd /var/spool/filter/avp # mkdir Bases ctl dev etc proc tmp tst usr var var/log # chown –R avpdaemon:filter Bases ctl tmp tst var # cd dev # mknod console c 0 0 # mknod null c 2 2
Последние две команды необходимы, так как антивирусный демон будет запускаться в chroot-окружении.
администрирование Покончив с деревом каталогов, берем дистрибутив Kaspersky Antivirus for Linux Server (kavwslinux-4.0.3.1.tgz), распаковываем: # tar xzvf kavwslinux-4.0.3.1.tgz # cd kavwslinux
Из всего списка получившихся файлов нам понадобится: AvpUnix.ini, kavdaemon, kavscanner, kavupdater, а также файл /etc/defUnix.prf. Перейдем к установке и настройке антивируса. Скопируем в каталог /var/spool/filter/avp перечисленные выше файлы. В результате должно получиться следующее (обратите внимание на имя владельца подкаталогов): # ls –all drwxr-xr-x drwxr-xr-x -rwxr-xr-x -rw-r--r-drwxr-xr-x drwxr-xr-x -rw-r--r-drwxr-xr-x drwxr-xr-x -rwxr-xr-x -rwxr-xr-x -rwxr-xr-x drwxr-xr-x drwxr-xr-x drwxr-xr-x drwxr-xr-x drwxr--r--
17 root wheel 4096 Фев 14 19:36 . 4 root wheel 4096 Янв 15 15:17 .. 1 root wheel 21713 Янв 14 19:44 avcheck 1 root wheel 170 Янв 17 11:54 AvpUnix.ini 2 avpdaemon filter 4096 Янв 27 13:31 Bases 2 avpdaemon filter 4096 Фев 14 18:15 ctl 1 root wheel 1865 Янв 15 14:44 defUnix.prf 2 root wheel 4096 Янв 15 14:31 dev 2 root wheel 4096 Янв 15 14:44 etc 1 avpdaemon filter 830742 Янв 24 2003 kavdaemon 1 root wheel 803771 Янв 24 2003 kavscanner 1 root wheel 647648 Янв 24 2003 kavupdater 2 root wheel 4096 Янв 15 14:30 proc 2 avpdaemon filter 4096 Фев 10 14:49 tmp 2 avpclient filter 4096 Фев 15 10:57 tst 2 root wheel 4096 Янв 15 14:30 usr 3 avpdaemon filter 4096 Янв 15 14:55 var
Перейдем к конфигурационным файлам. Ниже будут приведены лишь параметры, значения которых отличаются от принятых по умолчанию. AvpUnix.ini [AVP32] # Ìåíÿåì ïóòü ê îñíîâíîìó êîíôèãóðàöèîííîìó ôàéëó DefaultProfile=defUnix.prf [Configuration] KeysPath=. # Ïðîïèñûâàåì ïóòü ê âèðóñíûì áàçàì îò êîðíåâîãî êàòàëîãà BasePath=/Bases
Конфигурационный файл defUnix.prf описывает действия антивирусного сканера при обнаружении вирусов в теле сканируемого файла. defUnix.prf # same section with parameters for objects [Object] # Óêàçûâàåì êàòàëîã, â êîòîðîì áóäåì ïðîâîäèòü ñêàíèðîâàíèå # ôàéëà íà âèðóñû Names=*/tst # Îòêëþ÷àåì ñêàíèðîâàíèå ïàìÿòè è ñåêòîðîâ ëîãè÷åñêèõ òîìîâ Memory=No Sectors=No # Óêàçûâàåì ïàðàìåòðû ñêàíèðîâàíèÿ óïàêîâàííûõ ôàéëîâ, # àðõèâîâ, ñàìîðàçâîðà÷èâàþùèõñÿ àðõèâîâ è òàê äàëåå Packed=Yes Archives=Yes SelfExtArchives=Yes MailBases=Yes MailPlain=Yes Embedded=Yes # Óñòàíàâëèâàåì ðåàêöèþ íà îáíàðóæåíèå âèðóñà â òåëå ïèñüìà # «3» îçíà÷àåò óäàëåíèå òåëà âèðóñà áåç ïîïûòîê åãî ëå÷åíèÿ InfectedAction=3 # Òðåáóåì íå àðõèâèðîâàòü ïèñüìî â ñëó÷àå îáíàðóæåíèÿ âèðóñà
№2, февраль 2005
BackupInfected=No IfDisinfImpossible=1 [Report] # Òðåáóåì âåñòè ôàéë æóðíàëà Report=Yes UseSysLog=No # Ïðîïèñûâàåì ïóòü ê ôàéëó æóðíàëà ReportFileName=/var/log/kavscan.log # Îòêëþ÷àåì ðàñøèðåííûå ôîðìû çàïèñè â æóðíàëå ExtReport=No RepForEachDisk=No LongStrings=Yes # Îòêëþ÷àåì ïîëüçîâàòåëüñêèé ôàéë æóðíàëà UserReport=No # Óñòàíàâëèâàåì ðåàêöèþ íà îáíàðóæåíèå âèðóñà: íå äåëàòü # êîïèè ïèñåì [ActionWithInfected] InfectedCopy=No # Óñòàíàâëèâàåì ïîðÿäîê äåéñòâèé ïðè ïîäîçðåíèè íà âèðóñ: # íå äåëàòü êîïèè ïèñåì [ActionWithSuspicion] SuspiciousCopy=No # Óñòàíàâëèâàåì ïîðÿäîê äåéñòâèé â ñëó÷àå, åñëè íå óäàëîñü # ðàñïàêîâàòü ôàéë (ñ÷èòàåì åãî ïîâðåæäåííûì): íå äåëàòü # êîïèè ïèñåì [ActionWithCorrupted] CorruptedCopy=No [TempFiles] # Óñòàíàâëèâàåì ïðåäåëüíûé ðàçìåð ñêàíèðóåìûõ ôàéëîâ # è îïðåäåëÿåì êàòàëîã äëÿ âðåìåííûõ ôàéëîâ UseMemoryFiles=Yes LimitForMemFiles=6000 MemFilesMaxSize=20000 TempPath=/tmp [Customize] # Îòêëþ÷àåì ïðîâåðêó íåîáõîäèìîñòè îáíîâëåíèÿ âèðóñíûõ áàç. # Äàííàÿ îïöèÿ íåîáõîäèìà äëÿ ðàáîòû àíòèâèðóñíîé ïðîãðàììû # áåç âìåøàòåëüñòâà àäìèíèñòðàòîðà.  ïðîòèâíîì ñëó÷àå # îíà áóäåò ïðîñèòü îáíîâèòü áàçû êàæäûé ðàç ïðè çàãðóçêå UpdateCheck=No # Îòêëþ÷àåì âñå ëèøíèå ïðåäóïðåæäåíèÿ íà êîíñîëè ñåðâåðà OtherMessages=No RedundantMessage=No DeleteAllMessage=No
Основная идея состоит в том, чтобы максимально упростить ведение файлов журнала, и хранить в них только самое главное: проверен такой-то файл, инфицирован или нет. К тем параметрам, которые я менял в этом файле, написаны комментарии. Остальные параметры не менялись. Как можно заметить, все каталоги прописаны от корневого, в котором, собственно, и находится антивирусная программа. Все дело в том, что дальше мы будем запускать антивирус в chroot-окружении (зачем это нужно, будет объяснено чуть позже). Приведу сценарий для запуска процедуры обновления вирусных баз. Он достаточно маленький и состоит из двух строчек. Этот скрипт следует прописать в crontab для ежедневного выполнения, скажем, в час ночи. updater.sh #!/bin/sh /var/spool/filter/avp/kavupdater -y -kb ↵ -ui=http://downloads2.kaspersky-labs.com/updates/ ↵ -b=/var/spool/filter/avp/Bases /etc/init.d/kavd restart
Сценарий для запуска/остановки демона антивирусной программы (/etc/init.d/kavd) также необходимо несколько модифицировать. Здесь приведены лишь его части, подвергшиеся изменению. Остальное можно найти в дистрибутиве AVP.
27
администрирование /etc/init.d/kavd # Óêàçûâàåì êàòàëîã, â êîòîðûé óñòàíîâëåí àíòèâèðóñíûé ôèëüòð INSTPATH=/var/spool/filter/avp # Óêàçûâàåì êàòàëîã, â êîòîðîì áóäåì òåñòèðîâàòü ôàéëû AVPDIR="/tst" # Óêàçûâàåì êàòàëîã äëÿ õðàíåíèÿ ñîêåòà AVPPIPE="/ctl" # Óêàçûâàåì ïàðàìåòðû çàïóñêà äåìîíà DPARMS="-Y -MP -f=$AVPPIPE -dl -MD -I0 -o{$AVPDIR} $AVPDIR" # # # # # #
Ñòðîêè, ïðîâåðÿþùèå íàëè÷èå êîíôèãóðàöèîííûõ è êëþ÷åâûõ ôàéëîâ, ìîæíî èñêëþ÷èòü.  ñëó÷àå êîððåêòíîé íàñòðîéêè ñèñòåìû îíè íå íóæíû. Äàëåå â ñåêöèè çàïóñêà äåìîíà óáèðàåì ïðîâåðêó äåìîíñòðàöèîííîé âåðñèè, íàëè÷èÿ âèðóñíûõ áàç è êîððåêòèðóåì ñòðîêó çàïóñêà ñàìîãî äåìîíà. Ïî÷åìó îíà âûãëÿäèò èìåííî òàê, áóäåò îáúÿñíåíî ÷óòü ïîçæå
start () { if [ -r "$PIDFILE" ]; then echo "$NAME is running" exit 1 fi cd $INSTPATH echo -n "Starting $DESC: " daemon "/bin/env - HOME=/ /bin/nice ↵ /var/spool/filter/avp/uchroot ↵ -u avpdaemon $INSTPATH /kavdaemon $DPARMS" RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$NAME cd $WD }
Обратите внимание на строку запуска антивирусного демона: daemon "/bin/env - HOME=/ /bin/nice ↵ /var/spool/filter/avp/uchroot -u avpdaemon ↵ $INSTPATH /kavdaemon $DPARMS"
Как легко видеть, мы пытаемся запустить демона в chroot-окружение с использованием неких утилит. Дело в том, что входящий в комплект AVP шлюз kavkeeper, обеспечивающий связь между почтовым сервером и антивирусным демоном, корректно работает лишь в случае, когда в качестве фильтров используется только антивирус. При попытке привязать к системе дополнительные фильтры вроде Spamassassin образовывается некая внутренняя петля, и почтовое сообщение постоянно крутится по кругу внутри почтового сервера. Переписка с технической поддержкой лаборатории Касперского по этому поводу, к сожалению, ничего не дала. Поэтому вместо kavkeeper мы будем использовать avcheck (http://www.corpit.ru/avcheck), написанный Михаилом Токаревым. # # # # # # # #
cd /home/gennadiy tar xzvf avcheck-0.8.tar.gz cd avcheck-0.8 make cp avcheck /var/spool/filter/avp/ cp uchroot /var/spool/filter/avp/ mkdir /var/spool/filter/avp/infected cp infected.ex2.ru /var/spool/filter/avp/infected
Вот вроде бы и все. Единственный момент связан с тем, что я откорректировал конфигурационный файл infected. ex2.ru таким образом, чтобы в случае обнаружения вируса администратору высылалось лишь предупреждение без оригинального вложения. infected.ex2.ru # Îñòàâëÿåì äàííóþ ñòðî÷êó ïóñòîé, ÷òîáû íå ïîñûëàòü ëèøíèõ
28
# ñîîáùåíèé îòïðàâèòåëþ è ïîëó÷àòåëþ âèðóñíîãî ñîîáùåíèÿ VIRUS_ALERT= # Îòêëþ÷àåì èíôîðìàòèâíûå ñîîáùåíèÿ îòïðàâèòåëþ è ïîëó÷àòåëþ INFORM_SENDER=n # no send alert INFORM_RCPT=n # send alert to recipients # Óäàëÿåì ôóíêöèè attach_message() è attach_message_headers() # # # # # #
 ñåêöèè ãåíåðàöèè ïðåäóïðåæäåíèÿ àäìèíèñòðàòîðó óáèðàåì âûçîâ ôóíêöèè attach_message(). Òàêæå â ñëó÷àå, åñëè ìû íå èñïîëüçóåì èíôîðìàòèâíûå ñîîáùåíèÿ îòïðàâèòåëþ è ïîëó÷àòåëþ âèðóñíîãî ïèñüìà, ìîæíî óäàëèòü ñåêöèè «send alert to sender» «alert to recipients»
С настройкой антивирусного демона закончили. Перейдем к установке SpamAssassin.
SpamAssassin С фильтром на нежелательную почту все немного проще. Находим дистрибутив, скачиваем, распаковываем, устанавливаем: # # # #
tar xzvf Mail-SpamAssassin-3.0.2.tar.gz cd Mail-SpamAssassin-3.0.2 make make install
Инсталляцию можно произвести и из RPM-пакета, если таковой имеется у вас под рукой. Обратите внимание, в 3-й версии SpamAssassin некоторые параметры конфигурационных файлов сильно отличаются. Ниже будут приведены тексты именно для 3-й версии и выше. Файл конфигурации SpamAssassin, приведенный целиком и созданный мною практически с нуля, а также стартовый сценарий (/etc/init.d/ spamd), который пришлось слегка модифицировать для запуска демона от имени другого пользователя. Все необходимые каталоги были созданы нами ранее. Конфигурационный файл SpamAssassin: local.cf # don't use agent use_razor2 0 use_dcc 0 use_pyzor 0 # check rbl skip_rbl_checks 0 # autowhitelist use_auto_whitelist auto_whitelist_path
1 /var/spool/filter/spamd/auto_whitelist
# bayes use_bayes 1 bayes_auto_learn 0 bayes_path /var/spool/filter/spamd/bayes bayes_expiry_max_db_size 1500000 bayes_auto_learn_threshold_nonspam 0.1 bayes_auto_learn_threshold_spam 10.0 bayes_min_ham_num 100 bayes_min_spam_num 200 auto_learn ok_languages ok_locales
0 en ru en ru
# Spam header rewriting – clear_headers rewrite_header subject ****SPAM (_SCORE_)**** required_hits 3.5 # user rules allow_user_rules
0
# report options always_add_report
1
администрирование report_safe report_charset
0 koi8-r
# dns testing dns_available
no
Последние штрихи
# score options score FROM_ILLEGAL_CHARS score HEAD_ILLEGAL_CHARS score SUBJ_ILLEGAL_CHARS score SUBJ_HAS_SPACES score NO_REAL_NAME score PENIS_ENLARGE score PENIS_ENLARGE2 score FROM_HAS_MIXED_NUMS score FORGED_IMS_TAGS score FORGED_MUA_OUTLOOK score FORGED_OUTLOOK_TAGS
1.5 1.5 1.5 2.5 1.0 3.5 3.5 1.0 0.5 0.5 0.5
score BAYES_80 score BAYES_90 score BAYES_99
3.5 4.0 10.0
# network whitelist whitelist_from localhost
В скрипте запуска демона SpamAssassin меняем одну строку, относящуюся к стартовым параметрам демона. /etc/init.d/spamd # Set default spamd configuration. SPAMDOPTIONS="-d -m 5 -u avpclient -x ↵ -r /var/run/spamd/spamd.pid"
Особых изменений по сравнению с настройками для FreeBSD здесь не наблюдается (см. [1]), поэтому подробно останавливаться на данном вопросе мы не будем. Нам осталось лишь настроить фильтры на нашем почтовом сервере.
Postfix Каких-то особых хитростей в конфигурации данного почтового сервера я описывать не буду. Статей на эти темы предостаточно и в журнале, и в глобальной Сети. Нам интересны лишь моменты, связанные с использованием настроенных нами почтовых фильтров. Для этого в файле main.cf необходимо включить фильтр avcheck: main.cf content_filter=avcheck
И добавить фильтры в файл master.cf: master.cf # check mail with spamd and kaspersky avcheck unix n n 5 pipe flags=q user=avpclient argv=/usr/bin/spamc ↵ -u avpclient -e /var/spool/filter/avp/avcheck -i /var/spool/filter/avp/infected/infected.ex2.ru -h Ok -d /var/spool/filter/avp/./tst -s AVP:/var/spool/ ↵ filter/avp/ctl/AvpCtl -f ${sender} -S :1025 -- ${recipient} localhost:1025 inet n - n - - smtpd -o content_filter=
Теперь следует перезапустить Postfix и проверить работоспособность почтового сервера. Если он не функционирует, проверьте права доступа к каталогам, скрипты запуска, конфигурационные файлы. Приведенные мною примеры взяты с реального сервера.
№2, февраль 2005
Кажется, все хорошо. Почта работает, вирусы отлавливает, спам идентифицируется. Но, как всегда, хочется большего. В связке Sendmail + SpamAssassin в milter (это программа, предоставляющая транспорт между MTA и почтовым фильтром) можно было указать, на какой адрес пересылать всю нежелательную корреспонденцию. В Postfix мы воспользуемся стандартными средствами фильтрации по заголовкам письма. Однако прежде чем это делать, нам необходимо проверить, чтобы наш MTA был не ниже версии 2.1. Ранние версии не поддерживают инструкцию REDIRECT. Находим дистрибутив для RHEL 3.0, устанавливаем: # rpm –Uvh postfix-2.1.5-4.rhel3.i386.rpm
В файле main.cf добавляем строки, осуществляющие поиск в заголовках письма по регулярному выражению, сохраненному в /etc/postfix/header.regexp: main.cf header_checks=regexp:/etc/postfix/header.regexp
Затем создаем файл /etc/postfix/header.regexp следующего содержания: header.regexp /^X-Spam-Flag: YES/ REDIRECT spam@company.ru
т.е. требуем, чтобы все письма, в заголовке которых стоит флаг «X-Spam-Flag», перенаправлялись по адресу spam@ company.ru. Соответственно стоит позаботиться, чтобы у нас в системе присутствовал пользователь с именем «spam» или на существующего пользователя была сделана ссылка (alias). Таким образом, весь почтовый трафик, идентифицированный как потенциальный спам, будет перенаправлен на определенный адрес. Вот вроде бы и все.
Итоги В этой статье я попытался поделиться секретами настройки почтовых фильтров на базе MTA Postfix. Статей на подобные темы достаточно, однако некоторые из них слишком громоздки и используют очень большое количество связок. Другие же слишком неполны. Я попытался обойтись минимальными средствами, чтобы получить максимальный уровень защиты. Ведь чем проще система, тем легче за ней следить. Тексты конфигурационных файлов будут опубликованы на сайте журнала и на техническом форуме «Тринити». Хочется выразить огромную благодарность Сергею Тараненко, системному администратору фирмы «Тринити», за интеллектуальную поддержку.
Ссылки и литература: 1. Дмитриев Г. Почтовый сервер с защитой от спама и вирусов на основе FreeBSD. – журнал «Системный администратор», №1, январь 2005 г. – 68-73 с. 2. Большое количество полезной информации вы найдете на форуме http://www.3nity.ru.
29
администрирование
РЕЖЕМ СПАМ ДОПОЛНИТЕЛЬНЫЕ МЕТОДЫ Идет бабулька по подворотням, видит два парня третьего пинают, она cпрашивает: – За что это вы его так, сынки? – Бабка, да это спамер! – А ну тогда по почкам его! По почкам!
ДЕНИС НАЗАРОВ Знакомый анекдот? А в реальной жизни вы пробовали подсчитать количество «спама», проходящего через ваш почтовый сервер? Недавний аудит одной финансовой компании показал, что через почтовый сервер прошло больше 22 Гб почтового трафика в месяц. Я информировал об этом начальство и со спокойной душой решил, что аудит окончен, но я ошибся, руководство компании уверяло меня в том, что это просто невозможно, т.к. раньше у них был гораздо меньший объем почтового трафика, а новых сотруд-
30
ников они не набирали. Пришлось разбираться дальше. Проанализировав протоколы за месяц, я пришел к выводу, что 20 из этих 22 Гб были просто «спамом». Решено было избавлять компанию от этой назойливой почты...
Итак, мы имеем в наличии
! OpenBSD 3.6. ! Postfix 2.1.5 (считаю этот МТА одним из лучших благодаря гибкости настройки и возможностям).
администрирование ! DrWeb 4.32 (отличный антивирус, никогда не подводил, идеально работает с OpenBSD). ! Пользователи: примерно 500 человек. Задача: Обеспечить максимальную защиту от спама и вирусов, которые вам стремятся подсунуть по электронной почте.
Система Настройка системы начинается с файла /etc/rc.conf. С версии 3.3 OpenBSD имеет встроенный механизм защиты от спама, называемый spamd. Именно о настройке данного компонента системы мы и поговорим. Что есть spamd? Это демон, который пропускает через себя весь почтовый поток и принимает решения – отдать ли письмо на обработку «реальному» МТА или же отбросить, как «спам». Как работает spamd? В его распоряжении имеются 2 или 3 списка (в зависимости от того, что вы выберете в конфигурационном файле) – «черный список», «белый список», «серый список». Последний («серый список») может быть отключен, если вам нужен более жесткий контроль над спамерами. Демон spamd при запуске начинает слушать порт 8025 на интерфейсе 127.0.0.1 (переопределить порт можно, отредактировав файл /etc/services). Затем при помощи PF (OpenBSD Packet Filter) мы перенаправляем весь трафик с 25-го порта на порт 8025. И видим следующую картину: Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 bastion ESMTP spamd IP-based SPAM blocker;Tue Feb 15 12:57:50 2005
С виду похоже на приветствие обычного МТА, если не считать строчку: IP-based SPAM blocker. Вот теперь начинается самое интересное. Возвращяемся к спискам. ! «Черный список» – думаю все понятно. IP-адреса, перечисленные тут, будут вежливо отвергнуты. ! «Белый список» – IP-адреса из этого списка будут иметь доступ к «реальному» МТА напрямую, без прохождения спам-фильтра. Подразумевается, что в этот список вы вносите те IP-адреса, в которых уверены на 99,97%. ! Grey List – динамический список. Приходящее письмо с IP-адресом, не входящее ни в «черный список», ни в «белый список», будет помечено как «grey», и отвергнуто с ошибкой: «450. Temporary failure. Try again later.» Получая данный ответ, удаленный хост, отправивший вам сообщение, должен будет снова попытаться переслать сообщение через некоторое время. Обычно интервал ожидания может быть от 10 до 30 минут. Spamd следит за этим и, получая письмо второй раз, передает его почтовой системе, занося отправителя в «белый список». Зачем все это нужно? Получив в ответ ошибку, спамер будет настойчиво пытаться впихнуть нам свое сообщение. Возможно, даже выполняя множество попыток в течение одной минуты. Spamd считает такие попытки. При превышении определенного количества хост тут же попадает в «черный список». Чтобы окончательно понять, как это работает, приступим к конфигурированию.
№2, февраль 2005
/etc/rc.conf spamd_flags="" spamd_grey=YES
# for normal use: "" and see spamd-setup(8) # use spamd greylisting if YES
Влючаем spamd и указываем на то, что мы хотим использовать Grey Listing. Crontab 0 *
*
*
*
/usr/libexec/spamd-setup
В crontab для root прописываем запуск утилиты spamdsetup таким образом, чтобы она выполнялась в начале каждого часа. Данная утилита скачивает из Интернета списки с IP-адресами спамерских релеев и автоматически заносит их в ваш «черный список». /etc/spamd.conf all:\ :spamhaus:spews1:spews2:china:korea:whitelist:blacklist: # Mirrored from # http://spfilter.openrbl.org/data/sbl/SBL.cidr.bz2 spamhaus:\ :black:\ :msg="SPAM. Your address %A is in the Spamhaus Block ↵ List\n\ See http://www.spamhaus.org/sbl and\ http://www.abuse.net/sbl.phtml?IP=%A for more details":\ :method=http:\ :file=www.openbsd.org/spamd/SBL.cidr.gz # Mirrored from http://www.spews.org/spews_list_level1.txt spews1:\ :black:\ :msg="SPAM. Your address %A is in the spews level 1 ↵ database\n\ See http://www.spews.org/ask.cgi?x=%A for more details":\ :method=http:\ :file=www.openbsd.org/spamd/spews_list_level1.txt.gz # Mirrored from http://www.spews.org/spews_list_level2.txt spews2:\ :black:\ :msg="SPAM. Your address %A is in the spews level 2 ↵ database\n\ See http://www.spews.org/ask.cgi?x=%A for more details":\ :method=http:\ :file=www.openbsd.org/spamd/spews_list_level2.txt.gz # Mirrored from http://www.okean.com/chinacidr.txt china:\ :black:\ :msg="SPAM. Your address %A appears to be from China\n\ See http://www.okean.com/asianspamblocks.html ↵ for more details":\ :method=http:\ :file=www.openbsd.org/spamd/chinacidr.txt.gz # Mirrored from http://www.okean.com/koreacidr.txt korea:\ :black:\ :msg="SPAM. Your address %A appears to be from Korea\n\ See http://www.okean.com/asianspamblocks.html ↵ for more details":\ :method=http:\ :file=www.openbsd.org/spamd/koreacidr.txt.gz # Whitelists are done like this, and must be added # to «all» after each blacklist from which you want # the addresses in the whitelist removed whitelist:\ :white:\ :file=/var/mail/whitelist.txt blacklist:\ :black:\ :msg="SPAM! Go fsck anyone else!":\ :file=/var/mail/blacklist.txt relaydb-black:\ :black:\ :msg="SPAM. Your address %A is in my relaydb list.":\
31
администрирование :method=exec:\ :file=relaydb -4lb: relaydb-white:\ :white:\ :method=exec:\ :file=relaydb -4lw:
Данный файл отвечает за обработку этих самых «черных», «белых» и «серых» списков. Я перечислил основные самые крупные «черные списки» для спамеров, вы можете их смело использовать. Переходим к настройке ваших «черных» и «белых» списков. blacklist:\ :black:\ :msg="SPAM! Go fsck anyone else!":\ :file=/var/mail/blacklist.txt
Директива msg отвечает за то сообщение, которое мы будем отправлять во время сессии, в том случае если хост находится в «черном списке». Директива file определяет путь для файла со списком IP-адресов, которые автоматом при загрузке попадают в «черный список». # cat /var/mail/blacklist.txt 81.213.107.93 212.112.102.194 81.195.250.132 212.19.145.86
Конфигурационный файл похож и для «белого списка», за исключением директивы msg – она отсутствует. # cat /var/mail/whitelist.txt
192.168.0.0/24
ные файлы в /var/db, а также «привязывает» себя к пакетному фильтру. # ps ax | grep -i spa 3123 ?? Is 3:52.08 spamd: (pf <spamd-white> update) (spamd) 17434 ?? I 17:44.88 /usr/libexec/spamd -g 11229 ?? I 0:01.86 spamd: (/var/db/spamd update) (spamd) 26245 ?? Is 0:00.00 /usr/libexec/spamlogd 23886 p0 I+ 0:00.00 grep -i spa
Отлично. Спам-фильтр, основанный на проверке IP-адресов, это, конечно, хорошо и будет работать в связке с любым МТА, но мы сделаем еще лучше. Идем дальше.
Postfix Я не буду рассказывать, почему Postfix такой замечательный и гибкий, и быстрый, и мощный – просто сразу начнем его настраивать. /etc/postfix/main.cf # # Our stuff for coniguring Postfix goes here #
Запрещаем использование команды VRFY для проверки наличия пользователя в системе. disable_vrfy_command = yes
Обязываем удаленный хост здороваться с нами, а не сразу начинать нас забрасывать почтой. Софт, разработанный для рассылки спама, зачастую не умеет работать с smtp-командой helo. smtpd_helo_required = yes
Важно. all:\ :spamhaus:spews1:spews2:china:korea:whitelist:blacklist:
Решение принимается на основе последнего совпадения IP-адреса и записи в списках. Все. Основная конфигурация для spamd закончена. Так как мы перенаправляем письма в spamd при помощи PF (Packet Filter), то конфигурируем сам фильтр: /etc/pf.conf table <spamd> persist table <spamd-white> persist rdr pass inet proto tcp from <spamd> to any port ↵ smtp -> 127.0.0.1 port 8025 rdr pass inet proto tcp from !<spamd-white> to any port ↵ smtp -> 127.0.0.1 port 8025
Тем, кто читал в журнале мои предыдущие статьи о пакетном фильтре операционной системы OpenBSD, все будет понятно. Для остальных поясню – фильтр перенаправляет весь трафик с 25-го порта на порт 8025. Но если трафик на 25 порт идет с IP-адреса, который находится в «белом списке», то PF пропускает трафик сразу «реальному» МТА. Готово. Теперь осталось запустить spamd. Можно запустить spamd вручную, но лучше будет – перезагрузить сервер, т.к. при запуске spamd создает нуж-
32
Проверяем наличие «верного» имени хоста для отправителей. Иначе отказываем отправителю. smtpd_helo_restictions = permit_mynetworks, reject_invalid_hostname, reject_unknown_hostname, reject_non_fqdn_hostname
Проверяем наличе «верного» домена у отправителя, иначе отказываем. smtpd_sender_restrictions = reject_unknown_sender_domain, check_sender_access hash:/etc/postfix/reject
Если же намеренно хотим отказать отправителю – заносим его домен в файл /etc/postfix/reject и выполняем команду: # postmap /etc/postfix/reject
Утилита postmap создаст хеш из файла /etc/postfix/reject, который будет использоваться самим Postfix для отказа в приеме почты. Проверяем наличие «верного» имени домена для получателей, отказываем всем, чьё имя домена не соответствует требованиям FQDN, или не существует вообще.
администрирование smtpd_recipient_restrictions = reject_invalid_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, reject_unauth_pipelining, permit_mynetworks, reject_unauth_destination, reject_rbl_client relays.visi.com, reject_rbl_client relays.ordb.org, reject_rbl_client list.dsbl.org, reject_rbl_client dnsbl.sorbs.net, reject_rbl_client dnsbl.void.ru, reject_rbl_client blackholes.mail-abuse.org, reject_rbl_client relays.mail-abuse.org, reject_rbl_client dul.mail-abuse.org, reject_rbl_client relays.ordb.org, reject_rbl_client blackholes.wirehub.net, reject_rbl_client dynablock.wirehub.net, reject_rbl_client dnsbl.njabl.org, reject_rbl_client list.dsbl.org, reject_rbl_client opm.blitzed.org, reject_rbl_client http.dnsbl.sorbs.net, reject_rbl_client socks.dnsbl.sorbs.net, reject_rbl_client misc.dnsbl.sorbs.net, reject_rbl_client smtp.dnsbl.sorbs.net, reject_rbl_client web.dnsbl.sorbs.net, reject_rbl_client bl.spamcop.net, reject_rbl_client dev.null.dk, reject_rbl_client blackholes.mail-abuse.com, reject_rbl_client relays.mail-abuse.com, reject_rbl_client dialups.mail-abuse.com, reject_rbl_client relays.ordb.org, reject_rbl_client list.dsbl.org, reject_rbl_client multihop.dsbl.org, reject_rbl_client argentina.blackholes.us, reject_rbl_client brazil.blackholes.us, reject_rbl_client china.blackholes.us, reject_rbl_client cn-kr.blackholes.us, reject_rbl_client hongkong.blackholes.us, reject_rbl_client japan.blackholes.us, reject_rbl_client korea.blackholes.us, reject_rbl_client malaysia.blackholes.us, reject_rbl_client mexico.blackholes.us, reject_rbl_client nigeria.blackholes.us, reject_rbl_client singapore.blackholes.us, reject_rbl_client taiwan.blackholes.us, reject_rbl_client thailand.blackholes.us, reject_rbl_client turkey.blackholes.us
Строчка reject_rbl_client relays.visi.com … добавляет так называемую RBL-проверку. Принцип тот же, что и у spamd. Хост проверяется в «черном списке» у всех добавленных нами «reject_rbl_client», и, если будет обнаружен там, письмо отбрасывается, как спам. После данной настройки системы и Postfix в качестве MTA в протоколах мы можем наблюдать следующее:
Spam Received See: http://www.dnsbl.sorbs.net/lookup.shtml?195.2.80.59; from=<user@domain.name> to=<user@ourhost.com> proto=SMTP helo=<domain.name>
Система работает, как часы, все четко и точно. Если вам не нравится, как spamd распределяет IP-адреса отправителей по «черному» и «белому» списку, используем утилиту spamdb. # spamdb | more WHITE|12.111.30.74|||1105460517|1105466496|1108576943|2|0 WHITE|129.215.166.64|||1106681432|1106683445|1109793856|2|0 WHITE|13.16.138.21|||1105587869|1105591107|1108701528|3|0 WHITE|130.126.232.30|||1106168619|1106170545|1109281002|6|0 WHITE|134.130.3.130|||1107270887|1107274487|1110384920|2|0 WHITE|138.220.29.7|||1105954380|1105956190|1109066641|6|0
Данный листинг показывает ваши «белый список» и Grey Lists. Если вы намеренно хотите добавить в таблицу хост, помеченный как White, используем параметр -a, чтобы удалить -d: spamdb –a xxx.xxx.xxx.xxx spamdb –d xxx.xxx.xxx.xxx
Примечание. Добавить или удалить можно только IPадреса, помеченные как White. Для добавления IP-адреса в ваш «черный список» отредактируйте файл, указанный в директиве file файла /etc/spamd.conf. Основная настройка закончена. Остаются «рюшечки». Спам это, конечно, вредная вещь, но вирусы, трояны и прочее может принести вам еще больше проблем. Приступим. Берем последнюю версию DrWeb на сайте http://download. drweb.com/unix/OpenBSD+3.6 и оттуда же фильтр для Postfix. Установка DrWeb очень проста. Нужно распаковать архив и расположить файлы в системе так, как это сказано в readme. Также вам потребуется наличие демонстрационного ключа для запуска антивируса в демо-режиме. Если понравится работа DrWeb (а я надеюсь, так и будет), то вы можете без проблем приобрести нормальный ключ. Переходим к Postfix. Тут тоже все довольно просто. /etc/postfix/master.cf
# tail /var/log/daemon Feb 15 12:10:22 bastion spamd[17434]: 66.94.237.32: connected (1/0) Feb 15 12:10:23 bastion spamd[17434]: 66.94.237.32: disconnected after 1 seconds. Feb 15 12:13:29 bastion spamd[17434]: 222.67.37.40: connected (103/100), lists: china Feb 15 12:13:32 bastion spamd[17434]: 222.67.37.40: disconnected after 3 seconds. lists: china Feb 15 12:13:53 bastion spamd[17434]: 63.209.157.52: connected (1/1), lists: spamhaus Feb 15 12:21:11 bastion spamd[17434]: 63.209.157.52: disconnected after 438 seconds. lists: spamhaus
Из протоколов видно, какой IP-адрес и сколько раз пытался соединиться и отправить нам спам. # grep –i spam /var/log/maillog
Feb 15 01:19:34 bastion postfix/smtpd[8447]: NOQUEUE: reject: RCPT from domain.name[195.2.80.59]: 554 Service unavailable; Client host [195.2.80.59] blocked using dnsbl.sorbs.net;
№2, февраль 2005
Заменяем строчку: smtp
inet n
-
n
-
-
smtpd
-
↵
на smtp inet n n smtpd -o content_filter=filter:dummy
и добавляем в конец файла: filter unix n n pipe flags=R user=drweb argv=/usr/local/drweb/drweb-postfix ↵ -f ${sender} -- ${recipient}
Вот, в общем-то, и все. Отличная антивирусная защита к вашему Postfix готова. На сим заканчиваю, надеюсь, описанные мной способы помогут отбить все почки вашим спамерам.
33
администрирование
УНИВЕРСАЛЬНЫЙ ПРОКСИ-СЕРВЕР – Извини, Пух, – сказала САВА. – Тигра сжевал все провода от почтового сервера, и почта долго никак не приходила... – Провода, – подумал Пух злобно. – Носки вязать из таких проводов. Андрей Щербаков «9600 бод и все-все-все...»
ВАЛЕНТИН СИНИЦЫН В этой статье мы поговорим о протоколе SOCKS1. С его помощью можно решать самые разные задачи: организовывать защищенный доступ к службам, расположенным за межсетевым экраном (firewall), скрывать свой истинный IPадрес во время работы с недружелюбными сетевыми ресурсами или реализовать универсальный прокси-сервер, поддерживающий любые протоколы прикладного уровня (HTTP, FTP, POP3/SMTP, ICQ и т. д.). К сожалению, несмотря на всю простоту и богатство возможностей SOCKS, многие системные администраторы недостаточно хорошо знакомы с ним и не представляют, чем он может быть полезен. Хочется надеяться, что после прочтения данного материала незаслуженно забытый протокол займет достойное место в их арсенале. Сразу же оговоримся: все последующее изложение будет относиться к пятой версии SOCKS, SOCKS5. Предыдущая, четвертая версия (SOCKS4), все еще имеет хождение в Сети, однако ее возможности ограничены. 1
34
Socks (англ.) – носки, чулочки.
К слову сказать, название протокола не имеет ничего общего с упомянутыми в эпиграфе чулочными изделиями и является простым сокращением от «SOCK-et-S» – «гнезда», или, в более привычном уху компьютерного специалиста переводе – «сокеты». Термин был предложен создателями в качестве рабочего варианта, да так и прижился. Как известно, сокеты лежат в основе любого API, реализующего сетевое взаимодействие – UNIX, Winsock и т. п. Чтобы послать данные по сети, приложению достаточно просто записать их в сокет, подобно тому, как это делается при сохранении информации в локальном файле. И в том, и в другом случае программе не приходится заботиться о происходящем «за кулисами». Дополнение пользовательских данных служебной информацией, разбивка на сегменты с их последующей инкапсуляцией в датаграммы и физическая отправка осуществляются другими частями операционной системы – стеком TCP/IP и драйверами устройств, о которых приложению
администрирование ничего не известно. Такое «разделение труда» позволяет как угодно изменять процедуру доставки сообщений при условии, что интерфейс прикладного программирования остается постоянным. Именно эта особенность и лежит в основе идеологии SOCKS. Основная задача данного протокола – внедрить в «нормальный» процесс обмена данными некоего посредника, называемого SOCKS-сервером или SOCKSпрокси. Когда клиент (поддерживающее SOCKS приложение: веб-браузер Mozilla, клиент ICQ Miranda IM и др., см. ниже) желает отправить какую-либо информацию по сети, он устанавливает соединение не с реальным адресатом, а с SOCKS-сервером, который, в свою очередь, пересылает данные по назначению, но уже от своего имени. С точки зрения «настоящего» сервера (например, веб-узла, который пользователь желает просмотреть в Mozilla Firefox) SOCKS-прокси является самым обыкновенным клиентом. Таким образом, сущность (IP-адрес) истинного клиента оказывается скрытой от обслуживающего его сервера. Это весьма удобное обстоятельство таит в себе потенциальную опасность (можете спрятаться вы, но ведь могут и от вас), поэтому реально существующие SOCKS-сервера имеют развитые схемы контроля доступа (запрет входящих и исходящих соединений по заданному перечню адресов) и поддерживают авторизацию пользователей по паролю (см. ниже). Отметим, что коль скоро SOCKS работает на более низком по сравнению с прикладным (а именно транспортном) уровне модели OSI, его поддержка не потребует никаких изменений в логике работы клиента, а тем более сервера. Действительно, все что нужно, – это модифицировать реализацию функций, отвечающих за создание сетевого подключения и отправку данных: connect(), bind(), send() и т. п. На практике это обычно достигается перехватом системных вызовов с их последующей подменой поддерживающими SOCKS пользовательскими аналогами. Никаких правок в исходном коде клиентских приложений, а тем более самого доступа к исходным текстам, как правило, не требуется. Эта мощная процедура известна как «соксификация» и будет подробно рассмотрена ниже. Теперь, когда мы получили общее представление о SOCKS, можно перейти к более детальному рассмотрению данного протокола.
Спецификация SOCKS5 Протокол SOCKS5 подробно описан в RFC1928. В отличие от монстроподобных стандартов вроде «HTTP 1.1», спецификация SOCKS умещается на 9-ти страницах и может быть без труда разобрана любым желающим. Предлагаемые здесь сведения являются ее кратким конспектом и призваны помочь вам в этом несложном деле. Как уже отмечалось ранее, SOCKS5 является протоколом транспортного уровня. Его «соседи» – TCP и UDP непосредственно используются для передачи данных, поступающих с прикладного уровня (от пользовательских приложений), а значит, SOCKS-прокси должен уметь корректно работать с каждым из них. Отметим также, что протокол 2
3
ICMP, используемый утилитами ping и traceroute, находится ниже транспортного уровня, а потому соксификации, к сожалению, не поддается2. Прежде чем отправлять какие-либо данные, клиент должен пройти процедуру авторизации. Для этого он создает TCP-соединение с SOCKS-сервером (стандартный порт – 1080) и отправляет по нему специальное сообщение, содержащее кодовые номера поддерживаемых методов аутентификации (см. таблицу 1). SOCKS-сервер выбирает один из них по своему усмотрению и сообщает его номер клиенту. Как легко видеть, аутентификация может отсутствовать (на практике это скорее всего означает, что SOCKS-сервер различает клиентов по их IP-адресам) или производиться на основании имени пользователя и пароля. В последнем случае возможно большое количество различных вариантов, от тривиального «Username/Password Authentication» (RFC 1929), предусматривающего передачу пароля в открытом виде, до куда более безопасного CHAP (зашифрованный пароль, открытые данные) и GSSAPI (RFC 1961), которое может использоваться для полной криптографической защиты трафика. После успешной авторизации клиент получает возможность посылать запросы (команды), устанавливать исходящие соединения и даже принимать входящие.
Установка исходящего TCP-соединения Для установки исходящего TCP-соединения клиент отправляет SOCKS-серверу запрос «CONNECT», в котором указывает адрес и порт доставки. Для идентификации узла-получателя могут использоваться как IP-адреса (поддерживаются IPv4/IPv6), так и полноценные доменные имена. В последнем случае SOCKS-сервер берет на себя заботу по их разрешению, так что сеть, в которой работает клиент, в принципе может обходиться и без DNS-сервера. В ответном сообщении SOCKS-сервер сообщает код ошибки (как обычно, 0 обозначает, что операция прошла успешно), а также IP-адрес (BND.ADDR) и TCP-порт (BND.PORT), которые будут использоваться для фактической связи с запрошенным узлом. Поскольку SOCKS-сервера, как правило, имеют более одного сетевого интерфейса, данный IP-адрес может отличаться от того, с которым было установлено управляющее соединение. После этого клиент открывает новый TCP-сеанс с BND.ADDR:BND.PORT и осуществляет отправку данных. Исходящее TCP-соединение разрывается одновременно с закрытием управляющей сессии. Заметим, что запрос «CONNECT» может быть отклонен SOCKS-прокси, если адреса источника (клиента) или получателя (сервера) запрещены3 к обслуживанию системным администратором.
Установка исходящего UDP-соединения В отличие от потокового протокола TCP, подразумевающего установку сеанса, протокол UDP является датаграммным, а потому несколько более сложным в обращении. Его поддержка появилась лишь в SOCKS5. Перед отправкой UDP-датаграмм клиент запрашивает у
Существуют нестандартные расширения протокола SOCKS, позволяющие работать и с ICMP, однако в данной статье они рассматриваться не будут. Или явно не разрешены, в зависимости от конкретной реализации и выбранной политики.
№2, февраль 2005
35
администрирование SOCKS-сервера UDP-ассоциацию, используя для этого команду «UDP ASSOCIATE». UDP-ассоциация – это своего рода виртуальный сеанс между клиентом и SOCKS-сервером. В исходящем запросе клиент указывает предполагаемые адрес и порт, которые будут выступать в качестве источника будущих UDP-датаграмм. Если на момент установки UDPассоциации эта информация еще неизвестна, клиент должен использовать комбинацию 0.0.0.0:0 (или, скажем, x.x.x.x:0, если неизвестен только номер порта). В ответном сообщении SOCKS-сервер указывает IP-адрес (BND.ADDR) и UDP-порт (BND.PORT), на которые следует направлять исходящие датаграммы. При этом адрес и порт их реального получателя указываются прямо в теле (можно сказать, что имеет место UDP-инкапсуляция). Эти параметры, наряду с адресом и портом отправителя используются для принятия решения о допустимости отправки датаграммы. Как легко видеть, это создает дополнительную нагрузку на SOCKSсервер: правила фильтрации необходимо применять к каждой UDP-датаграмме, тогда как в случае TCP-соединения его легитимность оценивается один раз, в момент исполнения SOCKS-сервером команды «CONNECT». Согласно требованиям стандарта, SOCKS-сервер должен следить за тем, чтобы IP-адрес отправителя датаграммы совпадал с адресом узла, создавшего UDP-ассоциацию. UDP-ассоциация разрушается одновременно с закрытием управляющего TCP-сеанса, в рамках которого была послана команда «UDP ASSOCIATE». Многие из существующих SOCKS-серверов испытывают серьезные проблемы, если между ними и клиентом, запросившим UDP-ассоциацию, располагается межсетевой экран с функцией NAT (Network Address Translation). Причина этого кроется в изменении адреса и порта отправителя, которое происходит в тот момент, когда UDP-датаграмма пересекает межсетевой экран. Как следствие, сервер и ничего не подозревающее клиентское приложение начинают говорить на разных языках: предполагаемый адрес и порт источника, указанные в команде «UDP ASSOCIATE», перестают соответствовать реальным параметрам получаемых SOCKS-сервером датаграмм. В результате они оказываются отброшенными как не принадлежащие UDP-ассоциации. Проблему можно было бы решить, указав в качестве предполагаемого источника 0.0.0.0:0 (см. выше), что должно интерпретироваться SOCKS-сервером как «любая UDP-датаграмма, пришедшая с того же адреса, что и команда на создание ассоциации». К сожалению, большинство из реально существующих SOCKS-серверов трактуют стандарт более узко и не позволяют одновременно установить в ноль и предполагаемый адрес, и порт отправителя. Из протестированных автором реализаций описанный здесь «фокус с пробросом UDP через NAT» позволяет проделать лишь одна – Dante.
Прием входящих соединений Эта достаточно оригинальная возможность может оказаться полезной в случаях, когда клиент и «настоящий» сервер в описанной выше схеме меняются местами, что может произойти, например, в протоколах типа FTP. В целях дальнейшего рассмотрения будем предполагать, что между «клиентом» (стороной, собирающейся принять входящее со-
36
единение) и «сервером» (стороной, инициирующей входящее соединение) уже установлен «прямой» канал связи при помощи команды «CONNECT». Для открытия «обратного» канала «клиент» должен послать SOCKS-серверу команду «BIND», указав в ее параметрах IP-адрес и порт, которые будут использоваться им для приема входящего соединения. В ответ на это SOCKS-сервер сообщает IP-адрес и порт, выделенные им для поддержания «обратного» канала. Предполагается, что «клиент» передаст эти параметры «серверу», используя средства, предоставляемые протоколами прикладного уровня (например, команду «PORT» протокола FTP). После того, как SOCKS-сервер примет (или отбросит) входящее соединение, он повторно уведомляет об этом «клиента», сообщая ему IP-адрес и порт, используемые «сервером». Отметим, что прием входящих соединений может осуществлять лишь приложение, разработчики которого позаботились о поддержке SOCKS еще на этапе проектирования. В противном случае (если приложение работает с SOCKS-сервером через программу-соксификатор) оно не сможет предоставить корректную информацию об адресе ожидающего «обратной связи» сокета (т.е. сформирует неверную команду «PORT» в рассмотренном выше примере с FTP).
«Цепочки» SOCKS Давайте, работайте. Шесть арендованных «на раз» роутеров, через которые пробегает сигнал. И все достаточно стойкие к взлому. Сергей Лукьяненко «Лабиринт отражений»
Архитектура протокола SOCKS5 позволяет легко объединять SOCKS-сервера в каскады, или, как их еще называют, «цепочки» (chains). Примечательно, что все необходимые для этого действия могут быть произведены на стороне клиента. К «звеньям» цепочки предъявляется единственное требование: они должны «доверять» друг другу (т.е. допускать установку входящих и исходящих соединений). Если образующие каскад SOCKS-сервера не являются анонимными (т.е. используют схемы аутентификации Username/ Password, CHAP или подобные), необходимо также, чтобы пользователь мог успешно пройти процедуру авторизации на каждом из них. Предположим, что у нас имеется набор из N SOCKSсерверов с именами socks1, socks2, ..., socksN, удовлетворяющих всем вышеперечисленным требованиям. Тогда для создания каскада клиент может поступить следующим образом: ! В случае исходящего TCP-соединения: клиент подключается к socks1, проходит (при необходимости) процедуру авторизации и посылает команду «CONNECT», указав в качестве адреса доставки socks2. Выполняя этот запрос, socks1 создаст новое соединение с socks2 и будет исправно передавать всю идущую по нему информацию клиенту, при этом socks2 не будет даже догадываться, с кем он общается на самом деле. Далее процедура повторяется до тех пор, пока не будет установлено соединение между socks(N-1) и socksN. Последний сервер каскада подключается непосредственно к
администрирование узлу, который интересует клиента. Передача данных происходит в обычном режиме: клиент отправляет пакет на сервер socks1, который, в свою очередь, передает его socks2, и так до тех пор, пока не будет достигнут конечный узел. ! В случае исходящего UDP-соединения: клиент подключается к socks1, проходит процедуру авторизации и последовательно посылает две команды: «CONNECT» (адрес доставки – socks2) и «UDP ASSOCIATE». Таким образом, создаются два новых соединения: виртуальный UDP-канал между клиентом и socks1, а также TCP-сессия между socks1 и socks2. Используя эту TCP-сессию, клиент (от имени socks1) посылает команду «UDP ASSOCIATE» на сервер socks2 (открывает UDP-канал между socks1 и socks2) и «CONNECT» на сервер socks3. Процедура продолжается до тех пор, пока между всеми SOCKS-серверами каскада не будут установлены виртуальные UDP-каналы. Чтобы отослать какие-либо данные, клиент предварительно производит N-кратную инкапсуляцию UDP-датаграммы, указывая в качестве адреса доставки последовательно: socks1, socks2, socks3, socksN и адрес реального получателя, а затем отправляет ее на сервер socks1. Отметим, что на практике данный вариант каскадирования встречается крайне редко. Это связано с тем, что SOCKS-сервера, как и NAT Firewall, могут изменить порт источника датаграммы, что приведет к проблемам, подробно описанным в разделе «Установка исходящего UDP-соединения». Используя цепочки SOCKS-серверов, не требующих аутентификации, клиент может значительно повысить анонимность работы в Интернете (см. эпиграф). В Сети можно найти множество программ, реализующих описанные здесь схемы. Таковыми, например, являются SocksChain (http:// www.ufasoft.com/socks) для Windows или ProxyChains (http:// proxychains.sourceforge.net) для UNIX. Каскадирование SOCKS-серверов является также неотъемлемой частью некоторых соксификаторов, в первую очередь FreeCap (http:// www.freecap.ru).
SOCKS-сервера Теперь, когда принципы работы SOCKS-сервера нам хорошо знакомы, пора переходить от теории к практике. В мире существует большое количество программ, реализующих протокол SOCKS5. Они охватывают все популярные операционные системы (UNIX, Windows, ...) и способы распространения (freeware, shareware, open-source и т. д.). Здесь мы вкратце рассмотрим наиболее известные (или интересные с точки зрения автора) реализации. Начнем, пожалуй, с SOCKS5 Reference Implementation (http://www.socks.permeo.com), выполненной компанией NEC и принадлежащей в настоящий момент фирме Permeo. Текущая версия имеет номер 1.0r11 и датирована августом 2000 года. Как легко догадаться по названию, этот сервер является справочной реализацией протокола и, вообще говоря, не предназначен для промышленного использования. Тем не менее он является стандартом де-факто на платформе FreeBSD. Вероятно, большинству системных администраторов, волею судеб столкнувшихся с задачей уста-
№2, февраль 2005
новки SOCKS-сервера, гораздо проще набрать «cd /usr/ ports/net/socks5 && make && make install», чем разбираться во всем многообразии существующих на рынке решений. Продукт имеет поддержку GSSAPI и распространяется в исходных текстах, но по несвободной лицензии. Коммерческое применение данного сервера запрещено. Следом за ним идет Dante, разработанный норвежской компанией Inferno Nettverk и поддерживающий все основные UNIX-системы. Продукт развивается, хотя и не очень бурно (последняя версия, 1.1.15, датирована 31 января 2005 года) и вполне пригоден для практического применения. Как уже упоминалось ранее, Dante позволяет корректно работать с UDP-ассоциациями даже в том случае, если они проходят через NAT Firewall. Программа распространяется в исходных текстах по лицензии BSD. В состав Dante входит библиотека для прозрачной соксификации UNIX-приложений (см. ниже). Antinat (http://antinat.sourceforge.net) – динамично развивающийся, но еще не завершенный открытый продукт, работающий на платформах UNIX и Windows. Поддерживает интересные схемы аутентификации, например CHAP или HMAC-MD5. Умеет работать с цепочками SOCKS-серверов. В комплект поставки входит библиотека для разработки пользовательских приложений, поддерживающих SOCKS. Распространяется в исходных текстах. Лицензирован по GPL. Среди других разработок следует упомянуть отечественный 3proxy (http://security.nnov.ru/soft/3proxy), имеющий родную поддержку UNIX и Win32, Delegate (www.delegate.org) и коммерческий SOCKS-сервер Hopster (www.hopster.com) для Microsoft Windows. Приведенный здесь список далеко не полон. Думается, что читателю не составит труда найти и другие сервера. Для этого можно воспользоваться поисковыми панелями репозитариев Freshmeat (http://www.freshmeat.net) или SourceForge (http://www.sf.net), а также поисковыми машинами общего назначения. Учитывая многообразие существующих SOCKS-серверов, мы не будем подробно останавливаться на процедуре их настройки. Вся необходимая для этого информация может быть найдена в сопроводительной документации к конкретному программному продукту. Как правило, SOCKSсервера имеют один или несколько конфигурационных файлов, позволяющих указать предпочтительные схемы авторизации и, что более важно, ограничить доступ к сервису по списку IP-адресов. Не пренебрегайте этой возможностью! Неправильно настроенный SOCKS-прокси может быть использован злоумышленниками для рассылки спама через корпоративный почтовый сервер (с точки зрения которого SOCKS-прокси – это один из установленных в фирме компьютеров, поэтому отправка почты с его IP-адреса, скорее всего, не будет запрещена) и выполнения других антиобщественных действий.
SOCKS-клиенты Некоторые настольные приложения обладают встроенной поддержкой SOCKS. В их число входят веб-браузеры, построенные на коде Mozilla и ряд клиентов сетей мгновенного обмена сообщениями (Miranda IM, Mirabilis ICQ). Для включения поддержки SOCKS в этих программах достаточ-
37
администрирование но указать необходимые параметры в файле или диалоге настроек. Но что делать в случае, если приложение не умеет работать с SOCKS напрямую (классическим примером является Microsoft Internet Explorer)? Существует несколько вариантов решения данной проблемы. Если исходные тексты приложения доступны, можно собрать их заново, используя готовые клиентские библиотеки, например, входящие в состав Dante или Antinat. Однако, как уже упоминалось выше, наличие «исходников» не является обязательным требованием. Откомпилированное приложение можно заставить работать с SOCKS «обманным путем» при помощи программ-соксификаторов, подменяющих стандартные функции для работы с сокетами их аналогами, поддерживающими SOCKS. Так, модифицированная функция connect(), устанавливающая соединение с заданным узлом, на самом деле отсылает команду «CONNECT» на адрес указанного пользователем SOCKSсервера, а «соксифицированная» функция sendto() выполняет инкапсуляцию UDP-датаграммы и отправляет ее, используя заранее установленную UDP-ассоциацию. Процедура подмены функций существенно зависит от типа операционной системы. Так, в мире Windows для этих целей применяются «вирусные» методики. Например, соксификатор может запускать указанный пользователем процесс в режиме «Suspend», после чего внедрять в его память код, загружающий специальную DLL-библиотеку, перехватывающую обращения к API-вызовам LoadLibrary/GetProcAddress, ответственным за подключение сторонних DLL и поиск в них экспортируемых функций. После этого соксификатор отслеживает момент загрузки wsock32.dll и подменяет запрашиваемые адреса функций Winsock указателями на их SOCKS-аналоги. В мире UNIX, как это часто случается, все обстоит значительно проще. Динамический компоновщик ld.so использует специальную переменную окружения, LD_PRELOAD, а также файл /etc/ld.so.preload, чтобы определить список разделяемых библиотек, подлежащих предварительной загрузке до непосредственно запрашиваемых исполняемым файлом. Поскольку большая часть современных приложений использует динамическую компоновку, соксифицированные аналоги сетевых функций, оформленные в виде разделяемой библиотеки, перечисленной в LD_PRELOAD, будут найдены и использованы вместо стандартных вызовов, определенных в glibc. Этот метод, очевидно, не будет работать для приложений, использующих статическое связывание с glibc. Встречаться с таковыми (кроме низкоуровневых системных утилит) автору не приходилось. Кроме того, значение переменной LD_PRELOAD обрабатывается особым образом для исполняемых файлов, имеющих бит SUID. Среди клиентских (настольных) приложений они, как правило, не встречаются. Любая ли программа поддается соксификации описанными выше методами? Как легко видеть по ходу рассуждения, нет. Процедура внедрения кода Windows-соксификатора в чужой процесс может не сработать, если исполняемый файл имеет особую структуру, например, он сжат оригинальным образом или зашифрован. Кроме этого, пользовательские SOCKS-аналоги должны максимально точно повторять поведение (в том числе эмулировать недокумен-
38
тированные возможности) родных функций операционной системы, что, согласитесь, не всегда легкодостижимо. Чтобы не быть голословными, приведем примеры конкретных программ-соксификаторов. В среде Windows можно использовать SocksCap (http://www.socks.permeo.com) от все той же фирмы Permeo (и с все той же неудобной лицензией, ограничивающей коммерческое применение) или открытый (распространяющийся по GPL) продукт FreeCap (http://www.freecap.ru), написанный нашим соотечественником Максимом Артемьевым. В последнем случае в вашем распоряжении окажутся также исходные тексты на языке Object Pascal (Delphi), с помощью которых вы сможете глубоко разобраться во всех обсуждаемых в статье вопросах, от процедуры внедрения в чужой процесс до точного формата SOCKS-сообщений. В UNIX можно воспользоваться сценарием оболочки socksify, входящим в состав Dante. Наконец, рассмотрим особый случай – программы, обеспечивающие соксификацию всей системы целиком. В Windows этого можно достичь, подменив на диске файл wsock32.dll или более корректным образом, используя спецификацию Winsock Service Provider. В UNIX достаточно добавить команду, устанавливающую значение переменной LD_PRELOAD в один из стартовых сценариев (например, rc.local) или внести изменения в файл /etc/ld.so.preload. Примером такого «общесистемного соксификатора» могут служить WideCap Максима Артемьева (пребывающий пока в состоянии бета-версии) или Permeo Security Driver.
Вместо заключения Вот и подошло к концу наше повествование. Теперь, когда вы хорошо представляете себе, что такое SOCKS и с чем его едят, вам не составит труда придумать тысячу и одно применение данному протоколу. Например, на базе SOCKSсервера можно организовать шлюз для доступа из локальной сети организации в Интернет. Это будет особенно удобно в случае, когда использование традиционного HTTP-прокси не может дать требуемых результатов (например, необходимо реализовать поддержку дополнительных протоколов, не связанных с web), а открывать полноценный доступ через NAT почему-либо представляется нецелесообразным. Предлагаемую схему можно инвертировать и возложить на SOCKS-сервер функции «стража», допускающего выделенных пользователей (скажем, находящихся в командировке сотрудников службы технической поддержки) к внутренним ресурсам компании (корпоративной базе данных). Можно... да мало ли чего можно придумать, имея необходимые знания и смекалку! Дерзайте! Автор выражает благодарность Максиму Артемьеву за ценные замечания, высказанные в ходе обсуждения данной статьи. Òàáëèöà 1. Íåêîòîðûå ìåòîäû SOCKS-àóòåíòèôèêàöèè
Полный вариант таблицы можно найти по адресу: http:// www.iana.org/assignments/socks-methods.
администрирование
СТРОИМ ВИРТУАЛЬНУЮ СЕТЬ С TINC
СЕРГЕЙ ЯРЕМЧУК Когда зарождались протоколы, используемые в сегодняшнем Интернете, никто и не думал о том, что спустя некоторое время к нему без проблем смогут подключаться миллионы пользователей, а компании будут вести свой бизнес с его помощью. Со временем начали всплывать просчеты создателей и постепенно стали появляться проекты, основная цель которых – минимизировать допущенные ошибки. Сегодня после антивирусов и межсетевых экранов наиболее популярным и востребованным средством являются виртуальные частные сети (Virtual Private Network – VPN), которые, несмотря на первоначальную возню с установкой и отладкой, все-таки делают жизнь админа спокойнее и упрощают настройку многих сетевых сервисов. Хотя на рынке присутствует большое количество коммерческих продуктов, имеющих в том числе и аппаратные решения, большой интерес вызывают именно свободные проекты. Причин много. Так, не сразу становится ясно, подойдет ли конкретной организации схема сети с VPN, а вкладывать деньги эксперимента ради вряд ли кто рискнет. С другой стороны, очень часто переход сети на VPN является
№2, февраль 2005
личной инициативой администратора, которому надоели постоянно возникающие проблемы. Да и отношение к бесплатному софту как к некачественному, второсортному постепенно проходит. Большая часть свободных утилит для создания VPN доступна только под UNIX, и если администратор до этого не работал с такими системами, то у него не сразу получится разобраться с настройкой софта. Также не все проекты имеют реализации для различных операционных систем. Выход один – нужен удобный, простой и в то же время функциональный инструмент. Основной идеей проекта tinc (сокращение от There Is No Cabal) как раз и является простота настройки VPN-сети.
Что может tinc? Особенностью tinc является использование одного исполняемого файла – демона tincd, который является одновременно и сервером, и клиентом. Какие преимущества это дает? В tinc подключить новый компьютер в уже работающую виртуальную сеть довольно легко, для этого необходимо добавить всего лишь один файл, без запуска нового
39
администрирование демона или еще одного виртуального сетевого устройства. Удаленные компьютеры могут обмениваться информацией между собой, по прямому каналу, а не через основной сервер, как это реализовано в том же vtun. В vtun для обмена напрямую придется создавать еще один туннель, что при большом количестве клиентов затрудняет настройку. Хотя возможен запуск и нескольких экземпляров tinc на одном компьютере, что позволяет создавать несколько виртуальных сетей. При этом демон самостоятельно осуществляет маршрутизацию и направляет пакеты по назначению кратчайшим путем. Процесс обработки информации происходит в user space и не требует обязательной перекомпиляции ядра. Принято считать, что такие реализации работают медленнее и нагружают систему больше, но я, признаюсь, дотошных тестов не видел, а работа VPN зависит от многих обстоятельств, среди которых и используемое оборудование, канал, общая нагрузка на сеть, поэтому вряд ли можно говорить о полном и безоговорочном преимуществе VPN-систем, встроенных в ядро, все зависит от конкретной обстановки. Но вот устанавливать user space-реализации проще, хотя после включения IPSec в ядра серии 2.6 некоторые вопросы по настройке отпали сами по себе. Также tinc способен создавать мост для Ethernet-сегментов и соединять несколько сетей в одну, позволяя, например, использовать программы, нормально работающие только по LAN. На сегодняшний день поддерживаются операционные системы Linux, FreeBSD, OpenBSD, NetBSD, MacOS/X, Solaris, Windows 2000 и XP. На момент написания статьи последней версией была 1.0.3, которую и рекомендуется использовать. В более ранних версиях были обнаружены серьезные проблемы с безопасностью, заключающиеся в отсутствии порядковых номеров и опознавательного кода сообщения для каждого пакета, что позволяло производить атаку на отказ в обслуживании, повторно запуская старые пакеты. Устранение этого недостатка привело к тому, что более новые версии программы несовместимы со старыми, которые к тому же имеют реализацию не для всех платформ. Хотя криптография на месте не стоит и на сегодняшний день считается, что использование 32 бит для порядковых номеров и 4 бит, используемых по умолчанию для кода аутентификации сообщения (message authentication code – MAC), является уже недостаточным. Поэтому в конфигурации по умолчанию tinc менее защищен чем TLS или IPsec, а полностью переработать tinc разработчики планируют к версии 2.0. Я раньше думал, что самым простым в настройке и использовании является vtun (http://vtun.sourceforge.net), но после знакомства с героем сегодняшней статьи свое мнение пересмотрел.
Установка tinc Разработчиками взят курс на упрощение процесса установки и настройки систем на базе tinc, да и документации на сайте хотя и сравнительно немного, но в ней отражены практически все основные вопросы, которые могут возникнуть при настройке tinc, в том числе и особенности установки в различных операционных системах. Поэтому рассказывать обо всех возможных вариантах смысла, думаю, нет. Остановлюсь на установке VPN между двумя системами, для
40
которых процесс настройки диаметрально отличается. Одна построена на базе Linux, в качестве платформы для второй выбрана Windows XP. При этом в BSD и Solaris процесс настройки совсем немного отличается от Linux и, зная особенности работы сетевых сервисов в этих системах и уловив смысл производимых действий, установить tinc в них труда особого не составит.
Особенности установки tinc в Linux Система tinc использует для работы универсальные драйверы tun и tap. Tun применяется при туннелировании IPпакетов, а tap, он же ethertap, – при туннелировании фреймов Ethernet. Драйвер TUN/TAP позволяет пользовательским программам самостоятельно обрабатывать соответствующие пакеты. Поэтому эти устройства должны быть включены при конфигурировании ядра. В большинстве дистрибутивов это уже сделано, но если команды: # ls -al /dev/net/tun
и # ls -al /dev/tap*
ничего не выводят, то либо придется их создать самому, либо поручить это системе, прописав в файле /etc/modules.conf строку (для ядер 2.4.0 и выше): alias char-major-10-200 tun
После обновления зависимостей модуля ядра командой: # /sbin/depmod –a
вы должны увидеть необходимые устройства. Более подробно это все описано в файлах tuntap.txt и ethertap.txt, которые находятся в каталоге /usr/src/linux/Documentation/ networking (при установленных исходниках ядра). Иначе придется ядро все-таки пересобирать, включив следующие строки. Code maturity level options [*] Prompt for development and/or incomplete code/drivers Network device support <M> Universal tun/tap device driver support
Для шифрования трафика используется OpenSSL (http:// www.openssl.org), и для сжатия потока в системе должны быть установлены библиотеки zlib (http://www.gzip.org/zlib) и lzo (http://www.oberhumer.com/opensource/lzo). При этом zlib является опциональной и используется для сжатия UDPпакетов, а вот наличие lzo в системе обязательно, иначе получите ошибку при конфигурировании. В ближайшем будущем планируется избавить пользователя от поиска программ для удовлетворения всех зависимостей, собрав все необходимое в единый архив. После того как все библиотеки установлены, берем с сайта проекта http://www.tinc-vpn.org бинарные пакеты, скомпилированные под используемую операционную систему, либо исходные тексты. Размер архивов не превыша-
администрирование ет 600 Кб. Установка из исходников обычно проблем не вызывает и заключается в стандартном ./configure, make и make install. После чего в /usr/local/bin появится всего один бинарный файл tincd.
Установка под Windows Демон tinc в этой операционной системе использует драйвер TAP-Win32, который можно взять с сайта OpenVPN (http:// openvpn.sourceforge.net). Этот драйвер в настоящее время включен в последнюю версию пакета 1.0.3 для Windows и устанавливается вместе с tinc (рис. 1), поэтому отдельно устанавливать его уже не надо. Также для запуска демона понадобится наличие Cygwin (http://www.cygwin.com) или MinGW (http://www.mingw.org). Со второй работать не пробовал, а вот при работе через Cygwin потребуется только cygwin.dll.
и остановки сетевых VPN-интерфейсов (для UNIX-систем), и в отдельном файле (rsa_key.priv) находятся закрытые ключи сервера. В подкаталоге doc-архива с исходными текстами программы находится архив sample-config.tar.gz, содержащий примеры конфигурационных файлов. По умолчанию демон tincd будет искать свои конфигурационные файлы в каталоге /usr/local/etc/tinc/, если вы решили положить их, например, в /etc/tinc/, то при запуске необходимо будет дополнительно использовать опцию --config=/etc/tinc/.
Ðèñóíîê 3
Ðèñóíîê 1
После установки всех необходимых библиотек и tinc, заходим в каталог C:\Program Files\tinc\tap-win32 и запускаем файл addtap.bat. Теперь в меню «Сетевые подключения» должно появиться новое соединение, которому можно присвоить более осмысленное имя.
Ðèñóíîê 4
Ðèñóíîê 2
Выбираем «Свойства → Протокол Интернета TCP/IP» и редактируем его параметры, в которых указываем адрес и сетевую маску компьютера в виртуальной сети (рис. 3). В последней версии tinc сам прописывает себя в автоматически запускаемые сервисы после первого успешного запуска, но на всякий случай проверьте его наличие в меню «Настройка → Панель Управления → Администрирование → Службы» (рис. 4).
Конфигурационные файлы tinc Tinc во время своей работы использует два вида конфигурационных файлов, плюс в двух скриптах записываются системно-зависимые параметры, необходимые для поднятия
№2, февраль 2005
Для одной виртуальной сети такого расположения файлов вполне достаточно, но если планируется организация на компьютере нескольких виртуальных сетей, то для каждой необходимо будет создать подкаталог, выбрав в качестве его имени название VPN-сети. Например, для виртуальной сети vpn_net создаем каталог /usr/local/etc/tinc/vpn_net/. Теперь при запуске демона tincd при помощи опции –n vpn_net указываем название нужной виртуальной сети, в логах информация о ней будет отображаться как tinc.vpn_net. В созданном подкаталоге должны обязательно лежать два скрипта tinc-up и tinc-down, в которых описываются команды для сетевого интерфейса. Для Linux эти файлы выглядят так. Ôàéë tinc-up #!/bin/sh ifconfig $INTERFACE 192.168.10.1 netmask 255.255.0.0
41
администрирование метры нет необходимости, в самом общем случае файл будет выглядеть так:
Ôàéë tinc-down. #!/bin/sh ifconfig $INTERFACE down
Естественно, что для других UNIX-подобных систем эти строки будут немного другими, подробности смотрите в tinc Manual. Раздел 7. «Platform specific information». Для Windows эти файлы не требуются. Для работы в UNIX-подобных системах желательно наличие в файле /etc/services следующих строк. tinc tinc
655/tcp 655/udp
TINC TINC
И в /etc/networks должно быть указано символическое имя будущей VPN. vpn_net
192.168.10.0
Как говорилось выше, tincd является одновременно как клиентом, так и сервером. За счет этого существенно упрощена настройка системы, а также дальнейшее наращивание сети в любом направлении. Для работы серверной части демона используется файл tinc.conf, в нем клиенты, с которыми должен соединяться сервер, определяются параметром ConnectTo и более подробно описываются отдельным файлом, лежащим тут же в подкаталоге hosts и имеющим такое же название, как и в опции ConnectTo. При этом на противоположной стороне можно прописать встречное соединение, указав параметр ConnectTo, демоны на этих системах не передерутся за то, кто из них самый главный, но таким образом можно спокойно наращивать в дальнейшем виртуальную сеть, не заботясь о конфликтах. Все параметры файла tinc.conf приведены в таблице 1. Использовать все приведенные пара-
# Sample tinc configuration file Name = office ConnectTo = home ConnectTo = stock-room Device = /dev/net/tun
То есть демон, носящий имя office, будет пытаться соединиться с двумя компьютерами home и stock-room, описание которых находится в одноименных файлах, лежащих в подкаталоге hosts. В Windows вместо Device удобнее использовать параметр Interface, т.е. файл будет выглядеть так: Name = home ConnectTo = office ConnectTo = stock-room Interface = VPN
В случае если VPN-интерфейсов два, нужно написать Interface = VPN2 и назвать его так в «Сетевых подключениях». Все опции, которые можно использовать в файле описания удаленного узла, приведены в таблице 2. В простейшем файл home будет выглядеть так: # Sample host configuration file Address = 10.10.1.67 Port = 655 Subnet = 192.168.10.0/24 -----BEGIN RSA PUBLIC KEY----... -----END RSA PUBLIC KEY-----
Так как в Windows все параметры задаются при настройке сетевого соединения, то в файле клиентов достаточно только секции Subnet.
Òàáëèöà 1. Ïàðàìåòðû, êîòîðûå ìîãóò áûòü èñïîëüçîâàíû â ôàéëå tinc.conf
42
администрирование Но кроме правильно описанных параметров в конфигурационных файлах, для нормальной работы всей системы необходимо наличие пары ключей, при помощи которых будет происходить закрытие информации. Для генерации ключей запускается демон с ключом -К, а если на компьютере запускается несколько виртуальных сетей, то добавляется опция -n. Например, на компьютере office, выполняем такую команду:
все необходимые каталоги, либо указать новое место для pid-файла, использовав опцию --pidfile=/path/to/file. После этого узлы должны соединиться между собой и образовать единый канал. Для контроля можно проверить наличие необходимых интерфейсов командами ifconfig -a (ipconfig для Windows) и просмотреть открытые порты при помощи netstat -a.
# tincd –K -n vpn_net Generating 1024 bits keys: ............++++++ p .........++++++ q Done. Appending key to existing contents. Make sure only one key is stored in the file.
При этом по умолчанию закрытый ключ будет записан в файл /usr/local/etc/tinc/vpn_net /rsa_key.priv, а открытый ключ в файл /usr/local/etc/tinc/vpn_net/hosts/office. Для Windows это будет естественно немного другой путь, C:\Program Files\ tinc\vpn_net\rsa_key.priv, а открытый – C:\Program Files\ tinc\vpn_net\hosts\home. Отсюда наиболее оптимальным будет такой вариант настройки виртуальной сети. На каждом узле создается свой host-файл, в который кроме параметров записывается и открытый ключ. А затем администраторы обмениваются этими файлами между собой и подключают опцией ConnectTo. Теперь, когда все настроено, можно пробовать запустить демон. # tincd
-n vpn_net
Скорее всего, при первом запуске выскочит такая ошибка. Could write pid file /usr/local/var/run/tinc.vpn_net.pid: No such file or directory
Означающая, что демон не может найти файл tinc.vpn_ net.pid. Вероятной причиной появления такого сообщения является отсутствие нужных каталогов. Выход: либо создать
Ðèñóíîê 5
Для выявления возможных ошибок могут пригодиться дополнительные параметры запуска. Опция --logfile[=file] указывает на необходимость ведения журнала (по умолчанию запись будет вестись в /usr/local/var/log/tinc.netname.log), опция --debug задает уровень отладочных сообщений (0-5) и --bypass-security отключает шифрование. Вот в принципе и все. Несмотря на то, что написано много, настройка сервиса на отдельном компьютере не занимает времени больше 10-15 минут. Со временем разработчики собираются интегрировать все приложения, от которых зависит работа tinc, в единый пакет, что еще больше должно упростить установку. А так tinc представляет собой довольно удобный, гибкий и простой инструмент, позволяющий быстро создавать и легко наращивать виртуальные сети, доступный к тому же для всех популярных сегодня операционных систем.
Òàáëèöà 2. Ïàðàìåòðû ôàéëà îïèñàíèÿ êëèåíòîâ
№2, февраль 2005
43
администрирование
FreeBSD TIPS: NAT ПО СТАРИНКЕ
СЕРГЕЙ СУПРУНОВ В наши дни, когда свободных IP-адресов становится все меньше и меньше, трансляция сетевых адресов (Network Address Translation – NAT) становится все более актуальной. Трудно найти провайдера, который, не сопротивляясь, даст вам нужное количество реальных адресов. Наиболее типичная ситуация – когда в «комплекте» с подключением к сети Интернет выделяется один-единственный IP-адрес. В результате внутреннюю сеть приходится строить на одной из приватных подсетей (что это такое, мы рассмотрим немного позже), и эти «левые», как их называют в народе, адреса нужно каким-то образом транслировать в тот единственный реальный, данный провайдером. Cразу стоит указать и на определенное преимущество такого подхода – в большинстве случаев локальная сеть становится невидимой снаружи, то есть любой внешний хост может общаться только с сервером NAT, ничего не подозревая о наличии (не говоря уже о структуре) вашей внутренней сети. В данной статье будет показано, как настроить NAT-сервер на базе FreeBSD. В качестве инструмента будет использоваться демон natd в связке с ipfw. В современных версиях системы такая схема считается не самой лучшей в плане гибкости и нагрузки на систему, и более предпочтительным является вариант на базе IPFilter – ipnat (этот NAT будет рассмотрен в одной из следующих статей). Тем не менее старый добрый natd до сих пор честно служит на просторах Сети, и оставлять его без внимания было бы, по меньшей мере, невежливо. Сначала – немного теории. Как известно, в сети Интернет каждый хост, взаимодействующий с другими, должен иметь уникальный IP-адрес, по которому он и распознается. Несколько сетей (10.0.0.0/8, 172.16.0.0/12 и 192.168.0.0/ 16) выделяются в так называемый «нерегистрируемый» диапазон, то есть они могут использоваться для нужд внутренних сетей без какой-либо регистрации. Эти адреса именуют также приватными, или «серыми». Чтобы избежать конфликтов адресов в глобальной сети, пакеты с такими адресами не должны выходить за пределы шлюза, ограничивающего соответствующую внутреннюю сеть. Поэтому такие адреса иногда называют немаршрутизируемыми (хотя внутри локальной сети они замечательно маршрутизируются). Вообще нужно заметить, что деление на реальные и приватные весьма условно. Я, например, учитывая дефицит «настоящих» адресов, выданных на наш узел, широко практикую раздачу клиентам адресов из сети 192.168.x.x, «пряча» их за NAT. Таким образом, с точки зрения клиента эти адреса будут реальными. Суть NAT заключается в том, что в процессе преобразования адрес отправителя (который может быть любым, в том числе принадлежать немаршрутизируемой подсети) заменя-
44
ется адресом NAT-сервера (он должен быть «реальным», то есть входить в диапазон адресов, предоставленный вам провайдером). В отличие от прокси-серверов, которые берут на себя обработку запросов в соответствии с тем или иным протоколом, сервер NAT просто заменяет адрес и, как правило, порт отправителя (или получателя). Он не пытается разбираться в сути передаваемых данных, и благодаря этому использование NAT не ограничено списком поддерживаемых протоколов. Говоря упрощенно, NAT работает так: клиент, имеющий адрес «A», отправляет через порт «a» пакет на некоторый удаленный сервер. Этот пакет заворачивается (пока не будем уточнять, как именно) на сервер NAT, который вместо адреса «A» подставляет свой адрес «B» и отправляет этот пакет уже от своего имени через порт «b». Одновременно с этим в памяти сервера делается запись о выполненной подстановке, чтобы при получении ответного пакета на порт «b» знать, на кого из клиентов его пересылать. Описанная схема работы носит название «маскарадинг» (masquarading). Помимо маскарадинга существуют также понятия SNAT и DNAT (особенно хорошо известные пользователям Linux). Первый из них (Source NAT) отвечает за преобразования адреса-источника и во многом подобен маскарадингу, с тем исключением, что работает только со статическими адресами источников, и потому менее требователен к ресурсам. DNAT (Destination NAT) осуществляет преобразование адреса назначения, что используется для проброса внешних соединений, приходящих на конкретный порт (например, 80-й) на внутренние хосты, не имеющие реального адреса. Общий случай DNAT – статический NAT, когда все внешние соединения, поступающие на указанный адрес, перенаправляются на внутренний хост. Более полную информацию по технологии NAT можно найти в Интернете, а сейчас приступим к рассмотрению natd. Эта программа включена в систему FreeBSD, и потому никакая инсталляция не требуется. Перед началом ее использования должно быть выполнено следующее: ! Ядро должно быть собрано с опциями IPFIREWALL и IPDIVERT – это необходимо для перенаправления пакетов на сервер natd. ! Должна быть включена маршрутизация между интерфейсами, что можно сделать, добавив в /etc/rc.conf следующую опцию: «gateway_enable=YES». ! Должен быть разрешен брандмауэр опцией «firewall_ enable=YES» в том же rc.conf. Теперь можно запускать и сам сервер. Как было сказано выше, пакеты, нуждающиеся в трансляции адреса, дол-
администрирование жны быть перенаправлены на сервер NAT. При использовании natd это выполняется с помощью правила divert брандмауэра ipfw. Например, чтобы вывести в Интернет внутреннюю сеть 192.168.0.0/24, имея один реальный адрес 190.190.190.190 на интерфейсе ed0, «смотрящем» наружу, нужно сделать следующее: ! Запустить сервер natd (ключ -interface означает, что в качестве внешнего адреса будет использоваться адрес указанного интерфейса): # natd –interface ed0
! Перенаправить на демон natd пакеты, адресованные с внутренних адресов во внешнюю сеть, то есть нуждающиеся в трансляции: # ipfw add 100 divert natd ip from 192.168.0.0 ↵ to any out via ed0
! Завернуть на NAT входящие пакеты, адресованные непосредственно на внешний интерфейс (поскольку именно сюда будут приходить ответы): # ipfw add 110 divert natd ip from ↵ any to 190.190.190.190 in via ed0
В результате произойдет следующее: демон natd запустится с параметрами по умолчанию, то есть будет ждать пакеты на порту 8668 и в качестве внешнего IP-адреса использовать адрес указанного интерфейса, в данном случае – ed0. Отправленные клиентами пакеты ipfw будет заворачивать на порт 8668 (в /etc/services для него задано имя natd, поэтому можно использовать и его). Сервер NAT отправит эти пакеты по адресу назначения, но уже с адреса 190.190.190.190. Получив пакеты в ответ, NAT перенаправит их клиенту. Если вы хотите использовать в качестве внешнего адреса какой-то другой, вместо ключа -interface при запуске сервера следует использовать опцию -alias_address с указанием нужного адреса в качестве параметра. Какая-то из этих опций должна быть задана обязательно, но не обе одновременно. Дополнительно при запуске natd может быть задано достаточно большое количество опций, получить информацию по которым можно на страницах руководства man natd. Здесь рассмотрим только наиболее интересные. Например, можно задавать отличный от используемого по умолчанию номер divert-порта, с которым будет связан сервер natd. Для этого предназначен ключ -port <port>. Это свойство часто используется, если необходимо запустить несколько демонов natd. К сожалению, этот инструмент может обрабатывать пакеты только в соответствии с одним правилом. Поэтому, если вам нужно использовать свои параметры для различных клиентов, приходится запускать отдельные демоны на разных для каждой группы клиентов портах. Соответственно, в правилах ipfw перенаправление должно выполняться на те же порты, которые связаны с natd. Кроме того, с помощью опций -in_port и -out_port можно задавать отдельные порты для входящих и исходящих пакетов соответственно. Иногда может быть полезно, чтобы NAT пересылал пакеты с того же порта, с которого их отправлял клиент. Для
№2, февраль 2005
этого можно использовать опцию -same_ports, однако не забывайте, что при этом NAT не обязуется сохранять номер порта, а будет делать это по возможности. То есть в принципе порт может и поменяться (например, если нужный номер уже используется другим соединением, NAT «забудет» про ключ -same_ports и будет работать как обычно). Если natd запущен с опцией -interface, то есть привязывается к адресу конкретного интерфейса, то при изменении IP-адреса интерфейса IP-адрес, используемый демоном natd, останется прежним. Чтобы NAT отслеживал и автоматически учитывал изменения IP-адреса интерфейса, используется опция -dynamic. Опция -unregistered_only указывает, что NAT должен обрабатывать только пакеты, адрес источника которых входит в указанные выше «нерегистрируемые» сети. Ключи -redirect_port, -redirect_proto и -redirect_address служат для реализации «проброса» внешних соединений на внутренний адрес в зависимости от номера порта, протокола или адреса назначения входящих пакетов соответственно. Например, если во внутренней сети имеется HTTP-сервер 192.168.0.125, к которому нужно предоставить доступ снаружи, это можно сделать с помощью опции «-redirect_port tcp 192.168.0.125:80 80». Все опции можно вынести в конфигурационный файл, где в каждой строчке указывается одна опция с параметрами либо с пометкой «yes / no» для непараметрических опций. При запуске natd этот файл следует указать с помощью опции -config. Теперь, чтобы natd запускался автоматически при старте системы, в /etc/rc.conf можно указать следующие строки (показан конкретный пример): natd_enable = “YES” natd_interface = “ed0” natd_flags = “-config /etc/natd.conf”
При этом параметр «natd_interface» при запуске демона natd «превратится» в параметр -nterface, остальные параметры можно задать в строке natd_flags. Обычно здесь указывается только путь к конфигурационному файлу, в который выносятся все остальные настройки. Ну и как обычно – если режим работы сервера позволяет перезагрузку, то лучше это сделать, чтобы лишний раз убедиться в правильности всех настроек и отсутствии опечаток в конфигурационных файлах. В заключение отмечу несколько недостатков natd. Прежде всего это все-таки демон, и, следовательно, по производительности он несколько проигрывает тому же ipnat, который более тесно интегрирован с ядром. Реализация сложной политики предоставления доступа в Интернет, дифференцированная по различным группам пользователей, может потребовать запуска нескольких демонов natd, что через rc.conf уже не получится, и потребуется писать свой стартовый сценарий. Тем не менее в большинстве случаев natd вполне справляется с возложенными на него обязанностями, и если в системе уже работает ipfw, то такая схема может оказаться наиболее простым способом построения NAT-сервера. Дополнительная информация, как обычно, в man natd(8), divert(4), rc.conf(5), ipfw(8).
45
администрирование
ВСЕГДА НА СВЯЗИ, ИЛИ IP-РОУМИНГ: ВВОДНЫЙ КУРС СЕРГЕЙ ЯРЕМЧУК Сегодняшний мир – мир мобильности. Действительно, для того чтобы быть в курсе событий уже не надо находиться на одном месте. Те, кто пользуется мобильными устройствами, имеют большие возможности по выбору каналов для доступа в Интернет – встроенный модем, Ethernet, Wi-Fi, GPRS и пр. Чтобы оценить прогресс в этой области, достаточно вспомнить изменения, произошедшие в обычной телефонной связи с появлением сотовой. Сегодня пользователи мобильных сетей третьего поколения могут просматривать фильмы из Интернета прямо на своем телефоне. Пользователи мобильных компьютеров также хотят пользоваться Интернетом всегда и везде, однако поход за «настоящей мобильностью» еще только начинается. В недалеком будущем планируется покрыть беспроводными сетями целые города. Не надо путать настоящую мобильную работу с тем, что мы имеем сегодня. В настоящей мобильной сети активные сеансы не прерываются, когда пользователь меняет точку доступа в Интернет. Все необходимые переключения производятся автоматически, подобно тому, как это происходит при перемещении абонента мобильной телефонной связи от одной соты к другой. Конечно, между мобильной связью и TCP-сеансами существует множество отличий, однако с ростом популярности различных видов мобильных компьютеров возможность подобного «IP-роуминга» быстро найдет свое применение. Основная проблема состоит в том, что один и тот же компьютер в различное время имеет разный IP-адрес, что не вполне соответствует понятию «настоящая мобильность», поскольку применяемые сейчас протоколы определяют место назначения пакетов на основе IP-адресов, которые привязываются к конкретному сетевому интерфейсу. Для наиболее популярного в Интернете транспортного протокола TCP сеанс идентифицируется четырьмя параметрами: IP-адресом источника, IP-адресом приемника и двумя номерами портов. Потеря любой из этих составляющих ведет к прекращению сеанса. Поэтому все изменения точек прикрепления, а значит, и IP-адресов после перехода в другую сеть ведут к неизбежным потерям активных связей, а значит, и к отсутствию мобильности как таковой. Данная проблема не могла остаться незамеченной, и на сегодняшний момент уже существуют технологии, позволяющие закрепить за компьютером постоянный IP-адрес. Первые официальные документы по этой теме датированы октябрем 1996 года: RFC 2002 «IP Mobility Support». Последний и пока окончательный вариант «IP Mobility Support for IPv4» известен под номером 3344 и датирован августом 2002 года. В этом документе определены расширения протокола, допускающие маршрутизацию IP-датаграмм на мобильные ком-
46
пьютеры в Интернете, абсолютно прозрачную для приложений и протоколов более высокого уровня вроде TCP. В схеме, предлагаемой RFC 3344, компьютер всегда идентифицируется своим домашним адресом, независимо от текущей точки доступа в Интернет, хотя, естественно, в новой точке и получает новый адрес. Протокол предоставляет заботу о регистрации нового адреса домашнему агенту (Home Agent – HA). Такой агент распознает сеансы связи, заботится об их сохранении и пересылает датаграммы, предназначенные мобильному компьютеру (mobile node – MN) на его новый адрес, используя специальный IP-туннель. При этом, когда MN находится в домашней сети, обмен происходит обычным образом. Однако когда компьютер перемещается в другую сеть и получает новый IP-адрес (его называют «care-of»-адресом), он сообщает последний своему HA, который в свою очередь подтверждает или отклоняет регистрацию. «Care-of»-адрес может быть получен различными методами. В общем случае его выдает «посторонний агент» (Foreign Agent – FA). При этом различают два альтернативных способа выдачи такого адреса. В первой схеме, известной как «foreign agent care-of address», «care-of»-адрес является IP-адресом FA, который и поддерживает один из концов IP-туннеля (другой конец в любом случае принадлежит HA). Получив информацию из туннеля, FA декапсулирует пакет, после чего передает его в сеть, где он подхватывается мобильным компьютером. Второй способ, «co-located care-of address», заключается в выдаче «care-of»-адреса непосредственно мобильному компьютеру. В этом случае MN является конечной точкой туннеля и сам занимается декапсуляцией пакетов. Агенты Foreign Agent и Home Agent сообщают о своем присутствии посредством сообщений «Agent Advertisement». При необходимости мобильный узел может сам дополнительно попросить такое сообщение от локальных агентов, используя сообщение «Agent Solicitation». Сообщения «Agent Solicitation» используются для определения маршрутизатора в новой сети и обнаружения любых изменений в установках FA. По ответам на эти сообщения MN определяет, где он находится – в своей домашней сети или в чужой. При возврате мобильного узла из внешней сети в домашнюю он отменяет внешнюю регистрацию у домашнего агента посредством пары сообщений «Registration Request» и «Registration Reply». Таким образом, сообщения «Agent Advertisement» преследуют несколько целей: обнаружение мобильных компьютеров, выдачу «care-of»- адресов, назначение новых параметров маршрутизации, информирование мобильного компьютера об особенностях работы FA (например, наличии альтернативных методов инкапсуляции).
администрирование Информация, отправленная на домашний адрес мобильного узла, перехватывается домашним агентом и переправляется по туннелю на «care-of»-адрес либо через FA, который в свою очередь зная, где сейчас MN, передает информацию ему (схема «foreign agent care-of address»), либо непосредственно к MN. Для успешной доставки требуется, чтобы «care-of»-адрес выступал в качестве адреса назначения каждого из пакетов. Когда пакет приходит на «care-of»адрес, происходит обратное преобразование, и пакет снова в качестве адреса назначения получает домашний адрес мобильного узла. Так как служебная информация для протоколов более высокого уровня остается постоянной, то все активные сеансы начатые, например, в домашней сети, не будут разорваны. В обратном направлении информация проходит обычным путем, используя стандартные механизмы IP-маршрутизации и не обязательно затрагивая домашнего агента. В качестве IP-адреса приемника выступает адрес узла, с которым в данный момент установлено соединение, а в качестве адреса источника – домашний адрес компьютера, т.е. HA. При отправлении, как и при приеме пакета, MN может сам заниматься отправкой или делать это через FA. При перемещении MN он снова регистрируется у нового FA, после чего информация направляется по новому «care-of»-адресу. Некоторые реализации позволяют назначить мобильному компьютеру сразу несколько «care-of»-адресов, в этом случае HA пересылает информацию по всем известным ему адресам. Такой подход может быть полезен при постоянном перемещении клиентов. В течение последних лет стартовало несколько проектов, реализующих идею Mobile IP. К сожалению, большая часть этих программ развивается не очень активно, и в результате готовые реализации можно найти только для старых ядер Linux версий 2.0 и 2.2. Среди этих проектов следует упомянуть Linux Mobile IP [1] и Mosquito Net Mobile IP [2]. Довольно неплохие наработки имеются у проекта Dynamics Mobile IP [3]. Кроме ядер Linux версий 2.2 и 2.4, это приложение посредством Cygwin портировано и в Microsoft Windows (98SE, ME, NT4, 2000). К сожалению, с осени 2001 года Dynamic Mobile IP больше не развивается. Подход, заложенный в rfс 3344, имеет и недостатки. Самые существенные среди них – это большое количество туннелей, серьезная нагрузка на домашнего агента, от исправной работы которого, по сути, и зависит возможность реализации вышеописанной схемы и достижимость мобильного узла. Эту проблему может решить централизованное управление всей системой, а не контроль за отдельными сеансами. В таком случае возможно резервирование информации. Кроме того, централизация позволит оптимизировать связи, уменьшить количество туннелей, облегчит управление и защиту. Но, наверное, самым большим недостатком является необходимость использования дополнительного программного обеспечения, декапсулирующего пакеты и обеспечивающего взаимодействие с FA, на клиентских компьютерах, а также сам факт существования FA. Учитывая сегодняшнее разнообразие операционных систем и устройств, придется либо адаптировать программу для 1
всех систем, либо унифицировать оборудование в отдельно взятой организации, что не всегда приемлемо и возможно. Опять же убедить провайдера или системного администратора в установке постороннего ПО или устройства не всегда удается, поэтому говорить о глобальной мобильности пока еще рано, а вот в отдельной компании все вышеописанное вполне возможно.
Проект TMIP Другой весьма интересной альтернативой Mobile IP являются разработки открытого проекта TMIP (Transparent Mobile IP). С их помощью каждый мобильный узел с произвольным внешним IP-адресом также будет всегда идентифицироваться по одному домашнему IP-адресу, который не будет зависеть от местоположения компьютера. Кроме этого, TMIP гарантирует, что все активные сеансы TCP не будут разорваны при перемещении. Отличие этого проекта от Mobile IP состоит в том, что на стороне клиента не применяется никакого дополнительного программного обеспечения, а также не происходит вмешательства в структуру IP-стека. Для обеспечения связей между узлами, сеть сама изменяется, используя технику IP-туннелей. Сеть TMIP состоит из одного-двух реестров Mobile Location Register (MLR), нескольких корреспондентских узлов (Correspondent Nodes – CN) и мобильных клиентских станций (Mobile Station – MS). MLR предназначен для руководства клиентами (точнее, их Ethernet MAC-адресами), выяснения текущей позиции в сети и распределения IP. Для этого MLR хранит в памяти местоположение всех клиентских станций. При обнаружении новой станции он регистрирует ее, отмечая родительский и текущий CN. Так как потеря этой информации критична, то, как правило, применяется резервирование. Присоединением мобильных узлов занимается CN. Происходит это так. Мобильный компьютер, находясь в одной из сетей, получает IP-адрес при помощи интегрированного в CN сервера DHCP. Когда узел мигрирует в другую сеть и попадает к новому CN, этот переход обнаруживается при помощи различных методов1, после чего новый CN вступает в контакт с исходным (родительским) CN (обслуживающим домашнюю сеть узла) и, возможно, с предыдущим CN (обслуживающим сеть, откуда узел только что ушел). Вся необходимая информация берется у MLR. На этом этапе возможна повторная аутентификация клиента, в результате которой разрешается или запрещается дальнейшая работа. В настоящий момент она производится только на основе МАС-адресов. Абсолютно прозрачно, в пределах одной транзакции (в две стадии) узел передается новому CN с обновленными параметрами сети и старым IP-адресом. При этом миграции клиентов не влияют на таблицы маршрутизации конкретного сегмента сети. Исходящие от мобильного узла пакеты достигают места назначения обычным путем. Учитывая, что их исходный адрес не всегда соответствует текущему положению узла, родительский CN (при помощи MLR) узнает текущую позицию узла (точку доступа) и пересылает ответные пакеты нужному CN по туннелю. Последний в свою очередь передает их в локальную сеть, где они перехваты-
Например, по DHCP-запросу или попытке отослать информацию на старый шлюз, демонстрируя при этом «неродной» IP и незарегистрированный MAC-адрес.
№2, февраль 2005
47
администрирование ваются мобильным компьютером. При таком подходе максимально возможное число туннелей, которые можно получить, равняется n ∗ (n – 1), где n – общее число CN в сети.
Установка и настройка TMIP Основной сайт проекта расположен по адресу: http:// www.slyware.com/projects_tmip.shtml. Здесь можно найти документацию, а исходные тексты доступны с http://tmip.source forge.net. Текущая версия утилиты – 0.14a, архив имеет имя tmip_0.14a_release.tar.gz. Как вариант можно использовать пре-релиз tmipcore_0.5a-pre.tar.gz. Из принципа работы системы TMIP следует, что настраивать необходимо один-два MLR и несколько CN. Клиентские компьютеры не требуют настроек, но для упрощения работы они должны получать IP-адрес при помощи DHCP. MLR и CN должны работать под управлением ОС Linux, в которой необходимо установить библиотеку libpcap и включить поддержку «IP: Tunneling» в ядре (вкладка «Networking Options»). Рассмотрим процедуру установки TMIP. В первую очередь распакуем полученный архив. Для компиляции MLR следует зайти в подкаталог mlrd и дать команду «make», а образовавшийся в итоге исполняемый файл mlrd скопировать, например, в /usr/local/sbin. Аналогично поступаем с CN, работа которого управляется при помощи демона tmipd. Для компиляции демона необходимо выполнить команду «make» в каталоге tmipd. Для работы первичного и вторичного MLR-серверов необходимы два конфигурационных файла. Первый, mlrd-primary.rc, может выглядеть так:
рационного файла с детальными описаниями настроек находится в подкаталоге tmipd и называется tmipd.rc. # tmipd.rc mlr primary.my-tmip-mobility.com cn_name wlan. primary.my-tmip-mobility cn_if eth0 mobile_if wlan0 # èëè êàê âàðèàíò #mobile_if eth0 network_name my-tmip-mobility # ñëåäóþùàÿ îïöèÿ ïîíàäîáèòñÿ ïðè çàïóñêå íåñêîëüêèõ êîïèé # CN íà îäíîì êîìïüþòåðå # tunnel_prefix tmip # ðàçðåøèòü ðåãèñòðàöèþ òîëüêî çàíåñåííûõ â áàçó MLR-õîñòîâ registered_only false # ÷èñòêà òóííåëÿ ïðè çàïóñêå purge_tunnels true # òàê êàê CN çàíèìàåòñÿ ðàñïðåäåëåíèåì àäðåñîâ, # òî ðåàëèçîâàíà âîçìîæíîñòü çàäàíèÿ äèàïàçîíà addr_pool wlan0 * * # èëè êàê âàðèàíò # addr_pool eth1 10.10.15.32 +10.10.15.48 dns_server 10.10.15.7 debug_level 2 log true log_file /var/log/tmipd.log status_file /var/log/tmipd.status foreground true # pid_file /var/run/tmipd.pid # íàñòðîéêà ñåðâåðà DHCP enable_dhcp true # domain_name my-tmip-mobility.com # dhcp_lease_time 300 # DHCP lease time in seconds # âîçìîæíî èãíîðèðîâàíèå ïåðåìåùåíèÿ îòäåëüíûõ êëèåíòîâ # ignore_mac aa:bb:cc:11:22:33
Для проверки работоспособности системы запустите: #
# mlrd-primary.rc # CN äîëæíû èñïîëüçîâàòü àíàëîãè÷íîå èìÿ ñåòè network_name my-tmip-mobility port 6554 foreground true log_file /var/log/mlrd.log status_file /var/log/mlrd.status logtrue #ñòðîêà íèæå óêàçûâàåò íà âòîðè÷íûé MLR-ñåðâåð cc_mlr secondary.my-tmip-mobility.com:5555
Для вторичного сервера используется файл mlrdsecondary.rc. # mlrd-secondary.rc network_name my-tmip-mobility port 5555 foreground true log_file /var/log/mlrd.log status_file /var/log/mlrd.status log true grant primary.my-tmip-mobility.com
/usr/local/sbin/tmipd -Ef tmipd.rc
+ Starting TMip Correspondent Node [v0.14a] + Loading settings from configuration file... + Using mlrd.rc Network Connectivity Evaluation =============================== + MLR @ 10.10.14.100: Passed. + CN @ 10.10.15.1 [Oakhurst directional]: Passed. + CN @ 10.10.16.1 [Si's room]: Passed. + CN @ 10.10.17.1: Passed. + CN @ 10.10.18.1 [ECS Roof]: Passed.
Просмотрите полученные сообщения. Если все прошло нормально, то каждый CN должен соединяться с MLR по протоколу TCP, порт 6554 и обмениваться информацией с остальными CN по порту 5554. После этого можно произвести запуск в рабочем режиме. # /usr/local/sbin/tmipd
После этого можно запустить mlrd.
+ Starting TMip Correspondent Node [v0.14a]
# /usr/local/sbin/mlrd -f mlrd-primary.rc
+ Loading settings from configuration file... + Using tmipd.rc
+ Starting MLR Server [v0.14a]
+ Switching into daemon mode (Logging to /var/log/tmipd.log) + Loading settings from configuration file... + Using mlrd-primary.rc + Switching into daemon mode (Logging to /var/log/mlrd.log)
Аналогично запускаем сервис и на вторичном MLR-сервере. # /usr/local/sbin/mlrd -f mlrd-secondary.rc
Теперь приступаем к настройке CN. Пример конфигу-
48
Все сообщения об ошибках, регистрациях новых узлов находятся в файле журнала. # cat /var/log/tmipd.log
Если включена опция «registered_only true», то в регистрации компьютеров, не занесенных в базу MLR, будет отказано. В журнале это выглядит так.
администрирование -> Mobile station detected in my cell [00:0D:18:01:1C:05] (via DHCP activity - Discover) + Establishing host's address allocation + [WARNING]: MLRP communications failure! (Mobile host not registered in MLR) + [WARNING]: Failure to bind host locally! -> Ignoring host [00:0D:18:01:1C:05] for 600 second(s)
Для регистрации компьютеров, просмотра записей и обновления информации в MLR используется специализированный инструмент mlrp_query, компиляция которого производится в одноименном подкаталоге. Так, например, чтобы привязать компьютер с МАС-адресом 00:0D:18:01:1C:05 к CN 10.10.15.1 в MLR, находящемуся по адресу 10.10.15.100, следует выполнить команду. # mlrp_query -M10.10.15.100 -A -m00:0D:18:01:1C:05 ↵ -p10.10.15.1
Кроме того, в настоящее время идет разработка веб-инструмента, предназначенного для регистрации мобильных клиентов и вывода различной статистической информации. После произведенных настроек клиенты могут спокойно перемещаться по сетям. Единственной проблемой может стать использование на компьютере сменных адаптеров (например, PCMCIA), которые имеют различный MACадрес. Единственно возможным выходом в такой ситуации будет подмена МАС-адреса. # /sbin/ifconfig wlan0 hw ether 00:0D:18:01:1C:05
Проблемы Mobile IP Чтобы не создавать идеальную картину мобильного мира, просто необходимо сказать пару слов и о проблемах. Основной помехой при использовании Mobile IP являются защитные механизмы, так как реализовать подмену IP-адресов не просто, а очень просто. Поэтому описанные выше схемы потребуют введения дополнительной идентификации или шифрования трафика. Решений многих проблем ожидают от перехода на IPv6, для которого разрабатывается ряд расширений, позволяющих направлять потоки непосредственно на мобильный узел, полностью отказавшись от HA и FA [15]. Например, расширение Neighbor Discovery позволяет сообщать домашнему маршрутизатору о своем местонахождении и таким образом корректировать маршрут. Вероятно, по этой причине подавляющее большинство информации по вопросу Mobile IP имеет чисто теоретический и рекомендательный характер, так как после глобального перехода на IPv6 (когда это еще будет?) картина сильно изменится, и многие проблемы, если не отпадут полностью, будут выглядеть совсем иначе. Во всяком случае во время эксперимента в Гонконге, проходившего осенью 2000 года в сетях WiFi и GPRS использовался именно IPv6, а для связи с остальными узлами применялись специальные шлюзы (см. например: http://www.tdap.co.uk/uk/archive/ mobile/mob(ipv6_0103).html). Кроме того, использование IPsec для аутентификации источника и защиты целостности данных в IPv6 позволяет повысить безопасность таких соединений. Далее, не исключена возможность пропадания пакетов по причине их устаревания и отсечки межсете-
№2, февраль 2005
вым экраном, так как настройки большинства провайдеров исключают выход из сети пакетов с «неродным» IP-адресом. Было предложено несколько вариантов выхода из ситуации [16], самым простым из которых является обратный туннель от HA. Кроме того, судя по сообщениям, основные поставщики маршрутизаторов уже имеют модели, адаптированные под технологии Mobile IP. Проблема сохранения активных связей при перемещении пользователей назревает уже давно. По мере увеличения количества мобильных пользователей она будет становиться более остро. Вероятнее всего, ни о какой глобальной мобильности в настоящее время не может быть и речи. К сожалению, текущие реализации протоколов и операционных систем просто не предусматривают этого. Наверное, мобильность будет доступна только тем, кто использует для выхода в Интернет GPRS-модем. Вероятно, ситуацию может изменить вмешательство в IP-стек, ведь только в этой ситуации будут работать системы защиты информации вроде VPN. Однако разработки проекта TMIP и других вполне пригодны для использования на отдельно взятом предприятии и могут служить реальной заменой табличкам, извещающим об окончании зоны действия той или иной точки доступа. Поход за «настоящей мобильностью» еще только начинается!
Ссылки: 1. Linux Mobile IP: http://gunpowder.stanford.edu/mip/index.html. 2. Mosquito Net Mobile IP: http://mosquitonet.stanford.edu/ software/mip.html. 3. Dynamics Mobile IP: http://dynamics.sourceforge.net. 4. Проект SOWN (the Southampton Open Wireless Network: http://www.sown.org.uk/index.php/TransparentMobility? SESSID=6710e661be4e106c64143950e3dc04ea. 5. «An implementation of Mobile IP under Linux»: http:// www.hpl.hp.com/personal/Jean_Tourrilhes/MobileIP/ index.html. 6. Сингапурский университет: http://mip.ee.nus.sg. 7. «IP Routing for Wireless/Mobile Hosts (mobileip)»: http:// www.ietf.org/html.charters/mobileip-charter.html. 8. Monarch – MObile Networking ARCHitectures: http:// www.monarch.cs.rice.edu. 9. Реализация под Windows проект FOKUS (http:// www.mobile-ip.de/home.html). 10. Birdstep Intelligent Mobile IP: http://www.birdstep.com. 11. Mobile Networking Through Mobile IP: http://www.computer.org/ internet/v2n1/perkins.htm. 12. Secure Mobile Networking Project: http://www.cs.pdx.edu/ research/SMN. 13. Mobile IP Security page: http://www.ir.bbn.com/projects/ moips/moips-index.html. 14. RFC 3344 «IP Mobility Support for IPv4»: http://www.ietf.org/ rfc/rfc3344.txt. 15. Mobility Support in IPv6: ftp://ftp.ietf.org/internet-drafts/draftietf-mobileip-ipv6-07.txt. 16. «Firewall Traversal for Mobile IP: Guidelines for Firewalls and Mobile IP Entities»: ftp://ftp.ietf.org/internet-drafts/draftietf-mobileip-firewall-trav-00.txt и «Reverse Tunneling for Mobile I»: ftp://ftp.ietf.org/internet-drafts/draft-ietf-mobileiptunnel-reverse-04.txt.
49
администрирование
САГА О БИЛЛИНГЕ, ИЛИ СЧИТАЕМ ТРАФИК НА FreeBSD (ng_ipacct + Perl+ MySQL) ЧАСТЬ 1
ВЛАДИМИР ЧИЖИКОВ Рано или поздно перед каждым системным администратором встает вопрос подсчета интернет-трафика. И тут уже не важны причины – проверить ли провайдера или проконтролировать, какой объем трафика израсходовал подключенный пользователь, и выставить счет. Конечно, систем биллинга сейчас много. И найти их в Интернете не проблема, если задаться целью это сделать. Но многие хорошие и гибкие системы учета трафика, как правило, дороги или имеют достаточно сложный интерфейс, а некоторые затрудняют использование тех же squid или oops. В общем, из этой ситуации мне виделось два выхода – либо писать что-то свое, либо переделывать существующее. Исходя из соображений, что в компании уже создан корпоративный сервер статистики с единой системой авторизации и прочего, у меня не возникало особого желания прикручивать, например, тот же NetAms к нему, хотя последний и не лишен ряда достоинств и преимуществ. Да и многим организациям, которым предоставлен доступ в Интернет нашей компанией, не нужно то море статистики, которое выдает система биллинга. В итоге решение создать свою систему учета перевесило все остальное. Необходимо было просто посчитать, сколько трафика прошло через интерфейс. Так все начиналось. Развилось это в большой набор скриптов, которые не только считали объем трафика, но и определяли, локальный он или нет, какова доля локального трафика от общего объема, а также позволяли просмотреть все задействованные порты и протоколы, и сколько именно трафика пришлось на каждый из них. Что ж, скрипты это хорошо, но без самого главного, сердца всей этой системы, программы ng_ipacct, автором которой является Роман Палагин, ничего бы и не было. Эта программа, если так можно выразиться, является вариацией на тему ipacctd. Ipacctd работает с ipfw, а вот ng_ipacct уже с NETGRAPH, плюс она работает как модуль ядра. Почему именно ng_ipacct, а не просто ipacctd? NETGRAPH имеет ряд преимуществ. Наверняка многие замечали, как отличается объем трафика, который считаешь при помощи ipfw, и тот, который прислал провайдер со счетом. Объясняется все достаточно просто, ipfw отрабатывает не все пакеты, поступившие в bpf – пакетный фильтр системы. NETGRAPH выступает в данном слу-
50
чае как промежуточное звено, как маленькое кольцо, через которое проходят пакеты, считаются и перенаправляются дальше. Одно из его преимуществ – он работает на уровне ядра, используя минимум времени процессора и памяти. Тонкости работы и его возможности описаны в статье «Все о NETGRAPH» Арчи Коббса (перевод статьи на русский язык можно посмотреть на http://www.opennet.ru/docs/RUS/ netgraph_freebsd/index.html). Мы же разберем, как установить ng_ipacct и сам NETGRAPH. Что ж, приступим. Перед тем, как делать какие-либо шаги, скажу, что все это протестировано на FreeBSD 5.2.1-RELEASE-p10, 5.3RELEASE-p4, 4.10-RELEASE-p3, 4.11-RELEASE. Стоит обратить внимание, что с переходом на верссию 5.3 и выше потребуется пересборка ng_ipacct. Также пересобрать его потребуется и при каждой новой компиляции ядра (на 5-й ветке). Таким образом, исходные данные есть. Возьмемся за netgraph. Загружать в память его можно, используя два метода: запускать нужные модули при старте либо вкомпилировать сразу же в ядро. Мне предпочтителен последний вариант. Для этого нужно просто перекомпилировать ядро с его поддержкой. Делается все достаточно просто. Рассмотрим на примере для FreeBSD-4.10. Первым делом идем в /usr/src/sys/i386/conf/ и смотрим LINT-файл: # cd /usr/src/sys/i386/conf/ # less LINT ............. options NETGRAPH #netgraph(4) system options NETGRAPH_ASYNC options NETGRAPH_BPF options NETGRAPH_CISCO options NETGRAPH_ECHO options NETGRAPH_ETHER options NETGRAPH_FRAME_RELAY options NETGRAPH_HOLE options NETGRAPH_IFACE options NETGRAPH_KSOCKET options NETGRAPH_L2TP options NETGRAPH_LMI # MPPC compression requires proprietary files (not included) #options NETGRAPH_MPPC_COMPRESSION options NETGRAPH_MPPC_ENCRYPTION options NETGRAPH_ONE2MANY options NETGRAPH_PPP
администрирование options options options options options options options options .............
NETGRAPH_PPPOE NETGRAPH_PPTPGRE NETGRAPH_RFC1490 NETGRAPH_SOCKET NETGRAPH_TEE NETGRAPH_TTY NETGRAPH_UI NETGRAPH_VJC
То есть опций достаточно много и есть из чего выбрать. Для избежания проблем с разного рода устройствами можно их все включить в наше ядро, но в самом простом случае (считаем только с ethernet-устройства) нам потребуются только такие опции в ядре: options options options options
NETGRAPH NETGRAPH_ETHER NETGRAPH_SOCKET NETGRAPH_TEE
Дальнейшие наши действия заключаются в компиляции ядра. # config SKIF
– конфигурирование файла ядра, в моем случае это SKIF. Если ошибок в файле не было выявлено, то она выдаст: Don't forget to do a ``make depend'' Kernel build directory is ../../compile/SKIF
Это маленькое напоминание о том, что необходимо сделать make depend, и где это сделать. # cd ../../compile/SKIF && make depend ↵ && make && make install && make clean && rehash
– полный список команд, необходимый для того, чтобы перейти и скомпилировать наше ядро. Достаточно удобный, если никаких ошибок не ожидается, но если возникнут, то выяснить, на каком этапе они произошли, будет проблематично. Посему команды лучше выполнять по отдельности. После всех этих манипуляций перезагрузим сервер. # shutdown -r now
После перезагрузки мы получаем чистое ядро с поддержкой NETGRAPH. Что ж, часть работы выполнена. Устанавливаем ng_ipacct. Первым делом смотрим порты, имеющиеся в системе. Там присутствует только ipacct: # cd /usr/ports/ # make search key=ipacct Port: ipacctd-1.46 Path: /usr/ports/net-mgmt/ipacctd Info: IP accounting using divert socket Maint: skv@FreeBSD.org B-deps: R-deps:
Сам же ng_ipacct можно найти здесь: ftp://ftp.wuppy.net.ru/ pub/FreeBSD/local/kernel/ng_ipacct. На сервере присутствуют версии как для четвертой, так и для пятой ветки FreeBSD. Они неидентичны, так реализация NETGRAPH в этих версиях FreeBSD заметно отличает-
№2, февраль 2005
ся. Основное отличие – синхронизация. В RELENG_4 она осуществляется через уровни прерываний, о которых можно почитать в man 9 spl. Весь код netgraph должен выполняться на уровне splnet. Все граничные ноды, осуществляющие связь между NETGRAPH и другой подсистемой, например, ng_ether, переходят в уровень splnet, перед тем как отправить данные в граф. Если это невозможно, то данные ставятся в очередь и позже раздаются в нужной последовательности. Любые внешние вызовы, которые работают с netgraph, тоже должны первым делом вызывать splnet(). Таким образом, в одну единицу времени может существовать только один контекст выполнения NETGRAPH, и конфликтовать ему не с кем. В RELENG_5 ядро многонитевое (multithreads), и синхронизация netgraph осуществляется с помощью мьютексов (блокировок, используемых для реализации гарантированной исключительности) и атомарных операций. Ноды передают друг другу объекты (items) различных типов: данные (mbufs), сообщения (ng_mesg), ссылки на функции. У объекта есть атрибут – reader или writer. Нода может одновременно обрабатывать сколько угодно reader items или только одну writer item. По умолчанию объекты с данными – readers, а все остальные writers. Однако это можно указать как на уровне конкретных объектов, так и на уровне хуков (hooks). Важным является то, что в момент, когда выполняется код внутри ноды, тред не держит ни одного мьютекса, что позволяет граничным нодам вызывать методы других подсистем, избегая LOR (Lock order reversal – блокирования устанавливаемых изменений). То есть это грозит нам как минимум тем, что один и тот же ng_ipacct не будет работать на разных ветках FreeBSD. Что ж, скачиваем и распаковываем. # tar xfvz ng_ipacct-20040109.tar.gz # cd ng_ipacct/ # make && make install && make clean && rehash
Ничего особо сложного здесь нет, и программа без особых проблем проинсталлируется. В принципе это все, что было необходимо для установки ng_ipacct. В комплекте к ней идут четыре скрипта, которые объясняют, как запустить и остановить программу для подсчета трафика. Готовый скрипт для запуска и остановки: ng_ipacct_init.sh, он находится в распакованной папке ng_ipacct/script. Слегка подкорректировав, его можно смело поместить в /usr/local/ etc/rc.d/. Все, что нужно в нем прописать, это: ! Прослушиваемые интерфейсы INTERFACES=«ed0» – здесь это будет ed0. Для того чтобы указать более одного интерфейса, нужно перечислить их через запятую. ! VERBOSE=1 – уровень расширенного вывода статистики, по умолчанию в скрипте 1, которая выведет нам дополнительно, кроме IP-адреса источника и назначения количества пакетов и байт, еще и порты и протоколы, которые использовались. Стоит обратить внимание, что названия протоколов, если указан расширенный вывод (VERBOSE=1), будут отображены в числовом, а не буквенном виде. Что обозначает каждый номер, можно посмотреть в /etc/protocols/.
51
администрирование ! THRESHOLD=50000 – количество записей, которые бу-
! База должна разделять трафик за текущий и предыду-
дут храниться программой в памяти. На этот параметр стоит обратить особое внимание, так как неправильно подобранный размер threshold может привести к потери части данных или даже к панике ядра. Это возможно по той причине, что ng_ipacct работает на уровне ядра и ей не будет доступна полностью вся память, имеющаяся на машине, а только малая часть, зарезервированная непосредственно под ядро. В результате переполнения памяти, выделенной системе на ядро, может произойти паника со всеми вытекающими последствиями, в лучшем случае остановка сервера и потеря записей относительно трафика, прошедшего через него. Поэтому если у вас менее 128 Мб памяти, стоит себя ограничить 4000-5000 записями и чаще снимать статистику, чтобы не потерять нужные данные.
щий месяцы самостоятельно и иметь возможность предоставить пользователю отчет за каждый из них, чтобы таблицы бессмысленно не росли. Гораздо проще обработать одну маленькую за месяц, чем одну большую за год с выборкой за месяц. Просмотр статистики за предыдущие месяцы может быть необходим для отчета перед начальством или выставления счета клиенту, если такой имеется. В случае недоступности MySQL-сервера необходимо хранить полученные данные локально до тех пор, пока не будет устранена причина недоступности сервера базы данных. После чего данные автоматически должны быть перенесены в базу при следующем сеансе. Единый конфигурационный файл с удобным и интуитивно понятным содержанием. Графический или веб-интерфейс для удобного отображения статистики. Неплохо было бы, чтобы система, где необходимо, отличала локальный трафик от внешнего.
!
! !
Для снятия статистики в ng_ipacct необходимо проделать следующее: передать данные в checkpoint (контрольную точку), вывести статистику при помощи show из контрольной точки и очистить контрольную точку. Вот так это делается для интерфейса rl0: # ipacctctl rl0_ip_acct:rl0 checkpoint # ipacctctl rl0_ip_acct:rl0 show # ipacctctl rl0_ip_acct:rl0 clear
После show вы увидите все пакеты, которые проходили через интерфейс. Статистика выводится в достаточно удобном CISCO-формате: ip_èñòî÷íèêà port_èñòî÷íèêà ip_íàçíà÷åíèÿ port_íàçíà÷åíèÿ ïðîòîêîë ïàêåòîâ áàéò
Обычный режим имеет несколько другой формат вывода: ip_èñòî÷íèêà ip_íàçíà÷åíèÿ ïàêåòîâ áàéò
Стоит отметить, что имеется проблема с кодировками в man ipacctctl, просмотреть его удастся разве что в браузере. Но это легко вылечить: # zcat /usr/share/man/man8/ipacctctl.8.gz ↵ | nroff -man | gzip > /usr/share/man/cat8/ipacctctl.8.gz
Если вас интересует исключительно возможность поднять ng_ipacct, то на этом можно остановиться. Мы же проследуем дальше, ибо этого мне было мало. Мне требовалось, чтобы все данные хранились в базе MySQL для каждого хоста и интерфейса, разнесенные по дате и времени. Вот теперь опишем основные требования, которые были предъявлены биллингу (системе учета трафика – кому как больше нравится). ! Система должна хранить данные не только поинтерфейсно, но и по хостам, чтобы быстро разделить трафик между разными хостами/роутерами, с которых считывается статистика. При этом количество интерфейсов различно и их наименование может совпадать (куда ни глянь, почти везде есть rl0 или fxp0 или ed0).
52
!
В принципе этот список можно продолжить, приведенные же требования являются ключевыми. Итак, создадим, исходя из этого, наш конфигурационный файл. Все свои скрипты и программы я размещаю в папки, расположенные в /usr/local/script. В дальнейшем я буду отталкиваться именно от такого расположения папки, если у вас путь будет отличен от моего, внесите необходимые коррективы. Создаем рабочую папку со скриптами: # mkdir -p /usr/local/script/ng_stat # chown skif:wheel /usr/local/script/ng_stat
Смена владельца выполняется с целью защитить систему, в случае если наши скромные потуги в области программирования окажутся небезопасны. По крайней мере никто не увидит, что написано внутри скрипта, а значит, ломать его будет труднее. # mkdir /usr/local/script/ng_stat/etc # mkdir /usr/local/script/ng_stat/bin
Этим мы создали папки, где будут лежать наши конфигурационные и исполняемые файлы. Что ж, создадим конфигурационный файл и внесем первые параметры. По мере продвижения мы будем дополнять его нужными параметрами. # cd /usr/local/script/ng_stat/etc
Здесь мы создадим файл настройки ng_stat.conf и внесем следующие строки. # Èìÿ ñåðâåðà, ãäå íàõîäèòñÿ áàçà äàííûõ ñòàòèñòèêè server_db = freebsd # Èìÿ áàçû äàííûõ, ãäå áóäåò ñîõðàíÿòüñÿ ñòàòèñòèêà db_name = ng_stat # Èìÿ ïîëüçîâàòåëÿ äëÿ äîñòóïà ê áàçå db_user = nguser # Ïàðîëü äëÿ äîñòóïà ê áàçå db_pass = rfn.if # Èìÿ õîñòà, ñ êîòîðîãî ñíèìàåòñÿ ñòàòèñòèêà listen_host = freebsd2
администрирование # Èìåíà èíòåðôåéñîâ, êîòîðûå ïðîñëóøèâàþòñÿ íà êîìïüþòåðå. # Óêàçûâàòü ÷åðåç çàïÿòóþ listen_interfaces = rl0
Думаю пояснений к строкам приведенного конфигурационного файла не нужно. Вначале откажемся от поставляемого в комплекте с ng_ipacct скрипта для его старта и остановки. Лучше напишем свой: # cd /usr/local/script/ng_stat/bin # touch ng_stat_start.pl
Данный скрипт будет служить нам скелетом для последующих. Итак, первое, что мы сделаем, это объявим основной набор переменных: #!/usr/bin/perl -w ######################### # Ñïèñîê îñíîâíûõ ïåðåìåííûõ ######################### my $serverdb = "test"; my $dbname = "test"; my $dbuser = "test"; my $dbpass = "test"; my $table_auth = "test"; my $table_proto = "test"; my $listen_host = "test"; my @listen_interf;
Все переменные созвучны описанным в конфигурационном файле и являются глобальными для него. Внеся заранее значение «test» в них, мы избежали проблемы получить в самом неподходящем месте undef. Но обратите внимание, что прослушиваемые интерфейсы обозначены не переменной, а массивом. Сделано это потому, что интерфейсов может быть несколько, а не один. Почему были внесены такие непонятные значения переменных? Объясняется все достаточно просто. Во-первых, сюда можно внести значения реальных данных по умолчанию, которые будут считываться. Во-вторых, если на этапе отладки будут проблемы, изменив значения, вы сможете выяснить, с какой переменной у вас непорядок и где. open (CONFIG, "/usr/local/script/ng_stat/etc/ng_stat.conf"); while (<CONFIG>) { } close (CONFIG);
Этими строками открывается конфигурационный файл и при помощи while полностью считывается и закрывается. Обратите внимание, что в данном случае используется полное указание пути к файлу в явном виде, а впоследствии будем указывать его неявно, через переменные. Что ж, первое, что нам нужно сделать, это разобрать строки, которые поочередно считывает while до тех пор, пока не дойдет до конца файла. Но среди полезной информации конфигурационный файл несет в себе комментарии. От них нужно избавиться. Для этого в Perl имеется мощнейшие инструменты поиска в строках/словах. Один из них – конструкция вида m/шаблон/ограничитель, им и воспользуемся, условившись, что комментарием будет символ #: $comment = '#'; if(/^$comment/) { print "Êîììåíòàðèé\n";
№2, февраль 2005
} else { # ðàçáîð ñòðîê, íå îãðàíè÷åííûõ êîììåíòàðèåì }
Объясним конструкцию if ... else: если в начале строки присутствует символ комментария, то на экран будет выведено сообщение «Комментарий», в противном случае строка пойдет по else. Вывод сообщений о наличии комментариев нам необходим только на этапе отладки. Кстати, можете проверить, как скрипт работает, впоследствии он будет закомментирован. Но этого мало, необходимо разобрать и полезную строку. ($param,$arg) = split("=",$_); chomp $param; chomp$arg; $param =~ s/\s//g; $arg =~ s/\s//g;
Для разбора использовалась функция split, которая на основе разделителя «=», заданного еще в конфигурационном файле, разбила все полезные строки на две части: параметр и аргумент. Чтобы избавиться от пробельных символов, используется оператор замены s/шаблон/замена/ ограничитель.Так как необходимо избавиться от пробельных символов, а не поменять их на что-то другое, мы не используем параметр «замена», оставляя его пустым. Модификатор \s означает любой пробельный символ. Перед этим были убраны из обоих переменных символы перевода строки при помощи chomp. Если в строке присутствуют не только символы пробела, но и табуляции или если их несколько, то придется прибегнуть к следующей конструкции: $param =~ s/[\s\t]+//g; $arg =~ s/[\s\t]+//g;
Теперь необходимо присвоить каждой объявленной переменной ее истинное значение, находящееся в конфигурационном файле. В этом нам поможет конструкция следующего вида: if ($param eq "server_db"){ $serverdb = $arg; }
Объясним. Если левая часть полученной из файла строки соответствует server_db (смотрим наш конфигурационный файл), то правая часть присвоится соответственной переменной. Но у нас же есть еще несколько значений параметра в одной из строк. Их мы должны, предварительно разобрав, занести в массив. #!/usr/bin/perl -w use DBI; use POSIX ":sys_wait_h"; ######################### # Ñïèñîê îñíîâíûõ ïåðåìåííûõ ######################### my $serverdb = "test"; my $dbname = "test"; my $dbuser = "test"; my $dbpass = "test"; my $table_auth = "test"; my $table_proto = "test";
53
администрирование my my my my my
$listen_host = "test"; @listen_interf; $iface_set = "no"; @ng_modules; $ng_modules_def = "netgraph,ng_ether,ng_socket, ↵ ng_tee,ng_ipacct"; my$threshold = 5000; ######################### # ×èòàåì êîíôèãóðàöèîííûé ôàéë. ######################### open (CONFIG, "/usr/local/script/ng_stat/etc/ng_stat.conf"); while (<CONFIG>) { $comment = '#'; if(/^$comment/) { # print "Êîìåíòàðèé\n"; } else { ($param,$arg) = split("=",$_); chomp $param; chomp $arg; my $razdel = ""; $param =~ s/[\s\t]+/$razdel/g; $arg =~ s/[\s\t]+/$razdel/g; if ($param eq "server_db"){ $serverdb = $arg; } if ($param eq "db_name"){ $dbname = $arg; } if ($param eq "db_user") { $dbuser = $arg; } if ($param eq "db_pass") { $dbpass = $arg; } if ($param eq "table_auth") { $table_auth = $arg; } if ($param eq "table_protocols") { $table_proto = $arg; } if ($param eq "listen_host") { $listen_host = $arg; } if ($param eq "listen_interfaces") { my $coma = ','; if (defined $arg) { $iface_set = "ok"; if ($arg ne ""){ if ($arg =~ m/$coma/ ) { @listen_interf=split($coma,$arg); } else { @listen_interf = $arg; } } } } if ($param eq "ng_modules") { my $coma = ','; if ($arg =~ m/$coma/ ){ @ng_modules = split($coma,$arg); } else { }
@ng_modules = split ($coma,$ng_modules_def);
} } } close (CONFIG); if (!defined $listen_interf[0]) { print "Óñòàíîâèòå, ïîæàëóéñòà, â ðåæèì ïðîñëóøèâàíèÿ ↵ õîòÿ áû îäèí èíòåðôåéñ.\n"; } else { &check_kld_modules; &listening; }
54
Как видите, мы считали все параметры, и в случае если интерфейс по какой-либо причине установлен не будет, то на экран будет выдано сообщение об этом. А если все нормально, то в массив будут внесены необходимые имена интерфейсов (например, rl0, rl1,rl2,fxp0) и после проверки массива @listen_interf на наличие в нем не пустых значений будут выполнены подпрограммы: &check_kld_ modules и &listening. Первая проверяет, какие из обязательных модулей загружены. При необходимости будет проведена их загрузка. Вторая включает режим прослушивания интерфейсов. Рассмотрим первую. subcheck_kld_modules { my @modules; my $pid; my $ng_module_cfg; my $chk_ng_file = "/tmp/ng_file"; my $check_ng = 'kldstat -v | grep ng'; $check_ng = "$check_ng";# " > $chk_ng_file"; my $check_netgraph = 'kldstat -v | grep netgraph'; $check_netgraph = "$check_netgraph";#" >> $chk_ng_file"; # $pid = fork; @modules =split ("\n", `$check_ng && $check_netgraph`); my $mod; if (defined $modules[0]) { foreach my $modules (@modules) { $modules=~ s/\d+//g; if ($modules =~ s/.ko//g) { # } else { $modules =~ s/[\s\t]+//g; $mod = "$mod $modules "; } } chop $mod; foreach my $ng_modules (@ng_modules) { if ($mod=~m/$ng_modules/g){ # print "$mod ñîäåðæèò $ng_modules\n"; } else { my ($pid,$kid); $pid = fork; if (defined $pid) { if ($pid == 0){ print ↵ "Çàãðóçêà íåîáõîäèìîãî ìîäóëÿ ",$ng_modules,"\n"; exec "/sbin/kldload ↵ $ng_modules > /dev/null 2>&1" or die ↵ "Îøèáêà çàãðóçêè ìîäóëÿ $ng_modules !\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû ↵ íåâîçìîæíî.\n Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ↵ ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ ↵ â ñèñòåìå íåò èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n ↵ Îøèáêà!" and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí ↵ íå áëîêèðóþùèé âûçîâ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; } }
} else { foreach my $ng_modules (@ng_modules) { my ($pid,$kid);
администрирование $pid = fork; if (defined $pid) { if ($pid == 0){ print "Çàãðóçêà íåîáõîäèìîãî ↵ ìîäóëÿ ",$ng_modules,"\n"; exec "/sbin/kldload ↵ $ng_modules > /dev/null 2>&1" or die "Îøèáêà ↵ çàãðóçêè ìîäóëÿ $ng_modules !\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû ↵ íåâîçìîæíî.\n Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ↵ ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå ↵ íåò èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé ↵ âûçîâ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; } } }
Сначала объявляются действующие только в переделах этого модуля массивы и переменные. В нашем случае это @modules, куда будут заноситься все модули netgraph, присутствующие в ядре или загруженные на данный момент. $check_netgraph и $check_ng переменные, в которых записаны команды, проверки загруженных модулей ядра. Команда эта достаточно проста и имеет вид: # kldstat -v ............... 234 dummynet 235 if_gif 236 ipfw 237 if_loop 238 ng_async 239 ng_bpf 4 1 0xc272d000 4000 Containsmodules: Name 246 ng_ipacct ....................
В данном случае информация о том, из какого файла был загружен модуль (linux.ko, logo_server.ko или что-то другое), не нужна. Также не нужны ID загруженных модулей. Для их удаления используется все тот же m//: $modules=~ s/\d+//g;
«\d» означает любой цифровой символ. После удаления ID проверяется, что присутствует в выводе – информация о том, из какого модуля загрузился файл или сам модуль. На файл указывает присутствие расширения «.ko» в строке. А потому все полученные строки, где присутствует «.ko», подлежат удалению. В листинге вы видите, что на месте совпадения if с «.ko» стоит комментарий. Если хотите, можете провести синтаксический разбор и вывести на экран имя того модуля, который был загружен вручную. Нам же интересно только то, что находится после else. Вывод kldstat имеет пять колонок (Id,Refs,Address,Size,Name). Все они разделены между собой пробельными символами. К тому же первые колонки пусты и заполнены именно этими самыми пробельными символами. Так как нам необходима только одна колонка Name, то необходимо удалить все пробельные символы. Для удобства дальнейших манипуляций мы заносим отобранные элементы в одну строку: if ($modules =~ s/.ko//g) { # } else { $modules =~ s/[\s\t]+//g; $mod = "$mod $modules "; }
Здесь необходимо остановиться и вернуться немного назад. Только что мы получили список загруженных модулей. Хорошо, но этого мало. Необходимо еще знать, какие нам нужны для работы, и если их нет – загрузить.
ng_ipacct.ko
Как вы можете заметить, вывод немаленький, поэтому пришлось его урезать. Нам нужны не все модули, а только те, которые имеют отношение к NETGRAPH. Этим и займутся переменные, когда их используют как значения для оператора exec. Чтобы получить список загруженных модулей, используется split и обратные кавычки, в качестве разделителя выступает символ переноса строки:
# Çàãðóæàåìûå ìîäóëè NETGRAPH, íåîáõîäèìûå äëÿ èíòåðôåéñîâ, # êîòîðûå áóäåò îáñëóæèâàòü ïðîãðàììà. # Ïî óìîë÷àíèþ çàãðóæàþòñÿ ñëåäóþùèå ìîäóëè: netgraph, # ng_ether,ng_socket,ng_tee,ng_ipacct ng_modules = netgraph,ng_ether,ng_socket,ng_tee,ng_ipacct
И соответственно считать их. Для этого нужно также ввести еще несколько основных переменных. Точнее, массив и переменную. my@ng_modules; my$ng_modules_def = "netgraph,ng_ether,ng_socket, ↵ ng_tee,ng_ipacct";
@modules =split ("\n", `$check_ng && $check_netgraph`);
Данные из последней переменной будут загружены в массив в случае отсутствия в конфигурационном файле хотя бы одного модуля netgraph. Считывание необходимых к загрузке модулей нужно добавить к open ... close(CONFIG):
Дальше пойдем по проверенному пути, а именно – выясним, имеются ли в массиве хоть какие-то данные. Если полученный массив не пустой, то мы выполним проверку, какие модули нам необходимо подгрузить для работы.
if ($param eq "ng_modules") { my $coma = ','; if ($arg =~ m/$coma/ ){ @ng_modules = split($coma,$arg); } else { @ng_modules = split ($coma,$ng_modules_def); }
№2, февраль 2005
55
администрирование Теперь у нас есть необходимый список модулей. Можем проверить, нужно что-то загружать или нет. Для этого необходимо проделать достаточно простую операцию. Проверить наличие значения каждого элемента полученного массива @ng_modules в строке $mod. Основываясь на том, есть или нет такое значение массива в строке, и будет производиться загрузка соответствующего модуля. foreach my $ng_modules (@ng_modules) { if ($mod=~m/$ng_modules/g){ # print "$mod ñîäåðæèò $ng_modules\n"; } else { my $pid; $pid = fork; if (defined $pid) { if ($pid == 0){ print "Çàãðóçêà íåîáõîäèìîãî ↵ ìîäóëÿ ",$ng_modules,"\n"; exec "/sbin/kldload ↵ $ng_modules > /dev/null 2>&1" or die "Îøèáêà ↵ çàãðóçêè ìîäóëÿ $ng_modules !\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû ↵ íåâîçìîæíî.\n Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ↵ ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ ↵ â ñèñòåìå íåò èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n ↵ Îøèáêà!" and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí ↵ íå áëîêèðóþùèé âûçîâ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; undef $pid; } }
В этом примере выполнение внешней команды – загрузка модуля – производится посредством exec. Особенностью exec является то, что по выполнении этой функции производится останов программы и выход из процесса. Но так как присутствует необходимость загрузить не один модуль, то логичнее было бы использовать system. Но, по соображениям безопасности, это произвести нельзя. Решением этой проблемы является разделение программы на различные процессы. Для этого уже существует функция fork. Немного поясню, как она работает. По выполнении функции существующий процесс разделяется на два: родительский и дочерний. Сама функция возвращает два значения в случае удачного выполнения: номер ID для дочернего процесса в родительский и 0 в дочерний. Почему ноль, а не номер полученного процесса? Потому что дочерний процесс может в любой момент времени получить ID родительского, вызвав функцию getppid. Родительский же процесс получает ID дочернего потому, что способов узнать из всего объема процессов дочерний у него просто нет, или я его не знаю. Возможен также и третий вариант. Когда fork возвращает неопределенное значение undef. Это означает, что по какой-либо причине разделение на процессы не произошло.
56
Так же обратите внимание на такие строчки кода: do {
$kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ ↵ â ñèñòåìå íåò èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n ↵ Îøèáêà!" and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí ↵ íå áëîêèðóþùèé âûçîâ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid;
На данном этапе они не так актуальны. Здесь не сильно важна последовательность загрузки модулей и прочих процессов, но ниже, когда будет происходить запуск сбора статистики на интерфейсах, придерживание последовательности выполняемых команд будет первостепенным. Что же делает этот блок? Ключевым к нему является всего одна функция waitpid. Она аналогична в некоторой степени функции wait, но ждет завершения определенного дочернего процесса с указанным ID, в данном случае полученного при fork $pid. Функция возвращает одно из трех возможных значений: ! PID завершенного процесса. ! 0 – если флаги, что указаны, задают не блокирующий вызов, а процесс еще не завершен. ! -1 – если дочерних процессов нет. Родительский процесс на время выполнения waitpid как бы засыпает, ожидая результата. В итоге комбинацией fork + exec + waitpid мы добиваемся жесткой очередности выполнения как всех команд, так и сопутствующего программного кода. Вот эти особенности и использовались для запуска внешних программ. Но проверить и загрузить нужные модули мало. Нужно еще начать собирать статистику на интерфейсе. Для этого считываем параметр threshold из конфигурационного файла. Следовательно, необходимо создать глобальную переменную: my $threshold = 5000;
Мы ее создали и присвоили значение 5000 строк по умолчанию. В конфигурационном файле можно задать и другое значение. # Îòíåñèòåñü âíèìàòåëüíî ê âûáîðó ýòîãî ïàðàìåòðà. # Îí óêàçûâàåò, ñêîëüêî çàïèñåé áóäåò õðàíèòüñÿ â áóôåðå # Ïî óìîë÷àíèþ çíà÷åíèå ðàâíî 5000, íî åñëè ó âàñ ìåíüøå # 128 Ìá ïàìÿòè – óìåíüøèòå åãî. Çíà÷åíèå âî ìíîãîì # çàâèñèò îò òîãî, êàêàÿ ïîëîñà ïðîïóñêàíèÿ íà âàøåì êàíàëå, # è îò òîãî íàñêîëüêî îí çàãðóæåí. Äëÿ 128 Êá è 64 Ìá # ìîæíî áóäåò ñìåëî óñòàíîâèòü è 10000 çàïèñåé, ïðè óñëîâèè # ñíÿòèÿ còàòèñòèêè õîòÿ áû ðàç â 15-20 ìèíóò. Äëÿ êàíàëà # â 2 Ìáèòà ýòîãî âðåìåíè áóäåò óæå ñëèøêîì ìíîãî threshold = 5000
Теперь считаем параметр из файла: if ($param eq "threshold") { $threshold = $arg; }
Все. Основные переменные заданы, конфигурационный файл на данном этапе заполнен полностью.
администрирование Что ж, приступим к запуску. Я сразу приведу полный листинг модуля, а потом лишь поясню некоторые моменты, ибо сам по себе модуль достаточно прост, в нем только команды fork и exec. sub listening{ my $pid; $ngctl = "/usr/sbin/ngctl"; $ipacctctl = "/usr/local/sbin/ipacctctl"; while (@listen_interf){ $interface = shift @listen_interf; #/usr/sbin/ngctl mkpeer ${IFACE}: tee lower right $mkpeer = "$ngctl mkpeer $interface\: tee lower right"; $pid = fork; if (defined $pid) { ($pid == 0){ print "Ñîçäàíèå è ïîäêëþ÷åíèå íîâîãî ↵ NETGRAPH-óçëà ê óæå ñóùåñòâóþùåìó:\n $mkpeer\n"; exec "$mkpeer" or die "Îøèáêà ↵ ñîçäàíèÿ íîâîãî óçëà NETGRAPH!\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; #/usr/sbin/ngctl connect ${IFACE}: lower upper left $connect = "$ngctl connect $interface\: lower upper left"; $pid = fork; if (defined $pid) { if ($pid == 0){ print "Ñîåäèíåíèå äâóõ NETGRAPH-óçëîâ ↵ íà èíòåðôåéñå:\n$connect\n"; exec "$connect" or die "Îøèáêà ñîåäèíåíèÿ ↵ äâóõ NETGRAPH-óçëîâ!\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; #/usr/sbin/ngctl name ${IFACE}:lower ${IFACE}_acct_tee $name = "$ngctl name $interface\:lower ↵ $interface\_acct_tee "; $pid = fork; if (defined $pid) { ($pid == 0){ print "Ïðèñâîåíèå èìåíè ↵ ñîçäàííîìó óçëó:\n$name\n"; exec "$name" or die "Îøèáêà íà ýòàïå ↵
№2, февраль 2005
ïðèñâîåíèÿ èìåíè ñîçäàííîìó óçëó!\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; #/usr/sbin/ngctl mkpeer ${IFACE}_acct_tee: ipacct ↵ right2left ${IFACE}_in $mkpeer = "$ngctl mkpeer $interface\_acct_tee: ↵ ipacct right2left $interface\_in"; $pid = fork; if (defined $pid) { ($pid == 0){ print "Ñîçäàíèå è ïîäêëþ÷åíèå íîâîãî ↵ NETGRAPH-óçëà ê óæå ñóùåñòâóþùåìó:\n $mkpeer\n"; exec "$mkpeer" or die "Îøèáêà ↵ ñîçäàíèÿ íîâîãî óçëà NETGRAPH!\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; #/usr/sbin/ngctl name ${IFACE}_acct_tee:right2left ↵ ${IFACE}_ip_acct $name = "$ngctl name ↵ $interface\_acct_tee:right2left $interface\_ip_acct"; $pid = fork; if (defined $pid) { ($pid == 0){ print "Ïðèñâîåíèå èìåíè ↵ ñîçäàííîìó óçëó:\n$name\n"; exec "$name" or die "Îøèáêà íà ýòàïå ↵ ïðèñâîåíèÿ èìåíè ñîçäàííîìó óçëó!\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; }
57
администрирование die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; }
} until $kid=$pid; undef $pid; #/usr/sbin/ngctl connect ${IFACE}_acct_tee: ↵ ${IFACE}_ip_acct: left2right ${IFACE}_out $connect = "$ngctl connect $interface\_acct_tee: ↵ $interface\_ip_acct: left2right $interface\_out"; $pid = fork; if (defined $pid) { if ($pid == 0){ print "Ñîåäèíåíèå äâóõ NETGRAPH-óçëîâ ↵ íà èíòåðôåéñå:\n$connect\n"; exec "$connect" or die "Îøèáêà ñîåäèíåíèÿ ↵ äâóõ NETGRAPH-óçëîâ!\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; #$IPACCTCTL ${IFACE}_ip_acct:$IFACE verbose $VERBOSE $verbose = "$ipacctctl ↵ $interface\_ip_acct:$interface verbose 1"; $pid = fork; if (defined $pid) { ($pid == 0){ print "Óñòàíîâêà ðåæèìà âûâîäà ↵ èíôîðìàöèè:\n$verbose\n"; exec "$verbose" or die "Îøèáêà óñòàíîâêè ↵ ðåæèìà âûâîäà èíôîðìàöèè\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; #$IPACCTCTL ${IFACE}_ip_acct:$IFACE threshold $THRESHOLD $set_threshold = "$ipacctctl ↵ $interface\_ip_acct:$interface threshold $threshold"; $pid = fork; if (defined $pid) { if ($pid == 0){ print "Óñòàíîâêà ↵ THRESHOLD:\n$set_threshold\n"; exec "$set_threshold" or die "Îøèáêà ↵ óñòàíîâêè ïàðàìåòðà THRESHOLD\n"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n";
58
}
Первые две встречающиеся переменные – исполняемые файлы для netgraph и ng_ipacct, точнее, пути к ним. Следующим шагом является чтение из массива @listen_ interf поочередно всех занесенных туда интерфейсов и включение на них «прослушивания». При помощи mkpeer мы создаем новый узел (nodes) к уже существующему. При помощи connect соединяются узлы. При помощи name присваиваем имя узлу. Наиболее интересными является $ipacctctl $interface\_ip_ acct:$interface verbose 1 – здесь мы задаем, в каком режиме будет отображаться статистика. Нам необходим расширенный, посему устанавливаем значение 1. Должен отметить, что в man ipacctctl стоит значение on – вероятнее всего, что это ошибка, ибо такое значение не влияет на формат вывода статистики. В последнем – $ipacctctl $interface\_ip_acct:$interface threshold $threshold – мы указываем количество записей threshold. По ходу выполняется разветвление процессов, ожидание завершения дочернего, с последующим обнулением $pid, куда записывалось значение ID дочернего процесса. Здесь и всплывает важность waitpid для скрипта. Ибо выполняться все эти команды должны именно в строгой последовательности, а не как им заблагорассудится. В принципе стартовый скрипт создан. Что в итоге получилось, можно глянуть в ng_stat_start.pl (www.samag.ru/source). Сделав при помощи chmod файл исполняемым, можно пробовать его выполнить. Тут поджидает первый неприятный сюрприз. Данный скрипт выполняется с правами root. Что ж, на данном этапе можно его запустить и с такими правами. # sudo ./ng_stat_start.pl
Внимательно смотрите за выводом. Отсутствие «лишнего» говорит о том, что старт прошел без замечаний. Вот пример того, что скрипт выводит при старте: Загрузка необходимого модуля ng_ether Загрузка необходимого модуля ng_socket Загрузка необходимого модуля ng_tee
Создание и подключение нового NETGRAPH-узла к уже существующему: /usr/sbin/ngctl mkpeer fxp1: tee lower right
администрирование Соединение двух NETGRAPH-узлов на интерфейсе:
and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid;
/usr/sbin/ngctl connect fxp1: lower upper left
Присвоение имени созданному узлу: /usr/sbin/ngctl connect fxp1: lower upper left
Создание и подключение нового NETGRAPH-узла к уже существующему:
#
/usr/sbin/ngctl mkpeer fxp1_acct_tee: ipacct right2left fxp1_in
Присвоение имени созданному узлу: /usr/sbin/ngctl name fxp1_acct_tee:right2left fxp1_ip_acct
Соединение двух NETGRAPH-узлов на интерфейсе: /usr/sbin/ngctl connect fxp1_acct_tee: fxp1_ip_acct: left2right fxp1_out
Установка режима вывода информации: /usr/local/sbin/ipacctctl fxp1_ip_acct:fxp1 verbose 1
Установка THRESHOLD: /usr/local/sbin/ipacctctl fxp1_ip_acct:fxp1 threshold 7000
Для себя можете добавить что-либо, если необходима дополнительная информация при запуске. Для отработки разных этапов работы скрипта советую ввести конструкции типа print «Проверяем переменную $lin». Это поможет проконтролировать получение значений переменными в скрипте и получить своеобразный отладчик. Но в данном случае это полностью рабочий скрипт, а посему весь мусор отладки убран. Запустить мало, необходимо еще и уметь остановить. Для этого создадим похожий на ng_stat_start.pl скрипт ng_stat_stop.pl. В принципе их можно было бы объединить в один, но так проще. Итак, содержимое абсолютно идентично первому файлу, за исключением того, что отсутствуют sub и в конструкции if ... else содержится следующее: if (!defined $listen_interf[0]) { print "Óñòàíîâèòå, ïîæàëóéñòà, â ðåæèì ïðîñëóøèâàíèÿ ↵ õîòÿ áû îäèí èíòåðôåéñ.\n"; } else { foreach my $interface (@listen_interf){ #/usr/sbin/ngctl shutdown ${IFACE}_acct_tee: $shutdown = "$ngctl shutdown ↵ $interface\_acct_tee:"; my $pid; $pid = fork; if (defined $pid) { ($pid == 0){ print "Îòêëþ÷åíèå ñîçäàííûõ óçëîâ ↵ íà èíòåðôåéñå:\n$shutdown\n"; exec "$shutdown"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵
№2, февраль 2005
}
sleep 1; $shutdown = "$ngctl shutdown $interface\:"; $pid = fork; if (defined $pid) { if ($pid == 0){ print "Îòêëþ÷åíèå NETGRAPH ↵ íà èíòåðôåéñå:\n$shutdown\n"; exec "$shutdown"; exit; } } else { print "Ôàòàëüíàÿ îøèáêà ↵ âåòâëåíèÿ!\n.................\n"; die "Ðàçäåëåíèå íà ïðîöåññû íåâîçìîæíî.\n ↵ Ïðèíóäèòåëüíûé âûõîä èç äî÷åðíåãî ïðîöåññà: $!\n"; } do { $kid = waitpid $pid,0; if ($kid == -1) { print "Äî÷åðíèõ ïðîöåññîâ â ñèñòåìå íåò ↵ èëè ñèñòåìà íå ïîääåðæèâàåò èõ.\n Îøèáêà!" ↵ and die "Âûõîä!\n"; } elsif ($kid == 0) { print "Çàäàí íå áëîêèðóþùèé âûçîâ ↵ è ïðîöåññ åùå íå çàâåðøåí!\n"; } } until $kid=$pid; undef $pid; }
Здесь производится чтение из массива всех нужных интерфейсов и последовательно выполняется для них отключение. Посмотреть, как он выглядит полностью, можно в ng_ stat_stop.pl (www.samag.ru/source). Теперь у нас есть скрипты для старта и остановки ng_ ipacct. Но и этого мало. Нужно сделать запуск и останов системы при включении и отключении сервера. А посему напишем простенький скриптик на shell: #!/bin/sh case "$1" in start) /usr/local/script/ng_stat/bin/ng_stat_start.pl echo"ng_stat" ;; stop) /usr/local/script/ng_stat/bin/ng_stat_stop.pl ;; *) echo "" echo "Usage: `basename $0` { start | stop }" "" ;; esac
Сохраним его под названием ng_stat.sh. Когда система учета будет готова, достаточно лишь скопировать скрипт в /usr/local/etc/rc.d/, чтобы ng_stat запустился при старте или отключился при выключении питания. Половина дела, самая важная его часть, готова. Система стартовала там, где надо и с нужными параметрами. Осталось за малым – получить статистику. В следующей части статьи будет рассмотрено, как получить статистику от ng_ipacct, и передать ее в MySQL для последующего хранения и использования, а также приведен пример того, как получить наши результаты обратно.
59
администрирование
АВТОМАТИЧЕСКАЯ УСТАНОВКА ОС И СОПУТСТВУЮЩЕГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
ИВАН КОРОБКО В этой статье речь пойдет о создании загрузочного диска, с помощью которого можно в автоматическом режиме установить Microsoft Windows XP в описанной заранее конфигурации (Windows + Service Pack 1 + MUI + HotFixes ), и некоторых программ, а именно Office 2003, антивирусной программы и т. д. Данный диск может быть использован как для подготовки рабочей станции к функционированию в сети (после завершения процесса установки ОС и ПО останется только изменить имя рабочей станции и ввести ее в домен), так и для изолированного компьютера.
Автоматическая установка ОС и программного обеспечения Рассмотрим вариант установки Windows + MUI как более сложный. Если читатель предпочитает не использовать MUI, то он может пропустить раздел, который ему посвящен. В типовой набор программ входят следующие: проводник (Total Commander, Far, Dos Navigator и др.); антивирус (DrWeb, Norton Antivirus и др.), архиватор (WinZip, WinRar, WinAce и др.); программа, позволяющая просматривать множество графических форматов (ACDsee, InfraView ), Microsoft Office, программа, предназначенная для записи компакт-дисков (Nero, Easy CD Creator и др.), просмотра DVD-дисков (Power DVD, AsusDVD), кодек для просмотра видеоматериалов в формате MPEG4 (DivX).
Создание файла ответов для установки Windows Существует три типа автоматической установки Windows. В каждом из трех случаев используется индивидуальный файл ответа: Òàáëèöà 1
Файл ответа представляет собой текстовый файл со следующей структурой: [ðàçäåëM] ïàðàìåòð1M=çíà÷åíèå1M ïàðàìåòð2M=çíà÷åíèå2M ………………………. ïàðàìåòðNM=çíà÷åíèåNM
Файлы «Winnt.sif», «Sysprep.inf» должны находиться в каталоге i386, в то время как файлы «*.txt» могут располагаться в любом месте. Чтобы при установке Windows воспользоваться файлом ответа с расширением txt, необходимо выполнить следующую команду: «winnt[32] /u:<ôàéë îòâåòîâ>»;
Подготовка файловой структуры будущего диска Существует несколько способов автоматической установки Microsoft Windows на рабочую станцию. Самым распространенным из них является использование файла ответов во время инсталляции ОС. Автоматизация процесса установки ОС позволит специалистам системной поддержки: ! минимизировать влияние человеческого фактора (доверить специалисту системной поддержки решать вопросы, касающиеся введения регистрационного номера ОС, управления разделами жесткого диска); ! сократить время на установку ОС.
60
Оптимальным решением является создание базового шаблона с помощью Setup Manager, который затем надо доработать вручную. Утилита Setup Manager запускается с помощью файла «Setupmgr.exe», который находится в архиве «Deploy.cab» на диске с дистрибутивом Windows в папке «\Support\Tools». Документация по файлам ответов находится в том же архиве – файл «setupmrg.chm». Пример файла ответов см. на www.samag.ru/source. Для получения полной информации о параметрах командной строки «WinNT.exe» и «WinNT32.exe» используйте параметр /?, например, «winnt.exe /?».
администрирование Файловая структура дистрибутивного диска Windows Расположение папки $OEM$ зависит от используемого файла ответов. Если установка Windows запускается автоматически с компакт-диска, т.е. в качестве файла ответов используется «WinNT.sif», то папка $OEM$, содержащая все дополнительно устанавливаемые компоненты, должна располагаться в корневом каталоге загрузочного диска (см. рис. 1). Во всех остальных случаях папка $OEM$ должна находиться в каталоге «i386», а файлом ответов является «Unattended.txt» (см. рис. 2).
и папок осуществляется до запуска графического режима, поэтому все копируемые файлы и папки должны удовлетворять стандарту 8.3. На втором этапе, на основе информации из файла ответов осуществляется запуск скопированных приложений с указанием явных путей к файлам, инициализирующих процесс установки. На завершающем этапе установки последнего приложения происходит удаление каталогов, созданных во время установки.
Подготовка дистрибутива Windows В этом разделе расскажем о том, как создать дистрибутив Windows XP с интегрированным ServicePack, быстрыми исправлениями (hotfix) и интегрировать MUI для Windows. На первом этапе осуществляется интегрирование SP в дистрибутив. Данная задача решается успешно, если следовать инструкциям, предлагаемым Microsoft, а вот с HotFix и MUI возникают проблемы, если следовать документации, поэтому предлагается альтернативный способ решения этой задачи.
Ðèñóíîê 1
Ðèñóíîê 2
! Root – корневая папка загрузочного компакт-диска, содержащего дистрибутив ОС; ! i386 – в этой папке находятся установочные файлы Windows; ! $OEM$ – папка, содержимое которой используется для установки дополнительного программного обеспечения. Опишем файловую структуру папки $OEM$ (см. таблицу 2). Замечание: в разделе [Unattended] файла ответов параметру OEMPreinstall должно быть присвоено значение Yes: OEMPreinstall=Yes. Òàáëèöà 2
Интеграция пакета исправлений Service Pack в Windows Процесс интеграции Service Pack в Windows осуществляется следующим образом: на жестком диске создается две папки. В одну из них копируется содержимое компакт-диска, содержащего дистрибутив Microsoft Windows XP Professional (C:\BOOTCD\WINDOWS), в другую – дистрибутив Microsoft Service Pack (C:\BOOTCD\SERVICEPACK), который представляет собой архив. Его необходимо распаковать, выполнив следующую команду: C:\BOOTCD\SERVICEPACK\XP-SP1.EXE /U /X:C:\BOOTCD\SERVICEPACK
Интегрировать Service Pack в дистрибутив Windows можно таким образом: C:\BOOTCD\SERVICEPACK\UPDATE.EXE /S:C:\BOOTCD\WINDOWS
Замечание: в папке Windows должен находиться дистрибутив Windows, а не содержимое папки «i386».
Установка пакетов быстрых исправлений в автоматическом режиме Принципы именования hotfix Быстрые исправления (hotfix) представляют собой самораскрывающиеся архивы с расширением EXE. Имена hotfix присваиваются в соответствии со следующим шаблоном: Q######_XXX_YYY_ZZZ _LLL.exe
Механизм инсталляции быстрых исправлений На первом этапе осуществляется копирование инсталляционных файлов различных приложений, в т.ч. и hotfix, в соответствующий подкаталог $OEM$. Копирование файлов
№2, февраль 2005
где: ! Q###### – номер соответствующей статьи Microsoft Knowledge Base с описанием решения обнаруженной проблемы; ! XXX – сокращенное название операционной системы, для которой предназначено данное исправление;
61
администрирование ! YYY – номер пакета исправления (Service Pack), к которому относится hotfix; ! ZZZ – платформа рабочей станции.
мо выбрать из списка исправлений только необходимые из них, нужную языковую версию и версию ОС.
Для Windows XP в соответствии с данным шаблоном, имена имеют следующий вид: Q######_WXP_SP#_x86_EN.exe или Q######_WXP_SP#_x86_RU.exe.
Копирование hotfix из Интернета Копирование быстрых исправлений осуществляется с сайта компании Microsoft: http://v4.windowsupdate.microsoft.com\ catalog (см. рис. 3, 4).
Ðèñóíîê 3
После того как вход на сайт Windows Update успешно выполнен, для получения возможности копирования исправлений на жесткий диск вашей рабочей станции необходи-
Ðèñóíîê 5
62
Ðèñóíîê 4
Необходимо отметить, что не рекомендуется пользоваться общим адресом каталога Windows Update http://windows
администрирование update.microsoft.com\catalog, поскольку, если вы зайдете на него из операционной системы Windows XP, то будет выполнен редирект не на четвертую версию Windows Update, а на пятую: http://v5.windowsupdate.microsoft.com\catalog. К сожалению, в пятой версии еще не реализована возможность копирования hotfix на жесткий диск: их можно только установить.
Интеграция hotfix в дистрибутив Windows Дистрибутивы рекомендуется расположить в папке «$OEM$\ C\Install\Folder» (C – буква, MICS – Install\Folder), где «Folder» – соответствующее назначению название папки. Например, для hotfix рекомендуется использовать следующий путь: «$OEM$\C\Install\HotFix». В файле ответов необходимо сделать следующие изменения. Для того чтобы установка Windows скопировала данные из папки $OEM$ в соответствующие места, необходимо cделать следующие изменения в разделе [Unattended]: [Unattended] OEMPreinstall=Yes
Управление hotfix осуществляется следующим образом:
[GuiRunOnce] " C\Install\HotFix\Q######.exe /n /q /z" "%WinDir%\System32\Cmd.exe /c RmDir C\Install /s /q"
Для простоты восприятия в данном примере приведены только две строки: в первой строке осуществляется запуск процесса установки управления в скрытом режиме. Во второй – удаление каталога «C:\Install» с жесткого диска. Инициализация процесса инсталляции приложений осуществляется после завершения процесса установки операционной системы.
Ключи, используемые при установке hotfix
! /F – закрыть все открытые приложения после установки исправления перед перезагрузкой ОС;
! /N – не создавать файлы отката (backup files) для восстановления системы;
! /Z – не перезапускать рабочую станцию после внедрения hotfix;
! /Q – включить скрытый режим установки исправлений; ! /U – использовать автоматический режим установки. Вывод сообщений только о возникающих критических ошибках;
Ðèñóíîê 6
№2, февраль 2005
63
администрирование ! /L – вывести список установленных исправлений на ра-
Òàáëèöà 3
бочей станции. Данная информация также находится в реестре по пути: HKLM\Software\Microsoft\CurrentVersion\ HotFix\{SP\}Q######; HKLM\Software\Microsoft\Current Version\Uninstall\Q######. Для установки hotfix рекомендуется использовать следующую команду: Q######.exe /q /n /z
Установка MUI в автоматическом режиме Загруженный с сайта Microsoft MUI необходимо распаковать, используя тот же метод, что и при распаковке дистрибутива SP: C:\BOOTCD\MUI\MUI_RUS.EXE /U /X:C:\BOOTCD\MUI
Замечание: версия MUI зависит от номера Service Pack. Будьте внимательны при копировании SP с сайта: не ошибитесь версией. Затем необходимо скопировать его в папку $$\Install\MUI например, а в файле ответов сделать следующие изменения: [GuiRunOnce] "c:\MUIINST\MUISETUP.EXE /i 0419 /d 0419 /r /s" "%windir%\system32\cmd.exe /c rmdir c:\MUIINST /s/q"
Ключ /i указывает на кодовую страницу выбранного языка (0419 – русский), а ключ /d назначает язык интерфейса Windows по умолчанию. Полный список ключей, а также значений языков можно найти в файле muisetup.hlp, входящем в комплект поставки MUI.
Интеграция драйверов в дистрибутив Драйвера устройств, не вошедших в стандартный набор, можно интегрировать в дистрибутив. Для этого необходимо скопировать дистрибутивы драйверов в папку $OEM$\$1\ PnPDrivers, а соответствующие им INF-файлы в каталог $OEM$\$1\INF. В папку PnPDrivers можно сделать соответствующие подпапки, например, Sound, Video. В файле ответов в разделе [Unattended] необходимо прописать путь к этим драйверам. Каталоги разделяются точкой с запятой, пробелы в строке не допускаются. Рекомендуется также отключить проверку драйверов на «подпись», задав соответствующее значение параметру DriverSigningPolicy. Обязательным условием, как отмечалось ранее, является значение Yes параметра OEMPreinstall: [Unattended] OemPnPDriversPath=drivers\video;drivers\sound DriverSigningPolicy=Ignore OEMPreinstall=Yes
Автоматическая установка стандартных приложений Предлагается автоматизировать процесс установки следующих программ, образующих типовой набор:
64
Автоматическая установка антивируса: Norton Antivirus Помня о том, что рабочая станция будет функционировать в сети, необходимо устанавливать клиентскую часть Norton Antivirus, находящуюся на сервере, где он установлен по пути \\Server\VPHOME\CLT-INST\WIN32, и сконфигурировать его для работы в сети. В этой папке находятся дистрибутив и конфигурационный файл: GRC.DAT, представляющий собой текстовый файл. При установке программа ищет его сама в текущем каталоге и читает из него данные, если же его нет, то антивирус устанавливается и работает в автономном режиме. Команда автоматической установки NAV CE выглядит следующим образом: c:\Install\NAV\setup.exe /qn
Автоматическая установка навигатора Сегодня одним из популярных навигаторов является Total Commander. Его дистрибутив представляет собой самораскрывающийся ZIP-архив, в котором содержится файл ответов – «install.inf». Распакуйте этот архив с помощью WinZip и скорректируйте файл «install.inf». Исправленный пример конфигурационного файла с соответствующими комментариями находится на www.samag.ru/source. Запуск автоматической установки осуществляется с помощью исполнения файла install.exe.
Автоматическая установка архиватора: WinRar 3.x Для реализации автоматической установки WinRar в качестве параметра необходимо указать ключ /s. В этом случае WinRar будет ассоциирован со всеми типами архивов, а также будет создана соответствующая группа в меню Пуск: C:\Install\Wrar\Wrar340ru.exe /s
Если же использовать ключ /silent, то пользователю будет предложено выбрать, какие из вышеперечисленных функций ему понадобятся. Регистрационный файл wrar.reg, созданный на основе данных из реестра, необходимо экспортировать в него после установки программы с помощью команды: regedit /s c:\Install\WRar\wrar.reg
Автоматическая установка программы записи CD/DVD: Nero Burning Rom 6.3.0.x Полноценный режим работы Nero доступен только в том случае, если известен серийный номер продукта. Существует два способа регистрации программы.
администрирование Способ 1 Указать серийный номер в ходе инсталляции программы в качестве одного из параметров командной строки: c:\Install\Nero\Nero551054.exe /silent /noreboot ↵ /sn=xxxx-xxxx-xxxx-xxxx-xxxx-xxxx /write_sn
где: ! /silent обеспечивает автоматический режим установки программы; ! /noreboot не перезагружает рабочую станцию после установки программы; ! /sn=xxxx-xxxx-xxxx-xxxx-xxxx-xxxx – вместо xxxx-… указывается серийный номер продукта. Используется вместе с ключом /write_sn.
! Автоматическая установка с настройками по умолчанию. Процесс установки инициализируется следующей командой: Pro11.msi /qb
или Setup.exe /qb
! Автоматическая установка с использованием файла ответов с расширением MST, который создается с помощью соответствующего мастера из набора Resource Kit For Microsoft Office 2003. Установка осуществляется с помощью:
Способ 2 Cоздать REG-файл, который содержит данные о регистрации программного продукта. Для экспорта этих данных в реестр необходимо использовать следующую команду: Regedit /S c:\Install\Nero\nero.reg
Тогда команда, обеспечивающая автоматическую установку Nero, будет выглядеть следующим образом: c:\Install\Nero\Nero551054.exe /silent /noreboot
Файл Nero.Reg выглядит так: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Ahead\Nero - Burning Rom\Info] "User"="UserName" "Company"="CompanyName" "Serial6"=" xxxx-xxxx-xxxx-xxxx-xxxx-xxxx "
Автоматическая установка Adobe Acrobat Reader 6 Дистрибутив Adobe Acrobat Reader 6 представляет собой самораскрывающийся архив. Необходимо запустить установку Adobe Reader и дождаться, пока FEAD Optimizer распакует все файлы и будет предложено начать установку программы. В %WinDir%\Cache находится содержимое этого архива в папке «Adobe Reader 6». Все файлы из нее необходимо скопировать $OEM$\С\Install\Areader6. Файл «Adobe Reader 6.0.1.msi» необходимо переименовать в файл, удовлетворяющий формату имен 8.3, например Areader6.msi. Команда запуска автоматической установки будет выглядеть следующим образом: C:\Install\Areader\Areader.msi /qb
Автоматическая установка Microsoft Office 2003 и необходимых дополнений Способы автоматической установки Office В настоящее время существует как минимум два способа автоматической установки Microsoft Office.
№2, февраль 2005
Pro11.msi Transforms=FileName.mst /qb
или Setup.exe Transforms=FileName.mst /qb
Рассмотрим второй способ установки более подробно. Замечание: чтобы во время автоматической установки не запрашивался серийный номер, необходимо создать административную установку, которая хранит в себе такие важные параметры установки, как серийный номер Office, название организации, на которую зарегистрирован продукт.
Подготовка дистрибутива Office Создание административной установки Создание административной установки необходимо для реализации автоматической установки Microsoft Office и возможности интегрировать пакеты исправлений (SP) и обновления (updates) в дистрибутив. По своей сути создание административной установки является установкой Microsoft Office в специальном режиме, который инициализируется командой «Setup /a». В процессе установки Office будут запрошены серийный номер и название организации, на которую должен быть зарегистрирован продукт. Впоследствии при установке Office с этого дистрибутива серийный номер не будет запрашиваться.
Интеграция пакета исправлений и обновлений в Office Перед интеграцией в Office дистрибутив пакета исправлений, скопированный с сайта Microsoft, необходимо распаковать, выполнив команду: Office2003SP1-KB842532-fullfile-enu.exe ↵ /q /c /t:Ñ:\Office2003\SP1
где С:\Office2003\SP1 – путь, куда будет распаковано содержимое архива. Интеграция в дистрибутив осуществляется с помощью 2 команд:
65
администрирование MsiExec /p C:\Office2003\SP1\MainSp1f.msp /a ↵ C:\Office2003\Office\Pro11.msi ShortFileNames=True /qb MsiExec /p C:\Office2003\SP1\Owc11Sp1ff.msp /a ↵ C:\Office2003\Office\OWC11.msi ShortFileNames=True /qb
где C:\Office2003\SP1\ – путь к распакованной версии SP, а C:\Office2003\Office\ – путь к дистрибутиву Microsoft Office 2003. Интеграция обновлений осуществляется по такому же сценарию: сначала необходимо скопировать обновления с сайта Microsoft на жесткий диск, затем распаковать их и интегрировать в дистрибутив Office 2003. Стоит отметить, что имена файлов обновлений строятся по следующему принципу: XXX_KB######_YYY_ZZZ.exe
! XXX – версия офиса, для которой предназначено обновление;
! KB###### – номер статьи Microsoft Knowledge Base, в
www.via.com.tw). Для этого необходимо пометить в дистрибутив оба набора и устанавливать их драйвера как приложения, т.е. использовать второй вариант. Таким образом, при установке драйверов один из пакетов выдаст сообщение об ошибке, которое не будет отображено на экран, т.к. установка ведется в скрытом режиме, и установка данного пакета будет завершена. Нужный пакет драйверов будет установлен успешно.
Создание загрузочного диска В настоящее время практически любая программа, предназначенная для записи дисков, поддерживает возможность создания загрузочных дисков. Однако перед записью данных на диск рекомендуется протестировать дистрибутив, а именно создать его образ и установить с этого образа систему, используя VMWare Workstation (http://www.vmware.com). На этом процесс формирования дистрибутивного диска завершен. Осталось его протестировать и записать на диск.
которой приведен список исправлений;
! YYY – тип версии; ! ZZZ – языковая принадлежность. Итак, для Office 2003 файлы обновлений строятся по шаблону: Office2003_KB######_FullFile_Enu.exe
Создание файла-образа диска Для компиляции ISO-файлов на основе предоставляемых в папке данных существует множество различных программ. Рекомендуется использовать программу «CDIMAGE GUI». Это маленькая программа (1,4 Мб) с графическим интерфейсом, не требующая установки и обладающая необходимым функционалом:
Создание файла ответов MST Файл ответа для Microsoft Office можно создать с помощью мастера Custom Installation Wizard, входящего в набор Resource Kit соответствующей версии. Запустив мастер Custom Installation Wizard, необходимо создать новый MST-файл. Команда для установки Office в автоматическом режиме будет следующей: C:\Install\Office\setup.exe ↵ transforms= C:\Install\Office\answer.mst /qb- /noreboot
INF Update от Intel и Via В том случае если используются материнские платы на микросхемах компании Intel или Via, необходимо установить соответствующие наборы драйверов. Можно предложить как минимум два способа интеграции этих продуктов в дистрибутив. Первый – рассматривать дистрибутив как набор INFфайлов и, пользуясь этим фактом, устанавливать эту программу, как совокупность драйверов, интегрировав соответствующие файлы в папку PnPDrivers и INF. Второй вариант – рассматривать дистрибутив как программу и для автоматической установки использовать следующую команду: «setup.exe /s». Оба этих способа равнозначны, следует лишь отметить, что, скопировав дистрибутив из Интернета, его необходимо разархивировать с помощью программы WinZip. Замечание: можно сделать универсальный дистрибутив, содержащий набор драйверов для материнских плат на основе микросхем от Intel (http://www.intel.com) и Via (http://
66
Ðèñóíîê 7
Создавая файл-образ с помощью данной программы, необходимо указать метку тома будущего диска (см. таблицу 4), убрать ограничение размера файла в 650 Мб, включить скрытые файлы и каталоги в дистрибутив, указать файл, содержащий загрузчик диска. Загрузчик диска можно скачать из Интернета или указать ISO-образ-файл лицензионного диска, содержащего загрузчик. Òàáëèöà 4
В корневом каталоге дистрибутива, в зависимости от версии ОС и встроенного SP, должны присутствовать оп-
администрирование ределенные файлы, список которых приведен в таблицах 5 и 6. Загрузчик диска ОС, необходимый для автозапуска установки с компакт-диска, представляет собой файл с расширением BIN. Его можно скопировать из Интернета или сделать файл с расширением IMG с помощью любой соответствующей программы, например WinImage. Òàáëèöà 5
тановки ОС с файла-образа будут ОС и типовые программы.
Запись файла-образа на диск Сегодня почти любая программа, предназначенная для записи компакт-дисков, поддерживает возможность записи дисков из файлов-образов. Опишем процесс записи ISOфайла на диск на примере программы Ahead Nero. После запуска Nero необходимо выйти из запускаемого по умолчанию мастера и открыть ISO-файл: «File-Open File…», затем запустить процесс записи: «Recorder-Burn Compilation…». В появившемся диалоговом окне необходимо убедиться, что метод записи диска выбран «disk-at-once» и отмечена опция «Finalize CD».
Òàáëèöà 6
Тестирование ISO-файла Для тестирования ISO-файла рекомендуется использовать VMWare. Эта программа предназначена для эмуляции различных операционных систем на персональном компьютере. После установки VMWare необходимо запустить Virtual Machine Wizard: «File ® New ® New Virtual Machine…». Результатом работы мастера должен быть файл настроек, с помощью которого осуществляется эмуляция ОС Windows XP Professional. Затем необходимо изменить свойства загрузки Virtual Machine: «Edit ® Virtual Machine Settings…». Во вкладке «Hardware» необходимо сделать активной вкладку «CD-ROM» и в качестве компакт-диска указать путь к файлу, содержащему образ будущего диска.
Ðèñóíîê 9
Замечание: если вы уверены в том, что вы не допустили ошибок в файле ответов и формировании структуры подкаталогов дистрибутива Windows, то вы можете сразу приступить к записи дистрибутива на диск без создания и тестирования файла-образа. При этом необходимо учесть, что надо правильно указать метку тома диска; указать путь к файлу, содержащему загрузчик; отключить имитацию дискеты и выбрать количество загрузочных секторов – 4.
Ðèñóíîê 10 Ðèñóíîê 8
Теперь можно приступить к тестированию ISO-файла, запустив эмуляцию Windows XP Pro: «Power → Power On (<CTRL+B>)». После запуска Virtual Machine будет осуществляться попытка установить ОС с одного из доступных носителей, включая CD-ROM. Результатом автоматической ус-
№2, февраль 2005
Заключение Созданный диск предназначен для автоматической установки Windows и типового набора программ как для рабочей станции, которая будет впоследствии введена в домен, так и для домашнего компьютера.
67
программирование
ТЕХНИКА ОПТИМИЗАЦИИ ПОД LINUX …вычислительная машина – новый могущественный инструмент. Однако немногие имеют представление об источнике этого могущества. Дж Вейценбаум Дж «Возможности вычислительных машин и человеческий разум. От суждений к вычислениям»
Что находится под черной крышкой оптимизирующего компилятора? Чем один из них отличается от другого? Правда ли, что Intel C++ намного круче GCC, а сам GCC бьет любой Windows-компилятор? Хотите узнать, как помочь оптимизатору сгенерировать более эффективный код? Сегодня мы займемся исследованием двух наиболее популярных Linuxкомпиляторов: GCC 3.3.4 и Intel C++ 8.0, а конкретно – сравнением мощности их оптимизаторов. Для полноты картины в этот список включен Microsoft Visual C++ 6.0 – один из лучших Windows-компиляторов.
КРИС КАСПЕРСКИ Общие соображения по оптимизации Качество оптимизирующих компиляторов обычно оценивают по результатом комплексных тестов (мультимедийных, «общесистемных» или математических). Что именно оптимизируется и как – остается неясным. Основной «интеллект» оптимизаторов сосредоточен в высокоуровневом препроцессоре – своеобразном «ликвидаторе» наиболее очевидных программистских ошибок. Чем качественнее исходный код, тем хуже он поддается оптимизации. Только ведь… над качественным кодом работать надо! Много знать и ожесточенно думать, ломая карандаши или вгрызаясь в клавиатуру. Кому-то это в радость, а кто-то предпочитает писать кое-как. Все равно, мол, компилятор соптимизирует! Желание перебросить часть работы на транслятор – вполне естественное и нормальное (для творчества больше времени останется), но при этом нужно заранее знать, что именно он оптимизирует, а что только пытается. Но как это можно узнать? На фоне полнейшей терминологической неразберихи, когда одни и те же приемы оптимизации в каждом случае называются по-разному, прячась за ничего не говорящими штампами типа «copy propagation» (размножение копий) или «redundancy elimination» (устранение избыточности), требуется очень качественная документация на компилятор, но она – увы – обычно ограничивается тупым перечислением оптимизирующих ключей с краткой пометкой за что каждый из них отвечает. Какие копии размножает компилятор и с какой целью? Какую избыточность он устраняет и зачем? Не является ли размножение внесением избыточности, которую самому же оптимизатору и приходится удалять?! Взять хотя бы документацию на компилятор Intel C++ 7.0/ 8.0. Это просто перечень ключей командной строки, разбавленный словесным мусором, в котором нет никакой конкретики. Скачайте для сравнения документацию на компилятор фирмы Hewlett-Packard: http://docs.hp.com/en/B605696002/B6056-96002.pdf. Доходчивое описание архитектуры процессора, советы по кодированию, тактика и стратегия оптимизирующей трансляции на конкретных примерах. Настоящая библия программиста!
68
Остальные компиляторы оптимизируют примерно таким же образом, поэтому эта библия вполне приемлема и для них. «Эффективность оптимизации» из абстрактных цифр превращается в серию простых тестов, каждый из которых можно прогнать через транслятор и потрогать руками. Дизассемблирование откомпилированных файлов позволяет однозначно установить – справился оптимизатор со своей задачей или нет. В данной статье сравниваются два наиболее популярных Linux-компилятора: GCC 3.3.4 (стабильная версия, проверенная временем, входящая в большинство современных дистрибутивов) и Intel C++ 8.0 (далее по тексту icl), позиционируемый как самый эффективный компилятор всех временен и народов, 30-дневная ознакомительная, а также бесплатная для некоммерческого применения полнофункциональная Linux-версия которого лежит на ftp-сервере Intel: ftp:/ /download.intel.com/software/products/compilers/downloads/ l_cc_p_8.0.055.tar.gz. Некоммерческую лицензию можно оформить прямо на сайте компании (без лицензии компилятор работать не будет). Для полноты картины в этот список включен древний, но все еще используемый Windowsкомпилятор Microsoft Visual C++ 6.0, для краткости обозначаемый, как msvc. Если не оговорено обратное, приведенные примеры должны компилироваться со следующими ключами: -O3 -march= pentium3 (gcc), -O3 -mcpu=pentium4 (icl) и /Ox (msvc). Разница между архитектурами объясняется тем, что GCC 3.3.4 еще не поддерживает режима оптимизации для Pentium 4, а Intel C++8.0 не имеет специального ключа для Pentium III, в результате чего между ними возникает некоторая «нестыковка». Однако на результаты наших экспериментов она никак не влияет, поскольку никакие специфичные для Pentium 4 возможности здесь не используются.
Константы Cвертка констант Вычисление констант на стадии компиляции (оно же, «размножение» или «свертка» констант, constant elimination/ folding/propagation, или сокращенно CP) – популярный при-
программирование ем оптимизации, избавляющий программиста от необходимости постоянно иметь калькулятор под рукой. Все константные выражения (как целочисленные, так и вещественные) обрабатываются транслятором самостоятельно и в откомпилированный код не попадают. Рассмотрим следующий пример: a = 2 ∗ 2; b = 4 ∗ с / 2; Компиляторы, поддерживающие такую стратегию оптимизации, превратят его в: a = 4; b = 2 ∗ с; При этом возникает целый букет побочных эффектов: (4∗a/2) не эквивалентно (2∗a), потому что при (4∗a) может наступить переполнение, а при (a/2) уже может и не наступить! Скажите, что это наигранный пример? А вот и нет! При работе с битовыми масками – это норма. Выражение (k∗a/n) часто используется для сброса старших битов переменной с их последующем сдвигом вправо на заданное количество позиций. Но после того как над программой поработает оптимизатор, этот эффект теряется! С вещественной арифметикой еще хуже. Значение double (4∗a/2) вполне может и не совпасть с double (2∗a) даже безо всякого переполнения! В double начинает играть значение конечная точность. Если (int)4/(int)2 это точно 2, то double(4)/double(2) – это 2.0 плюс-минус что-то очень маленькое. В научном мире это правило часто формулируют так: «результат вычисления с double не может равняться 0» (поэтому все конструкции типа if(double_function(x)==0) надо заменять на if(fabs(double_function(x))<EPS), иначе не работает – результат вычислений будет зависеть от версии компилятора и ключей оптимизации). Если оптимизатор превращает (4∗a/2) просто в (2∗a) (выкидывает одну операцию), он теряет точность не два раза, а один. В нормально написанной программе (не оперирующей с очень малыми или очень большими числами) это несущественно, т.к. программист изначально проектирует код так, чтобы от этих погрешностей не зависеть. Видимо, разработчики компиляторов на эту категорию людей и ориентируются, а тот, кто умножает 10(-308) на 10(+307), желая получить именно 0.1, и, таким образом, завязывается на погрешность, – сам себе и виноват! Улучшенная свертка констант, в англоязычной литературе именуемая «advanced constant folding/propagation», заменяет все константные переменные их непосредственным значением, например: a = 2; b = 2 ∗ a; c = b - a; превращается в c = 2. Свертку констант в полной мере поддерживают все три рассматриваемых компилятора: msvc, icl и gcc.
Объединение констант Несколько строковых констант с идентичным содержимым для экономии памяти могут быть объедены («merge») в одну. То же самое относится и к вещественным значениям. Целочисленные 32-битные константы объединять невыгодно, поскольку ссылка на константу занимает больше места1, чем машинная команда с копией константы внутри, да и выборка константы из памяти быстродействию, в общемто, не способствует. Покажем технику объединения на следующем примере: 1
Ëèñòèíã 1. Íå îïòèìèçèðîâàííûé êàíäèäàò íà îáúåäèíåíèå êîíñòàíò printf("hello,world!\n"); // îäíà ñòðîêîâàÿ êîíñòàíòà printf("hello,world!\n"); // äðóãàÿ êîíñòàíòà, èäåíòè÷íàÿ ïåðâîé printf("i say hello, world!\n"); // êîíñòàíòà ñ èäåíòè÷íîé ïîäñòðîêîé
Компилятор msvc «честно» генерирует все три строковые константы, а gcc и icl только две из них: «hello,world!\n» и «i say hello, world!\n». На то, что первая строка совпадает с концом второй, ни один из компиляторов не обратил внимания. Приходится напрягаться и писать: Ëèñòèíã 2. ×àñòè÷íî îïòèìèçèðîâàííûé âàðèàíò char s[]="i say hello, world!\n"; // ðàçìåùàåì ñòðîêó â ïàìÿòè printf(&s[6]); // âûâîäèì ïîäñòðîêó printf(&s[6]); // âûâîäèì ïîäñòðîêó printf(s); // âûâîäèì âñþ ñòðîêó öåëèêîì
Но ведь позицию подстроки в строке придется определять вручную, тупым подсчетом букв! Или использовать макросы, определяя длину строки с помощью sizeof, только… это же сколько кодить надо! Ëèñòèíã 3. Ïîëíîñòüþ îïòèìèçèðîâàííûé âàðèàíò char *s1 = "I say hello,world!\n"; char *s2 = s1+sizeof("I say ")-1; //"hello, world!\n" printf(s2); printf(s2); printf(s1);
В отличие от ситуации с объедением двух полностью тождественных строк, практическая ценность которой сомнительна, задача объединения подстроки со строкой встречается довольно часто. Код на листинге 3 предпочтительнее, чем «частично оптимизированный вариант» (листинг 2), поскольку в нем отсутствует двукратное вычисление адресов (&s[6]), которое компилятор может и не догадаться свернуть, а учет смещения на sizeof(«I say »)-1 == 6 байт производится еще на этапе трансляции.
Константная подстановка в условиях Переменные, использующиеся для «принятия решения» о ветвлении, в каждой из веток имеют вполне предсказуемые значения, зачастую являющиеся константами. Код вида: if (a == 4) b = 2 ∗ a; может быть преобразован в: if (a == 4) b = 8, что компиляторы msvc/gcc и осуществляют, избавляясь от лишней операции умножения, а вот icl этого сделать не догадывается.
Константная подстановка в функциях Если все аргументы функции – константы и она не имеет никаких побочных эффектов типа модификации глобальных/ статических переменных (и не зависит от их значения), результат выполнения функции также будет константой. Компиляторы в подавляющем большинстве случаев об этом не
В 32-разрядном режиме ссылка занимает 32 бита плюс от одного до шести байтов на поля адресации (один байт «съедает» ModR/M, задающее способ адресации и выбирающее регистры, другой байт приходится на факультативное поле SIB – Scale-Index-Base, используемое для масштабирования, и еще четыре байта отводятся под непосредственное смещение константы в памяти), поэтому ссылки становятся выгодными только начиная с 64/80-битных констант.
№2, февраль 2005
69
программирование догадываются. Поле зрения оптимизатора ограничено телом функции. «Сквозная» подстановка аргументов («свертка функций») осуществляется лишь в случае встраиваемых (inline) функций или глобального режима оптимизации. Компилятор icl имеет специальный набор ключей -ip/-ipo, форсирующий глобальную оптимизацию в текущем файле и всех исходных текстах соответственно, что позволяет ему выполнять константную подстановку в следующем коде:
лиотеку), иначе после компоновки ваш код будет переполнен ненужными приложению функциями. Помещайте в файл только «родственные» функции, которые всегда используются в паре и по отдельности не имеют никакого смысла. Одна функция на объектный файл – это вполне нормально. Две-три еще терпимо, а вот больше – уже перебор. Присмотритесь, как устроены стандартные библиотеки языка Си, и ваши программы сразу похудеют.
Ëèñòèíã 4. Êàíäèäàò íà êîíñòàíòíóþ ïîäñòàíîâêó
Удаление неиспользуемых переменных
f1(int a, int b) { return a+b; }
Объявленные, но неиспользуемые переменные удаляются всеми современными компиляторами. Древние оптимизаторы удаляли лишь переменные, к которым не происходило ни одного обращения, сейчас же оптимизатор строит своеобразное «абстрактное дерево», и ветви, ведущие в никуда, полностью обрубаются. В приведенном ниже примере msvc, icl и gcc удаляют все три переменные – a, b и с:
f2 () { return f1(0x69, 0x96) + 0x666; }
Компилятор gcc достигает аналогичного результата лишь за счет того, что на уровне оптимизации -O3 он автоматически встраивает все мелкие функции в тело программы, в то время как msvc встраивает лишь некоторые из них. И даже если функция объявлена как «inline», оптимизатор оставляет за собой право решать: осуществлять ли встраивание в данном случае или нет.
Код и переменные
Ëèñòèíã 5. Ïðèìåð ïðîãðàììû ñ íåèñïîëüçóåìûìè ïåðåìåííûìè main(int n, char **v) { int a,b,c; a =n; b = a + 1; c = 6*b; // ïåðåìåííàÿ c íå èñïîëüçóåòñÿ, à çíà÷èò ïåðåìåííûå a è b ëèøíèå return n; }
Удаление мертвого кода
Удаление неиспользуемых выражений
«Мертвым кодом» (dead code) называется код, никогда не получающий управления и впустую транжирящий дисковое пространство и оперативную память. В простейшем случае он представляет собой условие, ложность которых очевидна еще на стадии трансляции, или код, расположенный после безусловного возврата из функции. Вот, например: if (0) n++; else n--. Это несложная задача, и с нею успешно справляются все три рассматриваемых компилятора. А вот в следующем случае бесполезность выражения «n++» уже не столь очевидна: for (a = 0; a < 6; a++) if (a < 0) n++. Условие (a < 0) всегда ложно, поскольку цикл начинается с 0 и продолжается до 6. Из всех трех рассматриваемых компиляторов это по «зубам» только icl.
Неиспользуемые выражения удаляются всеми тремя рассматриваемыми компиляторами. Например:
Удаление неиспользуемых функций Объявленные, но ни разу не вызванные функции компилятору не так-то просто удалить. Технология раздельной компиляции (один файл исходного текста – один объектный модуль) предполагает, что функция, не использующаяся в текущем модуле, вполне может вызываться из остальных. Реальный расклад выявляется лишь на стадии компоновки. Выходит, неиспользуемые функции должен удалить линкер? Но «выцарапать» мертвую функцию из объектного файла еще сложнее! Для этого компоновщику необходимо иметь мощный дизассемблер и нехилый искусственный интеллект впридачу. Компилятор icl в режиме глобальной оптимизации (ключ -ipo) отслеживает неиспользуемые функции еще на этапе трансляции и в объектный модуль они не попадают. Остальные компиляторы ничем подобным похвастать не могут. Поэтому категорически не рекомендуется держать весь проект в одном файле (особенно если вы пишите биб-
70
Ëèñòèíã 6. Ïðèìåð ïðîãðàììû ñ íåèñïîëüçóåìûìè âûðàæåíèÿìè main(int n, char** v) { int a,b; a = n+0x666; // íå èñïîëüçóåòñÿ, ïåðåêðûâàåòñÿ (2*n) b = n-0x999; // òåðÿåòñÿ ïðè âûõîäå èç ôóíêöèè a = 2*n; // åäèíñòâåííîå èñïîëüçóåìîå âûðàæåíèå return a; }
Выражение (n+0x666) не используется, поскольку перекрывается следующей операцией присвоения (2∗n). Выражение (n-0x999) теряется при выходе из функции. Следовательно, наш код эквивалентен: return (n – 0x999). Не всегда такая оптимизация проходит безболезненно. Компиляторы «забывают» о том, что некоторые вычисления имеют побочные эффекты в виде выброса исключения. Код вида: a = b/c; a = d, можно оптимизировать в том, и только в том случае, если переменная c заведомо не равна нулю. Но ни один из трех рассматриваемых компиляторов такой проверки не выполняет! Забавно, но в сокращении арифметических выражений (о которых речь еще впереди) оптимизаторы ведут себя намного более осторожно – никто из них не рискует сокращать выражение (a/a) до единицы, даже если переменная a заведомо не равна нулю! Бардак, в общем.
Удаление лишних обращений к памяти Компиляторы стремятся размещать переменные в регистрах, избегая «дорогостоящих» операций обращения к памяти. Взять хотя бы такой код: «i =*p+c; b = *p - d;». Очевидно,
программирование что второе обращение к *p лишнее и компиляторы поступают так: t =*p; i = t+c; b = t-d, при этом неявно полагается, что содержимое ячейки *p не изменяется никаким внешним кодом, в противном случае оптимизация будет носить диверсионно-разрушительный характер. Что если переменная используется для обмена данными/синхронизации нескольких потоков? Что, если какой-то драйвер возвращает через нее результат своей работы? Наконец, что если мы хотим получить исключение по обращению к странице памяти? Для усмирения оптимизатора во всех этих случаях необходимо объявлять переменную как volatile (буквально: «изменчивый», «неуловимый»), тогда при каждом обращении она будет перечитываться из памяти. Указатели – настоящий бич оптимизации. Компилятор никогда не может быть уверен, адресуют ли две переменные различные области памяти или обращаются к одной и той же ячейке памяти. Вот, например: Ëèñòèíã 7. Ïðèìåð ñ ëèøíèìè îáðàùåíèÿìè ê ïàìÿòè, îò êîòîðûõ íåëüçÿ èçáàâèòüñÿ f(int *a, int *b) { int x; x = *a + *b; // ñëîæåíèå ñîäåðæèìîãî äâóõ ÿ÷ååê *b = 0x69; // èçìåíåíèå ÿ÷åéêè *b, àäðåñ êîòîðîé íå èçâåñòåí êîìïèëÿòîðó x += *a; // íåò ãàðàíòèè, ÷òî çàïèñü â ÿ÷åéêó *b íå èçìåíèëà ÿ÷åéêó *a }
Компилятор не может разместить содержимое *a во временной переменной, поскольку, если ячейки *a и *b частично или полностью перекрываются, модификация ячейки *b приводит к неожиданному изменению ячейки *a! То же самое относится и к следующему примеру: Ëèñòèíã 8. Ïðèìåð ñ ëèøíèìè îáðàùåíèÿìè ê ïàìÿòè, îò êîòîðûõ ìîæíî èçáàâèòüñÿ âðó÷íóþ f(char *x, int *dst, int n) { int i; for (i = 0; i < n; i++) *dst += x[i]; }
Компилятор не может (не имеет права) выносить переменную dst за пределы цикла, и обращения к памяти будут происходить на каждой итерации. Чтобы этого избежать, программист должен переписать код так: Ëèñòèíã 9. Îïòèìèçèðîâàííûé âàðèàíò f(char *x, int *dst, int n) { int i,t =0; for (i=0;i<n;i++) t+=x[i]; // ñîõðàíåíèå ñóììû âî âðåìåííîé ïåðåìåííîé *dst+=t; // çàïèñü êîíå÷íîãî ðåçóëüòàòà â ïàìÿòü }
Удаление копий переменных Для экономии памяти компиляторы обычно сокращают количество используемых переменных, выполняя алгебраическое развертывание выражений и удаляя лишние копии. В англоязычной литературе за данной техникой оптимизации закреплен термин «copy propagation», суть которого поясняется в следующем примере:
№2, февраль 2005
Ëèñòèíã 10. Ïåðåìåííûå a è b – ëèøíèå main(int n, char** v) { int a,b; a = n+1; b = 1-a; // èçáàâëÿåòñÿ îò ïåðåìåííîé a: (1 – (n + 1)); return a-b; // èçáàâëÿåòñÿ îò ïåðåìåííîé b: ((n + 1) – (1 – (n + 1))); }
Очевидно, что его можно переписать, как (2∗n+1), избавившись сразу от двух переменных. Все три рассматриваемых компилятора именно так и поступают. (С технической точки зрения данный прием оптимизации является частным случаем более общего механизма алгебраического упрощения выражений и распределения регистров, который будет рассмотрен ниже.)
Размножение переменных На процессорах с конвейерной архитектурой удаление «лишних» копий порождает ложную зависимость по данным, приводящую к падению производительности и переменные приходится не только «сворачивать», но и размножать! Рассмотрим пример: Ëèñòèíã 11. Ëîæíàÿ çàâèñèìîñòü ïî äàííûì a b a c
= = = =
x a i a
+ + –
y; 1; // b çàâèñèò îò a j; 1; // ñ çàâèñèò îò a, òî÷íåå, îò åå âòîðîé «êîïèè»
Операции (x + y) и (i – j) могут быть выполнены одновременно, но чтобы сохранить результат вычислений, часть процессорных модулей вынуждена простаивать в ожидании, пока не освободится переменная a. Чтобы устранить эту зависимость, код необходимо переписать так: Ëèñòèíã 12. Ðàçìíîæåíèå ïåðåìåííîé a óñòðàíÿåò çàâèñèìîñòü ïî äàííûì a1 = x b = a1 a2 = i c = a2
+ + –
y; 1; // b çàâèñèò îò a1 j; 1; // c çàâèñèò îò a2, íî íå çàâèñèò îò a1
Простейшие зависимости по данным процессоры от Pentium Pro и выше устраняют самостоятельно. Ручное размножение переменных здесь только вредит – количество регистров общего назначения ограничено и компиляторам их катастрофически не хватает. Но это только снаружи. Внутри процессора содержится здоровый регистровый файл, автоматически «расщепляющий» регистры по мере необходимости. Сложные зависимости по данным на микроуровне уже неразрешимы и чтобы справится с ними, необходимо иметь доступ к исходному тексту программы. Компилятор icl устраняет большинство зависимостей, остальные же оставляют все как есть.
Распределение переменных по регистрам Регистров общего назначения всего семь, а чаще и того меньше. Регистр EBP используется для организации фреймов (так же называемых стековыми кадрами), регистр EAX по
71
программирование общепринятому соглашению используется для возвращения значения функции. Некоторые команды (строковые операции, умножение/деление) работают с фиксированным набором регистров, который на протяжении всей функции приходится держать «под сукном» или постоянно гонять данные от одного регистра к другому, что также не добавляет производительности. Стратегия оптимального распределения переменных по регистрам (global registers allocation) – сложная задача, которую еще предстоит решить. Пусть слово «global» не вводит вас в заблуждение. Эта глобальность сугубо локального масштаба, ограниченная одной-единственной функцией, а то и ее частью. Компиляторы стремятся помещать в регистры наиболее интенсивно используемые переменные, однако, под «интенсивностью» здесь понимается отнюдь не частота использования, а количество «упоминаний». Но ведь не все «упоминания» равнозначны! Вот, например, if (++a % 16) b++; else c++; обращение к переменной c происходит в 16 раз чаще! Статистка обращений не всегда может быть получена путем прямого анализа исходного кода программы, так что ждать помощи со стороны машины – наивно. Языки Си/Си++ поддерживают специальное ключевое слово «register», управляющее размещением переменных, однако, оно носит характер рекомендации, а не императива и все три рассматриваемых компилятора его игнорируют, предпочитая интеллекту программиста свой собственный машинный интеллект. Представляет интерес сравнить распределение переменных по регистрам в глубоко вложенных циклах, поскольку его вклад в общую производительность весьма значителен. Рассмотрим следующий пример: Ëèñòèíã 13. Ãëóáîêî âëîæåííûé öèêë ÷óâñòâèòåëåí ê êà÷åñòâó ðàñïðåäåëåíèÿ ïåðåìåííûõ ïî ðåãèñòðàì int *a, *b; main(int n, char **v) { int i,j; int sum=0; for (i = 0; i < n; i++) for (j = 0; j < n; j++) sum += sum*a[n*i + j] + sum/b[j] + x++; return sum+x; }
Компилятору msvс регистров общего назначения уже не хватило и три переменных, обрабатываемых внешним циклом, «вылетели» в стек. Компилятор icl «уложился» в 14 (!) стековых переменных, 5 (!) из которых обрабатываются во внутреннем цикле! О какой производительности после этого можно говорить?! Второе место занял gcc – из 10 стековых переменных 5 расположены во внутреннем цикле. А вы еще Microsoft ругаете… За счет чего достигается такой выигрыш? Обычно для распределения регистров используются графы, которые в зависимости от интенсивности использования регистров раскрашиваются в различные цвета – от «холодного» до «горячего» (поэтому эта техника часто называется colormap). Однако это в теории. На практике же для достижения приемлемого качества распределения приходится прибегать к некоторому набору эвристических шаблонов, делающих «интуитивные» предположения о реальной «загру-
72
женности» той или иной переменной. Судя по всему, компилятор msvc использует большое количество эвристических шаблонов, поэтому с большим отрывом и побеждает всех остальных.
Регистровые ре-ассоциации Для преодоления катастрофической нехватки регистров, некоторые компиляторы стремятся совмещать счетчик цикла с указателем на обрабатываемые данные. Код вида «for (i = 0; i < n; i++) n+=a[i];» превращается ими в «for (p= a; p < &a[n]; p++) n+=*p;» Экономия налицо! Впервые (насколько мне известно) эта техника была применена в компиляторах фирмы Hewlett-Packard, где она фигурировала под термином register reassociation. А что же конкуренты?! Рассмотрим следующий код (кстати, взятый из документации на компилятор HP): Ëèñòèíã 14. Íåîïòèìèçèðîâàííûé êàíäèäàò íà ðåãèñòðîâóþ ðå-àññîöèàöèþ int a[10][20][30]; void example (void) { int i, j, k; for (k = 0; k < 10; k++) for (j = 0; j < 10;j++) for (i = 0; i < 10; i++) a[i][j][k] = 1; }
Грамотный оптимизатор должен переписать его так: Ëèñòèíã 15. Îïòèìèçèðîâàííûé âàðèàíò — ñ÷åò÷èê öèêëà ñîâìåùåí ñ óêàçàòåëåì íà ìàññèâ int a[10][20][30]; void example (void) { int i, j, k; register int (*p)[20][30]; for (k = 0; k < 10; k++) for (j = 0; j < 10; j++) for (p = (int (*)[20][30]) &a[0][j][k], ↵ i = 0; i < 10; i++) *(p++[0][0]) = 1; }
Эксперимент показывает, что ни msvc, ни gcс не выполняют регистровых реассоциаций ни в сложных, ни даже в простейших случаях. С приведенным примером справился один лишь icl. Впрочем, это его все равно не спасает, и msvc оказывается впереди за счет более оптимального распределения регистров.
Выражения Упрощение выражений Выполнять алгебраические упрощения оптимизаторы научились лишь недавно, но эффект, как говорится, превзошел все ожидания. Редкий программистский код не содержит выражений, которые нельзя было бы сократить. Откройте документацию по MFC в разделе «Changing the Styles of a Window Created by MFC» и поучитесь, как нужно писать программы. Ëèñòèíã 16. Ýòî òàê Microsoft íàñ ó÷èò ïèñàòü ïðîãðàììû BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {
программирование // Create a window without min/max buttons or sizable // border cs.style = WS_OVERLAPPED | WS_SYSMENU | WS_BORDER; // Size the window to 1/3 screen size and center it cs.cy = ::GetSystemMetrics(SM_CYSCREEN) / 3; cs.cx = ::GetSystemMetrics(SM_CXSCREEN) / 3; cs.y = ((cs.cy * 3) - cs.cy) / 2; cs.x = ((cs.cx * 3) - cs.cx) / 2;
}
// Call the base-class version return CFrameWnd::PreCreateWindow(cs);
Неудивительно, что Windows так тормозит! Чтобы понять очевидное, парням из Microsoft потребовалось две операции умножения, две – деления и две – сложения. Итого: шесть операций. Проверим, сможет ли оптимизатор избавиться от мусорных операций, предварительно переписав код так: Ëèñòèíã 17. Íåîïòèìèçèðîâàííûé êàíäèäàò íà àëãåáðàè÷åñêîå óïðîùåíèå struct CS{int x;int y;}; f(int n,) { struct CS cs; cs.y = n; cs.x = n; y = ((cs.y * 3) - cs.y) / 2; x = ((cs.x * 3) - cs.x) / 2; return y - x; }
Компилятор msvc выбрасывает лишь часть операций, но чем он руководствуется при этом – непонятно. Оптимизатор легко раскрывает скобки ((cs.y∗3) – cs.y), но дальше этого он не идет, послушно выполняя бессмысленную операцию (cs.y∗2 / 2). И тут же, словно одумавшись, принудительно обнуляет регистр EAX, возвращая константный ноль. Создается устойчивое впечатление, что результат выражения вычисляется компилятором еще на стадии трансляции, но он никак не решается им воспользоваться:
}
int x,y; x = n-n; y = n+n; return x+y-2*n+(n/n);
Казалось бы, чего в нем сложного? Компиляторы msvc и gcc выкидывают все кроме (n/n), оставляя его на тот случай, если переменная n окажется равной нулю. Поразительно, но icl выполняет все вычисления целиком, не производя никаких упрощений. Таким образом, выполнение алгебраических упрощений – весьма капризная и непредсказуемая операция. Не надейтесь, что компилятор выполнит ее за вас!
Упрощение алгоритма Наибольший прирост производительности дает именно алгоритмическая оптимизация (например, замена пузырьковой сортировки на сортировку вставками). Никакой компилятор с этим справиться не в состоянии, во всяком случае, пока. Но первый шаг уже сделан. Современные компиляторы распознают (или во всяком случае пытаются распознать) смысловую нагрузку транслируемого кода и при необходимости заменяют исходный алгоритм другим, намного более эффективным. Вот, например: Ëèñòèíã 20. Êàíäèäàò íà óïðîùåíèå àëãîðèòìà main(int n, char **v) { int a = 0; int b = 0; for(i=0; i<n; i++) a++; // ìíîãîêðàòíîå ñëîæåíèå – ýòî óìíîæåíèå for(j=0; j<n; j++) b++; return a*b; }
Для человека очевидно, что этот код можно записать так: (n∗n). Мой любимый msvc именно так и поступает, а вот icl и gcc накручивают циклы на кардан.
Ëèñòèíã 18. Çàãàäî÷íûé êîä, ñãåíåðèðîâàííûé êîìïèëÿòîðîì msvc
Использование подвыражений mov eax, [esp+arg_0] ; çàãðóçêà n add eax, eax ; n *= 2; ( áåç ó÷åòà çíàêà) cdq ; ïðåîáðàçîâàòü äâîéíîå çíàêîâîå ñëîâî
Хорошие оптимизаторы никогда не вычисляют значение одного и того же выражения дважды. Рассмотрим следующий пример: Ëèñòèíã 21. Ïîäâûðàæåíèå (x*y) – îáùåå
sub eax, edx ; ó÷åñòü çíàê
a = x*y + n; b = x*y - n; // âûðàæåíèå (x*y) óæå âñòðå÷àëîñü! È x,y ñ òåõ ïîð íå ìåíÿëèñü!
sar eax, 1 ; n /= 2; xor eax, eax ; n = 0;
Чтобы избавиться от лишнего умножения, этот код необходимо переписать так: Ëèñòèíã 22. Îïòèìèçèðîâàííûé âàðèàíò
Компилятор icl выбрасывает мусорный код полностью, генерируя честный XOR EAX,EAX, а вот gcc вообще не выполняет никаких упрощений! Однако могущество icl очень переменчиво. Возьмем такой пример: Ëèñòèíã 19. Åùå îäèí êàíäèäàò íà àëãåáðàè÷åñêîå óïðîùåíèå f(int n) {
№2, февраль 2005
t = x*y; // âû÷èñëèòü âûðàæåíèå (x*y) è çàïîìíèòü ðåçóëüòàò a = t + n; // ïîäñòàíîâêà óæå âû÷èñëåííîãî çíà÷åíèÿ b = t - n; // ïîäñòàíîâêà óæå âû÷èñëåííîãî çíà÷åíèÿ
Американцы называют это «удалением избыточности» (redundancy elimination) или «совместным использованием общих выражений» (Share Common Subexpressions). Полное удаление избыточности (оно же Full Redundancy Elimination или сокращенно FRE) предполагает, что совместное использование выражений происходит только в основных путях
73
программирование (path) выполнения программы. Ветвления при этом игнорируются. Частичное удаление избыточности (оно же Partial redundancy elimination или сокращенно PRE) охватывает весь программный код – как внутри ветвлений, так и снаружи. То есть частичное удаление избыточности удаляет избыточность намного лучше, чем полное, хотя при полном программа компилируется чуть-чуть быстрее. Вот такая терминологическая путаница. Вся заковырка в том, что выражение «Partial redundancy elimination» переводится на русский язык отнюдь не как «частичное удаление избыточности» (хоть это и общепринятый вариант), а «удаление частичной избыточности», и «full redundancy elimination» – «удаление полной избыточности», что совсем не одно и то же! Все три рассматриваемых компилятора поддерживают совместное использование выражений. С приведенным примером они справляются легко. Но давайте усложним задачу, предложив им код подсчета суммы соседних элементов массива:
Компилятор msvc уже не справляется и генерирует две операции умножения, вместо одной. А вот компиляторы icl и gcc поступают правильно, вычисляя выражение (x∗y) всего один раз.
Сводная таблица качества оптимизации Òàáëèöà 1. Ìåõàíèçìû îïòèìèçàöèè, ïîääåðæèâàåìûå ðàçëè÷íûìè êîìïèëÿòîðàìè
Ëèñòèíã 23. Ïðèìåð ñ íåî÷åâèäíîé ðàçáèâêîé íà ïîäâûðàæåíèÿ /* Sum neighbors of i,j */ up = a[(i-1)*n + j ]; down = a[(i+1)*n + j ]; left = a[i*n + j-1]; right = a[i*n + j+1]; sum = up + down + left + right;
Даже человеку не всегда очевидно, что его можно переписать следующим образом, сократив количество операций умножения с четырех до одной: Ëèñòèíã 24. Ïîëíîñòüþ îïòèìèçèðîâàííûé âàðèàíò (ðó÷íàÿ îïòèìèçàöèÿ) inj = i*n + j; // îäíîêðàòíîå âû÷èñëåíèå ïîäâûðàæåíèÿ up = val[inj - n]; // èçáàâëåíèå îò ëèøíåãî ñëîæåíèÿ // èçáàâëåíèÿ îò îäíîãî ñëîæåíèÿ è óìíîæåíèÿ down = val[inj + n]; // èçáàâëåíèå îò îäíîãî ñëîæåíèÿ è óìíîæåíèÿ left = val[inj - 1]; // èçáàâëåíèå îò îäíîãî ñëîæåíèÿ è óìíîæåíèÿ right = val[inj + 1]; sum = up + down + left + right;
Компилятор msvc успешно удалил лишнее выражение (i∗n), избавившись от одного умножения, и сгенерировал довольно туманный и медленный код, не оправдывающий возлагаемых на него надежд. Аналогичным образом поступил и gcc. Его основной конкурент – icl хоть и сократил количество умножений наполовину, сгенерировал очень громоздкий код, сводящий на нет весь выигрыш от оптимизации. Короче говоря, с предложенным примером в полной мере не справился никто и для достижения наивысшей производительности программист должен выполнять все преобразования самостоятельно. По крайней мере необходимо добиться, чтобы все совместно используемые выражения в исходном тексте присутствовали в явном виде. А как обстоят дела с удалением частичной избыточности? Вот, например: Ëèñòèíã 25. Ñëó÷àé óäàëåíèÿ ÷àñòè÷íîé èçáûòî÷íîñòè if (n) a = x*y + n; else a = x*y - n;
74
Заключение Современные методики оптимизации носят довольно противоречивый характер. С одной стороны, они улучшают код, с другой – страдают непредсказуемыми побочными эффектами. Опытные программисты подобных «вольностей» не одобряют и режимом агрессивной оптимизации пользуются с большой осторожностью. Однако полностью отказываться от машинной оптимизации даже самые закоренелые консерваторы уже не решаются. Ручное «вылизывание» кода обходится слишком дорого, правда, последствия иной оптимизации выходят еще дороже. Наращивая мощь оптимизаторов, разработчики компиляторов допускают все больше ошибок и в ответственных случаях программистам приходится идти на компромисс, поручая оптимизатору только ту часть работы, в результате которой можно быть полностью уверенным (свертка констант, константная подстановка и т. д.). Собственно говоря, наше исследование компиляторов еще не закончено, и перечисленные приемы оптимизации это даже не верхушка айсберга, а небольшой его кусочек. В следующей статье этого цикла мы рассмотрим трансформацию циклов и прочие виды ветвлений. Уверяю вас, это очень интересная тема и здесь есть чему поучиться!
программирование
ZEND STUDIO 4.0 – НОВАЯ ВЕРСИЯ, НОВЫЕ ВОЗМОЖНОСТИ
ЕВГЕНИЙ ВОЯКИН Данный обзор посвящен рассмотрению функциональных возможностей одной из наиболее продвинутых интегрированных сред разработки приложений на языке PHP – Zend Studio версии 4.0, созданной самими разработчиками языка – компанией Zend. В январе 2005 года Zend объявила о доступности бета-версии Zend Studio четвертого поколения. Zend Studio представляет собой среду разработки, отладки и оптимизации программного кода PHP-приложений. Как гласит краткая аннотация в справочной системе пакета, полностью интегрированный интерфейс предназначен упростить процесс разработки приложений как для начинающих, так и для опытных пользователей.
№2, февраль 2005
Новая, четвертая версия отличается от предыдущей целым рядом улучшений. В пакет добавлена возможность работы с базами данных напрямую из интерфейса среды. Поддерживаются такие наиболее популярные БД, как MySQL, PostgreSQL, Oracle, IBM DB2, MS SQL Server, SQLite. Теперь в распоряжении разработчика библиотека шаблонов программных фрагментов, средство документирования. Рассмотрим, что же представляет собой Zend Studio, и что нового появилось в 4 версии. Программный пакет Zend Studio включает в себя два приложения – Zend Studio Server и Zend Studio Client. Zend Studio Server – это отладчик, управляемый средой Zend Studio Client и контролирующий про-
75
программирование цесс выполнения PHP-кода. При выполнении кода управляющие команды, например информация о точках останова (breakpoints), поступают от Zend Studio Client в Server. Последний, в свою очередь, управляет интерпретатором, выполняющим команды языка PHP. Кроме того, в интерпретаторе накапливается информация о работе программы, такая как результат выполнения, или вывод (output), значения переменных, стека и данные о возникших ошибках. Zend Studio Server отдает собранную информацию из интерпретатора в Client, где она отображается в соответствующих окнах. Пользовательский интерфейс Zend Studio Server представляет собой веб-оснастку, которая позволяет просматривать и изменять параметры настройки интерпретатора языка PHP и опции среды Zend. Пример вида оснастки приведен на рис.1. Разработка программ пользователем осуществляется в Zend Studio Client. Наиболее существенные возможности этой среды включают в себя: ! управление процессом исполнения кода с возможностью просмотра значений используемых переменных, стека, ошибок и результата работы; ! средство анализа кода, позволяющее, например, отследить фрагменты кода, которые никогда не будут выполнены, или выявить объявленные, но неиспользуемые переменные; ! работу с базами данных, выполнение запросов напрямую в интерфейсе среды (появилось в версии 4.0); ! отслеживание версий (CVS); ! автонабор кода со словарем, включающим лексемы языков PHP, HTML, переменные, переменные в составе классов и т. п.;
Ðèñóíîê 1. Ïîëüçîâàòåëüñêèé èíòåðôåéñ Zend Studio Server
76
! библиотеку шаблонов программного кода (в терминологии Zend Studio называемых Snippets). Библиотека постоянно обновляется, возможна автоматическая загрузка более новых шаблонов из сети или создание своих шаблонов (появилось в версии 4.0); ! выделение различных лексических элементов кода разными цветами, выравнивание текста при вводе; ! настраиваемые горячие клавиши на наиболее часто используемые задачи; ! справочную систему. Пользовательский интерфейс «клиентской» части пакета (см. рис. 2) существенно изменился по сравнению с предыдущей версией. Подавляющая часть интерфейса среды переведена на русский язык. Работать с полноцветными кнопками панели инструментов удобно, обозначения понятны и просты. Число «окошек» в рамках рабочего поля среды увеличилось, однако управление их отображением на экране не вызывает затруднений: по периметру главного окна среды разработки идет дополнительная «панель инструментов», отвечающая за отображение на экране тех или иных рабочих зон среды: окна вывода, окно сообщений отладки и т. п. Рабочее поле среды состоит из 7 окон, каждое из которых может менять отображаемую информацию в зависимости от необходимости. Все окна, кроме главного окна редактора, могут быть скрыты. Окно «Менеджера файлов» служит для осуществления навигации: 3 режима, в которых оно может «работать», позволяют перемещаться по дереву каталогов на локальных и удаленных дисках (по-
программирование средством подключения по FTP), по файлам проекта или дереву баз данных. (см. рис. 4). Окно «Инспекторы» предназначено для просмотра иерархической структуры файла проекта, включений в него других файлов, объявленных переменных, классов. В окне «Сообщения» можно просмотреть информацию о запуске/окончании отладки, предупреждения об ошибках интерпретации. В этом же окне выводятся сообщения встроенной системы документирования и оптимизатора кода (см. рис. 6). В окне отладчика во время процедуры отладки кода отображается текущее значение переменных, включая глобальные (см. рис 2.), состояние стека и т. д. Результат интерпретации PHP-сценария помещается в окно «Вывод». Данная функция неоценима при пошаговой отладке кода, так как сразу же позволяет отследить процесс интерпретации и быстро обнаружить логические ошибки. Выбрав пункт в контекстном меню окна, можно открыть результат выполнения сценария в окне браузера. Как следует из анонса версии, работа с базами данных (БД) является одной из наиболее существенных особенностей, отличающих 4-ю версию от предыдущих. Для работы с БД некоторые окна среды имеют соответствующие режимы, названные «SQL». Добавление подключения к серверу баз данных осуществляется в «менеджере файлов» в режиме SQL через контекстное меню. В этом же окне выполняется
выбор конкретной базы сервера и навигация по ней. Запросы набираются в отдельном окне «SQL», а результат их выполнения может быть просмотрен в основном окне редактора, также переключенном в режим SQL (см. рис. 4). Однако при анализе средств для работы с БД складывается ощущение некой незаконченности данного функционала – по сути, все эти средства представляют собой лишь интерфейс для просмотра данных и выполнения SQLкоманд. Более интересным был бы какой-либо функционал, который позволял бы строить обращения к базам данных на языке PHP путем, к примеру, перетаскивания SQL-запросов напрямую в окно редактора кода, с автоматическим превращением такого запроса в реализующий его PHP-код. К недостаткам также можно отнести и то, что в программе отсутствует поддержка русского языка для данных в БД. Как и в предыдущих версиях пакета, разработка программ осуществляется в режиме текстового редактора. Основное окно редактора, находящееся в центре рабочей области среды, представляет собой многофункциональный текстовый редактор, довольно удобный в использовании. Вводимые лексемы языка PHP (переменные, строковые и числовые константы, операторы и прочее) автоматически выделяются с помощью различных цветовых схем, которые разработчик может настроить по своему усмотрению. Все
Ðèñóíîê 2. Zend Studio Client
№2, февраль 2005
77
программирование строки кода программ нумеруются автоматически, что существенно облегчает навигацию по файлам проекта при получении сообщения об ошибке: все подобные сообщения содержат номер строки. Редактор обладает возможностью автоматического выравнивания набранного программного кода с помощью отступов, что позволяет сделать код более читаемым. Заметно улучшена система предиктивного набора текста: при вводе первых букв названия какой-либо функции появляется выпадающий список функций языка PHP, начинающихся с этих букв. При выборе функции появляется окно-подсказка с кратким описанием этой функции и ее параметров.
Ðèñóíîê 3. Ïðåäèêòèâíûé íàáîð òåêñòà
Ðèñóíîê 4. Ðàáîòà ñ áàçàìè äàííûõ â Zend Studio Client
78
В словарь этой системы также входят глобальные и локальные переменные, константы, теги. Появившаяся в этой версии система шаблонов программного кода позволяет включать в проект функции из библиотеки шаблонов, написанной другими программистами. Библиотека постоянно обновляется, возможна загрузка новых шаблонов из Интернета. Кроме того, разработчик может создавать собственные шаблоны функций или просто участков кода под свои нужды. К недостаткам этой системы следует отнести некоторую ее «скрытость» от разработчика – по сравнению с поиском пункта в главном меню среды более удобным был бы вызов библиотеки напрямую с помощью кнопки на панели инструментов. Zend Studio Client поддерживает разработку как отдельных файлов на PHP, так и проектов, состоящих из нескольких файлов. Файлы, входящие в проект, отображаются в окне «менеджера файлов». При работе с многофайловыми проектами полезной может оказаться функция, позволяющая отследить доступность всех файлов, используемых в проекте (в случае включения в один из файлов других директивами require и т. п.). Отдельного внимания заслуживает функция анализа программного кода. Данная операция позволяет выявить некоторые логические ошибки, допущенные разработчиком при программировании, например найти фрагменты кода,
программирование которые никогда не будут выполнены. Так, анализ фрагмента программы, содержащей такой текст: 16 if (0<-1){ 17 for ($i=0; $i<strlen($source);$i++){ 18 print(chr(ord($source[$i]) / 2)); 19 } 20 }
приведет к предупреждающему сообщению (см. рис. 6) анализатора: «Some code in the function cannot be reached. This part of the code is useless.» («Некоторые участки кода в функции никогда не будут выполнены. Эти участки бесполезны»). К сожалению, несмотря на локализацию интерфейса программы, рассмотренная версия имеет справочную систему на английском языке. Кроме того, справочная система еще явно «не обновлена» до четвертой версии. Вероятно, это связано именно с тем, что рассмотренная версия является бета-версией продукта. Отсутствует описание наиболее существенных нововведений, появившихся в новом пакете – работы с базами данных, средства документирования «PHPDocumenter». Ввиду полного отсутствия информации по последнему разобраться с принципом его работы автору так и не удалось. В заключение данного обзора хочется сказать, что Zend Studio 4.0 явно может претендовать на звание одного из наиболее серьезных средств разработки на PHP. Новая сре-
да существенно отличается от предыдущей удобством интерфейса, его локализацией. Тенденция встраивания в среду интерфейсов к смежным сферам разработки, с которыми сталкивается программист при написании кода – базам данных, документации, – доказывает, что компания Zend стремится вывести процесс разработки приложений на языке PHP на качественно более высокий уровень интеграции.
Ðèñóíîê 5. Îòñëåæèâàíèå öåëîñòíîñòè ïðîåêòà
Ðèñóíîê 6. Àíàëèçàòîð êîäà
№2, февраль 2005
79
hardware
ЗАПИСЬ CD-R/RW-ДИСКОВ В LINUX ЧАСТЬ 4
ВЛАДИМИР МЕШКОВ 80
hardware В статье рассматривается порядок записи информации на CD-R/RW-диски в режиме Session-at-once (SAO). Работоспособность всех примеров программ была проверена для ОС Linux, ядро 2.4.28. В ядре включены режим SCSI-эмуляции для ATAPI-устройств и поддержка SCSI Generic-драйвера. Использовались следующие модели приводов: ! TEAC CD-W524E Rev.1.0E ! ASUS DRW-1604P Rev.1.09 ! MITSUMI CR-48XATE Rev.1.0E
Особенности записи информации в режиме SAO В отличие от ранее рассмотренного режима записи Trackat-once (TAO) (см. [1, 2]), в режиме Session-at-once (SAO) треки примыкают друг к другу вплотную, промежутки (паузы) между ними отсутствуют. Для управления процессом записи устройству передается специальная структура, содержащая информацию о расположении (координатах) треков, форматах блоков основного канала и субканалов трека. Эта структура, которая называется CUE SHEET, является своего рода картой, на основании которой устройство сформирует входную (Lead-In) и выходную (Lead-Out) области сессии (диска). Структура CUE SHEET состоит из последовательности 8-байтовых блоков, самый первый блок описывает входную область сессии, последний – выходную область, остальные блоки содержат информацию о треках. Блоки имеют следующий формат:
Ðèñóíîê 1. Ôîðìàò áëîêà ñòðóêòóðû CUE SHEET
! ! ! ! ! !
Назначение полей: CTL/ADR – значение байта CTL/ADR-трека. TNO – номер трека. INDEX – индекс трека. DATA FORM – формат данных трека. SCMS – байт системы управления копированием. ABSOLUTE TIME – стартовые координаты трека в MSFформате.
Для Lead-In и Lead-Out-областей значение байта CTL/ ADR должно быть равно 0x01 (за исключением случая, когда поле Data Form = 0x41, однако он в этой статье не рассматривается). Байт TNO и INDEX для Lead-In-области принимают значение 0x00. Lead-Out-область, согласно спецификации, кодируется как трек под номером 0xAA, поле INDEX всегда равно 0x01.
Ðèñóíîê 3. Ñòðóêòóðà ïîëÿ DATA FORM
Поле Data Form of Main Data определяет формат блоков данных основного канала – CD-DA (аудиоданные), CD-ROM Mode 1, CD-ROM XA. При записи аудио это поле принимает значение 0x00, и устройству передается блок аудиоданных размером 2352 байта. При записи данных в формате Mode 1 (см. [1], таблица 1) поле Data Form of Main Data может принимать следующие значения: ! 0x10 – приложение передает устройству блок данных User Data размером 2048 байт, поля Sync/Header и EDC/ ECC генерируются устройством; ! 0x11 – приложение передает устройству блок данных размером 2352 байт в составе полей User Data (2048 байт), Sync/Header (16 байт) и EDC/ECC (288 байт), однако устройство игнорирует содержимое полей Sync/ Header и EDC/ECC и генерирует собственные значения. Поле Data Form of Sub-Channel определяет формат данных субканалов, передаваемых устройству, но в рамках данной статьи структура этого поля не рассматривается. Согласно спецификации [3], данные субканалов P и Q, переданные в структуре CUE SHEET, устройство игнорирует и генерирует собственные значения. Формат поля Data Form of SubChannel определен в [3], табл. 516. Значение поля DATA FORM при записи данных в формате CD-ROM XA представлены в табл. 514 спецификации [3]. Для Lead-In и Lead-Out областей значение поля DATA FORM всегда равно 0x01. После того как структура CUE SHEET сформирована, она передается устройству при помощи команды SEND CUE SHEET.
Формат байта CTL/ADR представлен на рис. 2. Старшие 4 разряда байта CTL/ADR занимает поле управления CTL, младшие 4 разряда – поле ADR.
Ðèñóíîê 2. Áàéò CTL/ADR
Поле CTL определяет тип информации, находящейся в треке, и может принимать следующие значения: ! 00xxb – 2 аудиоканала; ! 10xxb – 4 аудиоканала; ! 01xxb – трек данных; ! 11xxb – зарезервировано.
Ðèñóíîê 4. Ôîðìàò êîìàíäû SEND CUE SHEET
Параметр Cue Sheet Size содержит размер структуры CUE SHEET в байтах.
Примеры программ для записи дисков в режиме SAO
Поле ADR может принимать следующие значения:
! 01b – начальное время трека; ! 10b – код носителя по каталогу; ! 11b – код ISRC.
№2, февраль 2005
Запись данных на компакт-диск Рассмотрим пример программы, выполняющей запись односессионного CD-R/RW-диска в режиме SAO.
81
hardware В сессии находится только один трек, формат данных – CD-ROM Mode 1. Алгоритм работы программы следующий: ! проверяем возможность записи информации в режиме SAO; ! в странице параметров режима записи указываем требуемый режим записи – SAO: Write Type = 0x02 (см. [1]); ! формируем структуру CUE SHEET и отправляем ее устройству; ! выполняем запись данных на носитель. Для возможности записи в режиме SAO устройство должно обладать свойством CD Mastering (код 0x002E).
get_conf_cmd[0] = 0x46; // êîä êîìàíäû GET CONFIGURATION get_conf_cmd[1] = 2; // RT = 10b, ñ÷èòûâàåì òîëüêî // çàïðàøèâàåìîå ñâîéñòâî get_conf_cmd[2] = *((__u8 *)&f_num + 1); get_conf_cmd[3] = *((__u8 *)&f_num); get_conf_cmd[8] = FEATURE_LEN; send_cmd(get_conf_cmd, 10, SG_DXFER_FROM_DEV, ↵ data_buff, FEATURE_LEN, 20); memcpy((void *)&data_length, data_buff, 4); data_length = __swab32(data_length); if(data_length == 4) return -1; // ñâîéñòâî // íå ïîääåðæèâàåòñÿ /* Ïðîâåðÿåì, ÿâëÿåòñÿ ëè ñâîéñòâî òåêóùèì */ if(cd_sao->current != 1) return -1; /* Îïðåäåëÿåì òåêóùèé ïðîôèëü */ *((__u8 *)&cur_prof) = data_buff[7]; *((__u8 *)&cur_prof + 1) = data_buff[6]; printf("\nÒåêóùèé ïðîôèëü - 0x%04X\n", cur_prof); /* Âîçâðàùàåì çíà÷åíèå áèòà SAO */ return (cd_sao->sao); }
Ðèñóíîê 5. Ôîðìàò äåñêðèïòîðà ñâîéñòâà CD Mastering
Устройство поддерживает режим записи SAO, если бит SAO установлен в единицу. Формат дескриптора свойства можно описать при помощи следующей структуры: /* Ñâîéñòâî CD Mastering typedef struct { __u16 f_code; __u8 current __u8 persistent __u8 version __u8 res1 __u8 add_length; __u8 rw __u8 cd_rw __u8 test_write __u8 raw __u8 raw_ms __u8 sao __u8 BUF __u8 res2 __u8 max_cue_len[3]; } cd_sao_t;
*/ :1; :1; :4;
:1; :1; :1;
/* Ãëîáàëüíûå ïåðåìåííûå */ __u16 page5_len; __u8 *page5_data; wpm_t *wpm; // äàííûå ñòðàíèöû ïàðàìåòðîâ çàïèñè :2; :1; :1; :1; :1; :1;
Проверка наличия свойства выполняет при помощи команды GET CONFIGURATION (см. [1], «Свойства и профили устройства», [3]). Проверку выполняет функция check_ feature(). Входные параметры функции – код проверяемого свойства, в нашем случае это 0x002E: int check_feature(__u16 f_num) { #define FEATURE_LEN 16 __u8 get_conf_cmd[10]; // CDB __u8 data_buff[FEATURE_LEN]; // ðåçóëüòàòû ÷òåíèÿ __u32 data_length = 0; // ðåàëüíàÿ äëèíà äàííûõ __u16 cur_prof = 0; // òåêóùèé ïðîôèëü, Current Profile cd_sao_t *cd_sao; test_unit_ready(); memset(data_buff, 0, sizeof(data_buff)); cd_sao = (void *)(data_buff + 8); /* Äëÿ îïðåäåëåíèÿ ñâîéñòâ óñòðîéñòâà èñïîëüçóåòñÿ * êîìàíäà GET CONFIGURATION */ memset(get_conf_cmd, 0, 10);
82
Установку требуемого режима записи – SAO – выполняет связка функций mode_sense() и mode_select(). Эти функции были рассмотрены в [1] и [2], здесь мы только немного модифицируем функцию mode_sense() – при вызове этой функции сначала определяется реальная длина страницы параметров режима записи, а после этого считывается сама страница.
int mode_sense() { __u8 mode_sense_cmd[10]; test_unit_ready(); /* Îïðåäåëÿåì ðàçìåð ñòðàíèöû ïàðàìåòðîâ çàïèñè – ïåðâûå * äâà áàéòà áëîêà äàííûõ, âîçâðàùàåìûõ óñòðîéñòâîì * ïî êîìàíäå MODE_SENSE */ page5_data = (__u8 *)malloc(2); memset(mode_sense_cmd, 0, 10); mode_sense_cmd[0] = MODE_SENSE_10; mode_sense_cmd[2] = 5; // ñòðàíèöà ïàðàìåòðîâ çàïèñè mode_sense_cmd[8] = 2; send_cmd(mode_sense_cmd, 10, SG_DXFER_FROM_DEV, ↵ page5_data, 2, 200); *((__u8 *)&page5_len) = page5_data[1]; *((__u8 *)&page5_len + 1) = page5_data[0]; page5_len += 2; printf("Ðàçìåð ñòðàíèöû ïàðàìåòðîâ çàïèñè – ↵ %d áàéò\n", page5_len); free(page5_data); /* Ðàçìåð ñòðàíèöû ïàðàìåòðîâ ðåæèìà çàïèñè èçâåñòåí, * âûäåëÿåì ïàìÿòü äëÿ ñòðàíèöû */ page5_data = (__u8 *)malloc(page5_len); memset(page5_data, 0, page5_len); /* Ñòðóêòóðà wpm_t *wpm õðàíèò äàííûå ñòðàíèöû ïàðàìåòðîâ * çàïèñè, åå ôîðìàò áûë ðàññìîòðåí â [2]. wpm óêàçûâàåò * íà íà÷àëî ñòðàíèöû ïàðàìåòðîâ ðåæèìà çàïèñè */ wpm = (void *)(page5_data + 8); /* Ôîðìèðóåì êîìàíäó MODE_SENSE_10 */ memset(mode_sense_cmd, 0, 10); mode_sense_cmd[0] = MODE_SENSE_10; mode_sense_cmd[2] = 5; // ñòðàíèöà ïàðàìåòðîâ çàïèñè mode_sense_cmd[8] = page5_len;
hardware send_cmd(mode_sense_cmd, 10, SG_DXFER_FROM_DEV, ↵ page5_data, page5_len, 200); }
return 0;
Установку режима записи SAO выполняет функция mode_ select(): int mode_select() { __u8 mode_select_cmd[10]; /* Ñ÷èòûâàåì ñòðàíèöó ïàðàìåòðîâ ðåæèìà çàïèñè */ mode_sense(); /* Óñòàíàâëèâàåì íåîáõîäèìûé ðåæèì */ wpm->write_type = 2; // ðåæèì SAO wpm->multises = 0; // îäíîñåññèîííûé äèñê test_unit_ready(); /* Ôîðìèðóåì êîìàíäó MODE_SELECT_10 */ memset(mode_select_cmd, 0, 10); mode_select_cmd[0] = MODE_SELECT_10; mode_select_cmd[1] = 0x10; mode_select_cmd[7] = *((__u8 *)&page5_len + 1); mode_select_cmd[8] = *(__u8 *)&page5_len; if(send_cmd(mode_select_cmd, 10, SG_DXFER_TO_DEV, ↵ page5_data, page5_len, 200) < 0) return -1;
}
free(page5_data); return 0;
Формирование структуры CUE SHEET выполняет функция send_cue_sheet(). По условиям задачи на диск записывается сессия, состоящая из одного трека, формат данных – CD-ROM Mode 1. Структура CUE SHEET будет состоять из четырех 8-байтовых блоков, и ее размер равен 32 байта (4х8). Первый и последний блоки описывают входную и выходную области сессии, второй блок – Pre-Gapобласть трека, третий блок описывает сам трек. Для указания стартовых координат (поле ABSOLUTE TIME) необходимо выполнить преобразование адреса из формата LBA в MSF. Для этого спецификацией [3] (табл. 584) предусмотрены следующие формулы: M = IP((LBA + 150) / (60 * 75)) S = IP((LBA + 150 - M * 60 * 75) / 75) F = IP(LBA + 150 - M * 60 *7 5 - S * 75)
Здесь IP – это Integer Part (целая часть), параметр LBA принимает значение: -151 < LBA < 404850. Пересчет координат из LBA в MSF выполняется при помощи следующих макроопределений: #define LBA2MIN(LBA) ((LBA + 150) / (60 * 75)) #define LBA2SEC(LBA, MIN) ((LBA + 150 - ↵ (MIN * 60 * 75)) / 75) #define LBA2FRAME(LBA, MIN, SEC) ((LBA + 150 - ↵ (MIN * 60 * 75)) - (SEC * 75))
В параметрах функции send_cue_sheet() передается размер трека, исчисляемый в блоках по 2048 байт. int send_cue_sheet(__u32 trk_size) { __u8 cue_sheet_cmd[10]; __u8 cue_buff[32]; // ñòðóêòóðà CUE SHEET, // ðàçìåð 32 áàéòà __u8 min, sec, frame; // êîîðäèíàòû Lead-Out-îáëàñòè memset(cue_sheet_cmd, 0, 10); memset(buff, 0, 32);
№2, февраль 2005
/* Çàïîëíÿåì ïîëÿ cue_buff[0] = cue_buff[1] = cue_buff[2] = cue_buff[3] = cue_buff[4] = cue_buff[5] = cue_buff[6] = cue_buff[7] =
CUE SHEET. Íà÷èíàåì ñ Lead-In-îáëàñòè */ 0x01; // CTL/ARD 0x00; // íîìåð òðåêà, âñåãäà 0 0x00; // èíäåêñ òðåêà, âñåãäà 0 0x01; // DATA FORM, âñåãäà 1 0x00; // SCMS 0x00; // Minute = 0 0x00; // Second = 0 0x00; // Frame = 0
/* Pre-gap îáëàñòü ïåðâîãî òðåêà */ cue_buff[8] = 0x41; // CTL = 4 – òðåê ñîäåðæèò äàííûå cue_buff[9] = 0x01; // íîìåð òðåêà = 1 cue_buff[10] = 0x00; // èíäåêñ òðåêà = 0 cue_buff[11] = 0x10; // äàííûå â ôîðìàòå Mode 1 cue_buff[12] = 0x00; // SCMS /* Êîîðäèíàòû Pre-Gap-îáëàñòè – (-150) â ôîðìàòå LBA, * èëè 0/0/0 â MSF-ôîðìàòå */ cue_buff[13] = 0x00; // Minute cue_buff[14] = 0x00; // Second cue_buff[15] = 0x00; // Frame /* Ïåðâûé òðåê */ cue_buff[16] = cue_buff[17] = cue_buff[18] = cue_buff[19] = cue_buff[20] =
0x41; 0x01; 0x01; 0x10; 0x00;
// CTL = 4 – òðåê ñîäåðæèò äàííûå // íîìåð òðåêà = 1 // èíäåêñ òðåêà = 1 // äàííûå â ôîðìàòå Mode 1 // SCMS
/* Êîîðäèíàòû ïåðâîãî òðåêà * èëè 0/2/0 â MSF-ôîðìàòå */ cue_buff[21] = 0x00; // cue_buff[22] = 0x02; // cue_buff[23] = 0x00; //
Minute Second Frame
/* Lead-Out */ cue_buff[24] cue_buff[25] cue_buff[26] cue_buff[27] cue_buff[28]
CTL/ADR íîìåð òðåêà âûõîäíîé îáëàñòè èíäåêñ, äëÿ Lead-Out âñåãäà 1 DATA FORM, âñåãäà 1 SCMC
= = = = =
0x01; 0xAA; 0x01; 0x01; 0x00;
// // // // //
– 0 â ôîðìàòå LBA,
/* Îïðåäåëÿåì êîîðäèíàòû Lead-Out-îáëàñòè. Ýòà îáëàñòü * ðàñïîëîæåíà ñðàçó çà ïåðâûì òðåêîì. Ðàçìåð òðåêà íàì * èçâåñòåí */ min = LBA2MIN(trk_size); sec = LBA2SEC(trk_size, min); frame = LBA2FRAME(trk_size, min, sec); printf("\nMIN/SEC/FRAME: %.2d/%.2d/%.2d\n", min, sec, frame); cue_buff[29] = min; cue_buff[30] = sec; cue_buff[31] = frame; /* Ôîðìèðóåì êîìàíäó SEND CUE SHEET */ cue_sheet_cmd[0] = 0x5D; cue_sheet_cmd[8] = 32; if(send_cmd(cue_sheet_cmd, 10, SG_DXFER_TO_DEV, ↵ cue_buff, 32, 200) < 0) return -1; }
return 0;
После того как структура CUE SHEET отправлена устройству, можно приступить к записи данных на носитель. Запись выполняет функция write_iso(), параметр функции – имя файла-образа. Вначале записывается Pre-Gap-область первого трека, а затем сам трек. Стартовый адрес Pre-Gapобласти в формате MSF равен 0/0/0 (-150 в LBA-формате), размер Pre-Gap-области составляет 150 секторов. int write_iso(__u8 *file_name) { #define PREGAP_SIZE 307200 // ðàçìåð Pre-Gap-îáëàñòè: // 150 ñåêòîðîâ ïî 2048 áàéò int ret, in_f;
83
hardware __u8 write_cmd[10]; __u8 *write_buff; int lba, lba1 = -150;
drv_mmc.c), в результате чего функция приобретает следующий вид:
in_f = open(file_name, O_RDONLY);
LOCAL void
memset(write_cmd, 0, 10); write_cmd[0] = WRITE_10; write_cmd[8] = 150; // ðàçìåð Pre-Gap â ñåêòîðàõ
fillcue(cp, ca, tno, idx, dataform, scms, mp) struct mmc_cue *cp; /* The target cue entry */ int ca; /* Control/adr for this entry */ int tno; /* Track number for this entry */ int idx; /* Index for this entry */ int dataform; /* Data format for this entry */ int scms; /* Serial copy management */ msf_t *mp; /* MSF value for this entry */ { cp->cs_ctladr = ca; /* XXX wie lead in */ cp->cs_tno = tno; cp->cs_index = idx; cp->cs_dataform = dataform;/* XXX wie lead in */ cp->cs_scms = scms; cp->cs_min = mp->msf_min; cp->cs_sec = mp->msf_sec; cp->cs_frame = mp->msf_frame;
write_buff = (__u8 *)malloc(PREGAP_SIZE); memset(write_buff, 0, PREGAP_SIZE); /* Çàïèñûâàåì Pre-gap-îáëàñòü. Îíà íà÷èíàåòñÿ ñ ñåêòîðà – * 150 è ñîäåðæèò íóëè */ lba = __swab32(lba1); memcpy((write_cmd + 2), (void *)&lba, 4); lba1 += 150; while(1) { ret = send_cmd(write_cmd, 10, SG_DXFER_TO_DEV, ↵ write_buff, PREGAP_SIZE, 200); if(ret == 0) break; if(ret < 0) return -1; } free(write_buff); /* Äàëüøå âûïîëíÿåòñÿ çàïèñü äàííûõ ïåðâîãî òðåêà * àíàëîãè÷íî ðàññìîòðåííûì ðàíåå ïðèìåðàì (ñì. [2]) */ . . . . . . . }
Функция посылки команды устройству send_cmd() также претерпела некоторые изменения (полный текст не приводится): int send_cmd(__u8 *cmd, __u8 cmdlen, int direction, ↵ __u8 *data, __u32 datalen, unsigned int timeout) { ............ rep: if(ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("SG_IO ioctl"); return -1; }
/* Ýòîò êîä äîáàâëåí íàìè â ó÷åáíûõ öåëÿõ! */ printf("\n0x%.2X\t", cp->cs_ctladr); printf("0x%.2X\t",cp->cs_tno); printf("0x%.2X\t",cp->cs_index); printf("0x%.2X\t",cp->cs_dataform); printf("0x%.2X\t",cp->cs_scms); printf("0x%.2X\t",cp->cs_min); printf("0x%.2X\t",cp->cs_sec); printf("0x%.2X\n",cp->cs_frame); }
После внесения изменений пересоберем утилиту и запишем с ее помощью три аудиотрека: # cdrecord -dev=X,Y,Z -dao -pad -audio trk1.wav ↵ rk2.wav trk3.wav
Размер первого трека равен 17087 блоков, размер второго трека – 12923, размер третьего трека – 20580. Структура CUE SHEET имеет следующее содержание:
if((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) { if(io_hdr.sb_len_wr > 0) { /* Åñëè SK/ASC/ASCQ == 02/04/08 (Not Ready. Long Write * in Progress), òî íåîáõîäèìî ïîâòîðèòü êîìàíäó WRITE_10 */ if((SK == NOT_READY) && (ASC == 0x04) && ↵ (ASCQ == 0x08)) goto rep; ........... }
Здесь в случае возникновения ошибки типа «NOT READY. LONG WRITE IN PROGRESS» сразу выполняется переход на метку rep и повтор команды WRITE, без необходимости формирования командного пакета. Отметим, что эти изменения никак не связаны с рассматриваемым режимом записи. Полный текст программы для записи данных на CD-R/ RW диск в режиме SAO находится по адресу: http://bob. netport.com.ua/sao.tar.gz.
Запись Audio-CD При записи аудиоданных на компакт-диск в режиме SAO в одной сессии будет находиться несколько треков. Рассмотрим, как в этом случае формируется структура CUE SHEET, и для начала изучим ее формат на реальном примере. С этой целью внесем небольшое дополнение в исходный код утилиты cdrecord, а именно в функцию fillcue() (файл cdrecord/
84
Структура CUE SHEET состоит из восьми блоков, ее размер равен 64 байта. Первый и последний блоки – это входная и выходная области сессии. Блок 2 – Pre-Gap-область первого трека, стартовый адрес равен 150. Третий блок описывает первый трек, стартовый адрес трека равен 0. Четвертый блок – Pre-Gap-область второго трека, стартовый адрес трека 16937. Пятый блок – второй трек, стартовый адрес трека равен размеру первого трека – 17087 блоков. Шестой блок – Pre-Gap-область третьего трека, стартовый адрес равен 29860. Седьмой блок содержит описание третьего трека, его стартовый адрес равен 30010 (17087 + 12923). Параметр DATA FORM равен 0x00. Это означает, что на диск выполняется запись аудиоданных, и размер блока равен 2352 байта. Таким образом, опытным путем установлено, что треки действительно расположены вплотную друг к другу, и PreGap-область трека захватывает последние 150 секторов области данных предыдущего трека. Исключение составляет Pre-Gap-область первого трека. Перепишем функцию send_cue_sheet для возможности записи на диск трех аудиотреков.
hardware Входные параметры функции – размеры треков в блоках по 2352 байта: int send_cue_sheet(int trk1_size, int trk2_size, int trk3_size) { __u8 cue_sheet_cmd[10]; __u8 *cue_buff; __u8 min, sec, frame; int cb_size = 0; int trk = -150; /* Îïðåäåëÿåì ðàçìåð ñòðóêòóðû CUE SHEET. Â åå ñîñòàâ * âõîäÿò òðè Pre-Gap-îáëàñòè, òðè òðåêà, âõîäíàÿ * è âûõîäíàÿ îáëàñòè */ cb_size = 3 * 8 + 3 * 8 + 16; // Èòîãî 64 áàéòà cue_buff = (__u8 *)malloc(cb_size); memset(cue_sheet_cmd, 0, 10); memset(cue_buff, 0, cb_size); /* Ôîðìèðóåì Lead-In-îáëàñòü */ cue_buff[0] = 0x01; cue_buff[1] cue_buff[2] = 0x00; cue_buff[3] cue_buff[4] = 0x00; cue_buff[5] cue_buff[6] = 0x00; cue_buff[7]
= = = =
0x00; 0x01; 0x00; 0x00;
/* Pre-gap ïåðâîãî òðåêà */ cue_buff[8] = 0x01; cue_buff[9] = cue_buff[10] = 0x00; cue_buff[11] cue_buff[12] = 0x00; cue_buff[13] cue_buff[14] = 0x00; cue_buff[15]
0x01; = 0x00; = 0x00; = 0x00;
/* Ïåðâûé òðåê */ cue_buff[16] = cue_buff[18] = cue_buff[20] = cue_buff[22] =
= = = =
0x01; 0x01; 0x00; 0x02;
cue_buff[17] cue_buff[19] cue_buff[21] cue_buff[23]
0x01; 0x00; 0x00; 0x00;
/* Pre-gap-îáëàñòü âòîðîãî òðåêà çàõâàòûâàåò ïîñëåäíèå * 150 ñåêòîðîâ ïåðâîãî òðåêà */ trk += trk1_size; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); cue_buff[24] cue_buff[26] cue_buff[28] cue_buff[30]
= = = =
0x01; cue_buff[25] = 0x02; 0x00; cue_buff[27] = 0x00; 0x00; cue_buff[29] = min; sec; cue_buff[31] = frame;
/* Âòîðîé òðåê ðàñïîëîæåí ñðàçó çà ïåðâûì, ò.å. ïàóçà * ìåæäó òðåêàìè îòñóòñòâóåò */ sec += 2; // äëèíà Pre-Gap – 2 ñåêóíäû, èëè 150 ñåêòîðîâ cue_buff[32] = 0x01; cue_buff[33] = 0x02; cue_buff[34] = 0x01; cue_buff[35] = 0x00; cue_buff[36] = 0x00; cue_buff[37] = min; cue_buff[38] = sec; cue_buff[39] = frame; /* Pre-gap-îáëàñòü òðåòüåãî òðåêà çàõâàòûâàåò ïîñëåäíèå * 150 ñåêòîðîâ âòîðîãî òðåêà */ trk += trk2_size; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); cue_buff[40] cue_buff[42] cue_buff[44] cue_buff[46]
= = = =
0x01; cue_buff[41] = 0x03; 0x00; cue_buff[43] = 0x00; 0x00; cue_buff[45] = min; sec; cue_buff[47] = frame;
/* Òðåòèé òðåê */ sec += 2; cue_buff[48] = cue_buff[50] = cue_buff[52] = cue_buff[54] =
0x01; cue_buff[49] = 0x03; 0x01; cue_buff[51] = 0x00; 0x00; cue_buff[53] = min; sec; cue_buff[55] = frame;
/* Lead-Out-îáëàñòü */ trk += trk3_size + 150; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec);
№2, февраль 2005
cue_buff[56] cue_buff[58] cue_buff[60] cue_buff[62]
= = = =
0x01; cue_buff[57] = 0xAA; 0x01; cue_buff[59] = 0x01; 0x00; cue_buff[61] = min; sec; cue_buff[63] = frame;
/* Ôîðìèðóåì êîìàíäíûé ïàêåò */ cue_sheet_cmd[0] = 0x5D; cue_sheet_cmd[8] = cb_size; /* Îòïðàâëÿåì óñòðîéñòâó êîìàíäó SEND_CUE_SHEET, * îñâîáîæäàåì ïàìÿòü è âûõîäèì èç ôóíêöèè */ if(send_cmd(cue_sheet_cmd, 10, SG_DXFER_TO_DEV, ↵ cue_buff, cb_size, 200) < 0) return -1; free(cue_buff); return 0; }
Отправив устройству структуру CUE SHEET, приступаем к записи данных. Запись данных выполняет функция write_audio(). Входные параметры функции – имя файла в формате WAV и номер записываемого трека. Если записывается первый трек, то перед записью данных этого трека выполняется запись Pre-Gap-области размером 150 секторов. int write_audio(__u8 *file_name, int flag) { #define PREGAP_SIZE 352800 // 150 ñåêòîðîâ ïî 2352 áàéòà int ret, i; int in_f; __u8 write_cmd[10]; __u8 *write_buff; int lba = 0; static int lba1 = -150; // ñòàðòîâûé àäðåñ äëÿ çàïèñè in_f = open(file_name, O_RDONLY); /* Ïåðåøàãèâàåì ÷åðåç WAV-çàãîëîâîê */ lseek(in_f, 44, 0); memset(write_cmd, 0, 10); write_cmd[0] = WRITE_10; if(flag == 1) { // çàïèñûâàåì ïåðâûé òðåê write_buff = (__u8 *)malloc(PREGAP_SIZE); memset(write_buff, 0, PREGAP_SIZE); write_cmd[8] = 150; /* Çàïèñûâàåì Pre-gap-îáëàñòü ïåðâîãî òðåêà */ lba = __swab32(lba1); memcpy((write_cmd + 2), (void *)&lba, 4); lba1 += 150; while(1) { ret = send_cmd(write_cmd, 10, SG_DXFER_TO_DEV, ↵ write_buff, PREGAP_SIZE, 200); if(ret == 0) break; if(ret < 0) return 0; } }
free(write_buff);
/* Äàëüøå âûïîëíÿåòñÿ çàïèñü äàííûõ àóäèîòðåêà */ . . . . . . . }
Из всех Pre-Gap-областей на диск записывается только Pre-Gap-область первого трека, остальные просто обозначены в структуре CUE SHEET. Возникает вопрос – если не все Pre-Gap-области подлежат записи, то зачем их указывать в структуре CUE SHEET? Исключим «лишние» Pre-Gap-области из структуры CUE SHEET, и функция send_ cue_sheet() примет следующий вид:
85
hardware int send_cue_sheet(int trk1_size, int trk2_size, int trk3_size) { __u8 cue_sheet_cmd[10]; __u8 *cue_buff; __u8 min, sec, frame; int cb_size = 0; int trk = 0;
__u8 cue_sheet_cmd[10]; __u8 *cue_buff = NULL; __u8 min, sec, frame; struct stat s; int cb_size = 0; int trk = 0; cb_size = trk_num * 8 + 8 + 16;
/* Âû÷èñëÿåì ðàçìåð CUE SHEET – Pre-Gap-îáëàñòü ïåðâîãî * òðåêà, òðè òðåêà, Lead-In- è Lead-Out-îáëàñòè */ cb_size = 3 * 8 + 8 + 16; // Èòîãî 48 áàéò cue_buff = (__u8 *)malloc(cb_size); memset(cue_sheet_cmd, 0, 10); memset(cue_buff, 0, cb_size); /* Îïèñàíèå Lead-In-îáëàñòè, Pre-gap-îáëàñòè ïåðâîãî òðåêà * è íåïîñðåäñòâåííî ïåðâîãî òðåêà òî÷íî òàêèå æå, * êàê è â ïðåäûäóùåì ïðèìåðå */ /* Lead-In-îáëàñòü */ ....... /* Pre-gap ïåðâîãî òðåêà */ ....... /* Ïåðâûé òðåê */ ....... /* Âòîðîé òðåê */ trk += trk1_size; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); cue_buff[24] cue_buff[26] cue_buff[28] cue_buff[30]
= = = =
0x01; cue_buff[25] = 0x02; 0x01; cue_buff[27] = 0x00; 0x00; cue_buff[29] = min; sec; cue_buff[31] = frame;
/* Òðåòèé òðåê */ trk += trk2_size; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); cue_buff[32] cue_buff[34] cue_buff[36] cue_buff[38]
= = = =
0x01; cue_buff[33] = 0x03; 0x01; cue_buff[35] = 0x00; 0x00; cue_buff[37] = min; sec; cue_buff[39] = frame;
cue_buff = (__u8 *)malloc(cb_size); memset(cue_sheet_cmd, 0, 10); memset(cue_buff, 0, cb_size); /* Lead-In-îáëàñòü, Pre-gap ïåðâîãî òðåêà, ïåðâûé òðåê * (ñì. ïðåäûäóùèé ïðèìåð) */ ....... /* Çàïèñûâàåì â ñòðóêòóðó CUE SHEET èíôîðìàöèþ î òðåêàõ. * Íà÷èíàåì ñî âòîðîãî òðåêà, ò.ê. äëÿ ïåðâîãî çàïèñü * óæå ñôîðìèðîâàíà */ for(i = 1, n = 24; i < trk_num; i++, n += 8) { /* Îïðåäåëÿåì ðàçìåð òðåêà */ memset((void *)&s, 0, sizeof(struct stat)); stat(argv[i], &s); /* Ñòàðòîâûå êîîðäèíàòû òðåêà */ trk += s.st_size/CD_FRAMESIZE_RAW; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); /* Ôîðìèðóåì â ñòðóêòóðå CUE SHEET çàïèñü äëÿ òðåêà */ cue_buff[n] = 0x01; cue_buff[n + 1] = i + 1; cue_buff[n + 2] = 0x01; cue_buff[n + 3] = 0x00; cue_buff[n + 4] = 0x00; cue_buff[n + 5] = min; cue_buff[n + 6] = sec; cue_buff[n + 7] = frame; } /* Îïðåäåëÿåì ðàçìåð ïîñëåäíåãî òðåêà */ memset((void *)&s, 0, sizeof(struct stat)); stat(argv[trk_num], &s); /* Ôîðìèðóåì Lead-Out-îáëàñòü */ trk += s.st_size/CD_FRAMESIZE_RAW; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); cue_buff[n] = 0x01; cue_buff[n + 1] = 0xAA; cue_buff[n + 2] = 0x01; cue_buff[n + 3] = 0x01; cue_buff[n + 4] = 0x00; cue_buff[n + 5] = min; cue_buff[n + 6] = sec; cue_buff[n + 7] = frame;
/* Lead-Out */ trk += trk3_size; min = LBA2MIN(trk); sec = LBA2SEC(trk, min); frame = LBA2FRAME(trk, min, sec); cue_buff[40] cue_buff[42] cue_buff[44] cue_buff[46]
= = = =
0x01; cue_buff[41] = 0xAA; 0x01; cue_buff[43] = 0x01; 0x00; cue_buff[45] = min; sec; cue_buff[47] = frame;
cue_sheet_cmd[0] = 0x5D; cue_sheet_cmd[8] = cb_size; if(send_cmd(cue_sheet_cmd, 10, SG_DXFER_TO_DEV, ↵ cue_buff, cb_size, 200) < 0) return -1;
}
free(cue_buff); return 0;
Перепишем функцию send_cue_sheet() для возможности записи произвольного числа аудиотреков. Параметры функции – указатель на массив, содержащий имена файлов в формате WAV, и количество записываемых треков. int send_cue_sheet(char **argv, int trk_num) { int i = 0, n;
86
cue_sheet_cmd[0] = 0x5D; cue_sheet_cmd[8] = cb_size; if(send_cmd(cue_sheet_cmd, 10, SG_DXFER_TO_DEV, ↵ cue_buff, cb_size, 200) < 0) return -1;
}
free(cue_buff); return 0;
Исходный текст программы для записи Audio-CD в режиме SAO находится по адресу: http://bob.netport.com.ua/ sao.tar.gz.
Литература: 1. Мешков В. Запись CD-R/RW-дисков в Linux. Часть 1. – Журнал «Системный администратор», №11(24), ноябрь 2004 г. 2. Мешков В. Запись CD-R/RW-дисков в Linux. Часть 2. – Журнал «Системный администратор», №12(25), декабрь 2004 г. 3. SCSI Multimedia Command Set: http://www.t10.org/ftp/t10/ drafts/mmc5/mmc5r01.pdf.
образование
UNFORMAT ДЛЯ NTFS – Я у вас тут винчестер недавно купил. Так вот, он сдох! – Гарантия какая? – Пожизненная. – Раз сдох, значит, гарантия кончилась. Разговор продавца с покупателем
Случилось самое страшное: вы потеряли весь NTFS-раздел целиком. Случайно отформатировали или пережили разрушительный дисковый сбой. Где-то там остались миллиарды байт бесценных данных, теперь уже недоступных операционной системе. Как вернуть информацию к жизни? В этой статье автор делится советами ручного и автоматического восстановления NTFS-раздела.
КРИС КАСПЕРСКИ 88
образование До сих пор мы рассматривали лишь незначительные дисковые сбои и легкие разрушения данных типа ошибочно удаленных файлов. Теперь настал черед тяжелых повреждений, при которых прежнее содержимое тома становится недоступно операционной системе. Причиной может быть и непредумышленное форматирование, и искажение главной файловой таблицы, и… Но вы не падайте духом! Из любых переделок NTFS выходит с минимальными потерями, и во всех этих случаях возможно полное, стопроцентное восстановление данных. Проще всего начать с форматирования. Для экспериментов нам потребуется format.com, входящий в комплект штатной поставки Windows NT/2000/XP и дисковый раздел, не содержащий ничего полезного. Хорошо бы обзавестись виртуальной машиной – Virtual PC или VMWare, эмулирующей жесткий диск и ускоряющей процедуру форматирования в сотни (!) раз.
Ðèñóíîê 1. Ôîðìàòèðîâàíèå âèðòóàëüíîãî äèñêà â ñðåäå VMWare
«Живой» винчестер лучше не трогать (во всяком случае до тех пор, пока вы не научитесь его восстанавливать). Если нет виртуальной машины (до сих пор не поставили Осла? не пользуетесь IRC?), попробуйте приобрести ZIP-привод (который, кстати говоря, намного надежнее оптических накопителей) и форматируйте дискеты под NTFS – благо штатный форматер это позволяет. С гибкими дисками дело обстоит сложнее. По мнению Microsoft, их емкости недостаточ-
Восстановление NTFS-тома, отформатированного под FAT16/32 При переформатировании диска операционные системы семейства NT никогда не изменяют тип файловой системы (разве что попросить их это сделать явно), поэтому непреднамеренное форматирование NTFS-раздела в FAT16/32 крайне маловероятно. Windows 9x/MS-DOS, напротив, любой диск стремятся отформатировать под FAT16/32, не замечая, что на нем что-то уже находится. Непреднамеренная порча NTFS-разделов при установке Windows 9x/MSDOS поверх Windows NT – обычное дело, через которое проходит уже второе поколение пользователей. Стратегия спасения данных во всем похожа на восстановление NTFS-тома, отформатированного под NTFS, с той лишь разницей, что при создании таблицы размещения файлов (file allocation table) первые несколько тысяч фай-
№2, февраль 2005
но для размещения всех структур данных, хотя простейший расчет разбивает это утверждение в пух и прах, что утилита NTFSflp от Марка Руссиновича (mark@sysinternals.com), собственно говоря, и демонстрирует. Статья «NTFS Support for Floppy Disks» (http://www.sysinternals.com/ntw2k/freeware/ ntfsfloppy.shtml) подробно описывает, как обхитрить систему, заставив ее отформатировать гибкий диск под NTFS (для этого вам потребуется soft-ice).
Что происходит при форматировании Форматирование диска – это намного более сложная и намного более многостадийная операция, чем это может показаться на первый взгляд. Кто писал свой собственный форматер дискет (а в конце 80-х – начале 90-х его писали практически все), тот поймет. Свои исследования мы начнем с изучения NTFS-тома, отформатированного под NTFS (техника восстановления NTFS-томов, отформатированных под FAT16/32, дана в одноименной врезке). При выполнении команды «format X: /U /FS:NTFS» в файловой системе диска X: происходят следующие изменения (форматирование диска GUI-утилитой, вызываемой из контекстного меню «проводника», осуществляется по аналогичной схеме): ! Формируется boot-сектор в формате NTFS. ! Генерируется новый серийный номер диска и записывается в boot-сектор по смещению 48h байт от его начала. ! Рассчитывается новая контрольная сумма boot-сектора и записывается по смещению 50h от его начала (подробнее см. первую статью этого цикла: «Восстановление данных на NTFS-разделах»). ! Создается новый $MFT-файл, содержащий сведения обо всех файлах на диске и, как правило, размещаемый поверх старого $MFT-файла; исключения здесь крайне редки – разве что прежний $MFT был заблаговременно перемещен дефрагментатором, или при форматировании назначен новый размер кластера. Во всех остальных случаях первые ~24 файловые записи (FILE Record) мрут безвозвратно. В них находится – непосредственно сам $MFT, $MFTMirr, корневой каталог, /$LogFile – файл транзакций, /$BITMAP – карта свободного пространства, /$Secure – дескрипторы безопасности и другие служебные файлы. ловых записей в MFT затираются безвозвратно (впрочем, их еще можно собрать руками, действуя по методике описанной в разделе «Разгребая кластерные обломки» предыдущей статьи этого цикла). Файлы с уцелевшей FILE RECORD легко восстанавливаются R-Studio/GetDataBack/EasyRecovery. Для ручного восстановления всего тома его необходимо заново отформатировать под NTFS, предварительно определив количество секторов в кластере. Далее действуем по плану – увеличиваем размер $MFT, пускаем chkdsk и собираем все, что только можно собрать. Поскольку количество файлов, хранящихся на современных дисках, зачастую исчисляется многими миллионами, потеря первой тысячи из них не так уж и страшна (если только по закону подлости это не будут самые ценные файлы).
89
образование ! Инициируется $MFT:$DATA – назначается новая длина
!
!
!
! ! !
!
! ! !
($MFT:$30.AllocatedSize, $MFT:$30.RealSize, $MFT:$80. AllocatedSize, $MFT:$80.RealSize, $MFT:$80.Compression Size, $MFT:$80.InitializedSize, $MFT:$80.LastVCN), дата/ время создания/последней модификации ($MFT:$10.File CreationTime, $MFT:$10.FileAlertedTime, $MFT:$10.File ReadTime, $MFT:$30.FileCreationTime, $MFT:$30.File AlertedTime, $MFT:$30.MFTChangeTime, $MFT:$30.File ReadTime) и, самое главное, создается новый список отрезков (data-runs), необратимо затирающий старый, а это значит, что собирать фрагментированный $MFT нам придется по частям. Создается новый /$MFT:$BITMAP, отвечающий за занятость файловых записей в MFT – все старые записи помечаются как свободные, однако их фактического удаления при этом не происходит (поле FileRecord.flags остается нетронутым), благодаря чему процедура восстановления заметно упрощается. Чаще всего $MFT:$BITMAP располагается на том же самом месте, что и старый (т.е. между boot-сектором и MFT), забивая прежнее содержимое нулями, однако с помощью утилиты chkdsk его можно восстановить. Создается новый /$BITMAP-файл, отвечающий за распределение дискового пространства (свободные и занятые кластера), опять-таки затирающий старый, но восстанавливаемый chkdsk. Создается новый файл журнала транзакций – /$LogFile, в структуру которого мы углубляться все равно не будем, хотя в NTFS LINUX Project она описана достаточно подробно, но восстанавливать транзакции – это уж слишком. В заголовок файловой записи $MFT заносится новый LogFile Sequence Number или сокращенно LSN. $MFT назначается новый номер последовательности обновления (Update Sequence Number). Создается новое зеркало $MFTMirr, необратимо затирающее старое (в текущих системах оно расположено посередине NTFS-раздела), в результате чего возникает резонный вопрос – какой прок от зеркала, которое ничего не отражает? Создаются новые /$Volume, /$AttrDef и другие служебные файлы, играющие сугубо вспомогательную роль и легко восстанавливаемые chkdsk (хотя /$Volume и присутствует в зеркальной копии MFT, его ценность явно преувеличена). Осуществляется проверка целостности поверхности, и все обнаруженные плохие кластеры заносятся в файл /$BadClus. Формируется новый корневой каталог. Если до форматирования тома на нем присутствовал /System Volume Information-файл, то он обновляется, в противном случае новый /System Volume Information создается только после перезагрузки.
На самом деле процесс форматирования протекает намного сложнее, но не будем в него углубляться – мы же не форматер пишем! Интересующиеся могут взять в руки IDA Pro и расковырять format.com самостоятельно. Подсказка – format.com
90
содержит лишь высокоуровневую надстройку, опирающуюся на библиотеки ifsutil.dll, untfs.dll и… непосредственно сам драйвер файловой системы. Так что дизассемблировать придется много. Чтобы упросить себе работу, можно пронаблюдать за процессом форматирования с помощью шпионских средств – утилит Марка Руссиновича FileMon и DiskMon, бесплатные копии которых можно скачать с www.sysinternals.com. Также не забывайте о точках останова на основные native-API функции такие, как NtFsControlFile, NtDeviceIoControlFile и т. д. Да будет soft-ice вам в помощь!
Ðèñóíîê 2. Èññëåäîâàíèå ïðîöåññà ôîðìàòèðîâàíèÿ
Автоматическое восстановление отформатированного диска Форматирование не уничтожает файловые записи пользовательских файлов, и они могут быть полностью восстановлены. Спасением данных занимается множество утилит (R-Studio, EasyRecovery, GetDataBack и т. д.), однако прямых наследников unformat среди них как-то не наблюдается. Unformat восстанавливал весь том целиком, а эти всего лишь «вытягивают» отдельные уцелевшие файлы/каталоги, переписывая их на новый носитель. Да где же нам его взять?! Запись на оптические накопители отпадает сразу – вопервых, для сохранения 80-120 Гб жесткого диска в лучшем случае потребуется грузовик (на чем еще вы перевезете такое количество болванок?), во-вторых, непосредственная запись CD-R/RW не всегда возможна, ведь при крахе системы восстанавливающие утилиты приходится загружать с CD-ROM, а в большинстве компьютеров установлен только один оптический привод, и, в-третьих, ни одна известная мне утилита не позволяет «разрезать» большие файлы на несколько маленьких. Можно, конечно, перегнать данные по локальной сети (а она есть?) или установить дополнительный винчестер (корпус компьютера не опечатан, имеются свободные каналы контроллера и лишняя наличность в кармане). Но не слишком ли это хлопотно? Тем не менее для «вытягивания» пары сотен особо ценных файлов такая методика вполне подходит. Продемонстрируем технику автоматического восстановления данных на примере утилиты R-Studio от компании RTT Inc (www.r-tt.com). Это довольно мощный и в то же вре-
образование мя простой в управлении инструмент, на который можно положиться. После запуска утилиты мы сразу попадаем в окно «Drive View», где перечислены все физические устройства и логические разделы. Находим среди них «свой» и, нажав правую клавишу мыши, говорим «Scan». Нас запрашивают: начальный сектор для сканирования (start), по умолчанию равный 0, – оставляем его без изменений. Размер сканируемой области (size) по умолчанию развертывается на весь раздел. Это гарантирует, что сканер обнаружит все уцелевшие файловые записи, хотя сам поиск займет значительное время. Можно ли ускорить этот процесс? Давайте подсчитаем. Допустим, восстанавливаемый раздел содержит сто тысяч файлов. Типичный размер файловой записи составляет 1 Кб. При условии, что $MFT не фрагментирован, достаточно просканировать всего ~100 Мб от начала раздела. Если эта величина не превышает 10% полной емкости тома (размер пространства, зарезервированного под MFT) и диск никогда не заполнялся более чем на 90%, то, скорее всего, все так и есть. В противном случае $MFT наверняка фрагментирован и живописно разбросан по всему диску. Впрочем, в случае ошибки мы ничем не рискуем. Вводим N Кб, где N – предполагаемое количество файлов (каталог также считается файлом) и выполняем сканирование – если один или несколько файлов останутся необнаруженными, возвращаемся к настройкам по умолчанию и повторяем процедуру сканирования вновь (если количество имеющихся файлов заранее неизвестно, вводим 10% от емкости тома). В поле File System выбираем файловую систему NTFS, сбрасывая галочки напротив двух остальных (FAT и Ext2FS), т.к. они нам ни к чему, и нажимаем «Scan».
материнский каталог, представляющую собой номер записи в MFT. А запись корневого каталога всегда располагается по одному и тому же месту! Удаленные файловые записи могут ссылаться на уже уничтоженные каталоги. R-Studio складывает их в папки вида $$$FolderXXX, где XXX – порядковый номер директории. Иерархия подкаталогов в большинстве случаев успешно восстанавливается. Просмотр виртуального дерева обнаруженных файлов осуществляется нажатием кнопки <F5> или через одноименный пункт контекстного меню. Выбрав файл (или даже целый каталог с кучей подкаталогов), жмем <F2> или залезаем в предварительный просмотр/редактирование (пункт edit/view контекстного меню). Это достаточно мощный инструмент, отображающий внутреннее содержимое восстанавливаемого файла со всеми его атрибутами, списками отрезков и т. д. в «очеловеченном» формате, хотя до NT Exploder ему ох как далеко. При желании можно восстановить все файлы за раз («Recovery All») или выбрать восстановление по маске (Mask).
Ðèñóíîê 4. Âîññòàíîâëåííàÿ ñòðóêòóðà äèðåêòîðèé
Ðèñóíîê 3. R-Studio îñóùåñòâëÿåò ïîèñê óöåëåâøèõ ôàéëîâûõ çàïèñåé
В процессе сканирования будут найдены все уцелевшие файлы (как удаленные, так и нет) и восстановлена структура директорий по корневой каталог включительно. Постойте! Как же так?! Ведь при форматировании он был уничтожен и сформирован заново! Файловую систему NTFS можно уничтожить только динамитом. Как уже отмечалось, в отличие от FAT, в NTFS каталоги являются лишь вспомогательной структурой данных, проиндексированной для ускорения отображения их содержимого. Всякая файловая запись независимо от своего происхождения, содержит ссылку на
№2, февраль 2005
Ðèñóíîê 5. Êðàñèâûé èíòåðôåéñ Easy Recovery – êîìïåíñàöèÿ çà íèçêîå êà÷åñòâî âîññòàíîâëåíèÿ
Хваленный Easy Recovery от Data Recovery Software вопреки своему названию простотой управления отнюдь не отличается и имеет довольно специфические особенности поведения. С настройками по умолчанию никаких файлов на отформатированном разделе он не увидит, и чтобы зас-
91
образование
Запускаем NT Explorer, переходим в начало MFT («Goto → Mft»), щелкаем по $MF-файлу, находим атрибут $DATA (80h)
и увеличиваем поля Allocated Size/Real Size/Compressed Size на требуемую величину, параллельно с этим корректируя список отрезков (он же run-list). Поле Last VCN трогать не нужно – chkdsk исправит его и сам. Как определить длину не фрагментированного MFT-файла? Она равна разнице номеров первого и последнего секторов, в начале которых присутствует сигнатура «FILE», умноженная на 512 байт (исключая сектора, принадлежащие $MFTMirr). Известные мне дисковые редакторы не поддерживают поиска последнего вхождения, поэтому соответствующую утилиту приходится писать самостоятельно. Впрочем, точную длину MFT определять совершенно необязательно и вполне допустимо взять ее с запасом – лишнее все равно отсеет chkdsk. Действуйте по принципу – лучше перебрать, чем недобрать. Коварный NT Explorer не позволяет редактировать поля в естественном режиме отображения, заставляя нас переключаться в HEX-mode и искать смещения всех значений самостоятельно. Найти заголовок атрибута $DATA очень просто – в его начале расположена последовательность 80 00 00 00 xx 00 00 00 01. В NTFS версии 3.0 она находится по смещению F8h от начала сектора. Поле Real Size во всех версиях NTFS располагается по смещению 30h относительно заголовка, а поля Allocated Size и Initialized Size соответственно по смещениям 28h/38h байт, причем значение Allocates Size должно быть кратно размеру кластера. Кстати, о кластерах. Убедитесь, что при переформатировании диска размер кластера не изменился, в противном случае у вас ничего не получится. Как восстановить исходный размер кластера? Да очень просто – набраться мужества и переформатировать восстанавливаемый диск с ключом /A:x, где x – размер кластера. А как его определить? Возьмем любой файл с известным содержимым и проанализируем его run-list. Пускаем контекстный поиск по всему диску, находим файл, запоминаем (записываем на бумажке) его стартовый сектор, после чего открываем закрепленный за ним FILE Record, декодируем run-list и вычисляем номер первого кластера. Делим номер сектора на номер кластера и получаем искомую величину. Теперь необходимо сгенерировать новый run-list. В общем случае он будет выглядеть так: 13 XX XX XX YY 00, где XX XX XX – трехбайтовый размер $MFT в кластерах, а YY – стартовый кластер. Стартовый кластер обязательно должен указывать на первый кластер MFT, в противном случае chkdsk не сможет работать. Если новый run-list длиннее нынешнего (а именно так скорее всего и будет), необходимо скорректировать длину атрибутного заголовка (она расположена по смещению 04h от его начала). Проделав эту нехитрую операцию, запустим chkdsk с ключом /F и бла-
Источники угрозы
! некорректное поведение различных дисковых утилит
тавить этого зверюгу заработать в Advanced Options (дополнительные опции), необходимо указать Ignore MFT (игнорировать MFT), но и в этом случае качество восстановления будет оставлять желать лучшего.
Ручное восстановление отформатированного диска Нашей целью будет ручное восстановление всего отформатированного раздела без использования дополнительных носителей информации и дорогостоящих утилит от сторонних производителей. Все, что потребуется, – это любой редактор диска (предпочтительнее всего, конечно же, NT Explorer от Runtime Software, но, на худой конец, сойдет и бесплатный Disk Probe/Sector Inspector от Microsoft) и chkdsk. Очевидно, что в процессе форматирования происходит необратимое разрушение большого количества ключевых структур данных, восстанавливать которые вручную было бы слишком затруднительно. Да это, собственно, и не нужно! Идея состоит в том, чтобы вернуть разделу потерянные файловые записи, а все остальные ремонтные работы поручить chkdsk – пускай старается. Дизассемблирование показывает, что единственной структурой данных, без которой не может работать chkdsk, является атрибут $DATA файла $MFT. А раз так, все, что нам надо – воссоздать прежний $MFT:$DATA, разместив его поверх старых файловых записей. В простейшем случае (если $MFT:$DATA не фрагментирован) это достигается спекулятивным увеличением его длины. А как ее увеличить?
Ðèñóíîê 6. Ðó÷íîå âîññòàíîâëåíèå MFT. Ïîä÷åðêíóòû ïîëÿ, ïîäëåæàùèå èçìåíåíèþ
Почему погибают дисковые разделы? Ниже приводится список наиболее распространенных причин, отсортированных в порядке убывания их «популярности»: ! ошибки оператора, вирусы, троянские программы; ! отключение питания/зависание системы во время интенсивных дисковых операций, сопровождаемых обновлением MFT (например, удаление/добавление файлов или каталогов);
92
(Partition Magic, Ahead Nero, Norton Disk Doctor и т. д.);
! физические дефекты оперативной памяти, приводящие к нарушению целостности дискового кэша и как следствие – порче самого диска; ! некорректное поведение привилегированных драйверов, случайно или преднамеренно «залетающих» внутрь служебных структур NTFS-драйвера.
образование женно откинемся на спинку кресла, созерцая, как возрождаются наши милые папки и файлы. Единственное, что не восстанавливается – так это дескрипторы безопасности: всем файлам/папкам назначаются права доступа по умолчанию. В остальном же с отремонтированным диском вполне можно работать, не опасаясь, что он рухнет окончательно. Файлы, ссылающиеся на несуществующие каталоги, складываются в папку Found.xxx. Это «долгожители», пережившие несколько циклов переформатирования, в буквальном смысле вытащенные с того света. Сложнее восстановить том, чей MFT сильно фрагментирован. Прежний run-list при форматировании был уничтожен, зеркальная копия также пострадала. Ничего другого не остается, как собирать все фрагменты руками. Звучит намного страшнее, чем выглядит. В отличие от всех остальных файлов диска $MFT-файл имеет замечательную сигнатуру FILE, присутствующую в начале каждой файловой записи. Все, что нам нужно, – последовательно сканируя раздел от первого кластера до последнего, выписать начало и конец каждого
Полезные советы – план превентивных действий по спасению данных Чтобы предотвратить разрушение тома и упростить задачу восстановления данных, рекомендуется заблаговременно выполнить следующий комплекс мероприятий: ! Переместите $MFT подальше от начала раздела. Первые секторы раздела, как показывает практика, самое небезопасное место. Во-первых, сюда стремятся вирусы (миф о невозможности прямого доступа к диску под NT всего лишь миф – читайте описание функции CreateFile и инструкцию на ASPI32-драйвер), во-вторых, некоторые утилиты (и в частности Ahead Nero) при некоторых обстоятельствах путают жесткий диск с оптическим накопителем, записывая образ не «туда», а, значит, в первых ~700 Мбайтах физического диска (не логического тома!) не должно быть ничего ценного, в-третьих, если вы вдруг запустите WipeDisk или любую другую затирающую утилиту, первым погибнет именно $MFT, без которого весь дисковый том просто груда мусора, в-четвертых… да много разных причин можно найти. Просто переместите $MFT, вам что – трудно? Достаточно взять любой дефрагментатор, распространяющийся в исходных текстах (энтузиасты! ау! присоединяйтесь к проекту http://sourceforge.net/projects/ opendefrag), и слегка доработать его «напильником» под наши нужды. Естественно, валерьянка и резервная копия обязательно должны быть под рукой! ! Не допускайте фрагментации $MFT-файла! Не создавайте на диске огромного количества мелких файлов и не заполняйте его более чем на 90%. Стандартный дефрагментатор, входящий в комплект штатной поставки Windows 2000/XP, не позволяет дефрагментировать $MFT, и приходится прибегать к сторонним средствам, лучшим из которых на мой взгляд является O&O Defrag Pro от одноименной компании (www.oo-software.com). Это действительно профессиональный дефрагментатор, к тому же поддерживающий командную строку. ! Периодически создавайте резервную копию файловой записи $MFT – для этого достаточно сохранить один-един-
№2, февраль 2005
из фрагментов, принадлежащих MFT. Затем из этой цепочки необходимо исключить $MFTMirr. Его легко узнать – он расположен в середине раздела и содержит копии файловых записей $MFT, $MFTMirr, $LogFile и $Volume, причем $MFTMirr ссылается сам на себя. Допустим, наш список выглядит так: 08h – 333h, 669h – 966h, 1013 – 3210h. В грубом приближении ему будет соответствовать следующий run-list: 12 2B 03 08 22 23 03 69 96 22 FD 21 13 10 00. (Подробнее о кодировании/декодировании run-list см. «Списки отрезков» в прошлой статье этого цикла). «В грубом» потому, что мы не знаем, в какой последовательности располагались эти отрезки в файле (порядок расположения фрагментов на диске далеко не всегда совпадает с порядком отрезков в run-list). Что произойдет, если порядок сборки $MFT-файла окажется нарушен? А вот что – внутри MFT все файловые записи ссылаются друг на друга по своему порядковому номеру, представляющему индекс массива. Эти ссылки необходимы для восстановления структуры директорий, организации hard link и еще кое-чего. ственный (!) сектор – первый сектор MFT, номер которого содержится в boot, только не забывайте его периодически обновлять, ведь при добавлении новых файлов/ каталогов MFT планомерно расширяется и старые списки отрезков становятся все менее и менее актуальны.
Ðèñóíîê 7. Íîðìàëüíûé äèñêîâûé òîì (MFT, âûäåëåííûé æåëòûì öâåòîì, ðàñïîëîæåí â íà÷àëå ðàçäåëà)
Ðèñóíîê 8. «Èììóíèçèðîâàííûé» äèñêîâûé òîì (MFT ðàñïîëîæåí â ñåðåäèíå)
93
образование Ссылки на материнский каталог дублируются в индексах и восстанавливаются элементарно. Hard link мрут безвозвратно (ну разве что попробовать пересобрать $MFT-файл в другом порядке), но они практически нигде и никем не используются, как говорится, было бы что терять. По-настоящему туго приходится сильно фрагментированным файлам, занимающим несколько файловых записей, раскиданных по разным $MFT-фрагментам. Здесь выручает только перестановка фрагментов. К счастью, количество комбинаций обычно бывает невелико и процедура восстановления занимает совсем немного времени. Хорошая новость – начиная с NTFS версии 3.1 (соответствующей Windows XP) в MFT номера файловых записей хранятся в явном виде (четырехбайтовое поле, расположенное по смещению 2Ch от начала FILE Record), что делает задачу восстановления тривиальной.
Восстановление после тяжелых разрушений В результате сбоя содержимое дискового тома может стать недоступно операционной системе, и при попытке чтения его оглавления будут выдаваться сообщения в стиле «Файл или папка повреждены. Чтение невозможно», «Нет доступа к диску», «Системе не удается найти логическое устройство» и т. д. Chkdsk говорит, что «Повреждена основная таблица файлов» и прекращает работу. Караул! Верните мой том! Не паникуйте! Попробуйте запустить NT Explorer и посмотрите, что он покажет. Маловероятно, чтобы содержимое всего тома было утеряно целиком (это же что такое с ним нужно было сделать?!). Если хотя бы часть файловых записей уцелела, R-Studio/GetDataBack/EasyRecovery их обязательно восстановят! Анализ показывает, что основной причиной, по которой chkdsk отказывается проверять том, обычно становится порча файловой записи, описывающей $MFT. Если в процессе обновления $MFT внезапно отключить питание – такой исход событий вполне вероятен, особенно на жестких дисках с емким аппаратным кэшем – они не успевают завершить сохранение секторов на энергии, накопленной в конденсаторах, а вот их младшие собратья с этим справляются. То же самое происходит при неудачном перемещении $MFT-файла или физическом разрушении первого MFT-сектора. Зеркальная копия $MFT во всех этих случаях остается цела, однако chkdsk по каким-то таинственным причинам не хочет ей пользоваться, и вы должны восстановить ее самостоятельно. Просто скопируйте первый сектор $MFTMirr в первый сектор $MFT. Поклонники Sector Inspector могут воспользоваться командным файлом следующего содержания. Ëèñòèíã 1. Êîìàíäíûé ôàéë äëÿ ðó÷íîãî âîññòàíîâëåíèÿ $MFT èç $MFTMirr, XXX – íîìåð ñåêòîðà $MFTMirr, YYY – íîìåð ñåêòîðà $MFT SECINSPECT.EXE -backup d: backup.dsk SECINSPECT.EXE –restore d: backup.dsk
XXX YYY
1 CONFIRM
Теперь можно смело пускать chkdsk. Если же он по-прежнему не работает, значит, либо поврежден загрузочный
94
сектор (а методику его восстановления мы уже обсуждали), либо run-list файла $MFT:$DATA не совпадает с истинным началом MFT (найдите сектор с файловой записью $MFT внутри и исправьте run-list), либо размер кластера, прописанный в boot-секторе отличается от его фактического размера (о том, как определять истинный размер кластера, мы уже говорили выше). Если же сбой был настолько серьезен, что вместе с $MFT пострадало и зеркало, задача сводится к восстановлению отформатированного диска. Кстати говоря, при тяжелых разрушениях файловой структуры, когда на диске образуется настоящий кавардак, восстановление тома полезно начинать с… его форматирования. Нет, это не первоапрельская шутка! Утилита format формирует заведомо исправные ключевые структуры, ну а подключение файловых записей – не проблема. Главное – сохраните run-list файла $MFT:$DATA, если, конечно, он еще уцелел. Все остальное – дело техники!
Ðèñóíîê 9. Áåçóñïåøíàÿ ïîïûòêà ïðî÷èòàòü ïîâðåæäåííûé òîì
Заключение Наш затянувшийся разговор о восстановлении данных подходит к своему логическому концу, однако NTFS не стоит на месте, а интенсивно развивается. И хотя до сих пор эти изменения носили чисто косметический характер, в Windows Longhorn все обещает кардинальным образом измениться. Microsoft активно работает над новой файловой системой – Windows File System, или сокращенно WinFS, сроки выхода которой, кстати говоря, постоянно переносятся (разработка файловой системы это не шутка!). По словам вице-президента Microsoft Боба Маглиа, WinFS – это все та же NTFS, с прикрученным SQL и XML. Насколько изменятся базовые структуры файловой системы общественности до сих пор неясно. И уж совсем непонятно, зачем NTFS понадобился реляционный SQL, когда эти возможности в нее закладывались изначально, просто их не успели доделать. Любой системный программист запросто напишет драйвер, принимающий SQL/XML запросы и транслирующий их в обращения к драйверу текущей файловой системы! Что-либо менять внутри NTFS совсем необязательно. Сдается мне, что это всего лишь очередной маркетинговый трюк, подталкивающий пользователей к переходу на Longhorn! С другой стороны, развитие NTFS можно только приветствовать, поскольку оно дает пищу всем специалистам по восстановлению данных, ведь старые утилиты с новой файловой системой скорей всего окажутся несовместимы.
подписка на I полугодие 2005 Российская Федерация ! Подписной индекс: 81655
Каталог агентства «Роспечать»
!
Объединенный каталог «Пресса России» Адресный каталог «Подписка за рабочим столом» Адресный каталог «Библиотечный каталог» Альтернативные подписные агентства: Агентство «Интер-Почта» (095) 500-00-60, курьерская доставка по Москве Агентство «Вся Пресса» (095) 787-34-47 Агентство «Курьер-Прессервис» Агентство «ООО Урал-Пресс» (343) 375-62-74 Подписка On-line http://www.arzy.ru http://www.gazety.ru http://www.presscafe.ru
!
! Подписной индекс: 87836
!
!
! Казахстан
!
! !
СНГ В странах СНГ подписка принимается в почтовых отделениях по национальным каталогам или по списку номенклатуры АРЗИ: ! Азербайджан – по объединенному каталогу российских изданий через предприятие по распространению печати «Гасид» (370102, г. Баку, ул. Джавадхана, 21)
!
– по каталогу «Российская Пресса» через ОАО «Казпочта» и ЗАО «Евразия пресс» Беларусь – по каталогу изданий стран СНГ через РГО «Белпочта» (220050, г.Минск, пр-т Ф.Скорины, 10) Узбекистан – по каталогу «Davriy nashrlar» российские издания через агентство по распространению печати «Davriy nashrlar» (7000029, Ташкент, пл.Мустакиллик, 5/3, офис 33) Армения – по списку номенклатуры «АРЗИ» через ГЗАО «Армпечать» (375005, г.Ереван, пл.Сасунци Давида, д.2) и ЗАО «Контакт-Мамул» (375002, г. Ереван, ул.Сарьяна, 22) Грузия – по списку номенклатуры «АРЗИ» через АО «Сакпресса» ( 380019, г.Тбилиси, ул.Хошараульская, 29) и АО «Мацне» (380060, г.Тбилиси, пр-т Гамсахурдия, 42) Молдавия – по каталогу через ГП «Пошта Молдавей» (МД-2012, г.Кишинев, бул.Штефан чел Маре, 134) по списку через ГУП «Почта Приднестровья» (МD-3300, г.Тирасполь, ул.Ленина, 17) по прайслисту через ООО Агентство «Editil Periodice» (2012, г.Кишинев, бул. Штефан чел Маре, 134) Подписка для Украины: Киевский главпочтамп Подписное агентство «KSS» Телефон/факс (044)464-0220
Подписные индексы:
81655 по каталогу агентства «Роспечать»
87836 по каталогу агентства «Пресса России»
№2, февраль 2005
95
СИСТЕМНЫЙ АДМИНИСТРАТОР №2(27), Февраль, 2005 год РЕДАКЦИЯ Исполнительный директор Владимир Положевец Ответственный секретарь Наталья Хвостова sekretar@samag.ru Технический редактор Владимир Лукин Редакторы Андрей Бешков Алексей Барабанов Валентин Синицын РЕКЛАМНАЯ СЛУЖБА тел./факс: (095) 928-8253 Константин Меделян reсlama@samag.ru Верстка и оформление imposer@samag.ru maker_up@samag.ru Дизайн обложки Николай Петрочук 103045, г. Москва, Ананьевский переулок, дом 4/2 стр. 1 тел./факс: (095) 928-8253 Е-mail: info@samag.ru Internet: www.samag.ru РУКОВОДИТЕЛЬ ПРОЕКТА Петр Положевец УЧРЕДИТЕЛИ Владимир Положевец Александр Михалев ИЗДАТЕЛЬ ЗАО «Издательский дом «Учительская газета» Отпечатано типографией ГП «Московская Типография №13» Тираж 8000 экз. Журнал зарегистрирован в Министерстве РФ по делам печати, телерадиовещания и средств массовых коммуникаций (свидетельство ПИ № 77-12542 от 24 апреля 2002г.) За содержание статьи ответственность несет автор. За содержание рекламного обьявления ответственность несет рекламодатель. Все права на опубликованные материалы защищены. Редакция оставляет за собой право изменять содержание следующих номеров.
96
ЧИТАЙТЕ В СЛЕДУЮЩЕМ НОМЕРЕ: Система создания документации POD Разрабатываете ли вы утилиту или обширный пакет программ, строите ли аппаратный комплекс или создаёте программно-аппаратную среду, вы неминуемо столкнётесь с необходимостью создания качественной документации. Зачастую требуется, чтобы документация была доступна в нескольких форматах: для печати и для on-line ознакомления. Система POD (Perl Old Documen-tation) предлагает простой язык разметки документов и средства конвертирования, позволяющие получить документы в наиболее «ходовых» форматах: не размеченный текст, manстраница, HTML-страница, PostScript и PDF.
PhpGACL – cистема управления правами Авторизация, аутентификация – эти проблемы всегда появляются при разработке многопользовательских вебприложений. Для их решения применяются различные механизмы, которые давно и хорошо известны. Задача контроля прав доступа несколько сложнее, а её универсальное решение с произвольным количеством объектов и субъектов, причём с простым управлением, вырастает в довольно серьёзную работу. Я не раз и не два наблюдал, как
программист создал собственную систему управления правами, да и сам изобрёл пару велосипедов на этом фронте. PhpGACL – это набор функций, призванный, если и не решить проблему управления правами раз и навсегда, то по крайней мере, значительно её упростить. Он легко встраивается в готовоё приложение и позволяет реализовать довольно сложную схему полномочий пользователей. Объектами доступа могут быть веб-страницы сайта, базы данных, другие пользователи, хосты и т. д. Для установки не требуется ничего, кроме наличия реляционной базы данных (PostgreSQL, MySQL, Oracle) и абстрактный класс доступа к базам данных ADOdb.
FreeBSD в домене Microsoft Windows В статье расматривается использование учетной записи из Active Directory, а также PAM и пакета Samba для того, чтобы зарегистрироваться на компьютере под управлением FreeBSD 5.х с консоли или через xdm/kdm, а также использовать ftp и ssh для доступа к компьютеру и xlockmore для блокировки консоли. Расширенное использование PAM для ограничения пользователей домена, которые могут получить доступ к данному компьютеру, используя свои учетные записи из Active Directory.
Уважаемые читатели! Продолжается подписка на журнал на I-полугодие 2005 года. Если вы не успели подписаться на все шесть выпусков следующего полугодия, вы сможете приобрести недостающие номера через интернет-магазины
Доставка почтой в любую точку России.