понедельник, 5 сентября 2011 г.

Использование netlink для определения изменения маршрутов ipv6 в Linux.

   Протокол ipv6 позволяет конфигурировать сеть без учатия пользователя с помощью icmpv6, если узел работает в качестве клиента(forwarding установлен в 0).
Поэтому возникает задача: как отследить эти изменения. Как оказывается, это достаточно просто, если использовать протокол netlink. Описание данного протокола можно найти здесь (eng) и здесь(рус.).
   Для вывода информации об измененных маршрутах я создал функцию show_route. Вот ее исходный код:

/*
buffer - хранит полученные сообщения от netlink
size - размер полученных данных
*/
void show_route(char *buffer, int size)
{
   /*
   nlmp - указатель на структуру заголовка netlink
   routemsg - полезная нагрузка
   tb[RTA_MAX+1] - атрибуты маршрута
   */
   struct nlmsghdr *nlmp;
   struct rtmsg       *routemsg;
   struct rtattr * tb[RTA_MAX+1];

   /*
   Данные в буфере имеют следующий вид:
   |nlmsghdr|data|nlmsghdr|data|....|nlmsghdr|data|
   |-------------------size-----------------------|
   */

   for(nlmp = (struct nlmsghdr *)buffer; size > sizeof(*nlmp);){  

      int len;
      int req_len;
      /*Проверяем целостность netlink сообщения*/
      if (!NLMSG_OK(nlmp, size)) {
         perror("NLMSG not OK\n");
         return;
      }
      /*Это размер сообщения netlink*/
      int len = nlmp->nlmsg_len;
      /*Получаем размер полезной нагрузки*/
      int req_len = len - sizeof(*nlmp);
      /*Проверяем тип сообщения*/
      /*Если он не относится к маршрутам, то переходим на следующее сообщение*/
      switch(nlmp->nlmsg_type){
      case RTM_DELROUTE:
         printf("A route was deleted\n");
         break;
      case RTM_NEWROUTE:
         printf("A route was added\n");
         break;
      default:
         size -= NLMSG_ALIGN(len);
         nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
         continue;
     }
     /*Получаем указатель на полезную нагрузку и , после чего, выделяем атрибуты маршрута*/
     routemsg = (struct rtmsg *)NLMSG_DATA(nlmp);
     /*Данная функция описана ниже.*/
     parseRtattr(tb, RTA_MAX, RTM_RTA(routemsg), len);

     /*Код ниже выводит атрибуты маршрута*/
     if(tb[RTA_IIF]){
        int iface;
        iface = *(int*)RTA_DATA(tb[RTA_IIF]);
        printf("An input iface is %d\n", iface);

     }
     if(tb[RTA_OIF]){
        int iface;
        iface = *(int*)RTA_DATA(tb[RTA_OIF]);
        printf("An output iface is %d\n", iface);
     }
     if(tb[RTA_DST]){
        char routeAddr[128];
        inet_ntop(AF_INET6, RTA_DATA(tb[RTA_DST]), routeAddr, sizeof(routeAddr));
        printf("Destination route is %s\n", routeAddr);
     }else if (routemsg->rtm_dst_len) {
        printf( "0/%d \n", routemsg->rtm_dst_len);
     } else {
        printf("Destination route is default\n");
     }

     if(tb[RTA_SRC]){
        char routeAddr[128];
        inet_ntop(AF_INET6, RTA_DATA(tb[RTA_SRC]), routeAddr, sizeof(routeAddr));
        printf("Source route is %s\n", routeAddr);
     }
     if(tb[RTA_GATEWAY]){
        char routeAddr[128];
        inet_ntop(AF_INET6, RTA_DATA(tb[RTA_GATEWAY]), routeAddr, sizeof(routeAddr));
        printf("Gateway is %s\n", routeAddr);
     }
    if(tb[RTA_PRIORITY]){
        int metric ;
        metric = *(int*)RTA_DATA(tb[RTA_PRIORITY]);
        printf("Mertic is %d\n", metric);
     }
     /*Переходим на следующее сообщение*/
     size -= NLMSG_ALIGN(len);
     nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
  }//for(nlmp = (struct nlmsghdr *)buffer; size > sizeof(*nlmp);){
}
Полезная нагрузка в данном случае состоит из заголовка и атрибутов
маршрута. Поэтому общий вид пакета имеет следующий вид:


Таким образом, функция для выделения атрибутов:

void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
       memset(tb, 0, sizeof(struct rtattr *) * (max + 1));

       while(RTA_OK(rta, len)){
             if(rta->rta_type <= max){
                   tb[rta->rta_type] = rta;
             }

             rta = RTA_NEXT(rta, len);
      }
}

Теперь можно написать main функцию:

int main(int argc, char *argv[])
{
    int nd = 0;
    struct sockaddr_nl *local;
    char buffer[16384];

    nd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

    local = malloc(sizeof(struct sockaddr_nl ));
    memset(local, 0, sizeof(struct sockaddr_nl));

    local->nl_family = AF_NETLINK;
    /*Принимаем изменения, связанные с маршрутами ipv6*/
    local->nl_groups = RTMGRP_IPV6_ROUTE;
    local->nl_pid = getpid();

    if(bind(nd, (struct sockaddr *)local , sizeof(struct sockaddr_nl)) < 0){
         printf("Error bind\n");
         return 1;
    }

    while(1){
       /*
        Эта структура непосредственно передается через сокет.
        Она содержит в себе  указатель на блок полезных данных,количество
        данных блоков, а так же ряд дополнительных флагов и полей
      */
       struct msghdr msg;
       /*
        Эта структура служит хранилищем полезных данных,
        передаваемых через сокеты netlink. Полю iov_base присваивается указатель
        на байтовый массив. Именно в этот байтовый массив будут записаны
        данные сообщения.                   
       */
       struct iovec iov;
       int size = 0;

       iov.iov_base = buffer;
       iov.iov_len = 16000;

       msg.msg_name = &local;
       msg.msg_namelen = sizeof(*local);
       msg.msg_iov = &iov;
       msg.msg_iovlen = 1;

       size = recvmsg(nd, &msg, MSG_DONTWAIT);
       if(size  < 0){
             if(errno == EINTR || errno == EAGAIN){
                   usleep(250000);
                   continue;
            }
      }

      show_route(buffer, size);
   }
} 

Комментариев нет:

Отправить комментарий