Search code examples
clinux-kerneliptables

Print MAC address from Ethernet header


I want to check the Ethernet header of packet that is porececed by iptables (1.4), so i need to write a module that catch packet and apply my function. I am looking for the mac destination value in the Ethernet header (just for test purpose), so the code should be like this:

static bool match(const struct sk_buff *skb, struct xt_action_param *par)
{
    struct ethhdr *hdr;        
    hdr = eth_hdr(skb);
    printk(KERN_INFO "hdr->h_dest 0x%x\n", hdr->h_dest);
    printk(KERN_INFO "MACPROTO=%04x\n", hdr->h_proto);

The problem is that i cant get the correct value, i have some thing that is not even in the real frame (i checked that with Wireshark), so is it the right function to get Ethernet header attributs?

update: i used the solution presented in the post, but still have wrong output, it's like if the structure point to wrong place enter image description here

This image show result when i use nc to send "aaa" string, the ethernet header should be the same in the to frame, but as present in the result, it's not.


Solution

  • struct ethhdr is defined as such:

    /*
     *  This is an Ethernet frame header.
     */
    
    struct ethhdr {
        unsigned char   h_dest[ETH_ALEN];   /* destination eth addr */
        unsigned char   h_source[ETH_ALEN]; /* source ether addr    */
        __be16      h_proto;        /* packet type ID field */
    } __attribute__((packed));
    

    But your code is trying to print that byte array using %x:

    printk(KERN_INFO "hdr->h_dest 0x%x\n", hdr->h_dest);
    

    That makes no sense, and probably causes a compiler warning to be generated. This is kernel code - you are using -Werror and -Wall, right?


    Good news: printk supports printing MAC addresses directly. From documentation/printk-formats.txt:

    MAC/FDDI addresses:
    
        %pM 00:01:02:03:04:05
        %pMR    05:04:03:02:01:00
        %pMF    00-01-02-03-04-05
        %pm 000102030405
        %pmR    050403020100
    
        For printing 6-byte MAC/FDDI addresses in hex notation. The 'M' and 'm' 
        specifiers result in a printed address with ('M') or without ('m') byte
        separators. The default byte separator is the colon (':').
    
        Where FDDI addresses are concerned the 'F' specifier can be used after
        the 'M' specifier to use dash ('-') separators instead of the default
        separator.
    
        For Bluetooth addresses the 'R' specifier shall be used after the 'M' 
        specifier to use reversed byte order suitable for visual interpretation
        of Bluetooth addresses which are in the little endian order.
    
        Passed by reference.
    

    So you can just use this:

    printk(KERN_INFO "hdr->h_dest 0x%pM\n", hdr->h_dest);
    

    These format specifiers are provided anywhere vsnprintf is used. Here's an example:

        switch (dev->type) {
        case ARPHRD_ETHER:
                nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
                               eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
                               ntohs(eth_hdr(skb)->h_proto));
                return;
        default:
                break;
        }