Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Программирование под Unix/Linux > ARP запрос через socket, учитывая routing table


Автор: slavayo7777 22.6.2011, 14:57
Добрый день.
Уже замучался рыть в интернете. Может подскажите...
В моем устройстве необходимо узнать мак адресс узла, ip адрес которого мне выдает сервер. На этот адресс необходимо слать пакеты с данными.
Устройство работает на Linux-2.6.10.
На сколько я знаю, для генерации ARP нужен SOCK_RAW и самому забивать пакет и все заголовки. Что я и сделал. 
Код

if((raw_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP)))== -1)
{
   perror("Error creating raw socket: ");
   return -1;
}
/* Set socket to non-blocking */
flags = fcntl(raw_sock, F_GETFL, 0);
if (0 > flags) {
    ENT_ERR("SetRawSockNonblock() - can't get socket flags");
    return -1;
}
flags |= O_NONBLOCK;
if (0 > fcntl(raw_sock, F_SETFL, flags)) {
    ENT_ERR("SetRawSockNonblock() - can't make socket non-blocking, errno %d", errno);
    return -1;
}
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
strncpy((char *)ifr.ifr_name, "eth0", IFNAMSIZ);
if((ioctl(raw_sock, SIOCGIFINDEX, &ifr)) == -1)
{
   printf("Error getting Interface index !\n");
   return -1;
}
/* Bind our raw socket to this interface */
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(ETH_P_ARP); 
if((bind(raw_sock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
{
   perror("Error binding raw socket to interface\n");
   exit(-1);
}
/* Здесь формирую сам пакет packet */

/* Send the packet out ! */
if((len = write(raw_sock, packet, pkt_len)) != pkt_len)
{
/* Error */
   printf("Could only send %d bytes of packet of length %d\n", len, pkt_len);
   return -1;
}
/* Wait for ARP response */
in_pkt = (unsigned char *)malloc(MAX_RESP_LEN);
count = 100;
while(((len = read(raw_sock, in_pkt, pkt_len)) <= 0) && (count > 0))
{
   apps_tel_taskDelay(1);
   count--;
}
if ( len <= 0 )
{
   free(in_pkt);
   close(raw_sock);
   ENT_ERR("Socket read error, or no ARP response\n");
   return -1;
}
/*здесь расфасовываю ответ и узнаю адресс*/

Все прекрасно работает, пока не подключаешь устройство через шлюз к серверу.
Например мое устройство имеет айпи 10.60.20.2, шлюз 10.60.20.1 к серверу 10.80.1.1. И сервер может выдать произвольный айпи, к которому можно достучаться через этот же шлюз.
прописываю в своем устройстве шлюз 10.60.20.1: 
Код

route add -net 10.0.0.0 netmask 255.0.0.0 gw 10.60.20.1 dev eth0

Но так как я забиваю пакет сам, я в нем забиваю IP искомого узла, что в случае с gateway неверно (нужно подменить в теле ARP пакета искомый IP на IP гейта).
Но как узнать из routing table какой айпи подставлять?
утилита ping каким то образом это делает.
Есть ли другой способ генерации ARP? Чтоб система сама заботилась об IP и маршрутизации.

Автор: fish9370 22.6.2011, 15:10
Цитата(slavayo7777 @  22.6.2011,  14:57 Найти цитируемый пост)
В моем устройстве необходимо узнать мак адресс узла, ip адрес которого мне выдает сервер


это узнать невозможно.. ты можешь узнать мак только в той же зоне каллизии (не дальше ethernet-уровня)..

Автор: fish9370 22.6.2011, 15:43
хотя, если ты настроишь на маршрутизаторе (шлюзе) proxyarp.. то впринципе можно.. но это не по-дефолту в любом случае..

Автор: slavayo7777 22.6.2011, 15:46
Цитата(fish9370 @ 22.6.2011,  15:10)
это узнать невозможно.. ты можешь узнать мак только в той же зоне каллизии (не дальше ethernet-уровня)..


Я наверное неправильно выразился. Мне не нужен мак адресс конечного узла. Мне нужно узнать мак шлюза, чтобы слать через него пакеты на сервер. Моей программе заранее не известно, через что она работает: напрямую с сервером или через шлюз. Ну так вот, не охото добавлять в программу дополнительную настройку шлюза, хочется чтобы прописав маршрут в route, она могла сама понять, через что ей работать. И из настроек в программе был бы только IP сервера. А как до последнего дойдут пакеты, будет решать ОС. 

Автор: fish9370 22.6.2011, 15:52
Цитата(slavayo7777 @  22.6.2011,  15:46 Найти цитируемый пост)
напрямую с сервером или через шлюз


это вопрос маршрутизиции, зачем связываться с канальным уровнем? почему не работать на сетевом?

Автор: slavayo7777 22.6.2011, 16:08
Цитата(fish9370 @ 22.6.2011,  15:52)
это вопрос маршрутизиции, зачем связываться с канальным уровнем? почему не работать на сетевом?

Как тогда сделать ARP запрос на сетевом уровне?
В общем, задача узнать мак адресс шлюза, чтобы слать пакеты.

Автор: fish9370 22.6.2011, 16:27
Цитата(slavayo7777 @  22.6.2011,  16:08 Найти цитируемый пост)
Как тогда сделать ARP запрос на сетевом уровне?


на сетевом уровне ARP'а нет, там есть IP..

Цитата(slavayo7777 @  22.6.2011,  16:08 Найти цитируемый пост)
В общем, задача узнать мак адресс шлюза, чтобы слать пакеты.


вообще слать пакеты ты хочешь куда? на шлюз или на целевую машину за шлюзом?

Автор: fish9370 22.6.2011, 16:55
я понял, что ты хочешь (мне кажется, что понял)..

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

Автор: slavayo7777 23.6.2011, 07:04
Цитата(fish9370 @ 22.6.2011,  16:55)
я понял, что ты хочешь (мне кажется, что понял)..

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

Да, да. Именно это я и хочу. Я так это себе и представлял. Ну так весь вопрос был как посмотреть в эту таблицу маршрутизации?
Я думаю лучше сразу смотреть в таблицу маршрутизации и если его там нет, слать широковещательный пакет типа - у кого ip.
Весь нет перерыл и так и не нашел как туда смотреть. Нашел только как добавлять и убирать записи от туда (с помощью ioctl).

Автор: fish9370 23.6.2011, 09:49
Цитата(slavayo7777 @  23.6.2011,  07:04 Найти цитируемый пост)
Ну так весь вопрос был как посмотреть в эту таблицу маршрутизации?


/proc/net/route

Код

Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT
eth1    00000000       6964784F       0003   0           0         0         00000000    0          0            0


где, destenation = 00000000, mask = 000000000 - это дефолтный маршрут
gateway соответственно во внутреннем представлении айпишника..



Цитата(slavayo7777 @  23.6.2011,  07:04 Найти цитируемый пост)
Я думаю лучше сразу смотреть в таблицу маршрутизации и если его там нет, слать широковещательный пакет


нет, так лучше не делать, иначе ты не узнаешь, что машина находится не за шлюзом, а рядом.. и такой пакет не дойдет до назначения..

Автор: slavayo7777 23.6.2011, 10:18
Цитата(fish9370 @ 23.6.2011,  09:49)

/proc/net/route

Код

Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT
eth1    00000000       6964784F       0003   0           0         0         00000000    0          0            0


где, destenation = 00000000, mask = 000000000 - это дефолтный маршрут
gateway соответственно во внутреннем представлении айпишника..

про шеловскую команду route я знаю  smile 
ты предлагаешь делать шеловский вызов, а потом парсить stdout в программе и от туда выковыривать маршрут?
нет ли другого способа, какими нибудь вызовами достать эту информацию?
идеально было бы что нить типа функции, которая возвращает правильный маршрут, если укажешь ей нужный айпишник.
Цитата(fish9370 @ 23.6.2011,  09:49)

нет, так лучше не делать, иначе ты не узнаешь, что машина находится не за шлюзом, а рядом.. и такой пакет не дойдет до назначения..

ну например:
1. сервер находится в моей подсети без шлюза:
я смотрю в таблицу маршрутизацию и узнаю, что к серверу можно стучаться напрямую,
отправляю ARP, узнаю мак сервера.
2. сервер находится не в моей подсети за шлюзом:
я смотрю в таблицу маршрутизацию и узнаю, что к серверу можно стучаться через шлюз,
отправляю ARP, узнаю мак шлюза.

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

Автор: fish9370 23.6.2011, 10:37
Цитата(slavayo7777 @  23.6.2011,  10:18 Найти цитируемый пост)

1. сервер находится в моей подсети без шлюза:я смотрю в таблицу маршрутизацию и узнаю, что к серверу можно стучаться напрямую,отправляю ARP, узнаю мак сервера.
2. сервер находится не в моей подсети за шлюзом:я смотрю в таблицу маршрутизацию и узнаю, что к серверу можно стучаться через шлюз,отправляю ARP, узнаю мак шлюза.


тогда я предлагаю, еще усовершенствовать метод, и смотреть сначала в арп-таблицу (это избавит нас от лишнего широковещательного запроса)

что касается proc, то это не совсем шелл-метод.. хотя я с тобой согласен системный вызов, было бы получше.. поищем..

Автор: xvr 23.6.2011, 10:49
Цитата(slavayo7777 @  23.6.2011,  10:18 Найти цитируемый пост)
про шеловскую команду route я знаю

Это не шеловская команда, это файл такой - /proc/net/route. Открываешь его fopen'ом и читаешь построчно

Автор: slavayo7777 23.6.2011, 11:00
Цитата(xvr @ 23.6.2011,  10:49)
Цитата(slavayo7777 @  23.6.2011,  10:18 Найти цитируемый пост)
про шеловскую команду route я знаю

Это не шеловская команда, это файл такой - /proc/net/route. Открываешь его fopen'ом и читаешь построчно

Вот оно чо!!
Спасибо!!! блин, все так просто оказываеца)) 
если есть конечно путь попроще, типа функции, которая автоматом выдает маршрут, было бы неплохо...

Автор: fish9370 23.6.2011, 11:04
Цитата(slavayo7777 @  23.6.2011,  11:00 Найти цитируемый пост)
Спасибо!!! блин, все так просто оказываеца)) 


