使用sk_buff添加一个以太网帧头

时间:2022-03-14 11:03:13

I have a kernel module that captures outgoing Internet traffic(Netfilter hook: LOCAL_OUT) At this hook, there's still no Ethernet header.

我有一个内核模块,在这个钩子上捕获出站的Internet流量(Netfilter hook: LOCAL_OUT),仍然没有以太网头。

I built the Ethernet header and it's ready to use, but how can I attach it to the skb so that I can send the whole skb struct to dev_queue_xmit() ?

我构建了以太网头,它已经可以使用了,但是我如何将它附加到skb,以便能够将整个skb结构发送到dev_queue_xmit() ?

Is there any guide on how to manipulate sk_buff data that you can provide me for further information?

是否有关于如何操作sk_buff数据的指导,您可以为我提供进一步的信息?

As a sample, I try to do what I want to do on all ECHO ICMP traffic; this is the code I have. But when checking with Wireshark on the other machine, I get a working Ethernet header but an EMPTY IP packet...how come?

作为一个示例,我尝试在所有的ECHO ICMP流量上做我想做的事情;这是我的代码。但是当在另一台机器上与Wireshark进行检查时,我得到了一个可以工作的以太网头,但是一个空IP包……如何来吗?

static unsigned int post_icmp_check(unsigned int hooknum,
                 struct sk_buff *skb,
                 const struct net_device *in,
                 const struct net_device *out,
                 int (*okfn)(struct sk_buff *))
{
  struct iphdr *iph;
  struct icmphdr *icmph;
  struct sk_buff *sb;
  struct ethhdr *ethh;
  unsigned char *frame = NULL; //Used just to copy the skb->data and replace it, that's all
  unsigned char *ptr;

  /* Allocations and initial assignations  */
  sb = skb_copy(skb, GFP_ATOMIC); /* Making a private copy because we cannot modify all fields in the public one */
  iph = ip_hdr(sb);

  if(iph->protocol == IPPROTO_ICMP){
      printk(KERN_ALERT"POST ICMP traffic!\n");

      icmph = icmp_hdr(sb);
      if(icmph->type == ICMP_ECHO){
          frame = (unsigned char *)kmalloc(ntohs(iph->tot_len) + (ETH_HLEN*8), GFP_KERNEL);
          ptr = (frame +  (ETH_HLEN * 8));

          ethh = (struct ethhdr*) frame;      //00:1a:80:7e:96:ba
          ethh->h_source[0] = 0x00;   
          ethh->h_source[1] = 0x1a;           
          ethh->h_source[2] = 0x80;
          ethh->h_source[3] = 0x7e;
          ethh->h_source[4] = 0x96;
          ethh->h_source[5] = 0xba;
          ethh->h_dest[0] = 0xff;
          ethh->h_dest[1] = 0xff;
          ethh->h_dest[2] = 0xff;
          ethh->h_dest[3] = 0xff;
          ethh->h_dest[4] = 0xff;
          ethh->h_dest[5] = 0xff;
          ethh->h_proto = htons(ETH_P_IP);
          memcpy(ptr, skb->data, ntohs(iph->tot_len)); /* Copying the rest of the data from the skb to sb */
          sb->data = frame;

          //icmph = icmp_hdr(sb);

          //icmph->un.echo.sequence = htons(i++);         
          //icmph->checksum = 0;
          //icmph->checksum =  in_cksum((unsigned short *)icmph, ntohs(iph->tot_len) - sizeof(struct iphdr));
          sb->dev = out;
          printk(KERN_ALERT"%s\n",sb->dev->name);
          if(sb->dev == NULL)
              printk(KERN_ALERT"dev is NULL!");           
          dev_queue_xmit(sb);
          return NF_STOLEN;
}

EDIT : And yes, I am hardcoding my own MAC and BROADCASTING the frame, for the sake of simplicity only.

编辑:是的,我正在硬编码我自己的MAC和广播框架,只是为了简单。

EDIT2: So I am giving this function the original sk_buff caught by net_filter and the appropriate net_device I tried to follow what you said and read a little guide about the functions for the sK_buff. Still, when I dev_queue_xmit() the returned sk_buff of this function, I get a complete kernel crash.

我给这个函数一个最初的sk_buff,它是由net_filter捕获的,我试着按照您所说的,并阅读一些关于sk_buff函数的小指南。但是,当我dev_queue_xmit()返回这个函数的sk_buff时,我就会得到一个完整的内核崩溃。

static struct sk_buff* set_skb(struct sk_buff *org_skb, struct net_device *dev)
{
    int header_size;
    unsigned char *data;
    struct icmphdr *icmph;
    struct iphdr *iph;      
    struct icmphdr *temp_icmph;
    struct iphdr *temp_iph;         
    unsigned char myaddr[] = {0x00,0x1a,0x80,0x7e,0x96,0xba};
    unsigned char addr [] = {0xff,0xff,0xff,0xff,0xff,0xff};    
    struct sk_buff *skb;

    temp_iph = ip_hdr(org_skb); 
    temp_icmph = icmp_hdr(org_skb);

    skb = alloc_skb(ntohs((temp_iph->tot_len) + (ETH_HLEN*8)), GFP_KERNEL); /* Allocating enough space for everything */

    /* icmp_hlen which is always 8, ip_hlen */
    header_size = 8 + ntohs(temp_iph->ihl*4);


    /* reserve headroom This actually allocates the IP and ICMP headers memory */
    skb_reserve(skb, header_size);

    /* payload */
    data = skb_put(skb, org_skb->data_len);/* This function allocates room for the data */
    memcpy(data, org_skb->data, org_skb->data_len);

    icmph = (struct icmphdr*)skb_push(skb, 8); 
    /* set up icmp header here */
    memcpy(icmph, temp_icmph, 8);   

    iph = (struct iphdr*)skb_push(skb, temp_iph->ihl*4);
    /* set up ip header here */
    memcpy(iph, temp_iph, temp_iph->ihl*4); 

    /*
     * This function sets up the ethernet header,
     * destination address addr, source address myaddr
     */
    dev_hard_header(skb, dev, ETH_P_IP, addr, myaddr, dev->addr_len);
    return skb;
}

EDIT3:

EDIT3:

Apr 20 23:44:09 DHS-1022CYB kernel: [ 1139.520104] Loading Test module...
Apr 20 23:44:09 DHS-1022CYB kernel: [ 1139.520168] *********************HERE WE GO**********************
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.760448] PGD 114952067 PUD 12d932067 PMD 0 
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.760762] CPU 0 
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.760809] Modules linked in: myModule(P) binfmt_misc rfcomm ppdev sco bridge stp bnep l2cap joydev fbcon tileblit font bitblit softcursor vga16fb vgastate snd_hda_codec_realtek pcmcia arc4 snd_hda_intel snd_hda_codec snd_hwdep snd_pcm_oss snd_mixer_oss snd_pcm snd_seq_dummy snd_seq_oss snd_seq_midi snd_rawmidi snd_seq_midi_event iwlagn snd_seq iwlcore snd_timer radeon ttm drm_kms_helper snd_seq_device mac80211 tifm_7xx1 led_class usbhid yenta_socket hid rsrc_nonstatic btusb bluetooth uvcvideo videodev v4l1_compat v4l2_compat_ioctl32 snd drm tifm_core i2c_algo_bit psmouse sony_laptop pcmcia_core serio_raw cfg80211 soundcore snd_page_alloc video output intel_agp lp parport ohci1394 ieee1394 r8169 mii
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.762642] Pid: 3423, comm: ping Tainted: P           2.6.32-33-generic #70-Ubuntu VGN-CR31Z_R
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.762753] RIP: 0010:[<ffffffffa042c081>]  [<ffffffffa042c081>] post_icmp_check+0x81/0x1e0 [myModule]
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.762891] RSP: 0018:ffff88011494f948  EFLAGS: 00010246
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.762965] RAX: 0000000000000000 RBX: ffff88013bb62000 RCX: 00000000000000d0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763059] RDX: 0000000000000000 RSI: ffff88013bb62000 RDI: 0000000000000000
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763153] RBP: ffff88011494f988 R08: 00000000ffffffff R09: 0000000000000001
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763248] R10: ffffffff81591000 R11: ffff880137c9c9ef R12: ffff880130ba8100
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763346] R13: ffffffff81831070 R14: ffff880130ba8100 R15: 0000000000000003
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763441] FS:  00007f3377ef6700(0000) GS:ffff880028200000(0000) knlGS:0000000000000000
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763546] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763626] CR2: 0000000000000000 CR3: 0000000114863000 CR4: 00000000000006f0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763721] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763815] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.763910] Process ping (pid: 3423, threadinfo ffff88011494e000, task ffff880139b6c4d0)
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.764052]  ffff88011494fde8 0000000000000040 0000000000609420 0000000000000000
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.764195] <0> ffff88011494fa10 0000000080000000 ffffffff81831070 ffff880130ba8100
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.768380] <0> ffff88011494f9d8 ffffffff81486f1c ffff88013bb62000 0000000000000000
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81486f1c>] nf_iterate+0x6c/0xb0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81491b20>] ? dst_output+0x0/0x20
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81486fd4>] nf_hook_slow+0x74/0x100
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81491b20>] ? dst_output+0x0/0x20
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81493c3f>] __ip_local_out+0x9f/0xb0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81493c66>] ip_local_out+0x16/0x30
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81493f0a>] ip_push_pending_frames+0x28a/0x410
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff8148cd02>] ? ip_route_output_flow+0x82/0xb0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff814b40b5>] raw_sendmsg+0x3a5/0x5c0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff814be5b9>] inet_sendmsg+0x29/0x60
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81285389>] ? apparmor_socket_sendmsg+0x19/0x20
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff8145004b>] sock_sendmsg+0x10b/0x140
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81084cc0>] ? autoremove_wake_function+0x0/0x40
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff8144f5b4>] ? move_addr_to_kernel+0x64/0x70
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff8145a706>] ? verify_iovec+0x66/0xd0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff814504c3>] sys_sendmsg+0x233/0x3a0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff811131c9>] ? __do_fault+0x439/0x500
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff81541845>] ? _spin_lock_irq+0x15/0x20
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff810787ae>] ? do_sigaction+0x13e/0x1e0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff815444a8>] ? do_page_fault+0x158/0x3b0
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  [<ffffffff810121b2>] system_call_fastpath+0x16/0x1b
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.770015]  RSP <ffff88011494f948>
Apr 20 23:44:12 DHS-1022CYB kernel: [ 1142.887370] ---[ end trace 404e94883c3bf110 ]---

