Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

pumpMagazine. Журнал об IT технологиях.


Информационный Канал Subscribe.Ru


  ____                        __  __                       _
 |  _ \ _   _ _ __ ___  _ __ |  \/  | __ _  __ _  __ _ ___(_)_ __   ___
 | |_) | | | | '_   _ \| '_ \| |\/| |/ _  |/ _  |/ _ |_   / | '_ \ / _ \
 |  __/| |_| | | | | | | |_) | |  | | (_| | (_| | (_| |/ /| | | | |  __/
 |_|    \__,_|_| |_| |_| .__/|_|  |_|\__,_|\__, |\__,_/___|_|_| |_|\___|
                       |_|                 |___/

#1(Чтв Апр 1 00:00:00 MSK 2004) www.fsocp.w6.ru

Содержание:

  1. Введение.
  2. Передача данных через icmp.
  3. Сравнение Ipv4 и ipv6.
  4. Между двумя мирами.



Введение.

Вы видите перед глазами (или держите в руках) первый номер журнала. Он создан чтобы доказать некоторым людям, что и в нашей стране может получиться достойное электронное издание об IT индустрии и сопряженных с ней сферами, которые могут быть интересны читателям.

Мы будем стараться печатать статьи на интересные и нужные темы, которые плохо освещены в рунете. И самое главное мы не будем останавливаться, как многие, на чем-то одном, например безопасности. Изначально планировалось, что здесь будут только чисто технические статьи, но потом решили включить сюда просто интересное и даже рассказы. Рассказы также не будут ограничены одним стилем, например "киберпанком".

Сюда можно (и нужно) присылать свои отзывы и предложения относительно журнала. Если хотите стать автором заходите к нам на сайт, там любой желающий может отправить свои статьи. Опубликованы они будут, правда, только после подтверждения модератором. Выбор статьей для журнала делают зарегестрированные пользователи (зарегестрируйтесь! 8^), и самые лучшие, с их точки зрения, статьи появятся в журнале.

//Codepump team.

Передача данных через icmp.

Введение

Сам по себе протокол icmp является протоколом сетевого уровня, неотемлемой частью IP, а именно средством для сообщения о различных ошибках. Он абсолютно неприспособлен для передачи каких-либо данных. Вместо него вполне можно было бы использовать и IP c каким-то "своим" кодом верхнего протокола.

Преобразование ненадежного протокола в надежный, скорее напоминает создание протокола заново. То есть нам приходится превращать сетевой протокол в транспортый, решающий совсем иные задачи.

Транспортный уровень - сердце всей иерархии протоколов, поскольку именно он предоставляет приложениям надежные, независящие от нижних уровней сервисы. Он необходим даже в сетях, с надежной доставкой данных, таких как ATM, поскольку у пользователей нет средств для постоянного контроля всего сетевого уровня. Поломка даже одного маршрутизатора, может прервать несколько десятков соединений, и контроль этих проблем будет возложен на прикладной уровень.

Траспортный уровень отслеживает потерянные пакеты, пришедшие не по порядку, содержащие ошибки. Также им обеспечивается прозрачность для приложений, которым становится все равно как они подключены к Сети: через модем или через спутник. Это достигается за счет использования одного и того же набора примитивов для работы с сетью, предоставлямого транспортным уровенем.

Примитивы - это набор функций, обеспечивающих одни и те же действия, для тех, кто их использует. Они могут быть реализованы самыми разными способами - в виде библиотеки пользовательского уровня, или части ядра ОС. Чтобы получить представление о них рассмотрим 4 примитива:

  1. LISTEN - ожидание подключения (пассивное подключение).
  2. CONNECTING - осуществление активного подключения.
  3. ESTABLISHED - соединение установлено (объединение таких примитивов как SEND и RECV).
  4. CLOSE - закрыть соединение.

Поскольку большинство реализаций одного протокола предоставлет одинаковые или сходные реализации примитивов, то нет никаких проблем связаться с другим концом Земного шара, через сотни абсолютно разных сетей, притом связаться с компьютером абсолютно другой архитектуры (хотя это уже больше проблема представления данных).

Любой примитив можно считать отображением состояния протокола на данный момент, и поскольку они взаимосвязаны, можно построить граф преходов из одного примитива в другой, и он будет отражать все возможные состояния транспортного уровня и переходы между ними:

                         +----<-----+
                         |          |
              +-<----IDLE----->+    |
              |                |    +---+
              |                |        |
            LISTEN    CONNECTING        |
                |        |              |
                |        |              |
                ESTABLISHED             |
                     |                  |
                     |                  |
                  CLOSING----->---------+

Здесь добавлено еще одно состояние (IDLE), которое характеризуется полным отсутствием каких либо действий. То есть соединение и не открыто и не закрыто. Это стартовая точка. Соединение начинается этим состоянием и им заканчивается. Заметьте, что в разных протоколах набор состояний может быть разным, но минимальный набор примитивов представлен выше, это, так сказать, хребет транспортной сущности.

Но транспортный протокол совершенно не обязательно должен быть ориентированным на соединение и делать все выше названное, он может предоставлять и ненадежный сервис, как это делает например, UDP. Сказать о нем почти нечего, за исключением того, что он работает быстрее чем TCP (но только в случае когда надо послать одну датаграмму и одну получить, то есть не передавать массив данных), и в принципе основная его функция - осуществление мультиплексирования датаграмм между процессами, и удобный интерфейс.

Ненадежность протоколов более низкого уровня, (это не недостаток, просто они решают совсем другие задачи) таких как IP, ICMP, еще более существенны. Но как раз они играют большую роль в компьютерной безопасности. Поскольку разрешив доступ, например эхо запросу, мы практически открываем злоумышленнику двери в сеть. Ибо ничто ему не помешает создать туннель использующий для передачи данных протокол ICMP (естественно ему нужна еще возможность запустить в сети свой серверный компонент туннелирования ;). Вот об этом мы как раз и поговорим.

Самый нижний уровень.

Основным способом создать свою датаграмму в Linux и *BSD является использоваиние сокетов, помеченых как RAW_SOCK. Создав его мы получаем доступ ко всем заголовкам начиная от ip, заканчавая tcp. Правда прочитать мы сможем только датаграммы ip с неизвестным кодом протокола или icmp,igmp. Сегменты TCP и UDP не доставляются на "сырые" сокеты. Для перехвата этих пакетов нужно использовать интерфейс к канальному уровню, например AF_PACKET в Linux или BPF в *BSD (хотя лучше использовать библиотеку libpcap). Но нам это и не нужно. В архиве вы найдете реализацию функции отправляющий пакеты через raw сокеты. Рассмотрев ее вы поймете, как их использовать. (Заметьте, что мы формируем все заколовки, кроме заголовка ip. Ядро формирует его самостоятельно. Для того чтобы самим создавать ip заголовок нужно включить опцию сокета IP_HDRINCL.)

Заголовок ICMP.

Заголовок ICMP хранится в файле netinet/ip_icmp.h. Описан он в структуре struct icmp. Не будем детально рассматривать здесь формат заголовка, и как мы будем использовать его поля, об этом будет рассказано позднее.

Потеря пакетов.

Вероятность потери ICMP запросов очень велика, потому что в мире растет число маршрутизаторов, которые их отфильтровывают. Для защиты от потери пакетов мы воспользуемся почти тем же средством, что и TCP - подтверждениями (ack). Каждый пакет будет нести в себе свой порядовый номер, в ответном пакете будет высылаться такой же номер подтверждения. Отличием от подобной реализации в TCP является то, что в нем подтверждается каждый байт(поле seq содержит порядковый номер первого байта в пакете).