я думал, ты это понял..

вот еще интерфейс для получения arp-таблицы

/proc/net/arp

Автор: slavayo7777 23.6.2011, 11:15
Цитата(fish9370 @ 23.6.2011,  11:04)
я думал, ты это понял..
вот еще интерфейс для получения arp-таблицы
/proc/net/arp

Спасибо. Пойду писать парсеры для этих файлов. Думаю, вопрос можно пометить решенным.
Хотя конечно вдруг кто вспомнит функцию или вызов, которые исходя из таблицы возвращают айпишник, буду очень рад.
Ну или может, кто то уже делал такой парсер и выложит код...  smile 
Всем большое спасибо!!

Автор: slavayo7777 23.6.2011, 15:00
Вот сделал парсер, который возвращает адресс гейтвея, если он прописан (если кому надо). Взял за основу net-tools
Код

#define _PATH_PROCNET_ROUTE "/proc/net/route"
#define RTF_UP          0x0001          /* route usable                 */
#define RTF_GATEWAY     0x0002          /* destination is a gateway     */
#define SWAP32(x)  ( ((x & 0xff000000) >> 24) \
                   | ((x & 0x00ff0000) >>  8) \
                   | ((x & 0x0000ff00) <<  8) \
                   | ((x & 0x000000ff) << 24) )