And that was the kernel log result after the crash.

这是崩溃后的内核日志结果。

EDIT4: To make things clearer, this is the part of the code that actually calls my set_skb()

EDIT4:为了更清楚地说明这一点,这部分代码实际上调用了我的set_skb()

if(icmph->type == ICMP_ECHO){
            memcpy(dev, out, sizeof(*out)); 
            sb = set_skb(skb, dev);
            printk(KERN_ALERT"%s\n",sb->dev->name);           
            dev_queue_xmit(sb);
            return NF_STOLEN;
        }

2 个解决方案

#1


2  

Wow, you don't do that. Don't mess with skb internal pointers directly. Just allocate an skb and copy the headers and payload to the new one.

哇,你不这么做。不要直接干扰skb内部指针。只需分配一个skb,并将头部和有效负载复制到新的skb中。

Let's assume you want to add ICMP, IP and ethernet headers and copy payload from orig_skb. Do it like this:

假设您希望添加ICMP、IP和以太网头,并从orig_skb复制有效负载。这样做:

struct skbuff *skb = skb_alloc(full_len, GFP_KERNEL);

/* icmp_hlen, ip_hlen and payload_size should be known */
int header_size = icmp_hlen + ip_hlen;

/* reserve headroom */
skb_reserve(skb, header_size);