До подтверждения все отправленные пакеты будут храниться в специальном массиве. Для каждого пакета будет запущен таймер переотправки, когда он сработает пакет будет отправлен заново. Если в течении 5 таймаутов (см. ниже) не будет получено подтверждение, соединение будет считаться разорванным.

Таймауты.

Для вычисления таймаутов повторной передачи (RTO) мы воспользуемся cредством опять же взятом из tcp, а именно, будем вычислять сглаженную оценку RTT(время между отправкой пакета и получением подтверждения на него). Основная проблема здесь в том, что RTT может значительно меняться со временем, от нескольких микросекунд до нескольких секунд, на это влияет расстояние, скорость сети, перегрузки... Итак нам потребуется получить значение этого самого RTT, вычислить srtt - сглаженную оценку RTT, и rttvar - сглаженную оценку среднего отклонения. Из этих двух значений можно получить RTO, сложив их и умножив на 4. Вот несколько формул, которыми мы воспользуемся (написаны в нотации языка С).

   delta = RTT - srtt;
   srtt  = srtt + g * delta;
   rttvar = rttvar + h * (abs(delta) - rttvar);
   RTO = srtt + 4 * rttvar;
  

g - это приращение RTT равное 1/8.
h - это приращение rttvar, равное 1/4.

Если вас интересует математическая сторона этих формул, смотрите статью Джекобсона (Jacobson) в SIGCOMM'88, приложение А. Заметьте, что для каждого сегмента будет запущен свой таймер.

Также будут использоваться отложенные подтверждения. Это значит, что подтверждения будут отправляться не сразу, а ждать пока будет отправлен ответный пакет с данными, чтобы отправиться с ним. Это уменьшит число маленьких пакетов в сети. Буфер с отложенными подтверждениями каждые 200-300 мкс будет опустошаться (в отличие от повторной передачи таймер запускается не для каждого подтверждения, а для всех в буфере).

Данные приходящие не по порядку.

Для решения проблемы решено завести таблицу, в которой будут храниться сегменты, порядковые номера которых больше тех, что ожидаются. Потом, при каждом пришедшем сегменте будет просмотрена таблица, и если там есть следующий(ие) по порядку сегмент(ы) они будут добавлены в буфер к новым данным.

Перегрузка.

При возникновении перегрузки в сети маршрутизаторы в первую очередь начнут откидывать ICMP и UDP пакеты, поэтому важно не способствовать их появлению в сети. Для этого реализован еще один алгоритм из tcp, но не совсем так, как в нем.

Существует переменная (cwnd), в которой хранится "окно перегрузки", то есть максимальное число сегментов, которые можно выслать не дожидаясь подтверждения. С каждой повторной передачей окно перегрузки уменьшается на 1. Как только придут все подтверждения, которые мы ждем, причем не будет ни одной повторной передачи, оно увеличится в 2 раза (не совсем верная реализация алгоритма "начального разгона").

Управление потоком.

В каждом пакете есть указание (структура header, переменная window), сколько еще байт в состоянии принять отправитель этого пакета. Если для пришедшего пакета нет места в буфере, то он отбрасывается, без каких-либо еще действий.

Реализация.

Для обеспечения асинхронности ввода-вывода используется поток, в котором и происходит управление _установленным_ соединением. В основном потоке происходит установление/разрыв соединения, чтение и запись.

Для реализации таймеров используестя следующий прием: при каждом событии (приход пакета, таймаут select()) вызываеся обработчик таймаута, который проверяет прошло ли еще необходимое количество микросекунд ("точность" таймера - 10000), и если прошло, начинает свое выполнение. Таким образом таймер срабатывает один раз в 10000 микросекунд.

Структуры данных.

Ниже приведен заголовочный файл с комментариями. По ходу развития программы он, возможно, будет немного меняться.

   #ifndef ICMPWRAP_H
   #define ICMPWRAP_H
   #define __USE_BSD
   #include <stdio.h>
   #include <unistd.h>
   #include <sys/types.h>
   #include <netinet/in.h>
   #include <sys/socket.h>
   #include <sys/select.h>
   #include <netinet/ip.h>
   #include <netinet/ip_icmp.h>
   #include <netdb.h>
   #include <time.h>
   #include <stdlib.h>
   #include <string.h>
   #include <fcntl.h>
   #include <errno.h>
   #include <arpa/inet.h>
   #include <pthread.h>
   #include "mytable.h"
   #include "timer.h"
   #include "rtt.h"
   #include "buffer.h"
   #define MAXDATASIZE  512  //Максимальный размер данных в пакете.
   #define MAXSEQN 4294967295UL  //2^32 - 1.
   #define MAXPACKETSIZE (sizeof(struct icmp)+MAXDATASIZE+sizeof(header))
   #define DEFLTIMEOUT (PRECISION+5)   /* Чтобы select() не вызывала таймаут
                                        * слишком часто,
                                        * он ведь все равно обработается не чаще
                                        * чем через presicion секунд с предыдущего
                                        * вызова.
                                        * (PRECISION определен в timer.h)
                                        */
   #define MAXSEGMENTN 24 //Максимальное кол-во сегментов в буферах.
   #define BUFSIZE MAXSEGMENTN*MAXDATASIZE
   #define ACKTBLSIZE MAXSEGMENTN //Максимальное количество отложенных подтверждений.
   //Состояния соединения.
   typedef enum {LISTEN,CONNECTING,ESTABLISHED,CLOSING,CLOSED,ERROR} cstate;
   //Типы запросов.
   enum {CONNREQ, CONNACC, DISCONNREQ, DISCONNACC};
   //Макросы.
   #define min(a,b) ((a<b)?a:b)
   #define max(a,b) ((a>b)?a:b)
   //Получает общий размер отправляемой датаграммы включая все заголовки.
   #define getMessgSize(a) (sizeof(struct icmp)+sizeof(header)+\
                                ntohs(((header *)(a+sizeof(struct icmp)))->len))
   #define incSeq(a) ((a==MAXSEQN)?1:a+1) /* Если увеличенное значение
                                           * становится равно 0 (при переполнении)
                                           * возвращаем 1; Это нужно,
                                           * чтобы увеличенное значение seq
                                           * никогда не стало равно 0
                                           */
   //Заголовок пакета.
   typedef struct
   {
    u_int8_t  flag;   //Флаги. Используется для передачи опций типа CONNREQ.
    u_int32_t seq;    //Номер сообщения.
    u_int32_t ack;    //Номер подвержденного сообщения.
    u_int16_t len;    //Длина сообщения.
    u_int32_t window; //Максимальная длина сегмента, который сможет
                      // принять отправитель.
   }header;
  //Сокет.
   typedef struct icmpSock
   {
    int fd;
    u_int16_t magic;         //С помощью этих чисел определяем, отправлена ли
                             //датаграмма именно нам.
    u_int16_t peerMagic;     //Первое число - устанавливаем мы, второе другая
                             //сторона.
    pthread_t ppid;          //Идентификатор  нити.
    struct sockaddr_in to;   //Адрес собеседника.
    header local;            //Заголовок последнего отправленного сообщения.
    header peer;             //Заголовок последнего полученного сообщения.
                             //В них хранятся текущие размер
                             // окна, наачения seq..
                             //Все данные в этих структурах хранятся
                             // с порядком байт хоста.
    timers Timers;           //Другие таймеры.
    struct rttInfo rtt;      //Переменные для вычисления RTO.
    struct cwndInfo Cwnd;    //Окно перегрузки

    pthread_mutex_t inProgress; //Если мьютекс заблокирован, значит происходит
                                //некая операция над сокетом
    pthread_cond_t canWrite;    //Сигнализирует о возможности записи
                                //новой порции данных.
    pthread_cond_t canRead;     //Сигнализирует о возможности чтения
                                //новой порции данных.
    cstate status;        //Текущее состояние соединения.
    table *unAckedTable;  //Таблица неподтвержденных сегментов
    table *confusedTable; //Таблица сегментов пришедших в беспорядке.
    table *waitedAcks;    //Таблица отложенных подтверждений.
    buffer *input;   //Здесь будут храниться получаемые данные.
    buffer *output;  //А здесь исходящие данные.
    char error;
   }icmpSock;
   //Здесь должны быть определения ф-ций, но для экономии места
   //мы их пропускаем.
   #endif
   