/*input ipaddr in ulong format e.g 192.168.1.1 will be C0A80101
   output in the same format ip addr checked in route table
   return 0 if error
*/
unsigned long Check_route_table(unsigned long dest_ip_address)
{
    char buff[1024], iface[16];
    char gate_addr[128], net_addr[128];
    char mask_addr[128];
    unsigned long mask, net, gate;
    int num, iflags, metric, refcnt, use, mss, window, irtt;
    FILE *fp = fopen(_PATH_PROCNET_ROUTE, "r");
    char *fmt;

    if (!fp)
    {
        perror(_PATH_PROCNET_ROUTE);
        printf("INET (IPv4) not configured in this system.\n");
        return 0;
    }

    fmt = proc_gen_fmt(_PATH_PROCNET_ROUTE, 0, fp, "Iface", "%16s",
            "Destination", "%128s", "Gateway", "%128s", "Flags", "%X",
            "RefCnt", "%d", "Use", "%d", "Metric", "%d", "Mask", "%128s",
            "MTU", "%d", "Window", "%d", "IRTT", "%d", NULL);
    if (!fmt)
        return 0;

    while (fgets(buff, 1023, fp))
    {
        num = sscanf(buff, fmt, iface, net_addr, gate_addr, &iflags, &refcnt,
                &use, &metric, mask_addr, &mss, &window, &irtt);
        if (num < 10 || !(iflags & RTF_UP))
            continue;
        net = strtoul(net_addr, NULL, 16);
        gate = strtoul(gate_addr, NULL, 16);
        mask = strtoul(mask_addr, NULL, 16);
        /* Decode the flags. */
        if ((iflags & RTF_UP)&&(iflags&RTF_GATEWAY))
        {
            dest_ip_address = SWAP32(dest_ip_address);
            if((dest_ip_address&mask) == net)
                return SWAP32(gate);
        }
        /* Print the info. */
    }
    free(fmt);
    (void) fclose(fp);
    return dest_ip_address;
}