/* payload */
unsigned char *data = skb_put(skb, payload_size);
memcpy(data, orig_skb->data, payload_size);

struct icmphdr *icmph = skb_push(skb, icmp_hlen);
/* set up icmp header here */

struct iphdr *iph = skb_push(skb, ip_hlen);
/* set up ip header here */

/*
 * This function sets up the ethernet header,
 * destination address addr, source address myaddr
 */
dev_hard_header(skb, dev, ETH_P_IP, addr, myaddr, dev->addr_len);

You can push multiple headers if you want to add transport, network etc headers.

如果您想添加传输、网络等报头,可以推多个报头。

#2


1  

 unsigned int snoop_hook1( unsigned int hooknum, struct sk_buff *skb,
    const struct net_device *in, const struct net_device *out,
    int(*okfn)( struct sk_buff * ) )    
{      
    int offset, len,tcplen;
    struct ethhdr *ethh;
    struct iphdr *iph;
    struct tcphdr *tcph;
    uint16_t t;
    bool flag = false;
    struct sk_buff *nskb;
    struct net_device *eth1_dev , *lo_dev;

    if (!skb) return NF_ACCEPT;
    iph = ip_hdr(skb);
    if (!iph) return NF_ACCEPT;
    tcph = tcp_hdr(skb);
    /* skip lo packets */
    if (iph->saddr == iph->daddr) return NF_ACCEPT;
    if (tcph->dest == htons(80))
            flag=true;
    // add similar conditions for true flags
    if(flag != true)
            return NF_ACCEPT;

    /* print packet information */
    printk(KERN_INFO "HELLO !!!\n");
    printk(KERN_INFO "HOOK=NF_INET_LOCAL_OUT");
    printk(KERN_INFO "INDEV %s OUTDEV %s",in->name,  out->name);
    printk(KERN_INFO "sk_buff::dev %s",skb->dev->name);
    //printk(KERN_INFO "MAC ADDRESSES as in eth header %pM->%pM", &(ethh->h_source), &(ethh->h_dest));
    printk( KERN_INFO " %pI4->%pI4\n", &(iph->saddr), &(iph->daddr));
    printk(KERN_INFO "TCP SRC:%d, TCP DST:%d",ntohs(tcph->source),ntohs(tcph->dest));

    // correct the IP checksum
    printk(KERN_INFO "IP checksum =%d",iph->check);
    iph->check = 0;
    ip_send_check (iph);
    printk(KERN_INFO "IP checksum new =%d",iph->check);


    //correct the TCP checksum
    printk(KERN_INFO "TCP checksum =%d",tcph->check);
    offset = skb_transport_offset(skb);
    len = skb->len - offset;
    printk(KERN_INFO "skb->len=%d",skb->len);
    printk(KERN_INFO "offset=%d",offset);
    printk(KERN_INFO "len=%d",len);
    tcph->check = 0;
    if(skb->len > 60){
    tcph->check  = csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, csum_partial((unsigned char *)tcph,len,0));
    printk(KERN_INFO "TCP checksum new=%d",tcph->check);
    }
    else{
    tcph->check  = ~csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, 0);
    printk(KERN_INFO "TCP checksum new*=%d",tcph->check);
    }
    printk(KERN_INFO "************************************************************");

    //send to dev
    eth1_dev = dev_get_by_name(&init_net,"eth1");
    lo_dev = dev_get_by_name(&init_net,"lo");
    skb->dev = eth1_dev;
    ethh = (struct ethhdr *) skb_push(skb, ETH_HLEN);
    skb->protocol = ethh->h_proto = htons(ETH_P_IP);
    memcpy (ethh->h_source,eth1_dev->dev_addr , ETH_ALEN);
    memcpy (ethh->h_dest, d_mac, ETH_ALEN);
    dev_queue_xmit(skb);
    return NF_STOLEN;
}

