- •Методы локальной пользовательской маршрутизации Алгоритм Дейкстры
- •Лекция 4
- •Token Ring и ieee 802.5.
- •Сравнение Token Ring и ieee 802.5
- •Передача маркера
- •Физические соединения
- •Система приоритетов
- •Механизмы управления неисправостями
- •Формат блока данных
- •Протокол udp
- •Назначение полей udp пакета:
- •Протокол tcp
- •Назначение полей tcp пакета:
- •Установление соединения, передача данных и завершение соединения.
- •Механизмы обеспечения достоверности передаваемых данных.
- •Механизм управления потоком данных
- •Лекция 7 Маршрутизация в сетях tcp/ip
- •Алгоритмы маршрутизации
- •Дистанционно-векторный протокол rip.
- •Характеристики протокола rip.
- •Механизмы работы протокола rip.
- •Формат rip-пакета.
- •Лекция 8 Протокол состояния связей ospf
- •Принцип работы
- •Формат пакета ospf.
- •Лекция 9 Протоколы достижимости egp и bgp Протокол egp
- •Egp выполняет три основные функции:
- •Формат заголовка egp-пакета.
- •Протокол bgp
- •Формат заголовка bgp-пакета
- •Сообщения bgp.
- •1. Терминология
- •2. Формат заголовка iPv6
- •3. Ip версия 6 архитектуры адресации
- •4. Модель адресации
- •4.1. Представление записи адресов (текстовое представление адресов)
- •0:0:0:0:0:0:13.1.68.3 0:0:0:0:0:Ffff:129.144.52.38
- •4.2. Представление типа адреса
- •4.3. Уникастные адреса
- •4.3.1. Примеры уникастных адресов
- •4.4. Не специфицированный адрес
- •4.5. Адрес обратной связи
- •4.6. IPv6 адреса с вложенными iPv4 адресами
- •4.7. Nsap адреса
- •4.8. Ipx Адреса
- •4.9. Провайдерские глобальные уникаст-адреса
- •4.10. Локальные уникаст-адреса iPv6
- •4.11. Эникаст-адреса
- •4.11.1. Необходимые эникаст-адреса
- •4.12. Мульткаст-адреса
- •11111111 В начале адреса идентифицирует адрес, как мультикатинг-адрес.
- •4.12.1. Предопределенные мультикаст-адреса
- •4.13. Необходимые адреса узлов
- •5. Заголовки расширения iPv6
- •5.1. Порядок заголовков расширения
- •6. Опции
- •6.1. Опции заголовка Hop-by-Hop (шаг за шагом)
- •7. Маршрутный заголовок
- •8. Заголовок фрагмента
- •9. Заголовок опций места назначения
- •10. Отсутствие следующего заголовка
- •11. О размере пакетов
- •12. Метки потоков
- •13. Приоритет
- •14. О протоколе верхнего уровня 14.1 Контрольные суммы верхнего уровня
- •15. Максимальное время жизни пакета
- •16. Максимальный размер поля данных для протоколов высокого уровня
- •Sctp Материал из Википедии — свободной энциклопедии
- •Многопоточность
- •Достоинства
- •Причины появления
- •Сравнение возможностей протоколов транспортного уровня
- •Архитектура sctp
- •Функционирование sctp
- •Sctp Материал из Wiki.Inattack.Ru.
- •Проблемы tcp
- •Свойства sctp
- •Многодомность
- •Инициация
- •Передача данных
- •Отключение
- •Структура пакета
- •Обработка ошибок
- •Лекция 15 Технологии параллельного программирования. Message Passing Interface (mpi)
- •Mpi. Терминология и обозначения
- •Общие процедуры mpi
- •Прием/передача сообщений между отдельными процессами Прием/передача сообщений с блокировкой
- •Прием/передача сообщений без блокировки
- •Объединение запросов на взаимодействие
- •Совмещенные прием/передача сообщений
- •Коллективные взаимодействия процессов
- •Синхронизация процессов
- •Работа с группами процессов
- •Предопределенные константы Предопределенные константы типа элементов сообщений
15. Максимальное время жизни пакета
В отличие от IPv4, узлы IPv6 не требуют установки максимального времени жизни пакетов. По этой причине поле IPv4 "time to live" (TTL) переименовано в "hop limit" (предельное число шагов) для IPv6. На практике очень немногие IPv4 приложения, используют ограничения по TTL, так что фактически это не принципиальное изменение.
16. Максимальный размер поля данных для протоколов высокого уровня
При вычислении максимального размера поля данных, доступного для протокола верхнего уровня, должен приниматься во внимание большой размер заголовка IPv6 относительно IPv4. Например, в IPv4, mss опция TCP вычисляется как максимальный размер пакета (значение по умолчанию или величина полученная из MTU) минус 40 октетов (20 октетов для минимальной длины IPv4 заголовка и 20 октетов для минимальной длины TCP заголовка). При использовании TCP поверх IPv6, MSS должно быть вычислено как максимальная длина пакета минус 60 октетов, так как минимальная длина заголовка IPv6 (т.e., IPv6 заголовок без заголовков расширения) на 20 октетов больше, чем для IPv4.
Лекция 11
Пример-программа:
#include <stdio.h>
#include <pcap.h>
#define MAXLEN 1500
#define TIMEOUT 500
#define IP_H sizeof(struct ip)
#define ETH_H 14
#define TCP_H sizeof(struct tcphdr)
pcap_t *fd;
int main(int argc, char **argv)
{
struct bpf_program bpf_fil;
char err[PCAP_ERRBUF_SIZE];
char *device;
bpf_u_int32 net, mask;
pcap_handler process;
device=pcap_lookupdev(err);
printf("Using device: %s\n", device);
if((fd=pcap_open_live(device, MAXLEN, 1, TIMEOUT, err))==NULL)
{
printf("Cant open device %s\n",err);
exit(0);
}
/*if(pcap_datalink(fd)!=DLT_EN10MB)
*/
if((pcap_lookupnet(device,&net,&mask, err))<0)
{
printf("pcap_lookupnet error: %s\n", err);
pcap_close(fd);
exit(0);
}
printf("Net number: %s\n", inet_ntoa(net));
printf("Mask: %s\n", inet_ntoa(mask));
return 0;
}
#include <pcap.h>
#define MAXLEN 1500 /* максимальная длинна читаемого с интерфейса пакета -
1500 байт (стандартный size MTU для Ethernet) */
#define TIMEOUT 500 /* таймаут чтения с интерфейса */
#define IP_H sizeof(struct ip)
#define ETH_H 14
#define TCP_H sizeof(struct tcphdr)
void handler(char *, struct pcap_pkthdr *, u_char *); /* обработчик пакетов */
void sighandler(); /* функция обработчик сигналов */
pcap_t *fd; /* дескриптор интерфейса */
int main(int argc, char **argv)
{
struct bpf_program bpf_fil; /* структура для фильтра */
char err[PCAP_ERRBUF_SIZE]; /* буфер для записи ошибок */
char *device; /* буфер, в котором будет находиться название интрефейса */
bpf_u_int32 net, mask; /* номер сети и маска */
pcap_handler process; /* обработчик пакетов */
Описание основных функций:
char *pcap_lookupdev(char *errbuf) Данная функция возвращает имя сетевого интерфейса, установленного по умолчанию; в случае ошибки возвращает NULL и в аргумент функции записывает причину ошибки.
Пример:
#include <pcap.h>
char err[PCAP_ERRBUF_SIZE]; /* буфер для записи ошибок */
char *device; /* буфер, в котором будет находиться название интрефейса */
device = pcap_lookupdev(err);
int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) Функция позволяет получить номер/маску сети на сетевом интерфейсе заданным первым параметром, в случае ошибки возвращает -1 и записывает причину ошибки в последний аргумент. (bpf_u_int32 это unsigned int).
if ((pcap_lookupnet(device, &net, &mask, err)) < 0)
{ /* Обработка ошибки*/} pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) Функция возвращает дескриптор открытого сетевого интрефейса. Что делается с полученным? Происходит перехват пакетов с заданного первым аргументом, второй аргумент это максимальная длинна обрабатываемого пакета, обычно данное if ((fd = pcap_open_live(device, MAXLEN, 1, TIMEOUT, err)) == NULL)
{
printf("Cant open device %s\n", err);
exit(0);
}
значение равняется MTU на открываемом интерфейсе. Следующий аргумент задает: переводить ли интерфейс в PROMISC режим работы (1 - да, 0 - нет) (перехватываются пакеты, адресованные другим станциям сети). Идущий далее параметр задает таймаут чтения с сетевого интерфейса в миллисекундах. Последний параметр - это буфер, куда, при возникновении ошибок, будут записываться причины (при ошибке функция возвращает NULL). Тип данных pcap_t это структура:
struct pcap {
int fd; /* видимо реальный дескриптор интерфейса */
int snapshot;
int linktype; /* тип интерфейса, можно и не использовать, pcap_datalink,
а узнать тип интерфейса, просто pcap_t->linktype
int tzoff; /* оффсет временой зоны */
int offset; /* оффсет для правильной регулировки (чего?) */
struct pcap_sf sf;
struct pcap_md md;
int bufsize;
u_char *buffer;
u_char *bp;
int cc;
u_char *pkt; /* указатель на следующий пакет */
struct bpf_program fcode; /* структура для регулярного выражения
bpf, если bpf не встроен в ядро */
char errbuf[PCAP_ERRBUF_SIZE]; /* буфер для хранения информации об ошибках */
};
int pcap_stats(pcap_t *p, struct pcap_stat *ps) Данная функция используется для получения статистики сетевого интерфейса во время работы нашей программы, первый её аргумент - это дескриптор открытого сетевого интерфейса, второй это указатель на стуркутуру pcap_stat.
struct pcap_stat {
u_int ps_recv; /* кол-во принятых пакетов */
u_int ps_drop; /* кол-во отброшенных пакетов */
u_int ps_ifdrop; /* отброшено определенным интерфейсом (пока не работает) */
};
int pcap_datalink(pcap_t *p) Эта функция отображает тип открытого сетевого интерфейса, заданного его дескриптором. Например возвращает DLT_EN10MB, это означает, что сетевой интерфейс – 10, 100, 1000мбит Ethernet, или DLT_PPP - PPP интерфейс (данная функция удобна для получения информации об используемом канальном уровне на данном интерфейсе).
if (pcap_datalink(fd) != DLT_EN10MB)
{
printf("Only ethernet supported\n");
pcap_close(fd); // закрываем интерфейс
exit(0);
}
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) Данная функция используется для создания цикла для обработки захватываемых пакетов, первым параметром задается дескриптор открытого интерфейса, вторым - количество пакетов, которое необходимо обработать, если параметр отрицательный то число пакетов не ограничено и цикл будет бесконечным, если не возникнет ошибка или пройдет время чтения с интерфейса заданный в функции pcap_open_live. Третий параметр функции это указатель на функцию которая будет вызвана для обработки нового пакета. Прототип функции для обработчика сообщений, имеет следующей вид: void handler(char *, struct pcap_pkthdr *, u_char *); /* обработчик пакетов */
process = (pcap_handler) handler; // получаем указатель на функцию обработчик
if (pcap_loop(fd, -1, process, NULL))
{
pcap_perror(fd, "pcap_loop ");
pcap_close(fd);
exit(0);
}
/* открываем интерфейс и ставим его в PROMISC режим (3 аргумент единица) */
void handler(char *, struct pcap_pkthdr *, u_char *) int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) Используется для занесения в структуру bpf_program строки, заданной 3им аргументом, в которой записано регулярное выражение в стиле bpf (Berkley Packet Filter). Данная возможность позволяет достаточно легко задавать свои правила фильтрации захватываемых пакетов, без того, чтобы писать самому сложные условия обработчика пакетов. Для тех кто не знаком с регулярными выражениями bpf, приведу несколько примеров. Например нам нужно, чтобы в нашу программу попадали только пакеты по протоколу TCP, идущие на порт 21: ip proto TCP and port 21 Или например я хочу получать только пакеты с адреса 192.168.0.1 на адрес 192.168.0.2 по протоколу icmp src 192.168.0.1 and dst 192.168.0.2 and ip proto ICMP Хотим видеть весь траффик, исключая TCP-траффик на 22 порт: proto TCP and not port 22 Показывать пакеты по протоколу ICMP или UDP пакеты на порт 31337: ip proto ICMP or ip proto UDP and port 31337 Пакеты идущие с хоста www.gov.ru: src host www.gov.ru Для получения более подробной информации по регулярным выражениям bpf, читайте tcpdump(1). Четвертый аргументом в функции выглядит так: 1, если оптимизировать код, 0, если нет. Следует добавить, что при оптимизации программа ест гараздо больше ресурсов. /* встриваем фильтр на IPv4 TCP порт 21 */
if ((pcap_compile(fd, &bpf_fil, "ip proto TCP and port 21", 1, mask)) < 0)
{
pcap_perror(fd, "pcap_compile ");
pcap_close(fd);
exit(0);
}
int pcap_setfilter(pcap_t *p, struct bpf_program *fp) Данная функция используется для применения фильтра, встроенного функцией pcap_compile в структуру bpf_program. Функция возвращает -1 при ошибке, и 0 при удачном выполнении функции.
struct bpf_program bpf_fil; /* структура для фильтра */
/* применяем фильтр */
if ((pcap_setfilter(fd, &bpf_fil)) < 0)
{
pcap_perror(fd, "pcap_setfilter ");
pcap_close(fd);
exit(0);
}
char *pcap_geterr(pcap_t *p) Вернёт строку ошибки и будет использована для получения информации об ошибках, в таких функциях как: pcap_setfilter(), pcap_compile(), pcap_loop() и др. Первым аргументом прописывается дескриптор открытого интерфейса. void pcap_perror(pcap_t *p, char *prefix) Выдаёт произошедшую ошибку на экран, используется для получения ошибок в функциях: pcap_setfilter(), pcap_compile(), pcap_loop() и др. Первым аргументом задается дескриптор открытого интерфейса. u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) Функция возвращает указатель на следующий захваченный пакет. Вторым аргументом функции, задается указатель на достаточно полезную структуру pcap_pkthdr, которая будет заполнена данными после выполнения функции. Так же рассматриваемая структура используется в обработчике пакетов, вызываемом функцией pcap_loop.
struct pcap_pkthdr {
struct timeval ts; /* временной штамп */
bpf_u_int32 caplen; /* длина пасти в данный момент */
bpf_u_int32 len; /* размер текущего захваченного пакета */
};
void pcap_close(pcap_t *p) Данная функция закрывает открытый интерфейс. Вот минимальные набор функций, что необходимы для написания своего анализатора траффика (сниффера), теперь перейдем непосредственно к написанию.
Простейший сниффер ftp паролей.
#include <pcap.h>
#include <stdio.h>
#define MAXLEN 1500 /* максимальная длинна читаемого с интерфейса пакета -
1500 байт (стандартный size MTU для Ethernet) */
#define TIMEOUT 500 /* таймаут чтения с интерфейса */
#define IP_H sizeof(struct ip)
#define ETH_H 14
#define TCP_H sizeof(struct tcphdr)
void handler(char *, struct pcap_pkthdr *, u_char *); /* обработчик пакетов */
void sighandler(); /* функция обработчик сигналов */
pcap_t *fd; /* дескриптор интерфейса */
int main(int argc, char **argv)
{
struct bpf_program bpf_fil; /* структура для фильтра */
char err[PCAP_ERRBUF_SIZE]; /* буфер для записи ошибок */
char *device; /* буфер, в котором будет находиться название интрефейса */
bpf_u_int32 net, mask; /* номер сети и маска */
pcap_handler process; /* обработчик пакетов */
/* выбираем сетевой интерфейс среди полученных функцией
pcap_lookupdev, или устанавливаем заданный вручную */
if (argc < 2)
{
printf("FTP password sniffer 1.0\n");
printf("Usage: %s \n", argv[0]);
device = pcap_lookupdev(err);
if (device == NULL)
{
printf("pcap_lookupdev error: %s\n", err);
exit(0);
}
printf("Interface not defined, using default\n");
}
else
{
device = (char *) calloc(3, 3);
strncpy(device, argv[1], 3);
printf("Using device: %s\n", device);
}
process = (pcap_handler) handler; // получаем указатель на функцию обработчик
/* открываем интерфейс и ставим его в PROMISC режим (3 аргумент единица) */
if ((fd = pcap_open_live(device, MAXLEN, 1, TIMEOUT, err)) == NULL)
{
printf("Cant open device %s\n", err);
exit(0);
}
signal(SIGINT, sighandler); /* сигнал INT будет обработан нашей функцией sighandler */
/* для начала сделаем, что бы наша программа работала только на Ethernet,
т.к. придётся немного повозиться с размерами фреймов канального уровня,
используемого на интерфейсе */
if (pcap_datalink(fd) != DLT_EN10MB)
{
printf("Only ethernet supported\n");
pcap_close(fd); // закрываем интерфейс
exit(0);
}
/* получаем номер сети и маску сети */
if ((pcap_lookupnet(device, &net, &mask, err)) < 0)
{
printf("pcap_lookupnet error: %s\n", err);
pcap_close(fd);
exit(0);
}
/* встриваем фильтр на IPv4 TCP порт 21 */
if ((pcap_compile(fd, &bpf_fil, "ip proto TCP and port 21", 1, mask)) < 0)
{
pcap_perror(fd, "pcap_compile ");
pcap_close(fd);
exit(0);
}
/* применяем фильтр */
if ((pcap_setfilter(fd, &bpf_fil)) < 0)
{
pcap_perror(fd, "pcap_setfilter ");
pcap_close(fd);
exit(0);
}
if (pcap_loop(fd, -1, process, NULL))
{
pcap_perror(fd, "pcap_loop ");
pcap_close(fd);
exit(0);
}
return 0;
}
/* функция обработки пакетов */
void handler(char *user, struct pcap_pkthdr *pkthdr, u_char *packet)
{
struct ip *ip; /* сткуктура IPv4 заголовка */
char *data;
char string[32];
int i;
ip = (struct ip *) (packet + ETH_H); /* забиваем структуру ip c оффсетом длинны
заголовка канального уровня (ethernet) */
data = (char *) (packet + ETH_H + IP_H + TCP_H); /* получаем указатель на
нформацию пакете */
if ((strncmp(data, "USER", 4) == 0) || (strncmp(data, "PASS", 4) == 0) ||
(strncmp(data, "pass", 4) == 0 )) /* ищем в пакете строчки USER или PASS */
{
if (pkthdr->len-ETH_H-IP_H-TCP_H > 32) return;
printf("FTP: %s -> ", inet_ntoa(ip->ip_src));
printf("%s\n", inet_ntoa(ip->ip_dst));
memset(string, 0, 32);
strncpy(string, data, 32);
for (i=0; i
Все программы, основанные на pcap, следует линковать с библиотекой, в которой находятся все функции pcap. Это делаеться флагом -l в gcc например: gcc -o ftpsniff ftpsniff.c -lpcap Если компилятор выдает ошибки, что линкер не может найти данную бибиотеку убедитесь, что она установлена правильно и находится в папке где её видит ldconfig. Список всех загруженных библиотек можно посмотреть командой ldconfig с флагом -r под (*BSD) и с флагом -p под Linux\\\'ом. Так же привеем очень полезную функцию копирования регулярных выражений для bpf, которую я подсмотрел в dsniff, а Dug Song, автор dsniff, в свою очередь, сдул её у программистов tcpdump ;)) Хочу всем порекомендовать держать под рукой исходники linniff и tcpdump: оттуда много полезного можно взять, и найти решение, при написании чего-то нового. Так же не стоит ограничиваться, при написание анализаторов траффика (снифферов) libpcap, есть такая замечательная библиотека libnids, используемая в dsniff и других подобных программах, позволяющая выделять tcp сессии из траффика, что ОЧЕНЬ удобно в написании снифферов паролей и NIDS системах.
char *copy_argv(char **argv)
{
char **p, *buf, *src, *dst;
u_int len = 0;
p = argv;
if (*p == 0)
return (0);
while (*p)
len += strlen(*p++) + 1;
if ((buf = (char *)malloc(len)) == NULL)
err(1, "copy_argv: malloc");
p = argv;
dst = buf;
while ((src = *p++) != NULL) {
while ((*dst++ = *src++) != \'\0\')
;
dst[-1] = \'\';
}
dst[-1] = \'\0\';
return (buf);
}
Лекция 13
Протокол передачи SCTP
(Протокол передачи с управлением потоком объединяет преимущества протоколов TCP и UDP)
Протокол передачи с управлением потоком (Stream Control Transmission Protocol, SCTP) − это надежный транспортный протокол, который обеспечивает стабильную, упорядоченную (с сохранением порядка следования пакетов) передачу данных между двумя конечными точками (подобно TCP). Кроме того, протокол обеспечивает сохранение границ отдельных сообщений (подобно UDP). Однако в отличие от протоколов TCP и UDP протокол SCTP имеет дополнительные преимущества, такие как поддержка множественной адресации (multihoming) и многопоточности (multi-streaming) - каждая из этих возможностей увеличивает доступность узла передачи данных. В этой статье мы познакомимся с основными характеристиками протокола SCTP ядра Linux® 2.6 и рассмотрим исходный текст программ сервера и клиента, демонстрирующий возможности протокола по многопоточной передаче данных.
Протокол
SCTP представляет собой надежный
универсальный протокол транспортного
уровня для сетей IP. Несмотря на то, что
протокол изначально разрабатывался
для передачи телефонных сигналов (RFC
2960), SCTP имеет дополнительные преимущества
− он лишен некоторых ограничений
протокола TCP, обладая при этом возможностями
протокола UDP. Протокол SCTP предоставляет
возможности, обеспечивающие высокую
доступность, повышенную надежность и
улучшенную безопасность сокетов. На
рисунке 1 показана многоуровневая
архитектура стека IP.

Рисунок 1. Многоуровневая архитектура стека IP
В этой статье дается общее представление о протоколе SCTP в ядре Linux 2.6, подчеркиваются его расширенные функциональные возможности (такие как множественная адресация и многопоточность), а также содержатся фрагменты (со ссылкой на полную версию) исходного текста программ клиента и сервера для демонстрации возможностей готовности и многопоточности.
Начнем с обзора стека протокола IP.
Стек протокола IP.
Набор протоколов Internet разделяется на несколько уровней; каждый уровень предоставляет определенный набор функциональных возможностей (см. рисунок 1).
Рассмотрим уровни стека протокола IP, начиная с нижнего:
Канальный уровеньпредоставляет физический интерфейс к среде передачи (например, Ethernet).
Сетевой уровень управляет передачей пакетов по сети, обеспечивая их доставку получателю (этот процесс называют маршрутизацией).
Транспортный уровень обеспечивает управление потоком пакетов между двумя хостами в интересах прикладного уровня. Он также предоставляет приложению конечную точку взаимодействия, называемую портом.
Наконец, прикладной уровень обеспечивает представление данных, передаваемых через сокет. Эти данные могут, например, состоять из сообщений электронной почты, передаваемых по протоколу SMTP, или Web-страниц, передаваемых по протоколу HTTP.
В качестве интерфейса к протоколу транспортного уровня все протоколы прикладного уровня используют уровень сокетов. Интерфейс Sockets API был разработан для операционной системы UNIX® ® Калифорнийским университетом в Беркли (UC Berkeley).
Перед рассмотрением функционирования протокола SCTP вспомним, как работают традиционные протоколы транспортного уровня.
Протоколы транспортного уровня.
Двумя наиболее популярными протоколами транспортного уровня являются протоколы TCP и UDP:
Протокол TCP - это надежный протокол, который гарантирует последовательную упорядоченную передачу данных и обеспечивает управление нагрузкой в сети.
Протокол UDP, ориентированный на работу с сообщениями, не предоставляет никаких гарантий ни по очередности доставки сообщений, ни по контролю перегрузки.
Тем не менее UDP является быстрым протоколом и обеспечивает сохранение границ сообщений.
В этой статье рассматривается альтернативный протокол SCTP. Он обеспечивает надежную упорядоченную передачу данных подобно протоколу TCP, но ориентирован на передачу сообщений подобно протоколу UDP. Кроме того, протокол SCTP предоставляет дополнительные возможности:
множественная адресация;
многопотоковая передача данных;
безопасность устанавливаемого подключения;
формирование кадров сообщений;
настраиваемая неупорядоченная передача данных;
поэтапное завершение работы.
Основные особенности SCTP:
По сравнению с традиционными транспортными протоколами наиболее значительные улучшения в протоколе SCTP состоят в поддержке множественной адресации конечных хостов и многопотоковой передачи данных.
Множественная адресация
По сравнению с протоколом TCP поддержка протоколом SCTP множественной адресации обеспечивает приложениям повышенную готовность. Хост, подключенный к нескольким сетевым интерфейсам и потому имеющий несколько IP адресов, называется multi-homed хост. В протоколе TCP соединением называется канал между двумя конечными точками (в данном случае сокет между интерфейсами двух хостов). Протокол SCTP вводит понятие ассоциации, которая устанавливается между двумя хостами и в рамках которой возможна организация взаимодействия между несколькими интерфейсами каждого хоста.
На рисунке 2 показано отличие соединения протокола TCP и ассоциации протокола SCTP.

Рисунок 2. Отличие между соединением протокола TCP и ассоциацией протокола SCTP
В верхней части рисунка показано соединение протокола TCP. Каждый хост имеет один сетевой интерфейс, соединение устанавливается между одним интерфейсом на клиенте и одним интерфейсом на сервере. Установленное соединение привязано к конкретному интерфейсу.
В нижней части рисунка представлена архитектура, в которой каждый хост имеет два сетевых интерфейса. Обеспечивается два маршрута по независимым сетям: один от интерфейса C0 к интерфейсу S0, другой — от интерфейса C1 к интерфейсу S1. В протоколе SCTP два этих маршрута объединяются в ассоциацию.
Протокол SCTP отслеживает состояние маршрутов в ассоциации с помощью встроенного механизма контрольных сообщений; при нарушении маршрута передача данных продолжается по альтернативному маршруту. При этом приложению даже не обязательно знать о фактах нарушения и восстановления маршрута.
Переключение на резервный канал также может обеспечивать непрерывность связи для сетевых приложений. Для примера рассмотрим ноутбук, который имеет беспроводный интерфейс 802.11 и интерфейс Ethernet. Пока ноутбук подключен к док-станции, предпочтительнее использовать более скоростной интерфейс Ethernet (в протоколе SCTP используется термин основной адрес); при нарушении этого соединения (в случае отключения от док-станции), будет использоваться соединение по беспроводному интерфейсу. При повторном подключении к док-станции будет обнаружено соединение по интерфейсу Ethernet, через который будет продолжен обмен данными. Таким образом, протокол SCTP реализует эффективный механизм, обеспечивающий высокую готовность и повышенную надежность.
Многопотоковая передача данных
В некоторой степени ассоциация протокола SCTP похожа на соединение протокола TCP. Отличие состоит в том, что протокол SCTP поддерживает несколько потоков в рамках одной ассоциации. Все потоки являются независимыми, но принадлежат одной ассоциации (см. рисунок 3).

Рисунок 3. Взаимосвязь между ассоциацией протокола SCTP и потоками.
Каждому потоку ассоциации присваивается номер, который включается в передающиеся пакеты SCTP. Важность многопотоковой передачи обусловлена тем, что блокировка какого-либо потока (например, из-за ожидания повторной передачи при потере пакета) не оказывает влияния на другие потоки в ассоциации. В общем случае данная проблема получила название блокировка головы очереди (head-of-line blocking). Протокол TCP уязвим для подобных блокировок.
Каким образом множество потоков обеспечивают лучшую оперативность при передаче данных? Например, в протоколе HTTP данные и служебная информация передаются по одному и тому же сокету. Web-клиент запрашивает файл, и сервер посылает файл назад к клиенту по тому же самому соединению. Многопотоковый HTTP-сервер сможет обеспечить более быструю передачу, так как множество запросов может обслуживаться по независимым потокам одной ассоциации. Такая возможность позволяет распараллелить ответы сервера. Это если и не повысит скорость отображения страницы, то позволит обеспечить ее лучшее восприятие благодаря одновременной загрузке кода HTML и графических изображений.
Многопотоковая передача − это важнейшая особенность протокола SCTP, особенно в том, что касается одновременной передачи данных и служебной информации в рамках протокола. В протоколе TCP данные и служебная информация передаются по одному соединению. Это может стать причиной проблем, так как служебные пакеты из-за передачи данных будут передаваться с задержкой. Если служебные пакеты и пакеты данных передаются по независимым потокам, то служебная информация будет обрабатываться своевременно, что, в свою очередь, приведет к лучшему использованию доступных ресурсов.
Безопасность устанавливаемого подключения
Создание нового подключения в протоколах TCP и SCTP происходит при помощи механизма подтверждения (квитирования) пакетов. В протоколе TCP данная процедура получила название трехэтапное квитирование (three-way handshake). Клиент посылает пакет SYN (сокр. Synchronize). Сервер отвечает пакетом SYN-ACK (Synchronize-Acknowledge). Клиент подтверждает прием пакета SYN-ACK пакетом ACK. На этом процедура установления соединения (показана на рисунке 4) завершается.

Рисунок 4. Обмен пакетами при установлении соединения по протоколам TCP и SCTP.
Протокол TCP имеет потенциальную уязвимость, обусловленную тем, что нарушитель, установив фальшивый IP-адрес отправителя, может послать серверу множество пакетов SYN. При получении пакета SYN сервер выделяет часть своих ресурсов для установления нового соединения. Обработка множества пакетов SYN рано или поздно потребует займет всех ресурсыов сервера невозможны обработ новых запросов. Такая атака получила название «отказ в обслуживании» ( Denial of Service (DoS)).
Протокол SCTP защищен от подобных атак с помощью механизма четырехэтапного квитирования (four-way handshake) и вводом маркера (cookie). По протоколу SCTP клиент начинает процедуру установления соединения посылкой пакета INIT. В ответ сервер посылает пакет INIT-ACK, который содержит маркер (уникальный ключ, идентифицирующий новое соединение). Затем клиент отвечает посылкой пакета COOKIE-ECHO, в котором содержится маркер, посланный сервером. Только после этого сервер выделяет свои ресурсы новому подключению и подтверждает это отправлением клиенту пакета COOKIE-ACK.
Для решения проблемы задержки пересылки данных при выполнении процедуры четырехэтапного квитирования в протоколе SCTP допускается включение данных в пакеты COOKIE-ECHO и COOKIE-ACK.
Формирование кадров сообщения
При формировании кадров сообщения обеспечивается сохранение границ сообщения в том виде, в котором оно передается сокету; это означает, что если клиент посылает серверу 100 байт, за которыми следуют 50 байт, то сервер воспринимает 100 байт и 50 байт за две операции чтения. Точно так же функционирует протокол UDP, это является особенностью протоколов, ориентированных на работу с сообщениями.
В противоположность им протокол TCP обрабатывает неструктурированный поток байт. Если не использовать процедуру формирования кадров сообщения, то узел сети может получать данные по размеру больше или меньше отправленных. Такой режим функционирования требует, чтобы для протоколов, ориентированных на работу с сообщениями и функционирующих поверх протокола TCP, на прикладном уровне был предоставлен специальный буфер данных и выполнялась процедура формирования кадров сообщений (что потенциально является сложной задачей).
Протокол SCTP обеспечивает формирование кадров при передаче данных. Когда узел выполняет запись в сокет, его корреспондент с гарантией получает блок данных того же размера (см. рисунок 5).

Рисунок 5. Формирование кадров сообщений по протоколу UDP/SCTP и по протоколу, обрабатывающему неструктурированный поток байт.
При передаче потоковых данных (аудио- и видеоданных) процедура формирования кадров необязательна.
Настраиваемая неупорядоченная передача данных
Сообщения по протоколу SCTP передаются с высокой степенью надежности, но необязательно в нужном порядке. Протокол TCP гарантирует, что данные будут получены именно в том порядке, в котором они отправлялись (это хорошо, учитывая, что TCP – это потоковый протокол). Протокол UDP не гарантирует упорядоченную доставку. При необходимости вы можете настроить потоки в протоколе SCTP так, чтобы они принимали неупорядоченные сообщения.
Эта особенность может быть востребована в протоколах, ориентированных на работу с сообщениями, так как запросы в них независимы и последовательность поступления данных не очень важна. Кроме того, вы можете настроить использование неупорядоченной передачи по номеру потока в ассоциации, т.е. принимая сообщения по потокам.
Поэтапное завершение передачи данных
В отличие от протокола UDP, функционирование которого не предполагает установления соединения, протоколы TCP и SCTP являются протоколами с установлением соединения. Оба эти протокола требуют выполнения процедуры установления и разрыва соединения между корреспондентами. Рассмотрим отличия между процедурой закрытия сокетов протокола SCTP и процедурой частичного закрытия (half-close) протокола TCP.
На рисунке 6 показана последовательность разрыва соединения в протоколах TCP и SCTP.

Рисунок 6. Последовательность разрыва соединения по протоколам TCP и SCTP.
В протоколе TCP возможна ситуация, когда узел закрывает у себя сокет (выполняя посылку пакета FIN), но продолжает принимать данные. Пакет FIN указывает корреспонденту на отсутствие данных для передачи, однако до тех пор, пока корреспондент не закроет свой сокет, он может продолжать передавать данные. Состояние частичного закрытия используется приложениями крайне редко, поэтому разработчики протокола SCTP посчитали нужным заменить его последовательностью сообщений для разрыва существующей ассоциации. Когда узел закрывает свой сокет (посылает сообщение SHUTDOWN), оба корреспондента должны прекратить передачу данных, при этом разрешается лишь обмен пакетами, подтверждающими прием ранее отправленных данных.
Пример реализации многопотоковой передачи.
Теперь, когда вам известны основные возможности протокола SCTP, рассмотрим пример реализации серверного и клиентского приложений. Исходный текст программы написан на языке программирования C и демонстрирует возможности протокола SCTP по многопотоковой передаче данных.
В этом примере сервер реализует одну из форм протокола точного времени, сообщая текущее время подключенному клиенту. Однако для демонстрации возможностей протокола SCTP, я передаю местное время по потоку 0 и время по Гринвичу (GMT) в потоке 1. Этот простой пример позволяет продемонстрировать интерфейс API потокового взаимодействия.
На рисунке 7 демонстрируется весь процесс взаимодействия: показан не только поток данных приложения по API сокетов, но и взаимосвязи между клиентом и сервером.
Серверное и клиентское приложения были разработаны в операционной системе GNU/Linux с ядром 2.6.11 пакетом проекта Linux Kernel SCTP (lksctp). В пакете lksctp, доступном на SourceForge, реализованы нестандартные функции сокетов. Более подробные сведения об этом компоненте можно найти в разделе Ресурсы.

Рисунок 7. Функции сокетов, используемые при многопотоковой реализации сервера и клиента точного времени.
Сервер точного времени
Исходный код приложения многопоточного сервера точного времени показан в листинге 1. Для удобства чтения в листинг 1 не включен код контроля ошибок, однако по ссылке ниже вы можете загрузить полный исходный текст программы, включающий функции контроля ошибок и другие расширения сокетов протокола SCTP.
Листинг 1. Сервер точного времени для протокола SCTP, использующий многопотоковую передачу.
|
int main() { int listenSock, connSock, ret; struct sockaddr_in servaddr; char buffer[MAX_BUFFER+1]; time_t currentTime;
/* Создание сокета SCTP в стиле TCP */ listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Принимается соединение с любого интерфейса */ bzero( (void *)&servaddr, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons(MY_PORT_NUM);
/* Адрес привязки – любой, порт - MY_PORT_NUM */ ret = bind( listenSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
/* Сокет сервера переводится в состояние ожидания соединения */ listen( listenSock, 5 );
/* Цикл работы сервера... */ while( 1 ) {
/* Ожидание соединения клиента */ connSock = accept( listenSock, (struct sockaddr *)NULL, (int *)NULL );
/* Соединение с новым клиентом */
/* Выясняется текущее время */ currentTime = time(NULL);
/* Посылается текущее время по потоку 0 (поток для локального времени) */ snprintf( buffer, MAX_BUFFER, "%s\n", ctime(¤tTime) );
ret = sctp_sendmsg( connSock, (void *)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, LOCALTIME_STREAM, 0, 0 );
/* Посылается GMT по потоку 1 (поток для GMT) */ snprintf( buffer, MAX_BUFFER, "%s\n", asctime( gmtime( ¤tTime ) ) );
ret = sctp_sendmsg( connSock, (void *)buffer, (size_t)strlen(buffer), NULL, 0, 0, 0, GMT_STREAM, 0, 0 );
/* Закрывается клиентское соединение */ close( connSock );
}
return 0; } |
Исходный код в листинге 1 начинается с создания сокета сервера (IPPROTO_SCTP используется для создания сокета SCTP прямого соединения). Затем создается структура sockaddr, в которой указывается, что разрешаются соединения к любому локальному интерфейсу (используется подстановочный (wildcard) адрес INADDR_ANY). Структура sockaddr привязывается к сокету с помощью вызова bind, а потом сокет сервера переводится в состояние прослушивания (listening state). С этого момента возможны входящие соединения.
Обратите внимание на то, что протокол SCTP использует многие из тех же сокетов API, что и протоколы TCP и UDP. Некоторые дополнительные функции API реализованы в инструментах разработки lksctp (см. раздел Ресурсы).
Серверная программа ожидает в цикле нового подключения клиента. При возвращении из функции accept создается новое клиентское подключение, определяемое сокетом connSock. С помощью функции time выясняется текущее время и переводится в строку функцией snprintf. С помощью функции sctp_sendmsg (нестандартный вызов сокета) строка посылается клиенту по заданному потоку (LOCALTIME_STREAM). После того как строка с локальным временем послана, текущее время переводится в формат GMT и посылается по потоку GMT_STREAM.
Таким образом, сервер выполнил свои функции, поэтому сокет закрывается и переходит в режим ожидания нового клиентского подключения. Просто, не так ли? Теперь рассмотрим, как клиент протокола точного времени обрабатывает многопотоковую передачу.
Клиент протокола точного времени
Реализация многопоточного клиента приводится в листинге 2.
Листинг 2. Реализация клиента точного времени для протокола SCTP, использующего многопотоковую передачу.
|
int main() { int connSock, in, i, flags; struct sockaddr_in servaddr; struct sctp_sndrcvinfo sndrcvinfo; struct sctp_event_subscribe events; char buffer[MAX_BUFFER+1];
/* Создание сокета SCTP в стиле TCP */ connSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Определяется адрес точки соединения */ bzero( (void *)&servaddr, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(MY_PORT_NUM); servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
/* Соединение с сервером */ connect( connSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
/* Ожидается получение данных SCTP Snd/Rcv с помощью функции sctp_recvmsg */ memset( (void *)&events, 0, sizeof(events) ); events.sctp_data_io_event = 1; setsockopt( connSock, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events) );
/* Ожидается получение двух сообщений */ for (i = 0 ; i < 2 ; i++) {
in = sctp_recvmsg( connSock, (void *)buffer, sizeof(buffer), (struct sockaddr *)NULL, 0, &sndrcvinfo, &flags );
/* Завершающий символ строки – 0 */ buffer[in] = 0;
if (sndrcvinfo.sinfo_stream == LOCALTIME_STREAM) { printf("(Local) %s\n", buffer); } else if (sndrcvinfo.sinfo_stream == GMT_STREAM) { printf("(GMT ) %s\n", buffer); }
}
/* Закрытие сокета и выход */ close(connSock);
return 0; } |
В клиентском приложении создается сокет протокола SCTP, а затем структура sockaddr, содержащая информацию о конечной точке подключения. После этого с помощью функции connect устанавливается соединение с сервером. Для того чтобы получить номер потока сообщений по протоколу SCTP, необходимо указать параметр сокета sctp_data_io_event.
При получении сообщения с помощью функции API sctp_recvmsg дополнительно принимается структура sctp_sndrcvinfo, содержащая номер потока. Этот номер позволяет различать сообщения потока 0 (местное время) и потока 1 (GMT).
Будущее протокола SCTP.
Протокол SCTP является сравнительно новым протоколом, учитывая то, что он был представлен в виде RFC в октябре 2000 года. С этого момента он начал использоваться во многих операционных системах, включая GNU/Linux, BSD и Solaris. В виде дополнительного коммерческого пакета независимого поставщика он также доступен для операционной системы Microsoft® Windows®.
По мере расширения доступности протокола SCTP его будут использовать в качестве основного транспортного протокола все новые приложения. На базе SCTP уже созданы такие традиционные приложения, как FTP и HTTP. Протокол SCTP также используется и в других протоколах, например, протоколе установления сессии (Session Initiation Protocol (SIP)) и протоколе канальной сигнализации № 7 (Common Channel Signaling System No7 (SS7)).. Коммерческую реализацию протокола SCTP имеет операционная система Cisco IOS.
После включения протокола SCTP в ядро Linux 2.6 стало возможным построение и развертывание надежных сетевых приложений высокой готовности. Основываясь на протоколе IP, SCTP позволяет прозрачно заменить протоколы TCP и UDP, введя при этом дополнительные возможности: множественную адресацию, многопоточную передачу и повышенную безопасность. Сейчас, когда вы познакомились с некоторыми преимуществами протокола SCTP, вы можете исследовать другие его возможности. В проекте Linux Kernel SCTP (lksctp) имеются документация и дополнительные функции API, которые помогут вам в ваших исследованиях.
