struct ethhdr结构体详解

在linux系统中,使用struct ethhdr结构体来表示以太网帧的头部。这个struct ethhdr结构体位于#include<linux/if_ether.h>之中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define ETH_ALEN 6  //定义了以太网接口的MAC地址的长度为6个字节
 
#define ETH_HLAN 14  //定义了以太网帧的头长度为14个字节
 
#define ETH_ZLEN 60  //定义了以太网帧的最小长度为 ETH_ZLEN + ETH_FCS_LEN = 64个字节
 
#define ETH_DATA_LEN 1500  //定义了以太网帧的最大负载为1500个字节
 
#define ETH_FRAME_LEN 1514  //定义了以太网正的最大长度为ETH_DATA_LEN + ETH_FCS_LEN = 1518个字节
 
#define ETH_FCS_LEN 4   //定义了以太网帧的CRC值占4个字节
 
 
struct  ethhdr
{
     unsigned  char  h_dest[ETH_ALEN];  //目的MAC地址
     
     unsigned  char  h_source[ETH_ALEN];  //源MAC地址
     
     __u16 h_proto ;  //网络层所使用的协议类型
}__attribute__((packed))   //用于告诉编译器不要对这个结构体中的缝隙部分进行填充操作;


网络层所使用的协议类型有(常见的类型):


#define  ETH_P_IP 0x0800 //IP协议


#define  ETH_P_ARP 0x0806  //地址解析协议(Address Resolution Protocol)


#define  ETH_P_RARP 0x8035  //返向地址解析协议(Reverse Address Resolution Protocol)


#define  ETH_P_IPV6 0x86DD  //IPV6协议



static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)

{

     return (struct ethhdr *)skb_mac_header(skb);

}


//MAC地址的输出格式。 "%02x"所表示的意思是:以16进制的形式输出,每一个16进制字符占一个字节

#define MAC_FMT  "%02x:%02x:%02x:%02x:%02x:%02x" 


#define MAC_BUF_LEN 18 //定义了用于存放MAC字符的缓存的大小


#define DECLARE_MAC_BUF(var)  char var[MAC_BUF_LEN] //定义了一个MAC字符缓存




    1.创建一个以太网头结构体struct ethhdr:

     int eth_header(struct sk_buff *skb, struct net_device *dev,

                 u16 type, void *daddr, void *saddr, unsigned len)

      EXPORT_SYMBOL(eth_header);

     skb : 将要去修改的struct sk_buff;

     dev : 原网络设备

     type: 网络层的协议类型

     daddr:目的MAC地址

     saddr:源MAC地址

     len  :一般可为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int  eth_header( struct  sk_buff *skb,  struct  net_device *dev, u16 type,  void  *daddr,  void  *saddr,  int  len)
{
     //将skb->data = skb->data + ETH_ALEN;
     struct  ethhdr *eth = ( struct  ethhdr*)skb_push(skb, ETH_ALEN);
     
     if (type != ETH_P_802_3)
        eth->proto = htons(type);  // htons()将本地类型转换为网络类型
      else
        eth->proto = htons(len);
     
     //如果 saddr = NULL的话,以太网帧头中的源MAC地址为dev的MAC地址   
     if (!saddr)
        saddr = dev->dev_addr;
     memcpy (eth->saddr, saddr, ETH_ALEN);
     
     if (daddr)
     {
        memcpy (eth->daddr, daddr, ETH_ALEN);
        return  ETH_HLEN ;  //返回值为14
     }
     
     return  -ETH_HLEN;
}

    

    2.判断一个网络设备正在接受的struct sk_buff中的网络层所使用的协议类型:

      __be16 eth_type_trans(struct sk_buff *skb, 

                             struct net_device *dev);

     EXPORT_SYMBOL(eth_type_trans);

     skb : 为正在接收的数据包;

     dev : 为正在使用的网络设备;

     返回值:为网络字节序列,所以要使用ntohs()进行转换;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__be16 eth_type_trans( struct  sk_buff *skb,  struct  net_device *dev)
{
     struct  ethhdr *eth;
     
     skb->dev = dev;
     eth = eth_hdr(skb);
     
     if (netdev_uses_dsa_tags(dev))
          return  htons(ETH_P_DSA);
          
     if (netdev_uses_trailer_tags(dev))
          return  htons(ETH_P_TRAILER);
          
     if ( ntohs(eth->h_proto) >= 1536 )
          return  eth->h_proto;   
}


    3.从一个数据包(struct sk_buff)中提取源MAC地址:

    int eth_header_parse(struct sk_buff *skb, u8 *haddr)

    EXPORT_SYMBOL(eth_header_parse);

    skb : 接收到的数据包;

    haddr : 用于存放从接收的数据包中提取的硬件地址;