Я не буду давать здесь исходный код функций работающих с буферами, таблицами и сетью поскольку ничего сложного в них нет, и вы сможете изучить их в прилагаемом архиве с исходниками.

Получение пакета.

Вот функция которая получает пакет, так сказать входная точка.

   #include "icmpwrap.h"
   void *
   readPth (icmpSock * sock)
   {
     char inBuffer[IP_MAXPACKET]; //Буфер для приема ip датаграммы.
     int rc;
     int fds = sock->fd + 1;
     fd_set rfd;
     struct timeval timeout;
     FD_ZERO (&rfd);
     header *hdr =
       (header *) (inBuffer + sizeof (struct icmp) + sizeof (struct ip));
     while ((sock->status!=CLOSED) &&
           (sock->status!=ERROR))
       {
         timeout.tv_sec = 0;
         timeout.tv_usec = DEFLTIMEOUT;
         FD_SET (sock->fd, &rfd);
         rc = select (fds, &rfd, NULL, NULL, &timeout);
         if (rc == -1)
           {
            if (errno == EINTR)
              continue;
            else
              goto err;
           }
        if (rc == 0)        //Таймаут
          {
            goto timeout;
          }
        rc =
         recv (sock->fd, inBuffer, sizeof (inBuffer), 0);
         if (rc == 0)
            continue;
         if (rc == -1)
            {
             if (errno == EINTR) //Если получение пакета было прервано сигналом,
                                 //повторим.
                continue;
             else
                goto err;
            }
         updateHeader (hdr);
         /* Проверяем, для нас сообщение или нет.
          * Проверяетcя чтобы все размеры были правильными
          * Чтобы было верное число magic
          */
       if ((rc = testMessage (inBuffer, sock, rc)))
         {
          continue;
         }
         //Происходит выбор типа датаграммы и ее обработка
        //(данные, подтверждение и так далее..).
         choice(sock,hdr);
   timeout:
           timeoutHandle(sock);
        }
   err:
     //Будим всех, кто ждет событий, чтобы они тут же узнали об ошибке.
     pthread_cond_broadcast (&sock->canRead);
     pthread_cond_broadcast (&sock->canWrite);
     printf ("Connection lost...\n");
     return (void *) errno;
   }
 

Определение типа пакета.

Ниже представлена одна из самых важных ф-ций, определяющая и обрабатывающая пакеты.

   #include "icmpwrap.h"
   //Если поле seq > sock->peer.seq, пришел пакет с данными,
   //если поле ack > 0, пакет c подтверждением,
   //если поле window > 0 пакет с (возможно) новым окном.
   int
   choice (icmpSock * sock, header * hdr)
   {
     pthread_mutex_lock (&sock->inProgress);
     if (hdr->ack != 0)
          getAck (hdr, sock);
     if (hdr->window != sock->peer.window)
          resizePeerWindow (hdr, sock);
     if (hdr->seq >= incSeq (sock->peer.seq))
       {
         if (hdr->seq == incSeq (sock->peer.seq))
            {
             getNewSegment (hdr, sock); //получен новый сегмент.
            }
         if (hdr->seq > incSeq (sock->peer.seq))
              getConfusedSegment (hdr, sock); //Получаем сегмент,
                                              //пришедший не по порядку.
         refreshConfusedTable (sock); //Обновим таблицу беспорядочных сегментов

     //Если удаленная сторона запрашивает отсоединения..
         if(hdr->flag==DISCONNREQ)
           {
            passiveDisconnect(sock);
            sock->status=CLOSED;
           }
       }
     else if (hdr->seq != 0)
        sendAck(sock,hdr->seq); //Старый пакет.
     pthread_mutex_unlock (&sock->inProgress);
   }
   

Обработчики пакетов.

