strncmp() и if() не согласен, что я упускаю?? (сырые сокеты)

Я пытаюсь создать простой эхо-сервер/клиент, который работает на уровне Ethernet (используя необработанные сокеты). Серверная часть сама по себе работает и показывает все входящие пакеты на eth0. Клиент работает и отправляет пакеты Ethernet на eth0 (я проверил это с помощью wireshark и вижу исходящие пакеты). Теперь я хочу сделать фильтр, чтобы просматривать только интересующие меня пакеты. (Это основано на адресах назначения/источника.)

В приведенном ниже коде может кто-нибудь объяснить мне, почему strncmp возвращает ноль (это означает, что строки совпадают), но все же «if (ethernet_header-> h_dest == mac)» не выполняется (не совпадают). Обе переменные «mac» и «ethernet_header->h_dest» имеют одинаковый тип и длину.

Еще немного предыстории: - Это делается на 64-битной Linux (ubuntu) - Я использую eth0 на той же машине для отправки/получения.... Я не думаю, что это должно быть проблемой?

Я просто не понимаю, почему strcmp возвращает совпадение, а если нет. Что мне не хватает??

void ParseEthernetHeader(unsigned char *packet, int len) {
    struct ethhdr *ethernet_header;
 unsigned char mac[ETH_ALEN] = {0x01, 0x55, 0x56, 0x88, 0x32, 0x7c}; 

 if (len > sizeof(struct ethhdr)) {
  ethernet_header = (struct ethhdr *) packet;

  int result = strncmp(ethernet_header->h_dest, mac, ETH_ALEN);
  printf("Result: %d\n", result);

  if(ethernet_header->h_dest == mac) {
   /* First set of 6 bytes are Destination MAC */
   PrintInHex("Destination MAC: ", ethernet_header->h_dest, 6);
   printf("\n");

   /* Second set of 6 bytes are Source MAC */
   PrintInHex("Source MAC: ", ethernet_header->h_source, 6);
   printf("\n");

   /* Last 2 bytes in the Ethernet header are the protocol it carries */
   PrintInHex("Protocol: ", (void *) &ethernet_header->h_proto, 2);
   printf("\n\n");
   printf("Length: %d\n",len);
  }

 } else {
  printf("Packet size too small (length: %d)!\n",len);
 }

}

person NomadAlien    schedule 29.07.2010    source источник
comment
Ах, верно! истинный! истинный! Простая ошибка! Спасибо всем!   -  person NomadAlien    schedule 29.07.2010


Ответы (6)


Ни strncmp, ни голый if не должны использоваться для сравнения MAC-адресов.

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

ff ff 00 ff ff ff
ff ff 00 aa aa aa

будет истинным (он проверяет только до первого нулевого байта).

Второй не сработает, потому что вы сравниваете указатели, а не содержимое, на которое указывают указатели. Если у вас следующая схема памяти:

0x12345678 (mac) | 0x11111111 |
0x1234567c (eth) | 0x11111111 |

тогда сравнение mac с eth с if (mac == eth) даст вам false, поскольку они являются разными указателями, один из которых заканчивается на 78, а другой - на 7c.

Вместо этого вы должны использовать memcmp, так как он будет сравнивать необработанные байты памяти, не останавливаясь на раннем нулевом байте:

int result = memcmp (ethernet_header->h_dest, mac, ETH_ALEN);
person paxdiablo    schedule 29.07.2010
comment
Обратите внимание, что такого рода ошибка (с использованием strcmp() вместо memcmp() была гарантирована) в прошлом была проблемой безопасности, например. при сравнении хеш-значений. - person caf; 29.07.2010

Вы не можете проверить равенство строк с помощью оператора ==. Вот почему в первую очередь существуют функции strcmp().

person Community    schedule 29.07.2010

strncmp принимает указатели на char в качестве первых двух аргументов.

strncmp возвращает ноль, потому что строки в этих двух местах одинаковы для ETH_ALEN символов — это не означает, что ethernet_header->h_dest и mac равны. Это два разных указателя.

int main()
{
        char a1[] = "asdf";
        char a2[] = "asdf";
        char *p1 = "asdf";
        char *p2 = "asdf";
        char *s1 = malloc(5);
        char *s2 = malloc(5);
        strcpy(s1, "asdf");
        strcpy(s2, "asdf");
        printf("a1 and a2: strcmp gives %d and they are %s\n", strcmp(a1, a2), a1 == a2 ? "equal" : "different");
        printf("p1 and p2: strcmp gives %d and they are %s\n", strcmp(p1, p2), p1 == p2 ? "equal" : "different");
        printf("s1 and s2: strcmp gives %d and they are %s\n", strcmp(s1, s2), s1 == s2 ? "equal" : "different");
        return 0;
}

Выход:

a1 and a2: strcmp gives 0 and they are different  
p1 and p2: strcmp gives 0 and they are equal  
s1 and s2: strcmp gives 0 and they are different
  • p1 и p2 равны, потому что они оба указывают на одну и ту же константную строку в памяти.
  • В случае массивов каждой переменной массива (в стеке) выделяется непрерывный блок из 5 байтов, и строка asdf\0 копируется в эти места.
  • s1 и s2 — это два разных указателя, которые указывают на два разных блока 5-байтовых последовательностей в куче, которые содержат одно и то же значение.
person Amarghosh    schedule 29.07.2010

Что это за код?

if(ethernet_header->h_dest == mac)

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

person leppie    schedule 29.07.2010

if(ethernet_header->h_dest == mac) просто сравнивает необработанные значения указателя. Это означает, что он проверяет, начинаются ли обе строки с одного и того же адреса памяти. Обычно это не то, что вы хотите.

Чтобы сравнить содержимое двух c-строк, всегда используйте strncmp().

person cypheon    schedule 29.07.2010

В C == не работает со строками, как вы думаете. Вместо этого вы должны использовать strncmp().

Просто измените

 if(ethernet_header->h_dest == mac) {

to

if(result == 0) {
person vtorhonen    schedule 29.07.2010