1
2
3
4
5
6
int  eth_header_parse( struct  sk_buff *skb, u8 *haddr)
{
    struct  ethhdr *eth = eth_hdr(skb);
    memcpy (haddr, eth->h_source, ETH_ALEN);  //可知haddr中存放的是源MAC地址;
    return  ETH_ALEN;
}


    

    4.在struct ethhdr中MAC地址为6个字节,并不是我们常见的MAC字符串地址,那么如果将6字节的MAC地址转化为我们常见的MAC字符串地址,使用下面这个函数:

    char *print_mac(char *buffer, const unsigned char *addr);

    EXPORT_SYMBOL(print_mac);

    buffer : 为MAC字符串地址存放的地方;

    addr   : 为6字节MAC地址;

1
2
3
4
5
6
7
8
char  *print_mac( char  *buffer,  const  unsigned  char  *addr)
{
    // MAC_BUF_SIZE = 18
    // ETH_ALEN = 6
    _format_mac_addr(buffer, MAC_BUF_SIZE, addr, ETH_ALEN);
    
    return  buffer;
}


    

      5.重新设置一个网络设备的MAC地址:

     int eth_mac_addr(struct net_device *dev, void *p);

     EXPORT_SYMBOL(eth_mac_addr);

     dev : 为将要被设置的网络设备;

     p   : 为socket address;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int  eth_mac_addr( struct  net_device *dev,  void  *p)
{
     struct  sockaddr *addr = p;
     
     //用于判断网络设备是否正在运行
     if (netif_running(dev))
        return  -EBUSY;
        
     if ( !is_valid_ether_addr(addr->sa_data) )
        return  -ETHADDRNOTAVAIL;
      
     memcpy (dev->dev_addr, addr->sa_data, ETH_ALEN);
     return  0;
}


     

      6.对一个struct net_device以太网网络设备进行初始化:

      void ether_setup(struct net_device *dev);

      EXPORT_SYMBOL(ether_setup);

wKiom1SB1mOSR0seAAFE6O5LCC0286.jpg


wKioL1SB1wLgpJLpAALeXlHLR6M389.jpg



    7.分配一个以太网网络设备,并对其进行初始化:

      struct net_device *alloc_etherdev_mq(int sizeof_priv,

                       u32 queue_count)

      EXPORT_SYMBOL(alloc_etherdev_mq);

1
2
3
4
5
6
7
8
struct  net_device *alloc_etherdev_mq( int  sizeof_priv, unsigned  int  queue_count)
{
    // ether_setup为对分配的struct net_device进行初始化的函数;
    //这个ether_setup是内核的导出函数,可以直接使用;
     return  alloc_netdev_mq(sizeof_priv,  "eth%d" , ether_setup, queue_count);
}
 
#define alloc_etherdev(sizeof_priv)  alloc_etherdev_mq(sizeof_priv, 1)


     下面的这些函数用于struct ethhdr中的MAC地址的判断:

    

    1.int is_zero_ether_addr(const u8 *addr);

          用于判断一个MAC地址是否为零;

1
2
3
4
static  inline  int  is_zero_ether_addr( const  u8 *addr)
{
    return  !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]);
}


     2.int is_multicast_ether_addr(const u8 *addr)

           用于判断addr中的MAC地址是否是组播MAC地址;

1
2
3
4
5
static  inline  int  is_multicast_ether_addr( const  u8 *addr)
{
    //组播MAC地址的判断方法:如果一个MAC地址的最低一位是1的话,则这个MAC地址为组播MAC地址;
     return  (0x01 & addr[0]); 
}



      3.int is_broadcast_ether_addr(const u8 *addr)

            用于判断addr中的MAC地址是否是广播地址;