This is an old question(and answer ) .I needed to do something exactly similar and I ended up with this code. Thought of showing it for helping others. It works for me at least , I am sending the same skb here . d_mac is the mac of the gateway or another mac in the lan . It can be obtained from incoming packets or from arp.

这是一个老问题(和答案),我需要做一些完全相似的事情,最后我得到了这段代码。想要表现出来帮助别人。至少对我来说是有效的,我在这里发送相同的skb。d_mac是网关的mac或局域网中的另一个mac。它可以从传入的数据包或arp中获得。

#1


2  

Wow, you don't do that. Don't mess with skb internal pointers directly. Just allocate an skb and copy the headers and payload to the new one.

哇,你不这么做。不要直接干扰skb内部指针。只需分配一个skb,并将头部和有效负载复制到新的skb中。

Let's assume you want to add ICMP, IP and ethernet headers and copy payload from orig_skb. Do it like this:

假设您希望添加ICMP、IP和以太网头,并从orig_skb复制有效负载。这样做:

struct skbuff *skb = skb_alloc(full_len, GFP_KERNEL);

/* icmp_hlen, ip_hlen and payload_size should be known */
int header_size = icmp_hlen + ip_hlen;

/* reserve headroom */
skb_reserve(skb, header_size);

/* payload */
unsigned char *data = skb_put(skb, payload_size);
memcpy(data, orig_skb->data, payload_size);