char *proc_gen_fmt(char *name, int more, FILE * fh,...)
{
    char buf[512], format[512] = "";
    char *title, *head, *hdr;
    va_list ap;

    if (!fgets(buf, (sizeof buf) - 1, fh))
    return NULL;
    strcat(buf, " ");

    va_start(ap, fh);
    title = va_arg(ap, char *);
    for (hdr = buf; hdr;) {
    while (isspace(*hdr) || *hdr == '|')
        hdr++;
    head = hdr;
    hdr = strpbrk(hdr, "| \t\n");
    if (hdr)
        *hdr++ = 0;

    if (!strcmp(title, head)) {
        strcat(format, va_arg(ap, char *));
        title = va_arg(ap, char *);
        if (!title || !head)
        break;
    } else {
        strcat(format, "%*s");    /* XXX */
    }
    strcat(format, " ");
    }
    va_end(ap);

    if (!more && title) {
    fprintf(stderr, "warning: %s does not contain required field %s\n",
        name, title);
    return NULL;
    }
    return strdup(format);
}

Автор: fish9370 23.6.2011, 21:16
неплохой код.. спасибо..
мне аж захотелось поделиться с тобой своим парсером..

Автор: slavayo7777 24.6.2011, 07:57
Цитата(fish9370 @ 23.6.2011,  21:16)
неплохой код.. спасибо..
мне аж захотелось поделиться с тобой своим парсером..

ну так делись  smile 

Автор: fish9370 24.6.2011, 10:17
Код

/*
 *
 * \section copyright Copyright and author
 *
 * Copyright (C) 2008 - 2011, fish9370.
 *
 * \author Oleg Shteinliht <fish9370@fpg.ru>
 *
 */

/*! \file
  \brief source file for Mifi  - the Open Source test program.

 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h> /* for bool type */
#include <getopt.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "logger.h"
#include "gateway.h"

#define MAX_WORDS 256

int errcode = 0;

int next_word(char *str) {
        char *pos = str;
        if(pos)
                while(*pos && ((unsigned char) *pos) < 33)
                        pos++;

        return pos - str;
}

int next_space(char *str) {
        char *pos = str;
        if(pos)
                while(*pos && ((unsigned char) *pos) > 32)
                        pos++;

        return pos - str;
}

int split_words(char *buff, char *ap[], int size) {
        if (!buff)
                return 0;
        int len = strlen(buff), n = 0;

        char *pos = buff;
        for (pos = buff + next_word(pos); pos < buff + len; n++) {
                if (n == size - 1)
                        break;
                ap[n] = pos;
                pos += next_space(pos);
                *pos++ = '\0';
                pos += next_word(pos);
        }

        ap[n] = NULL;

        return n;
}

void parse_route(char *default_gw)
{
        char buff[LINE_MAX];
        char *buffv[MAX_WORDS];
        FILE *f;

        if (!(f = fopen("/proc/net/route", "r"))) {
                mifi_verbose(LOG_VERBOSE, "open file error: %s\n", strerror(errno));
                return;
        }

        while (fgets(buff, sizeof(buff), f)) {
                if (split_words(buff, buffv, MAX_WORDS) != 11)
                        continue;

                if (!strcmp("00000000", buffv[1]) && !strcmp("00000000", buffv[7]))
                        strcpy(default_gw, buffv[2]);
        }

        fclose(f);
}