1
2
3
4
static  inline  int  is_broadcast_ether_addr( const  u8 *addr)
{
     return  ( addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5] ) == 0xff;
}


     

      4. int is_valid_ether_addr(const u8* addr)

             用于判断addr中的MAC地址是否是有效的MAC地址;

1
2
3
4
5
static  inline  int  is_valid_ether_addr( const  u8 *addr)
{
    //既不是组播地址,也不为0的MAC地址为有效的MAC地址;
    return  !is_multicast_ether_addr(addr) && !is_zero_ether_addr(addr);
}


      5. void random_ether_addr(u8 *addr)

             用于软件随机产生一个MAC地址,然后存放与addr之中;

1
2
3
4
5
6
static  inline  void  random_ether_addr(u8 *addr)
{
      get_random_bytes(addr, ETH_ALEN);
      addr[0] & = 0xfe;
      addr[0] |= 0x02;  // IEEE802本地MAC地址
}



      6.int is_local_ether_addr(const u8 *addr)

           用于判断addr中MAC地址是否是IEEE802中的本地MAC地址。

1
2
3
4
static  inline  int  is_local_ether_addr( const  u8 *addr)
{
     return  (0x02 & addr[0]);
}


   关于IEEE802 MAC地址的须知:

  IEEE802 LAN6字节MAC地址是目前广泛使用的LAN物理地址。IEEE802规定LAN地址字段的第一个字节的最低位表示I/G(Individual /Group)比特,即单地址/组地址比特。当它为“0”时,表示它代表一个单播地址,而这个位是“1”时,表示它代表一个组地址。
  IEEE802规定LAN地址字段的第一个字节的最低第二位表示G/L(Globe/Local)比特,即全球/本地比特。当这个比特为“0”时,表 示全球管理,物理地址是由全球局域网地址的法定管理机构统一管理,全球管理地址在全球范围内不会发生地址冲突。当这个比特为“1”时,就是本地管理,局域 网管理员可以任意分配局部管理的网络上的地址,只要在自己网络中地址唯一不产生冲突即可,对外则没有意义,局部管理很少使用。
  在6个字节的其他46个比特用来标识一个特定的MAC地址,46位的地址空间可表示约70万亿个地址,可以保证全球地址的唯一性。   


  

     7.unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2)

           用于比较两个MAC地址是否相等,相等返回0,不相等返回1;

1
2
3
4
5
6
7
static  inline  unsigned compare_ether_addr( const  u8 *addr1,  const  u8 *addr2)
{
     const  u16 *a = ( const  u16*)addr1;
     const  u16 *b = ( const  u16*)addr2;
     
     return  ( (a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]) ) != 0;
}


     以上的所有的函数可以通过 #include<linux/etherdevice.h>头文件,来直接使用。

