Search code examples
ciplibpcap

Problems about ethernet/IP header host/addr using libpcap?


I'm using libpcap to decode some ip packets.

But I find both the ethernet ether_shost/ether_dhost and ip saddr/daddr I decode are the same.

Where do I mess up?

Thanks in advance.

Here's part of the callback function:

void
got_packet(u_char *args, const struct pcap_pkthdr *header,
    const u_char *packet)
{
    ...
    eth_h = (struct ether_header *) packet;
    struct ether_addr shost, dhost;
    memcpy(&shost, eth_h->ether_shost, sizeof(shost));
    memcpy(&dhost, eth_h->ether_dhost, sizeof(dhost));
    printf("L2 DLT_EN10MB: %s -> %s\n", ether_ntoa(&shost), ether_ntoa(&dhost)); // shost == dhost?

    if (ntohs(eth_h->ether_type) != ETHERTYPE_IP) {
        return;
    }

    // only work for L2 DLT_EN10MB
    ip_h = (struct iphdr *) (packet + sizeof(struct ether_header));
    if (ip_h->version != 4) {
        return;
    }

    struct in_addr saddr, daddr;
    saddr.s_addr = ip_h->saddr;
    daddr.s_addr = ip_h->daddr;

    printf("%s -> %s\n", inet_ntoa(saddr), inet_ntoa(daddr)); // saddr == daddr?
    printf("%" PRIu32 " -> %" PRIu32 "\n", ntohl(ip_h->saddr), ntohl(ip_h->daddr)); // actually not the same
    ...
}

Solution

  • inet_ntoa does the following:

    • Puts the string form of the address into a buffer
    • Returns the address of the buffer

    The key thing is that it uses the same buffer each time you call it!

    So when this line runs:

    printf("%s -> %s\n", inet_ntoa(saddr), inet_ntoa(daddr));
    

    first it will put one address in the buffer, then it will put the other address in the buffer, then it will print the contents of the buffer twice.

    You could fix this by storing the strings in your own separate buffers:

    char saddr_str[INET_ADDRSTRLEN];
    char daddr_str[INET_ADDRSTRLEN];
    strcpy(saddr_str, inet_ntoa(saddr));
    strcpy(daddr_str, inet_ntoa(daddr));
    printf("%s -> %s\n", saddr_str, daddr_str);
    

    or by calling printf twice:

    printf("%s -> ", inet_ntoa(saddr));
    printf("%s\n", inet_ntoa(daddr));
    

     

    ether_ntoa has the same problem.