void usage(const char * const name)
{
        mifi_verbose(LOG_VERBOSE, "usage:\n%s [options]\n", name);
        mifi_verbose(LOG_VERBOSE, "options:\n");
        mifi_verbose(LOG_VERBOSE, "  --help\t\t\tThis help.\n");
        mifi_verbose(LOG_VERBOSE, "  -d, --debug\t\t\tPrint lots of debugging information.\n");
        mifi_verbose(LOG_VERBOSE, "  -V, --version \t\tPrint program version.\n");

        mifi_verbose(LOG_VERBOSE, "\nreport bugs to <fish9370@fpg.ru>\n");
}

int main(int argc, char *argv[])
{

        static struct option long_options[] = {
                {"help", 0, 0, '?'},
                {"debug", 0, 0, 'd'},
                {"version", 0, 0, 'V'},
                {0, 0, 0, 0}
        };

        int c, index = 0;
        while((c = getopt_long(argc, argv, "hdV", long_options, &index)) != -1) {
                switch(c) {
                case 'd':
                        flags.debug = true;
                        break;

                case ':':
                        mifi_verbose(LOG_VERBOSE, "Option -%c requires an operand\n", optopt);
                        usage(argv[0]);
                        errcode = -1;
                        goto exit;

                case 'V':
                        mifi_verbose(LOG_VERBOSE, "%s %s\n", PROGNAME, PROGVERSION);
                        mifi_verbose(LOG_VERBOSE, "Copyright (C) 2010 ");
                        mifi_verbose(LOG_NORMAL_BOLD, "fish9370\n");
                        mifi_verbose(LOG_VERBOSE, "This is free software; see the source for copying conditions.  There is NO\n");
                        mifi_verbose(LOG_VERBOSE, "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
                        goto exit;

                case '?':
                        /*printf("Unrecognized option: -%c\n", optopt);*/
                        usage(argv[0]);
                        goto exit;
                }
        }

        char default_gw[LINE_MAX];
        parse_route(default_gw);

        mifi_verbose(LOG_VERBOSE, "default gw: %s\n", default_gw);

exit:
        return errcode;
}



здесь не все декларации, если есть вопросы - добро пожаловать в мою учебную помойку http://79.120.100.108/mifi/
файл называется test/gateway.c

Автор: slavayo7777 27.6.2011, 13:13
Цитата(slavayo7777 @ 23.6.2011,  15:00)
Вот сделал парсер, который возвращает адресс гейтвея, если он прописан (если кому надо). Взял за основу net-tools

хреновый однако парсер я сделал)) ошибочка вышла небольшая, но эт не страшно. с несколькими маршрутами гейтвеев  мой парсер отказался работать, исправил:
Код

unsigned long Check_route_table(unsigned long dest_ip_address)
{
    char buff[1024], iface[16];
    char gate_addr[128], net_addr[128];
    char mask_addr[128];
    unsigned long mask, net, gate;
    unsigned long dest_ip_swaped = SWAP32(dest_ip_address);
    int num, iflags, metric, refcnt, use, mss, window, irtt;
    FILE *fp = fopen(_PATH_PROCNET_ROUTE, "r");
    char *fmt;

    if (!fp)
    {
        perror(_PATH_PROCNET_ROUTE);
        printf("INET (IPv4) not configured in this system.\n");
        return 0;
    }

    fmt = proc_gen_fmt(_PATH_PROCNET_ROUTE, 0, fp, "Iface", "%16s",
            "Destination", "%128s", "Gateway", "%128s", "Flags", "%X",
            "RefCnt", "%d", "Use", "%d", "Metric", "%d", "Mask", "%128s",
            "MTU", "%d", "Window", "%d", "IRTT", "%d", NULL);
    if (!fmt)
        return 0;

    while (fgets(buff, 1023, fp))
    {
        num = sscanf(buff, fmt, iface, net_addr, gate_addr, &iflags, &refcnt,
                &use, &metric, mask_addr, &mss, &window, &irtt);
        if (num < 10 || !(iflags & RTF_UP))
            continue;
        net = strtoul(net_addr, NULL, 16);
        gate = strtoul(gate_addr, NULL, 16);
        mask = strtoul(mask_addr, NULL, 16);
        /* Decode the flags. */
        if ((iflags & RTF_UP)&&(iflags&RTF_GATEWAY))
        {
            if((dest_ip_swaped & mask) == net)
                return SWAP32(gate);
        }
        /* Print the info. */
    }
    free(fmt);
    (void) fclose(fp);
    return dest_ip_address;
}


Цитата

здесь не все декларации, если есть вопросы - добро пожаловать в мою учебную помойку

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)