Эти функции вызываются из choice(), для обработки различных пакетов. Работа с окном перегрузки частично размазана между ними. Если что-то не понятно в его работе, это станет понятно после рассмотрения кода таймера.

   #include "icmpwrap.h"
   //Устанвлнивает новое значение окна.
   size_t setNewLocalWindSize(icmpSock *sock)
   {
     return (sock->local.window=bufFreeSpace(sock->input));
   }
   //Отправляет новый сегмент.
   int sendSegment(icmpSock *sock,char *data,int len)
   {
    table *p;
    u_int32_t ack=0;
    if(addToUnAckTable(sock,data,len,incSeq(sock->local.seq))!=1)
       return 0; //Не смогли сохранить неподтвержденное сообщение.
    sock->local.seq=incSeq(sock->local.seq);
    //Ищем одно из отложенных подтверждений, чтобы отправить его с
    //новыми данными.
    if((p=testTable(sock->waitedAcks,ACKTBLSIZE)))
      {
       ack=p->n;
       deleteTableEnt(p);
      }
    //Запускаем ф-цию расчета RTT.
    startRTT(sock,sock->local.seq);
    return sendData(sock,data,(u_int16_t)len, ack);
   }
   //Подтверждает сегмент.
   void
   ackSegment(header * hdr, icmpSock * sock)
   {
    table *p;
    //Сегмент пришел 2ой раз (видимо повторная передача), сразу подтврждаем.
    if((p=findElement(sock->waitedAcks,hdr->seq,ACKTBLSIZE)))
       {
        deleteTableEnt(p);
        goto rawAck;
       }
    if(!(p=findFree(sock->waitedAcks,ACKTBLSIZE)))
        {
         goto rawAck; //Если не можем "отложить" подтверждение,
                  //сразу подтвердим.
        }
    addToTable (p,NULL,0,hdr->seq,0);
    return;
   rawAck:
    setNewLocalWindSize(sock);
    sendAck(sock,hdr->seq);
    return;
   }
   //Обрабатывает сегмент-подтверждение.
   void
   getAck (header * hdr, icmpSock * sock)
   {
     table *p;
     if ((p = findElement (sock->unAckedTable, hdr->ack, MAXSEGMENTN)) == NULL)
          return; //Этот сегмент уже продтвержден и удален из таблицы.
     //Обновляем значение RTT ( ведь это время между отправкой сегмента и
     //получением подтверждения на него)
     updateRTT(sock,hdr);
     deleteTableEnt (p);
     //Здесь начинается работа с cwnd.
     updateCwnd(&sock->Cwnd,PACK_ACK);
   }
   //Изменяет в параметрах сокета размер окна удаленной стороны.
   inline void
   resizePeerWindow (header * hdr, icmpSock * sock)
   {
    sock->peer.window = hdr->window;
   }
   //Отправляет новое окно.
  void
  resizeLocalWindow (icmpSock * sock)
   {
    u_int32_t old;
    old = sock->local.window;
    setNewLocalWindSize(sock);
    //Отошлем новое значение окна, только если оно меняется с(на) ноль.
    if ((old!=sock->local.window) &&
         ((old==0) || (sock->local.window==0)))
         sendNewWindow (sock);
   }
   //Пытается найти в таблице с беспорядочными сегментами те,
   //которые следуют за последним,который пришел по порядку
   //(то есть если пришел сегмент 5, ищет 6,7,8,9...).
   void
   refreshConfusedTable (icmpSock * sock)
    {
     int i = 0;
     table *p;
     sortTable(sock->confusedTable,MAXSEGMENTN);
     if((p=findElement(sock->confusedTable, incSeq(sock->peer.seq),
                                                MAXSEGMENTN))==NULL)
        return;
     for(;i<=MAXSEGMENTN && (p->n==incSeq(sock->peer.seq)); i++,p++)
      {
        if(bufFreeSpace(sock->input) < p->len)
            break;
        addToBuffer(sock->input,p->data,p->len);
        sock->peer.seq=p->n;
        deleteTableEnt(p);
      }
    pthread_cond_signal(&sock->canRead); //Игнорируем ошибку (может быть
                                         //ничего не добавили в буффер)
   }
   //Принимает новый сегмент (пришел по порядку).
   void
   getNewSegment (header * hdr, icmpSock * sock)
   {
     char *buf = (char *) (hdr + 1);
   //Не хватает места в буфере, игнорируем пакет.
   //При такой реализации мы не можем добавить в буфер "кусок" пакета.
     if(bufFreeSpace(sock->input) < hdr->len)
        return;
     addToBuffer(sock->input, buf, hdr->len);
     sock->peer.seq = hdr->seq;
     ackSegment(hdr,sock);      //Подтвердим приход пакета.
     pthread_cond_signal(&sock->canRead);
   }
   //Получает беспорядочные сегменты, если в таблице нет места пакет игнорируется.
   void
   getConfusedSegment (header * hdr, icmpSock * sock)
   {
    table *p;
    char *buf = (char *) (hdr + 1);
    //Повтор.
    if (findElement (sock->confusedTable, hdr->seq,MAXSEGMENTN) != NULL)
       {
        return;
       }
    if ((p = findFree (sock->confusedTable,MAXSEGMENTN)) == NULL)
          return; //Нет места.
    addToTable (p, buf, hdr->len, hdr->seq, 0);
    ackSegment (hdr,sock); //Подтвердим приход пакета.
   }
   //Опустошает буфер с иходящими данными, советуясь с окном перегрузки.
   void flushBuffer(icmpSock *sock)
   {
    size_t bytes; //Максимальное число байт, которое мы можем записать.
    size_t out;   //Число байтов в выходном буфере.
    char buf[MAXDATASIZE];
    bytes=min(sock->peer.window,getCwnd(&sock->Cwnd)*MAXDATASIZE);
    if(bytes==0)
        return;
    if((out=inBuffer(sock->output))==0)
         return;
    bytes=min(bytes,out);
    while(bytes)
     {
      if(findFree(sock->unAckedTable,MAXSEGMENTN)==NULL)
         break;
      out=getFromBuffer(sock->output,buf,min(bytes,MAXDATASIZE));
      if(sendSegment(sock,buf,out)<=0)
         break;
      bytes-=out;
      updateCwnd(&sock->Cwnd,PACK_SEND);
     }
    pthread_cond_signal(&sock->canWrite);
   }
   

Работа с таймером.

Вот исходник, вначале идет заголовочный файл.

   #define WINDPROBETIME  100000000
   #define ACKTIMEOUT 200000 //200 мс.
   #define PRECISION  10000 //Каждые precision микросекунд будет срабатывать
                            //обработчик таймера.
   #define LIVE 5 // Максимальное число переотправки одного и того же сегмента.
   typedef struct
   {
    time_t windProbe;    //Таймаут посылки пробы окна
    time_t waitAck;      //Таймаут отсылки неподтвержденных сегментов.
    struct timeval prev; //Время последнего изменения в  микросекундах.
   }timers;
   //"Вычитает" структуру timeval из переменной t, в которой хранится значение
   //в микросекундах.
   inline void
   updateTimer (time_t * t, struct timeval *i)
   {
    time_t p = ((i->tv_sec * 1000000) + i->tv_usec);
    if (p > (*t))
       *t = 0;
    else
       *t = (*t) - p;
   }
   //Возвращает 1 если прошло достаточно времени с момента предыдущего вызова.
   //В структуре now возвращается прошедшее число секунд/микросекунд с
   //момента old.
   int
   checkTimer (struct timeval *now, struct timeval *old)
   {
     time_t p;
     tvSub (now, old);
     p = ((now->tv_sec * 1000000) + now->tv_usec);
     if (p>=PRECISION)
         return 1;
     return 0;
   }
   //Собственно сам обработчик таймаута.
   void
   timeoutHandle (icmpSock * sock)
   {
     struct timeval now;      //Текущее время.
     struct timeval interval; //Число микросекуд прошедших с предидущего вызова
     if (gettimeofday (&now, NULL) == -1)
       return;
     interval = now;
     pthread_mutex_lock (&sock->inProgress);
     if (checkTimer (&interval, &sock->Timers.prev) == 0)
       goto end;
     sock->Timers.prev = now;
     waitedAckHandle (sock, &interval);
     resendHandle (sock, &interval);
     winProbeHandle (sock, &interval);
     flushBuffer(sock);
   end:
     pthread_mutex_unlock (&sock->inProgress);
   }
   //Отправляет отложенные подтверждения.
   void
   waitedAckHandle (icmpSock * sock, struct timeval *interval)
   {
    int i;
    table *p = sock->waitedAcks;
    updateTimer (&sock->Timers.waitAck, interval);
    if (sock->Timers.waitAck == 0)
      {
        for (i = 0; i <= ACKTBLSIZE; i++, p++)
         {
          if (p->n != 0)
            {
              sendAck (sock, p->n);
              deleteTableEnt (p);    //Удаляем.
            }
         }
        sock->Timers.waitAck = ACKTIMEOUT;
      }
   }
//Выполняет повторную передачу.
   void
   resendHandle (icmpSock * sock, struct timeval *interval)
   {
    int i;
    table *p = sock->unAckedTable;
    sortTable (p, MAXSEGMENTN);
    for (i = 0; i <= MAXSEGMENTN; i++, p++)
       {
        if (p->n != 0)
          {
           updateTimer (&p->timeout, interval);
           if (p->timeout == 0)
             {
               createPacket (sock->fd, p->n, 0, sock->local.window,
                     sock->magic, &sock->to, p->data, p->len);
               //Возможно был потерян пакет "которым" мерили RTT
               if (rttLost (&sock->rtt, p->n)==-1)
                 {
                  sock->status=ERROR; //Если этот пакет терялся слшком много раз.
                  return;
                 }
               p->timeout = rttTimeout (sock->rtt);
               updateCwnd(&sock->Cwnd,PACK_LOST);
            }
        }
      }
   }
   //Если абонент долго молчит пошлем окно.
   //Чтобы он не думал, что оно у нас равно 0.
   void
   winProbeHandle (icmpSock * sock, struct timeval *interval)
   {
     updateTimer (&sock->Timers.windProbe, interval);
     if (sock->Timers.windProbe == 0)
       {
         sendNewWindow (sock);
         sock->Timers.windProbe = WINDPROBETIME;
       }
   }
   //Обновляет таймаут пересылки.
   inline void
   updateRTT (icmpSock * sock, header * hdr)
   {
    rttUpdate (&sock->rtt, hdr->ack);
   }
   inline void
   startRTT (icmpSock * sock, u_int32_t seq)
   {
    rttStart (&sock->rtt, seq);
   }
   

