struct ethhdr结构体详解

2023-05-16

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

#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

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()进行转换;

__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 : 用于存放从接收的数据包中提取的硬件地址;

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地址;

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;

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);

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地址是否为零;

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地址;

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地址是否是广播地址;

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地址;

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之中;

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地址。

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;

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>头文件,来直接使用。

转载于:https://blog.51cto.com/weiguozhihui/1586856

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

struct ethhdr结构体详解 的相关文章

  • Vue实战篇(PC端商城项目)

    这是一个基于vue全家桶 43 node js 43 express 43 mysql实现的商城网站 项目github地址 xff1a vueMall 查看demo 地址 如果觉得对您有帮助 xff0c 您可以在左下角给我个喜欢支持一下 x
  • https安全证书过期失效的原因以及解决方法

    一 网站https安全证书过期原因分析 xff1a 1 当前电脑系统时间错误 xff0c 所有的http安全证书都有颁发日期和截止日期 xff0c 电脑系统时间在证书有效时间区间之外有可能导致浏览器提示网站https安全证书已过期或还未生效
  • 安装OPENWRT后打不开管理页面的解决方法

    路由器刷入了OPENWRT 结果发现刷入的固件没有带Luci 以下是本人手动安装Luci的方法 开始安装Luci所需的依赖包 opkg install http 192 168 1 2 uhttpd 27 brcm47xx ipk opkg
  • Postgresql查看表结构和字段注释

    Postgresql查看表结构和字段注释 一 xff1a 查看表结构 xff08 字段 xff09 信息 xff1a Select table name column name data type character maximum len
  • 技术淘宝

    精度前端学习 前端开发100天 xff08 置顶 xff09 http alloyteam github io CodeGuide https github com AlloyTeam CodeGuide cmd控制台的小技巧 xff1a
  • Migration: Find Duplicate Objects in Application Desinger Projects

    The sql will identify duplicate objects in different application designer projects to eliminate duplicate work by develo
  • SQL server loginname alias mapping problem.

    exec sp changedbowner 39 Aambriore mra 39 EXEC sp change users login 39 Auto Fix 39 39 lts 39 null Exec sp change users
  • [ASP.NET MVC 小牛之路]08 - Area 使用

    ASP NET MVC允许使用 Area xff08 区域 xff09 来组织Web应用程序 xff0c 每个Area代表应用程序的不同功能模块 这对于大的工程非常有用 xff0c Area 使每个功能模块都有各自的文件夹 xff0c 文件
  • OpennVINS运行、评估笔记

    1 安装运行 1 1 下载 amp 编译 span class token builtin class name cd span catkin ws src span class token function git span clone
  • 关于apm飞控烧bootloader

    最近一直由于学校大创申请的的四轴 xff0c 一直在做这玩意 xff0c 哎 xff0c 无奈这货实在不像想象的那样简单 xff0c 自己写了N久飞控 xff0c 也没把飞机飞起来 xff0c 只能先用开源的apm飞控练练手呗 xff0c
  • c语言实现subs指令,周立功LPC系统移植学习之c启动代码修改一

    一 系统移植用c启动代码 Copyright c Guangzou ZLG MCU Development Co LTD graduate school http www zlgmcu com File Info
  • Google开源项目phpdoc-zh【PHP中文手册】

    为什么80 的码农都做不了架构师 xff1f gt gt gt 现在不用愁找不到好的PHP中文手册 xff0c 没乱码 xff0c 更新速度快 xff0c 翻译准确 xff01 项目地址 xff1a http code google com
  • 计算机与我的生活英语作文,描写一天的生活英语作文(通用7篇)

    描写一天的生活英语作文 通用7篇 在平凡的学习 工作 生活中 xff0c 大家都不可避免地要接触到作文吧 xff0c 作文要求篇章结构完整 xff0c 一定要避免无结尾作文的出现 相信很多朋友都对写作文感到非常苦恼吧 xff0c 下面是小编
  • reStructuredText语法简单说明

    reStructuredText 是扩展名为 rst的纯文本文件 xff0c 含义为 34 重新构建的文本 34 34 xff0c 也被简称为 xff1a RST或reST 官方网址 xff1a http docutils sourcefo
  • 优秀的程序员需要擅长数学吗?

    天有很多年轻人或经验不足的程序员 在 论坛发帖 在 Stack Exchange 网站问 xff1a 为了成为优秀的程序员 xff0c 我需要擅长数学吗 xff1f xff0c 在我还年轻的时候 xff0c 我也问自己同样的问题 最近 xf
  • datax同步MySQL数据到mongodb

    根据生产需要 xff0c 同步非实时数据到mongodb 经过同事间的不断研究 xff0c 特弄出了这样一套方案 xff1a MySQL xff08 RDS xff09 gt Datax gt mongodb 我们的mysql用的是阿里云的
  • 网络虚拟化

    网络虚拟化的内容一般指虚拟专用网络 对网络连接的概念进行了抽象 xff0c 允许远程用户访问组织的内部网络 xff0c 就像物理上连接到该网络一样 网络虚拟化可以帮助保护 IT 环境 xff0c 防止来自 Internet 的威胁 xff0
  • C语言实现wake on lan(网络唤醒)

    wake on lan是一种网络唤醒功能 xff0c 它可以实现远程开机 xff0c 刚好实验室有一台ftp服务器 xff0c 因为不是24小时开机的 xff0c 所以每次开机都要跑过去用手按开关 xff0c 非常麻烦 xff0c 于是在网
  • 裸辞2个月,找不到新工作,我这样的程序员多吗?

    如题 xff0c 年后回来毅然决然从公司离职 xff0c 离职一时爽 xff0c 此时心已凉 离职2个月了 xff0c 记不得多久以前已经找不到新公司投简历了 面试机会太少 xff0c 私活太少 快活不起了 像我这样的程序员现在多吗 xff
  • Linux驱动示例

    本文首先描述了一个可以实际测试运行的驱动实例 xff0c 然后由此去讨论Linux下驱动模板的要素 xff0c 以及Linux上应用程序到驱动的执行过程 相信这样由浅入深 由具体实例到抽象理论的描述更容易初学者入手Linux驱动的大门 一

随机推荐