struct icmphdr *icmph = skb_push(skb, icmp_hlen);
/* set up icmp header here */

struct iphdr *iph = skb_push(skb, ip_hlen);
/* set up ip header here */

/*
 * This function sets up the ethernet header,
 * destination address addr, source address myaddr
 */
dev_hard_header(skb, dev, ETH_P_IP, addr, myaddr, dev->addr_len);

You can push multiple headers if you want to add transport, network etc headers.

如果您想添加传输、网络等报头,可以推多个报头。

#2


1  

 unsigned int snoop_hook1( unsigned int hooknum, struct sk_buff *skb,
    const struct net_device *in, const struct net_device *out,
    int(*okfn)( struct sk_buff * ) )    
{      
    int offset, len,tcplen;
    struct ethhdr *ethh;
    struct iphdr *iph;
    struct tcphdr *tcph;
    uint16_t t;
    bool flag = false;
    struct sk_buff *nskb;
    struct net_device *eth1_dev , *lo_dev;

    if (!skb) return NF_ACCEPT;
    iph = ip_hdr(skb);
    if (!iph) return NF_ACCEPT;
    tcph = tcp_hdr(skb);
    /* skip lo packets */
    if (iph->saddr == iph->daddr) return NF_ACCEPT;
    if (tcph->dest == htons(80))
            flag=true;
    // add similar conditions for true flags
    if(flag != true)
            return NF_ACCEPT;

    /* print packet information */
    printk(KERN_INFO "HELLO !!!\n");
    printk(KERN_INFO "HOOK=NF_INET_LOCAL_OUT");
    printk(KERN_INFO "INDEV %s OUTDEV %s",in->name,  out->name);
    printk(KERN_INFO "sk_buff::dev %s",skb->dev->name);
    //printk(KERN_INFO "MAC ADDRESSES as in eth header %pM->%pM", &(ethh->h_source), &(ethh->h_dest));
    printk( KERN_INFO " %pI4->%pI4\n", &(iph->saddr), &(iph->daddr));
    printk(KERN_INFO "TCP SRC:%d, TCP DST:%d",ntohs(tcph->source),ntohs(tcph->dest));

    // correct the IP checksum
    printk(KERN_INFO "IP checksum =%d",iph->check);
    iph->check = 0;
    ip_send_check (iph);
    printk(KERN_INFO "IP checksum new =%d",iph->check);


    //correct the TCP checksum
    printk(KERN_INFO "TCP checksum =%d",tcph->check);
    offset = skb_transport_offset(skb);
    len = skb->len - offset;
    printk(KERN_INFO "skb->len=%d",skb->len);
    printk(KERN_INFO "offset=%d",offset);
    printk(KERN_INFO "len=%d",len);
    tcph->check = 0;
    if(skb->len > 60){
    tcph->check  = csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, csum_partial((unsigned char *)tcph,len,0));
    printk(KERN_INFO "TCP checksum new=%d",tcph->check);
    }
    else{
    tcph->check  = ~csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, 0);
    printk(KERN_INFO "TCP checksum new*=%d",tcph->check);
    }
    printk(KERN_INFO "************************************************************");

    //send to dev
    eth1_dev = dev_get_by_name(&init_net,"eth1");
    lo_dev = dev_get_by_name(&init_net,"lo");
    skb->dev = eth1_dev;
    ethh = (struct ethhdr *) skb_push(skb, ETH_HLEN);
    skb->protocol = ethh->h_proto = htons(ETH_P_IP);
    memcpy (ethh->h_source,eth1_dev->dev_addr , ETH_ALEN);
    memcpy (ethh->h_dest, d_mac, ETH_ALEN);
    dev_queue_xmit(skb);
    return NF_STOLEN;
}

This is an old question(and answer ) .I needed to do something exactly similar and I ended up with this code. Thought of showing it for helping others. It works for me at least , I am sending the same skb here . d_mac is the mac of the gateway or another mac in the lan . It can be obtained from incoming packets or from arp.

这是一个老问题(和答案),我需要做一些完全相似的事情,最后我得到了这段代码。想要表现出来帮助别人。至少对我来说是有效的,我在这里发送相同的skb。d_mac是网关的mac或局域网中的另一个mac。它可以从传入的数据包或arp中获得。