Расчет RTT и RTO.

Заголовочный файл:

   #include <sys/types.h>
   #include <sys/time.h>
   #include <stdio.h>
                          //Все следующие определения даны в секундах.
   #define RTT_RXTMIN 2   //Минимальное значение таймаута для повторной передачи.
   #define RTT_RXTMAX 60  //Максимальное значение таймаута для повторной передачи.
   #define RTT_LIVE   5   //Максимальное количество повторных передач одного сегмента.
   /*
    * Этот макрос вычисляет RTO на основе текущих значений:
    * RTO = RTT + (4xRTTVAR)
    * SRTT - сглаженная величина отклонения в микросекундах.
    */
   #define RTOCALC(p) (p->rtt_srtt + (4 * p->rtt_rttvar))
   //Возвращает значение таймаута.
   #define rttTimeout(p) (time_t)(p.rtt_rto)
   struct rttInfo
   {
    time_t rtt_send;   //Время отправки тестового пакета
                       //в микросекундах с момента rtt_base (см. ниже)
                       //Если равно 0, то пакет был перепослан и нужно
               //отправить еще один тестовый пакет.
    time_t rtt_seq;    //Номер тестового пакета.
    time_t rtt_rtt;    //Последнее измеренное значение rtt в микросекундах
    time_t rtt_srtt;   //Сглаженное значение rtt в микросекундах
    time_t rtt_rttvar; //Среднее значение отклонения в микросекундах
    time_t rtt_rto;    //Текщее значение rto в микросекундах.
    time_t rtt_base;   //Количество Секунд с начала эпохи на момент запуска
                       //программы.
    int rtt_live;
   };
  

Исходный код:

   #include "rtt.h"
   //Преобразует текущее время в число микросекунд
   //прошедших с момента запуска программы.
   time_t
   localTime (struct rttInfo *info)
   {
     struct timeval tv;
     if (gettimeofday (&tv, NULL) == -1)
       return -1;
     return ((tv.tv_sec * 1000000 + tv.tv_usec) - info->rtt_base);
   }
   //Проверяет, не превысило ли значение RTO установленные
   //пределы снизу и сверху.
   time_t
   rtoMinMax (time_t rto)
   {
     if (rto < (RTT_RXTMIN * 1000000))
       return (RTT_RXTMIN * 1000000);
     else if (rto > (RTT_RXTMAX * 1000000))
       return (RTT_RXTMAX * 1000000);
     return rto;
   }
   //Инициализирует структуру rttInfo.
   int
   rttInit (struct rttInfo *info)
   {
     info->rtt_base = 0;
     info->rtt_base = localTime (info);
     info->rtt_seq = 0;
     info->rtt_rtt = 0;
     info->rtt_srtt = 0;
     info->rtt_rttvar = 750000;
     info->rtt_rto = rtoMinMax (RTOCALC (info)); //Вычисляем первое rto;
     info->rtt_live=RTT_LIVE;
     return 0;
   }
   //Эта функция должна выполнятся после отправки пакета.
   void
   rttStart (struct rttInfo *info, u_int32_t seq)
   {
     if (info->rtt_seq != 0)
        return;  //Вычисление уже проводится c другим пакетом.
     if ((info->rtt_send = localTime (info)) == -1)
        return;
     info->rtt_seq = seq;
     info->rtt_live=RTT_LIVE;
   }
   //Вычисляет RTO
   void
   rttUpdate (struct rttInfo *info, u_int32_t ack)
   {
     time_t delta;
     //Проверяем был ли отправлен тестовый пакет
     if (info->rtt_seq == ack)    //И тот ли это пакет, который мы ждем.
       {
         info->rtt_rtt = localTime (info) - info->rtt_send;
         delta = info->rtt_rtt - info->rtt_srtt;
         info->rtt_srtt += delta / 8;    //g=1/8
         if (delta < 0)
             delta = -delta; //abs(delta)
         info->rtt_rttvar += (delta - info->rtt_rttvar) / 4;    //р=1/4
         info->rtt_rto = rtoMinMax (RTOCALC (info));
         info->rtt_seq = 0;
       }
    }
   //Обрабатывает ситуацию, когда потерян тестовый пакет.
   //В этом случае новый таймаут равен старому умноженному на 2.
   //Если была повторная передача то нельзя расчитать RTT  по
   //пришедшему подтверждению
   //Так как непонятно на какой из двух пакетов оно пришло.
   void
   rttLost (struct rttInfo *info, u_int32_t ack)
   {
    if (info->rtt_seq == ack)
      {
        info->rtt_seq = 0; //Помечаем, что нужно новые вычисление.
        info->rtt_rto = rtoMinMax (info->rtt_rto * 2);
        info->rtt_live--;
        if(!info->rtt_live)
           return -1;
      }
   }
   

Реализация окна перегрузки.

В TCP все реализуется далеко не так просто как здесь, реализация в нем хорошо описана у Стивенса в книге "TCP/IP Illustrated"

   #define ALL_ACKED  1
   #define PACK_LOST  2
   #define PACK_SEND  3
   #define PACK_ACK   4
   #define MAXCWND 256
   typedef struct cwndInfo
   {
    int dyn_cwnd; //Изменяется динамичски, при патере пакета.
    int cwnd;     //Всего не дожидаясь подтверждения можно отправить.
    int maxWrite; //Можно еще отправить, не дожидаясь подтверждения.
    int sendet;   //Всего одновременно отправлено.
    int acked;    //Из них подтверждено.
   }cwndInfo;
   #include "cwnd.h"
   int
   cwndInit (cwndInfo * info)
   {
     info->cwnd = 1;
     info->dyn_cwnd = 1;
     info->maxWrite = 1;
     info->sendet = 0;
     info->acked = 0;
   }
   //Возвращает сколько еще сегментов можно отправить не дожидаясь подтверждения.
   int
   getCwnd (cwndInfo * info)
   {
     if (info->acked == info->sendet && (info->sendet != 0))//Если все
                                               //отправленные пакеты
                                               //подтверждены, изменим cwnd и
       {                                   //вернем новое значение.
         updateCwnd (info, ALL_ACKED);
       }
     return info->maxWrite;
   }
   void
   updateCwnd (cwndInfo * info, int event)
   {
     switch (event)
       {
        case PACK_ACK:
         info->acked++;
         if (info->acked < info->sendet)
           break;
        case ALL_ACKED:
         if (info->cwnd == info->dyn_cwnd) //Если ни один пакет не потерялся.
                info->dyn_cwnd=min((info->dyn_cwnd * 2),MAXCWND);
         info->cwnd = info->dyn_cwnd;
         info->sendet = 0;
         info->acked = 0;
         info->maxWrite=info->cwnd;
          break;
        case PACK_LOST:
         if (info->dyn_cwnd > 1)
           info->dyn_cwnd--;
         break;
        case PACK_SEND:
         if (info->maxWrite > 0)
           info->maxWrite--;
         info->sendet++;
         break;
      }
   }
  

Установка и разрыв соединения.

