Текст книги "Операционная система UNIX"
Автор книги: Андрей Робачевский
Жанр:
ОС и Сети
сообщить о нарушении
Текущая страница: 36 (всего у книги 39 страниц)
Поддержка сети в BSD UNIX
Перейдем теперь к обсуждению внутренней архитектуры сетевого в UNIX. Разговор начнем с ветви UNIX, в которой реализация TCP/IP появилась впервые – BSD UNIX.
Сетевая подсистема UNIX может быть представлена состоящей из трех уровней, каждый из которых отвечает за выполнение определенных функций:
Транспортный уровень | Обмен данными между процессами |
Сетевой уровень | Маршрутизация сообщений |
Уровень сетевого интерфейса | Передача данных по физической сети |
Два верхних уровня представляют собой модули коммуникационных протоколов, а нижний уровень по существу является драйвером устройства. Легко заметить, что представленные уровни соответствуют транспортному, сетевому уровням и уровню канала данных модели OSI.
Транспортный уровень является самым верхним в системе и призван обеспечить необходимую адресацию и требуемые характеристики передачи данных, определенных коммуникационным узлом процесса, которым является сокет. Например, сокет потока предполагает надежную последовательную доставку данных, и в семействе TCP/IP модуль данного уровня реализует протокол TCP. Следующий, сетевой, уровень обеспечивает передачу данных, адресованных удаленному сетевому или транспортному модулю. Для этого модуль данного уровня должен иметь доступ к информации о маршрутах сети (таблице маршрутизации). Наконец, последний уровень отвечает за передачу данных хостам, подключенным к одной физической среде передачи (например, находящимся в одном сегменте Ethernet).
Внутренняя структура сетевой подсистемы изолирована от непосредственного доступа прикладных процессов. Единым (и единственным) интерфейсом доступа к сетевым услугам является интерфейс сокетов, рассмотренный в главе 3 в разделе "Межпроцессное взаимодействие в BSD UNIX. Сокеты". Для обеспечения возможности работы с конкретным коммуникационным протоколом соответствующий модуль экспортирует интерфейсу сокетов функцию пользовательского запроса. При этом данные от прикладного процесса передаются от интерфейса сокетов требуемым транспортным модулям с помощью соответствующих вызовов экспортированных функций. И наоборот, данные, полученные из сети, проходят обработку в соответствующих модулях протоколов и помещаются в очередь приема сокета-адресата.
Движение данных вниз (т.е. от верхних уровней к нижним) обычно инициируется системными вызовами и может иметь синхронный характер. Принимаемые данные из сети поступают в случайные моменты времени и передаются сетевым драйвером в очередь приема соответствующего протокола. При этом функции модуля протокола и обработка данных не вызываются непосредственно сетевым драйвером. Вместо этого последний устанавливает бит соответствующего программного прерывания, в контексте которого система позднее и запускает необходимые функции. Если данные предназначены протоколу верхнего уровня (транспортному), его функция обработки будет вызвана непосредственно модулем сетевого уровня. Если же сообщение предназначено другому хосту, и система выполняет функции шлюза, сообщение будет передано уровню сетевого интерфейса для последующей передачи.
Прежде чем более подробно ознакомиться со взаимодействием различных модулей сетевой подсистемы BSD UNIX, рассмотрим сначала структуры данных, определяющие сокет, коммуникационный протокол и сетевой интерфейс.
Структуры данныхСтруктура данных socket
, описывающая сокет, представлена на рис. 6.21. В этой структуре хранится информация о типе сокета (so_type
), его текущем состоянии (so_state
) и используемом протоколе (so_proto
).
Рис. 6.21. Структуры данных сокета
Сокет является коммуникационным узлом и обеспечивает буферизацию получаемых и отправляемых данных. Как только данные попадают в распоряжение сокета в результате системного вызова (например, write(2) или send(2)), сокет немедленно передает их модулю протокола для последующего отправления. Данные передаются в виде связанного списка специальных буферов mbuf, структура которых также показана на рис. 6.21. Модуль протокола может ожидать подтверждения получения отправленных данных или отложить их отправку. В обоих случаях сообщения остаются в буфере передачи сокета до момента окончательной отправки или получения подтверждения. Аналогично, данные, полученные из сети, в конечном итоге буферизуются в приемной очереди сокета-адресата, пока не будут извлечены оттуда системным вызовом (например, read(2) или recv(2)).
Для избежания переполнения буфер (структура sockbuf
) хранит параметр sb_hiwat
– значение верхней ватерлинии. Модуль коммуникационного протокола может использовать это значение для управления потоком данных. Например, модуль TCP устанавливает максимальное значение окна приема равным этому параметру.
Сокеты, используемые для приема и обработки запросов на установление связи (зарегистрированные с помощью системного вызова listen(2)), адресуют два связанных списка: список сокетов, связь для которых не полностью установлена, и список сокетов, обеспечивающих доступ к созданным каналам передачи данных.
Следующая структура данных, которую мы рассмотрим, относится к коммуникационным протоколам. Каждый модуль протокола представляет собой набор функций обработки и структур данных и описывается структурой данных, называемой коммутатором протокола. Коммутатор протокола хранит адреса стандартных функций протокола, например, функций ввода (pr_input()
) и вывода (pr_output()
), и выполняет ту же роль, что и элемент коммутатора устройств, рассмотренный в главе 5. Поле so_proto сокета содержит адрес этой структуры для соответствующего протокола. Вид коммутатора протокола показан на рис. 6.22.
Рис. 6.22. Коммутатор протокола
Перед первым использованием модуля вызывается функция его инициализации pr_init()
. После этого система будет вызывать функции таймера модуля протокола pr_fasttimo()
каждые 200 миллисекунд и pr_slowtimo()
каждые 500 миллисекунд, если протокол определил эти функции. Например, модуль протокола TCP использует функции таймера для обработки тайм-аутов при установлении связи и повторных передачах. Функция pr_drain()
вызывается системой при недостатке свободной памяти и позволяет модулю уничтожить некритичные сообщения для освобождения места.
С помощью функции pr_usrreq()
модулю протокола передаются сообщения от прикладного процесса. Таким образом, эта функция определяет интерфейс взаимодействия между сокетом и протоколом нижнего уровня. Одним из параметров этой функции является номер запроса, зависящий от произведенного системного вызова. Интерфейс взаимодействия сокета с прикладными процессами является стандартным интерфейсом системных вызовов и преобразует вызовы bind(2), listen(2), send(2), sendto(2) и т.д. в соответствующие запросы функции pr_usrreq()
. Некоторые из них приведены в табл. 6.7.
Таблица 6.7. Запросы функции pr_usrreq()
close(2) | Прекратить обмен данными | PRU_ABORT |
accept(2) | Обработать запрос на установление связи | PRU_ACCEPT |
bind(2) | Связать сокет с адресом | PRU_BIND |
connect(2) | Установить связь | PRU_CONNECT |
listen(2) | Разрешить обслуживание запросов | PRU_LISTEN |
send(2), sendto(2) | Отправить данные | PRU_SEND |
fstat(2) | Определить состояние сокета | PRU_SENSE |
getsockname(2) | Получить адрес локального сокета | PRU_SOCKADDR |
getpeername(2) | Получить адрес удаленного сокета | PRU_PEERADDR |
ioctl(2) | Передать команду модулю протокола | PRU_CONTROL |
Функции pr_input()
и pr_output()
определяют интерфейс взаимодействия протокол-протокол и служат для передачи данных между модулями соседних уровней. Аналогично для обмена управляющими командами между модулями протоколов используются функции pr_ctlinput()
и pr_ctloutput()
. Цепочка взаимодействующих протоколов производит размещение и освобождение памяти при обмене сообщениями, которые передаются посредством рассмотренных структур mbuf
: при передаче сообщений от сети прикладному процессу за освобождение буферов mbuf
отвечает модуль верхнего уровня и наоборот, при передаче сообщений в сеть память, занимаемая сообщением, освобождается на самом нижнем уровне.
Поле pr_flags
определяет некоторые характеристики протокола и режим его функционирования, которые в основном относятся к уровню сокетов. Например, протоколы, предусматривающие предварительное установление связи, указывают это с помощью флага PR_CONNREQUIRED
, не позволяя тем самым функциям сокета передавать данные модулю до создания виртуального канала. Если установлен флаг PR_WANTRCVD
, соответствующие функции сокета будут уведомлять модуль протокола, когда прикладной процесс получает данные из буфера приема. Это может служить сигналом протоколу для отправления подтверждения о получении, а также для обновления значения окна в соответствии с освободившимся местом.
Заметим, что каждый модуль протокола имеет собственные очереди сообщений, используемые для приема и передачи данных.
Каждый сетевой интерфейс системы представлен структурой данных, показанной на рис. 6.23. Сетевой интерфейс обычно связан с соответствующим сетевым адаптером, хотя это не является обязательным условием. Например, внутренний сетевой интерфейс loopback представляет собой псевдоустройство, используемое для унифицированного взаимодействия сетевых процессов в рамках одного хоста, отладки и т.п.
Рис. 6.23. Сетевой интерфейс
Решение об использовании того или иного сетевого интерфейса для передачи сообщения базируется на таблице маршрутизации и производится модулем сетевого уровня. Интерфейс может обслуживать протоколы различных коммуникационных доменов. Соответственно, один и тот же интерфейс может иметь несколько адресов, определенных для каждого семейства протоколов. Структуры, определяющие локальный и широковещательный (broadcast) адреса интерфейса, а также сетевую маску, хранятся в виде связанного списка.
Каждый сетевой интерфейс имеет очередь, в которую помещаются сообщения для последующей передачи, выполняемой функцией if_output()
. Интерфейс также может определить процедуры инициализации if_init()
, сброса if_reset()
и обработки таймера if_watchdog()
. Последняя может использоваться для управления потенциально ненадежными устройствами или для периодического сбора статистики устройства.
Состояние интерфейса характеризуется флагами, хранящимися в поле if_flags
. Возможные флаги приведены в табл. 6.8.
Таблица 6.8. Состояния интерфейса
IFF_UP | Интерфейс доступен для использования |
IFF_BROADCAST | Интерфейс поддерживает широковещательные адреса |
IFF_MULTICAST | Интерфейс поддерживает групповые адреса |
IFF_DEBUG | Интерфейс обеспечивает возможность отладки |
IFF_LOOPBACK | Программный внутренний интерфейс |
IFF_POINTOPOINT | Интерфейс для канала точка-точка |
IFF RUNNING | Ресурсы интерфейса успешно размещены |
IFF_NOARP | Интерфейс не использует протокол трансляции адреса |
Флаг IFF_UP
свидетельствует о готовности интерфейса передавать сообщения. Если сетевой интерфейс подключен к физической сети, поддерживающей широковещательную адресацию (broadcast), например, Ethernet, для интерфейса будет установлен флаг IFF_BROADCAST
и определен широковещательный адрес (поле ifa_broadaddr
структуры адресов ifaddr
для соответствующего коммуникационного домена). Если же интерфейс используется для канала точка-точка, будет установлен флаг IFF_POINTOPOINT
и определен адрес хоста (интерфейса), расположенного на противоположном конце (поле ifa_dstaddr
). Заметим, что эти два флага являются взаимоисключающими, a ifa_broadaddr
и ifa_dstaddr
являются различными именами одного и того же поля. Интерфейс устанавливает флаг IFF_RUNNING
после размещения необходимых структур данных и отправления начального запроса на чтение устройству (например, сетевому адаптеру), с которым он ассоциирован.
Состояние интерфейса и ряд других параметров можно просмотреть с помощью команды ifconfig(1M):
$ ifconfig le0
le0: flags=863
inet 194.85.160.50 netmask: ffffff00 broadcast 194.85.160.255
Легко заметить, что команда выводит значение следующих полей структуры ifnet
для интерфейса le0
(if_name
): if_flags
, if_mtu
(Maximum Transmission Unit, MTU) определяющее максимальный размер пакета, который может быть передан по физической сети, а также значения полей структуры ifaddr: адрес интерфейса inet
(ifa_addr
), маску netmask
(ifa_netmask
) и широковещательный адрес broadcast
(ifa_broadaddr
).
Интерфейс хранит статистическую информацию, которая может быть использована при мониторинге сети. В частности, эта информация включает число полученных пакетов уровня канала (if_ipackets
), количество ошибок при приеме (if_ierrors
), число отправленных пакетов уровня канала (if_opackets
), количество ошибок при передаче (if_oerrors
) и число коллизий (if_collisions
). Команда netstat(1M) позволяет получить эту информацию для сконфигурированных интерфейсов в системе:
$ netstat -in
Name Mtu Net/Dest Address Ipkts Ierrs Opkts Oerrs Collis
lo0 823 127.0.0.0 127.0.0.1 168761 0 168761 0 0
le0 1500 194.85.160.0 194.85.160.50 1624636 1042 110166 1933 382604
Сетевая подсистема предназначена для работы в коммуникационной среде, представляющей собой набор сетевых сегментов, связанных между собой. Связь между отдельными сегментами достигается путем подключения их к хостам, имеющим несколько различных сетевых интерфейсов, как показано на рис. 6.24. Такие хосты при необходимости выполняют передачу данных от одного сегмента к другому (forwarding).[83]83
Заметим, что каждый интерфейс такого хоста-шлюза имеет собственный адрес, соответствующий той сети, к которой он непосредственно подключен. Например, для сетей с разделяемой средой передачи сетевая часть этого адреса равна адресу сети.
[Закрыть] Для сетей пакетной коммутации, о которых идет речь, выполнение этой задачи непосредственно связано с выбором маршрута прохождения пакетов данных (routing). Для этого система хранит таблицы маршрутизации, которые используются протоколами сетевого уровня (например, IP) для выбора требуемого интерфейса для передачи пакета адресату.
Рис. 6.24. Коммуникационная среда UNIX (internetwork)
Маршрутизационная информация хранится в виде двух таблиц, одна из которых предназначена для маршрутов к хостам, а другая – для маршрутов к сетям. Такой подход позволяет использовать универсальные механизмы определения маршрута как для сетей с разделяемой средой передачи (например, Ethernet), так и для сетей с каналами типа точка-точка. Например, для доставки пакета удаленному хосту, подключенному к сети первого типа, достаточно знать адрес этой сети, в то время как для каналов точка-точка необходимо явно задать адрес интерфейса противоположного конца канала.[84]84
Вспомним, что IP-адрес состоит из двух частей – адреса сети и адреса хоста в этой сети. Для интерфейса, подключенного к разделяемой среде, каковой является большинство локальных сетей, существенным является лишь первая часть адреса-получателя, поскольку через этот интерфейс непосредственно доступны все хосты с данным адресом сети. Напротив, через сетевой интерфейс типа точка-точка непосредственный доступ осуществляется к единственному хосту, расположенному на другом конце канала, и, таким образом, необходимо определение полного адреса удаленного интерфейса.
[Закрыть]
При определении маршрута модуль сетевого протокола (IP) сначала просматривает элементы таблицы для хостов, а затем для сетей. Если оба поиска не дают результата, используется маршрут по умолчанию (если такой установлен), определенный как маршрут в сеть с адресом 0. Обычно используется первый найденный маршрут. Таким образом, порядок поиска обеспечивает приоритетность маршрутов к хостам по отношению к маршрутам к сетям, что естественно, поскольку первые представлены более конкретными адресами.
Каждый элемент таблицы маршрутизации, показанный на рис. 6.25, содержит адрес получателя (это может быть адрес сети получателя или адрес конкретного хоста). Это значение хранится в поле rt_dst
. Следующее поле, rt_gateway
, определяет следующий шлюз, которому необходимо направить пакет, чтобы последний в конечном итоге достиг адресата. Поле rt_flags
определяет тип маршрута (к хосту или к сети), а также его состояние. В поле rt_use
хранится число переданных по данному маршруту пакетов, a rt_refcnt
определяет использование маршрута сетевыми процессами (виртуальными каналами). Наконец, поле rt_ifp
адресует сетевой интерфейс, которому необходимо направить пакет для дальнейшей передачи по данному маршруту.
Рис. 6.25. Элемент таблицы маршрутизации
Различают не только маршруты к хостам и сетям, но также маршруты прямые (direct) и косвенные (indirect). Первое различие определяет критерий сравнения адреса получателя пакета с полем rt_dst
элемента таблицы маршрутизации. Если маршрут к сети, то сравнивается только сетевая часть адреса, в противном случае требуется полное совпадение адресов.
Определение маршрута как прямого или косвенного зависит от того, имеется ли непосредственная связь между получателем, указанным в поле rt_dst
, и сетевым интерфейсом, обслуживающим данный маршрут. Например, маршрут в сеть, непосредственно подключенную к сетевому интерфейсу, является прямым. Напротив, маршрут по умолчанию является косвенным маршрутом, поскольку всегда адресует получателя, расположенного вне непосредственно доступных сетевых сегментов. Эта информация необходима при формировании кадра уровня канала данных. Если пакет адресован хосту или сети, которые непосредственно не подключены к сетевому интерфейсу, то, хотя сетевой адрес этого пакета будет равен сетевому адресу фактического получателя данных, заголовок уровня канала данных будет адресовать соседний шлюз, используемый для дальнейшей передачи пакета. Если пакет не выходит за пределы непосредственно подключенной сети, адреса и сетевого уровня и уровня канала будут совпадать с соответствующими адресами фактического получателя.
Данный аспект проиллюстрирован на рис. 6.26. Здесь мы рассмотрели процесс передачи IP-датаграммы хосту, расположенному в удаленном сетевом сегменте Ethernet. Поскольку доставка датаграммы предполагает использование промежуточного шлюза, передача данных на канальном уровне требует соответствующей адресации: на первом "перегоне" в качестве адреса получателя используется МАС-адрес шлюза, и только затем – МАС-адрес фактического адресата.
Рис. 6.26. Инкапсуляция пакетов для косвенных маршрутов
На то, что маршрут является косвенным, указывает флаг RTF_GATEWAY
элемента таблицы маршрутов. В этом случае MAC-адрес получателя при формировании кадра канального уровня, будет определяться исходя из сетевого адреса шлюза, хранящегося в поле rt_gateway
.[85]85
Для определения соответствия между IP-адресами интерфейсов и их MAC-адресами используется протокол ARP (Address Resolution Protocol), позволяющий производить формирование адреса кадра уровня канала данных.
[Закрыть]
Модуль протокола имеет возможность доступа к маршрутизационной информации с помощью трех функций: rtalloc()
для получения маршрута, rtfree()
для его освобождения и rtredirect()
для обработки управляющих сообщений о перенаправлении маршрута (ICMP REDIRECT
).
Функция rtalloc()
позволяет модулю протокола определить маршрут к требуемому адресату. В результате модуль размещает структуру route
, имеющую следующие поля:
struct rtentry *ro_rt | Указатель на соответствующий элемент таблицы маршрутизации |
struct sockaddr ro_dst | Адрес получателя данных |
Возвращаемый функцией rtalloc()
маршрут может быть освобожден с помощью функции rtfree()
(это не означает, что маршрут будет удален из таблицы маршрутизации). Время жизни маршрута зависит от протокола верхнего уровня. Например, модуль протокола TCP хранит маршрут на протяжении жизни виртуального канала.
Функция rtredirect()
обычно вызывается модулем протокола в ответ на получение от соседних шлюзов управляющих сообщений о перенаправлении маршрута.[86]86
В семействе протоколов TCP/IP для этих целей служит протокол ICMP. Сообщения о перенаправлении маршрутов ICMP REDIRECT
формируются IP-модулем шлюза и информируют IP-модули соседних хостов (шлюзов) о существовании более выгодного маршрута к данному адресату.
[Закрыть] Шлюз генерирует такое сообщение в случае, когда обнаружен более предпочтительный маршрут для передаваемого пакета. Например, если хосты А и В находятся в одной и той же сети, и хост А направляет пакеты В через шлюз С, последний отправит А сообщение о перенаправлении маршрута, информирующее, что А в дальнейшем должен посылать данные В непосредственно. Этот процесс показан на рис. 6.27.
Рис. 6.27. Перенаправление маршрутов
Данная возможность может использоваться для упрощения процедуры формирования таблицы маршрутизации. Например, рабочие станции могут хранить только маршрут по умолчанию (в сеть 0), адресующий соседний шлюз. При передаче данных хостам той же сети, что и источник, шлюз будет информировать последний о перенаправлении маршрутов, позволяя тем самым заполнить элементы маршрутизационной таблицы.
Функция rtredirect()
вызывается с параметрами, указывающими на адрес получателя, новый адрес шлюза, который необходимо миновать для достижения адресата, а также источник перенаправления маршрута. Заметим, что сообщения о перенаправлении маршрута принимаются только от текущего шлюза для данного получателя. Если существует маршрут, отличный от маршрута по умолчанию, то для него изменяется поле rt_gateway
согласно указанному в сообщении новому адресу шлюза. В противном случае создается новая запись таблицы маршрутизации.
Вопросы определения маршрутов в UNIX являются прерогативой специальных прикладных процессов, а не ядра операционной системы. Ядро размещает и хранит необходимую маршрутизационную информацию, а также обеспечивает интерфейс доступа к этой информации. Процесс имеет возможность добавить или удалить маршрут с помощью системного вызова ioctl(2). Для добавления маршрута используется команда SIOCADDRT
, а для удаления – SIOCDELRT
.
В качестве процессов, отвечающих за заполнение таблиц маршрутизации и ее динамическое обновление, можно назвать стандартный демон routed(1M), использующий протокол RIP (Routing Information Protocol) для динамического определения и обновления маршрутов, а также демон gated(1M), поддерживающий работу нескольких протоколов обмена маршрутизационной информацией (RIP, OSPF, BGP).
Текущую таблицу маршрутизации можно увидеть, воспользовавшись командой netstat(1M):
$ netstat -rn
Routing Table:
Destination Gateway Flags Ref Use Interface
– – – – – –
127.0.0.1 127.0.0.1 UH 0 5054 lo0
194.85.160.0 194.85.160.50 U 3 30926 le0
default 194.85.160.1 UG 0 47150 le0
Первая запись таблицы показывает маршрут для псевдохоста (localhost) логической сети операционной системы. Следующий маршрут адресует непосредственно подключенную к интерфейсу (его адрес 194.85.160.50) сеть (194.85.160.0). Наконец, последняя запись определяет маршрут по умолчанию, направляя все пакеты, адресованные получателям "внешнего мира", для которых наш хост не знает конкретных маршрутов, на шлюз с адресом 194.85.160.1, который обладает большей информацией о возможных маршрутах.