更多关于 android开发 的文章
LOCAL S32 send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip) { int err = -1; unsigned char buffer[BUF_SIZE]; memset(buffer, 0, sizeof(buffer)); struct sockaddr_ll socket_address; socket_address.sll_family = AF_PACKET; socket_address.sll_protocol = htons(ETH_P_ARP); socket_address.sll_ifindex = ifindex; socket_address.sll_hatype = htons(ARPHRD_ETHER); socket_address.sll_pkttype = (PACKET_BROADCAST); socket_address.sll_halen = MAC_LENGTH; socket_address.sll_addr[6] = 0x00; socket_address.sll_addr[7] = 0x00; struct ethhdr *send_req = (struct ethhdr *) buffer; ARP_HEADER *arp_req = (ARP_HEADER *) (buffer + ETH2_HEADER_LEN); int index; ssize_t ret, length = 0; //Broadcast memset(send_req->h_dest, 0xff, MAC_LENGTH); //Target MAC zero memset(arp_req->target_mac, 0x00, MAC_LENGTH); //Set source mac to our MAC address memcpy(send_req->h_source, src_mac, MAC_LENGTH); memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH); memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH); /* Setting protocol of the packet */ send_req->h_proto = htons(ETH_P_ARP); /* Creating ARP request */ arp_req->hardware_type = htons(HW_TYPE); arp_req->protocol_type = htons(ETH_P_IP); arp_req->hardware_len = MAC_LENGTH; arp_req->protocol_len = IPV4_LENGTH; arp_req->opcode = htons(ARP_REQUEST); debug("Copy IP address to arp_req"); memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t)); memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t)); ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address)); if (ret == -1) { perror("sendto():"); goto out; } err = 0; out: return err; } 详细解析这个函数,解释每一行,包括各个变量、函数、语块的意义
08-27
#include <sys/socket.h> #include <sys/ioctl.h> #include <asm/types.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if_arp.h> #include <arpa/inet.h> //htons etc #define PROTO_ARP 0x0806 #define ETH2_HEADER_LEN 14 #define HW_TYPE 1 #define MAC_LENGTH 6 #define IPV4_LENGTH 4 #define ARP_REQUEST 0x01 #define ARP_REPLY 0x02 #define BUF_SIZE 60 #define debug(x...) printf(x);printf("\n"); #define info(x...) printf(x);printf("\n"); #define warn(x...) printf(x);printf("\n"); #define err(x...) printf(x);printf("\n"); struct arp_header { unsigned short hardware_type; unsigned short protocol_type; unsigned char hardware_len; unsigned char protocol_len; unsigned short opcode; unsigned char sender_mac[MAC_LENGTH]; unsigned char sender_ip[IPV4_LENGTH]; unsigned char target_mac[MAC_LENGTH]; unsigned char target_ip[IPV4_LENGTH]; }; /* * Converts struct sockaddr with an IPv4 address to network byte order uin32_t. * Returns 0 on success. */ int int_ip4(struct sockaddr *addr, uint32_t *ip) { if (addr->sa_family == AF_INET) { struct sockaddr_in *i = (struct sockaddr_in *) addr; *ip = i->sin_addr.s_addr; return 0; } else { err("Not AF_INET"); return 1; } } /* * Formats sockaddr containing IPv4 address as human readable string. * Returns 0 on success. */ int format_ip4(struct sockaddr *addr, char *out) { if (addr->sa_family == AF_INET) { struct sockaddr_in *i = (struct sockaddr_in *) addr; const char *ip = inet_ntoa(i->sin_addr); if (!ip) { return -2; } else { strcpy(out, ip); return 0; } } else { return -1; } } /* * Writes interface IPv4 address as network byte order to ip. * Returns 0 on success. */ int get_if_ip4(int fd, const char *ifname, uint32_t *ip) { int err = -1; struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); if (strlen(ifname) > (IFNAMSIZ - 1)) { err("Too long interface name"); goto out; } strcpy(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) { perror("SIOCGIFADDR"); goto out; } if (int_ip4(&ifr.ifr_addr, ip)) { goto out; } err = 0; out: return err; } /* * Sends an ARP who-has request to dst_ip * on interface ifindex, using source mac src_mac and source ip src_ip. */ int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip) { int err = -1; unsigned char buffer[BUF_SIZE]; memset(buffer, 0, sizeof(buffer)); struct sockaddr_ll socket_address; socket_address.sll_family = AF_PACKET; socket_address.sll_protocol = htons(ETH_P_ARP); socket_address.sll_ifindex = ifindex; socket_address.sll_hatype = htons(ARPHRD_ETHER); socket_address.sll_pkttype = (PACKET_BROADCAST); socket_address.sll_halen = MAC_LENGTH; socket_address.sll_addr[6] = 0x00; socket_address.sll_addr[7] = 0x00; struct ethhdr *send_req = (struct ethhdr *) buffer; struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN); int index; ssize_t ret, length = 0; //Broadcast memset(send_req->h_dest, 0xff, MAC_LENGTH); //Target MAC zero memset(arp_req->target_mac, 0x00, MAC_LENGTH); //Set source mac to our MAC address memcpy(send_req->h_source, src_mac, MAC_LENGTH); memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH); memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH); /* Setting protocol of the packet */ send_req->h_proto = htons(ETH_P_ARP); /* Creating ARP request */ arp_req->hardware_type = htons(HW_TYPE); arp_req->protocol_type = htons(ETH_P_IP); arp_req->hardware_len = MAC_LENGTH; arp_req->protocol_len = IPV4_LENGTH; arp_req->opcode = htons(ARP_REQUEST); debug("Copy IP address to arp_req"); memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t)); memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t)); ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address)); if (ret == -1) { perror("sendto():"); goto out; } err = 0; out: return err; } /* * Gets interface information by name: * IPv4 * MAC * ifindex */ int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex) { debug("get_if_info for %s", ifname); int err = -1; struct ifreq ifr; int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); if (sd <= 0) { perror("socket()"); goto out; } if (strlen(ifname) > (IFNAMSIZ - 1)) { printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1); goto out; } strcpy(ifr.ifr_name, ifname); //Get interface index using name if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) { perror("SIOCGIFINDEX"); goto out; } *ifindex = ifr.ifr_ifindex; printf("interface index is %d\n", *ifindex); //Get MAC address of the interface if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) { perror("SIOCGIFINDEX"); goto out; } //Copy mac address to output memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH); if (get_if_ip4(sd, ifname, ip)) { goto out; } debug("get_if_info OK"); err = 0; out: if (sd > 0) { debug("Clean up temporary socket"); close(sd); } return err; } /* * Creates a raw socket that listens for ARP traffic on specific ifindex. * Writes out the socket's FD. * Return 0 on success. */ int bind_arp(int ifindex, int *fd) { debug("bind_arp: ifindex=%i", ifindex); int ret = -1; // Submit request for a raw socket descriptor. *fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); if (*fd < 1) { perror("socket()"); goto out; } debug("Binding to ifindex %i", ifindex); struct sockaddr_ll sll; memset(&sll, 0, sizeof(struct sockaddr_ll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) { perror("bind"); goto out; } ret = 0; out: if (ret && *fd > 0) { debug("Cleanup socket"); close(*fd); } return ret; } /* * Reads a single ARP reply from fd. * Return 0 on success. */ int read_arp(int fd) { debug("read_arp"); int ret = -1; unsigned char buffer[BUF_SIZE]; ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL); int index; if (length == -1) { perror("recvfrom()"); goto out; } struct ethhdr *rcv_resp = (struct ethhdr *) buffer; struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN); if (ntohs(rcv_resp->h_proto) != PROTO_ARP) { debug("Not an ARP packet"); goto out; } if (ntohs(arp_resp->opcode) != ARP_REPLY) { debug("Not an ARP reply"); goto out; } debug("received ARP len=%ld", length); struct in_addr sender_a; memset(&sender_a, 0, sizeof(struct in_addr)); memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t)); debug("Sender IP: %s", inet_ntoa(sender_a)); debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X", arp_resp->sender_mac[0], arp_resp->sender_mac[1], arp_resp->sender_mac[2], arp_resp->sender_mac[3], arp_resp->sender_mac[4], arp_resp->sender_mac[5]); ret = 0; out: return ret; } /* * * Sample code that sends an ARP who-has request on * interface <ifname> to IPv4 address <ip>. * Returns 0 on success. */ int test_arping(const char *ifname, const char *ip) { int ret = -1; uint32_t dst = inet_addr(ip); if (dst == 0 || dst == 0xffffffff) { printf("Invalid source IP\n"); return 1; } int src; int ifindex; char mac[MAC_LENGTH]; if (get_if_info(ifname, &src, mac, &ifindex)) { err("get_if_info failed, interface %s not found or no IP set?", ifname); goto out; } int arp_fd; if (bind_arp(ifindex, &arp_fd)) { err("Failed to bind_arp()"); goto out; } if (send_arp(arp_fd, ifindex, mac, src, dst)) { err("Failed to send_arp"); goto out; } while(1) { int r = read_arp(arp_fd); if (r == 0) { info("Got reply, break out"); break; } } ret = 0; out: if (arp_fd) { close(arp_fd); arp_fd = 0; } return ret; } int main(int argc, const char **argv) { int ret = -1; if (argc != 3) { printf("Usage: %s <INTERFACE> <DEST_IP>\n", argv[0]); return 1; } const char *ifname = argv[1]; const char *ip = argv[2]; return test_arping(ifname, ip); }你可以帮我解释一下这一段代码吗?告诉我详细意思
08-26
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值