Вот одни из самых сложных ф-йй. Они устанавливают и разрывают соединение. Для установки соединения используется тройное рукопожатие, как в tcp. Установка соедиения:

   //Активно пытается запросить соединение.
   int
   activeConnect (icmpSock * sock)
   {
     int tryes = 5;
     char inBuffer[IP_MAXPACKET];
     int timeout = 5;
     int rc;
     header *hdr =
       (header *) (inBuffer + sizeof (struct icmp) + sizeof (struct ip));
     struct icmp *Icmp = (struct icmp *) (inBuffer + sizeof (struct ip));
     memset(inBuffer,0,sizeof(inBuffer)); //На всякий случай, если придет "левый" пакет.
     sock->status=CONNECTING;
     while (tryes>0)
       {
        //Отправляем запрос соединения.
         if (sendConnectReq (sock) == -1)
           break;
   receiving:
      //Ф-ция recvAnswer пытестся получить пакет (recv) в течение timeout секунд.
         if ((rc = recvAnswer (sock, inBuffer, sizeof(inBuffer), timeout)) == -1)
       {
         if (errno == EWOULDBLOCK) //Таймаут
           {
             tryes--;
             timeout *= 2;
             continue;
           }
         break;
       }
         updateHeader (hdr);
         sock->peerMagic = ntohs (Icmp->icmp_id);
         //Проверяем заголовок в полученном ответе.
         if((sock->peerMagic == sock->magic)) //Это наш собственный пакет.
           goto receiving;
         if ((hdr->ack != sock->local.seq) ||
            ((hdr->flag != CONNACC)))
           continue;
         sock->peer = (*hdr);
         if (sendAck (sock, sock->peer.seq) == -1)
           break;
         sock->status = ESTABLISHED;
         return 0;
       }
     errno=ETIMEDOUT;
     sock->status = ERROR;
     return -1;
   }
   //Принимает соединение.
   int
   passiveConnect (icmpSock * sock)
   {
     char inBuffer[IP_MAXPACKET];
     int tryes = 5;
     int rc;
     int timeout = 5;
     header *hdr =
       (header *) (inBuffer + sizeof (struct icmp) + sizeof (struct ip));
     struct icmp *Icmp = (struct icmp *) (inBuffer + sizeof (struct ip));
     memset(inBuffer,0,sizeof(inBuffer));
     sock->status=LISTEN;
   again:
  //Получаем запрос соедиения.
     if (recvAnswer (sock, inBuffer,sizeof(inBuffer), 0) == -1)
        goto exit;
     updateHeader (hdr);
     if (hdr->flag != CONNREQ)
       goto again;
     sock->peer = (*hdr);
     sock->peerMagic = ntohs (Icmp->icmp_id);
     while (tryes)
       {
         //Отправляем подтверждение соедиенения.\n");
         if (sendConnectAck (sock) == -1)
           break;
         if ((rc = recvAnswer (sock, inBuffer,sizeof(inBuffer), timeout)) == -1)
           {
            if (errno == EWOULDBLOCK)    //Таймаут
              {
               tryes--;
               timeout *= 2;
               continue;
              }
            break;
           }
         updateHeader (hdr);
         if ((hdr->ack != sock->local.seq) ||
         (sock->peerMagic != ntohs (Icmp->icmp_id)))
              continue;
         sock->status = ESTABLISHED;
         return 0;
       }
   exit:
     sock->status = ERROR;
     return -1;
   }

   //Активно разрывает соединение.
   int
   activeDisconnect (icmpSock * sock)
   {
     int tryes = 5;
     char inBuffer[IP_MAXPACKET];
     int timeout = 5;
     int rc;
     header *hdr =
       (header *) (inBuffer + sizeof (struct icmp) + sizeof (struct ip));
     struct icmp *Icmp = (struct icmp *) (inBuffer + sizeof (struct ip));
     memset(inBuffer,0,sizeof(inBuffer));
     sock->status=CLOSING;
     sock->local.seq=incSeq(sock->local.seq);
     while (tryes>0)
       {
         if (sendDisconnectReq (sock) == -1)
           break;
         if ((rc = recvAnswer (sock, inBuffer,sizeof(inBuffer), timeout)) == -1)
           {
             if (errno == EWOULDBLOCK)  //Таймаут
               {
                tryes--;
                timeout *= 2;
                continue;
               }
             break;
           }
         updateHeader (hdr);
         if ((hdr->ack != sock->local.seq) ||
            ((hdr->flag != DISCONNACC) || (sock->peerMagic !=  ntohs (Icmp->icmp_id))))
           continue;
         sock->peer = (*hdr);
         if (sendAck (sock, sock->peer.seq) == -1)
           break;
         sock->status = CLOSED;
         break;
       }
     return 0;
   }
   int
   passiveDisconnect (icmpSock * sock)
   {
     char inBuffer[IP_MAXPACKET];
     int timeout = 5;
     sendDisconnectAck (sock);
     recvAnswer (sock, inBuffer,sizeof(inBuffer), timeout);
     sock->status = CLOSED;
     return -1;
   }
  

The End.

Вот на этом вроде бы все. Придумайте как сделать, чтобы makeSocket возврщала не указатель на icmpSock, а десктриптор (int), с которым можно было бы работать обычными ф-циями read и write.

//oxid (Птн Мар 26 10:03:54 MSK 2004)



Сравнение IPv4 и IPv6.

Ip - основной протокол сетевого уровня, который используется на данный момент в Internet. Его разработка началась так же давно, как и сама Internet. Над ним работало множество ученых, соответственно многие проблемы, присущие другим сетевым протоколам в нем отсутствуют. Но вначале Internet использовали только ученые и военные, а теперь он пришел в каждый дом. В 1990 году стало понятно, что используемый протокол ipv4 не готов к такому росту своей популярности. Адреса расходуются с огромной скоростью, что уже через к 1996 году не было бы ни одного свободного. Вскоре был найден выход - CIDR и NAT, с помощью которого необязательно было перерасходывать адреса подсетей. Но с другой стороны большую проблему представляла плохая "гибкость" IP: достаточно сложно было вставить новую опцию в заголовок, не придумывая еще одного "верхнего" уровня. Для этого бы потребовалось изменить весь протокол. В связи с этим были выдвинуты основные требования к новой версии:

  1. Поддержка миллиардов хостов, даже если адреса будут расходоваться неразумно.
  2. Уменьшение таблиц маршрутизации.
  3. Упрощение протокола, для ускорения работы маршрутизаторов.
  4. Высокая безопасность (аутентификация и конфиденциальность).
  5. Большее внимание к типу сервиса.
  6. Упрощение многоадресной маршрутизации.
  7. Возможность изменить положение хоста без необходимости менять адрес.
  8. Легкое добавление новых возможностей.
  9. Возможность существования старого и нового протокола одновременно.

В 1993 году был выбран протокол Диринга и Френсиса, как наиболее подходящий из остальных претендентов. В дальнейшем он был назван Ipv6. Внедрение этого протокола еще до сих пор не завершено (и некоторые считают что не завершится никогда), как и его разработка. Здесь вам предлагается оценить основные различия между четвертой и шестой версий этого протокола.

Заголовок.

К заголовку подошли с новой концепцией. Если в четвертой редакции практически все возможности были отражены в заголовке, то в шестой версии в этом заголовке лежат только самые основные детали. Остальное (включая фрагментацию и ip опции) воплощено в виде нижележащих заголовков, на которые указывает поле "следующий заголовок" в пакете. Размер основного заголовка стал фиксированным.

   Заголовок ipv4:
   0     4      8         16    19    24   31
   +-----+-----+----------+-----+-----+-----+  --+
   | ver |hlen |   TOS    | Полная длина    |    |
   +-----+-----+----------+-----+-----------+    |
   |    Идентификатор     |Флаги|Ук. Фрагм. |    |
   +-----+-----+----------+-----+-----------+    |
   |Время жизни| Протокол |Контрольная сумма|    | 20 байт
   +-----+-----+----------+-----------------+    +--------
   |         Ip-адрес отправителя           |    |
   +----------------------------------------+    |
   |         Ip-адрес получателя            |    |
   +-------------------------+--------------+  --+
   |     Ip-опции(если есть) | Заполнитель  |
   +-------------------------+--------------+
   |               Данные                   |
   +----------------------------------------+
   Заголовок ipv6:
   0     4     8     12    16    19    24    31
   +-----+-----+-----+-----+-----+-----+-----+  --+
   | ver |Приор|     Метка потока.           |    |
   +-----+-----+-----------+-----------+-----+    |
   |   Размер поля данных  |Сл. Загол. |Hops |    |
   +-----+-----+-----------+-----------+-----+    |
   |                                         |    |
   +          Адрес отправителя.             +    | 40 байт
   |                                         |    |--------
   +-----------------------------------------+    |
   |                                         |    |
   +          Адрес получателя.              +    |
   |                                         |    |
   +-----------------------------------------+  --+
  

Адресация.

Разрядность адреса - одна из основных причин, по которым появился ipv6. И здесь произошли очень заметные изменения. В-первых адрес из 32 битного, стал 128 битным. Это самое заметное изменение. Но дальше, больше. Если в старой редакции протокола адреса были с тремя уровнями иерархии (адрес сети, адрес подсети, адрес хоста), то здесь уровней иерархии может быть сколько угодно, это сильно поможет сократить таблицы маршрутизации. В новом протоколе отсутствует понятие хоста, вместо него введено понятие интерфейса. Хотя это понятие было и раньше, но ipv4 адресовал хост, а не интерфейс этого хоста. Теперь все наоборот. К тому же существует три основных новых типов адресов.

  • Unicast:
  • Идентификатор одиночного интерфейса. Пакет, посланный по уникастному адресу, доставляется интерфейсу, указанному в адресе.
  • Anycast:
  • Идентификатор набора интерфейсов (принадлежащих разным узлам). Пакет, посланный по эникастному адресу, доставляется одному из интерфейсов, указанному в адресе (ближайший, в соответствии с мерой, определенной протоколом маршрутизации).
  • Multicast:
  • Идентификатор набора интерфейсов (обычно принадлежащих разным узлам). Пакет, посланный по мультикастинг-адресу, доставляется всем интерфейсам, заданным этим адресом.

Отсутствуют широковещательные адреса, вместо них испольуются multicast адреса.

TTL - hops.

В старом протоколе опция ttl могла определяться по разному - либо как число секунд, либо как число маршрутизаторов, через которые мог пройти пакет. Во всяком случае, когда оно становилось нулем пакет удалялся. В ipv6 поле hops идентифицируется однозначно - максимальное число маршрутизаторов, через которые может пройти датаграмма.

Дополнительные заголовки.

Как было сказано выше, весь потенциал нового протокола реализуется с помощью дополнительных заголовков. Вот пример двух из них:

  • Фрагментация.
  • Теперь датаграмма никогда не будет фрагментироваться на маршрутизаторе. Это всегда будет делать хост, который выслал датаграмму. Возможно это сделано, чтобы разгрузить маршрутизаторы. Каждый фрагментируемый пакет уникально помечается в поле идентификация. Это поле одинаково у всех фрагментов исходной датаграммы. К тому же имеется только 60 секунд на сборку пакета. Т.е. все фрагменты должны придти за это время.
           +----------------+--------+---------------------+--------+---+
           |След. заголовок | Резерв | Указатель фрагмента | Резерв | М |
           +----------------+--------+---------------------+--------+---+
           |                        Идентификация.                      |
           +------------------------------------------------------------+
         
  • Опции.
  • Имеют тоже назначение, что и в ipv4. Но теперь обзавелись своим заголовком. Пока определены два типа опций Pad1 - заполнителя не требуется, опции hop-by-hop - набор, который просматривается каждым маршрутизатором на пути датаграммы.

Метки потоков.

24-битовое поле метки потока в заголовке IPv6 может использоваться отправителем для выделения пакетов, для которых требуется специальная обработка в маршрутизаторе, такая например, как нестандартная QoS или "real-time " сервис, также возможно будет некоторая поддержка ориентированных на соединение сервисов (имеется в виду соединение на уровне ip, а не tcp), которые имеют свои преимущества (маршрутизация выполняется только один раз, нагрузка на сеть заранее планируется), и недостатки (при выходе из строя одного маршрутизатора, все соединения проходящие через него разрываются) но их обсуждение выходит за рамки статьи. К тому же этот аспект IPv6 является пока экспериментальным и может быть изменен позднее. Для ЭВМ или маршрутизаторов, которые не поддерживают функцию пометки потоков, это поле должно быть обнулено при формировании пакета, сохраняться без изменения при переадресации и игнорироваться при получении. Возможно существование нескольких потоков между отправителем и получателем. Практическое значение меток еще не до конца определено, и исследования в этой области еще продолжаются.

Приоретет (TOS).

Это поле аналогично флагу Tos в Ipv4. Но теперь определяется 7 видов трафика, вместо 3 в старой версии.
Код приоритета. Назначение
0 Нехарактеризованный трафик.
1 Заполняющий трафик (например, сетевые новости)
2 Несущественный информационный трафик (например, электронная почта)
3 Резерв
4 Существенный трафик (напр. FTP, HTTP, NFS)
5 Резерв
6 Интерактивный трафик (напр. telnet, x)
7 Управляющий трафик Интернет (напр., маршрутные протоколы, snmp)

Как вы могли заметить, с увеличением номера растет приоретет.

Защита данных.

Это очень существенная часть Ipv6 протокола. Теперь виртуальные частные сети и аутентификация поддерживаются на уровне сетевого протокола, что должно увеличить безопасность и надежность его работы. Но эта тема слишком обьемна для статьи, поэтому за более подробной информацией обращайтесь к RFC, или к книгам Шнайера.

Программирование.

Практически никаких отличий в написании программ не предвидится. Одно из немногих - константа AF_INET6, вместо константы AF_INET. Далее все по старому. Существенные отличие понесла многоадресная передача, так как в новом протоколе все происходит по другому. Также с помощью SOCK_RAW вы не сможете сформировать полностью заголовок ipv6, но практически все поля заголовка доступны через интерфейс setsockopt(). Возможно наиболее сложно будет писать программы, работающие независимо от протокола, но здесь нет никаких особых трудностей и если хост поддерживает оба стека, ядро ОС во многих случаях само заботиться о проблемах. Поэтому если захотите соединиться с хостом, который поддерживает только 4 версию протокола, проблем не возникнет. Уже стандартизованы несколько функций преобразования адресов не зависящих от протокола, это ф-ции getaddrinfo() и getnameinfo().

Вот мое небольшое IMHO, а заодно и резюме:

Ipv6 является более совершенным во всех отношениях, по сравнению со своим прешественником, он вполне решает все поставленные перед ним задачи (см. начало статьи). Он полностью соовместим с другими протоколами, такими как TCP, UDP, ICMP, IGMP, OSPF, DNS, BGP и другими, возможно иногда потребуется минимальное изменение кода.

Главная проблема внедрения нового протокола, именно в том, что он новый, пока проблема с адресами не стоит столь остро, и его внедрение может продолжаться бесконечно долго, поскольку многие боятся что-то менять, кому-то это просто пока не нужно. Одна из проблем состоит в том, что для перевода сети на новый сетевой протокол потребуется не только настройка ОС, а возможно покупка нового оборудования. Например многие "интеллектуальные" мосты когда хотят узнать куда отправлять пакет, заглядывают в поле данных кадра, нарушая всю модель OSI. Благодаря этой особенности они могут работать только с Ipv4. Естественно это все не такие большие проблемы. К тому же в ipv6 прекрасная реализация VPN (виртуальных частных сетей). Теперь шифрование трафика абсолютно прозрачно для транспортного (атаки типа tcp hijacking уходят в прошлое, но атаку "человек посередине" все еще можно провести) и прикладного уровня (хотя надо заметить что некоторая поддержка всего этого есть и в 4 версии). Да, очень удобно, что существует совместимость этих двух протоколов (одно из требований к нему, обьявленных еще до начала разработки) его совместимость с 4 версией. Если ядро ОС поддерживает два стека, оно передаст данные программе работающей с ipv6, которые были получены как из кадров ipv4, так и из кадров ipv6, поскольку адреса 4 версии можно отобразить в адреса 6 версии.

Библиография.

  1. У.Р. Стивенс. UNIX: разработка сетевых приложений. Спб., ПИТЕР, 2003
  2. Э. Таненбаум. Компьютерные сети. 4-е издание, Спб., ПИТЕР, 2003
  3. Архив сайта www.citforum.ru
  4. RFC 2460 - 2466 (ipv6)

//oxid(Птн Фев 20 09:55:35 MSK 2004)

Между двумя мирами

Сегодня я хочу рассказать о людях, которые застряли между двумя мирами. Каждый выбирает для себя лучшее, если не занимается самоистязанием намеренно. Каждый выбирает для себя мир, где он сильнее, могущественнее. Каждый день, когда я прихожу домой после занятий, первым делом я включаю компьютер, как миллионы подобных мне людей. Мой друг поступает точно так же. Но между нами - целая пропасть. Я люблю возвращаться в реальный мир, он - нет. Почему-то он не чувствует себя комфортно ни в одном из миров. Мерцание монитора в полной темноте манит его, как манит огонь мотылька. Но он не находит в киберпространстве ничего, что могло бы удержать его там. Так же дело обстоит и с реальным миром. Времени не хватает решительно ни на что. Даже для простых человеческих чувств нет времени и места. Но он всегда найдет пару секунд, чтобы обсудить "достоинства" каждой симпатичной девушки, проходящей мимо. И затем следует коронная реплика: "Я бы ее трахнул". Грустно наблюдать за всем этим. Ему нет места ни в реальной жизни, ни в киберпространстве. Он называет себя программистом, не написав ни одной мало-мальски нужной программы. Тем не менее, он хороший человек.

Человек, невиновный в том, что его ничто не связывает ни с одним из миров. Каждый верит в то, что он вершит свою судьбу, что он - творец своего счастья. Самообман. Сладкая ложь. Человек не в состоянии рассчитать что случится с ним в ближайшие несколько часов даже в первом приближении. Разве тонущий в иллюзиях постарается их развеять, если осознает, что его ждет гибель? Нет. Так же как и наркоман, который понимает от чего он умрет, но не может отказаться от наркотиков только из-за того, что он потерял влясть над собой. Сразу же возникает вопрос: "А была ли она? Дарована ли изначально человеку власть над собой?". Тут можно долго спорить, доказывая свою правоту с пеной у рта, но ни на йоту не сдвинуться в каком-либо направлении. Я считаю, что человеку дается право выбора. Но оно дается в таких малозначащих ситуациях... Все самые важные события все равно произойдут, несмотря на все попытки что-то изменить. Даже если вывести систему из равновесия, она в него вернется, независимо от желания. Это неприятно, т.к. ощущаешь свою слабость. Это служит оправданием людям, которым лень жить. Но это дает каждому право стать великим представителем человечества, ибо это событие само по себе не является глобально важным. к сожалению, мы не в состоянии помочь заблудившимся. Только они способны вернуть себя к реальности. Глупо надеяться, что они осознают всю сложность положения, в котором оказались, и вернутся. Это так же глупо, как надеяться на чудесное прозрение всех слепых людей, на то, что исчезнут в никуда злые, завистливые люди, что человечество когда-то откроет все тайны природы.

Это похоже на парадокс колдуна... Как-то раз один альпинист перед очередным походом пошел к колдуну. Старый колдун своим опытным глазом увидел, что клиент денежный. Другим глазом он увидел древнее проклятие и поведал обо всем без утайки. Альпинист потратил немало денег на нейтрализацию этого проклятия и заплатил еще и за второй ритуал, который должен приносить удачу. Через несколько дней состоялось восхождение команды, в которой был и тот самый альпинист. Их накрыло лавиной. Погибли все, кроме клиента колдуна, который отделался тяжедым обморожением ступней. Вскоре спасатели обнаружили его и доставили в госпиталь. Ступни пришлось ампутировать. С тех пор бедолага никак не может понять, что же произошло: колдун отвел опасность (ведь все товарищи альпиниста погибли) или, наоборот, обратившись к колдуну, он прогневал Бога и понес справедливое наказание? Каждый, кто слышит эту историю впервые, поначалу категоричен в своих выводах. Но затем эта уверенность уходит, так как мы не в состоянии отличить действие сверхъестественных сил от законов природы. И, следовательно, не в состоянии познать их. Возможно, это одна и таже сила. Чтобы доказать или опровергнуть существование Бога, нужно уподобиться ему. Познав Его, человеку откроются все тайны природы. Но для того, чтобы человек смог осознать Его в полной мере, необходимо быть так же всемогущим и обладать абсолютыми знаниями. Именно поэтому наши знания никогда не станут абсолютным знанием, так же как и график функции y=e^(-x) никогда не пересечет ось OX.

Никто не в состоянии видеть все. Также и мое мнение не является чем-то абсолютно истинным. Давно пора привыкнуть, что абсолютной истины (как и всего иного абсолютного) для человечества не существует. Все познается в сравнении, без точки отсчета все наши доказательства - пустое сотрясание воздуха. Даже абсолютного нуля не существует. -273C - всего лишь числовая оценка несуществующего. Для того, чтобы хотя бы 1 атом охладить до абсолютного нуля, нужно бесконечно много энергии. При достижении абсолютного нуля атом перестает существовать: электроны останавливаются и падают на ядро, в итоге происходит аннигиляция вещества с выделением энергии. Но мы не можем остановить движение электронов. Мы можем создать ядро водорода, гелия, лития, но не можем уничтожить атом. Выходит, что даже человечество не является абсолютным творением природы, раз нам неподвластны ее основы. Поэтому нам остается лишь одно - жить. Жить теряясь в нереальности, находясь, теряясь снова... Этот процесс бесконечен, как бесконечен процесс самопознания.

//GoodWin (Пнд Мар 16 16:57:16 MSK 2004)



Об авторах.

Для вас этот журнал сделали:
Oxid [идея, статьи, верстка]
GoodWin [статьи, рассказы, верстка]
Black c0de [Поддержка сайта]



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу


В избранное