linux之网络子系统-MAC帧、数据报、段 的头部信息

一、MAC帧 格式

MAC帧是属于链路层,网卡发送数据的格式。

MAC帧主要有两种格式,一种是以太网V2标准,一种是IEEE 802.3,常用的是前者。

 DMAC(Destination MAC)是目的MAC地址。DMAC字段长度为6个字节,标识帧的接收者。
SMAC(Source MAC)是源MAC地址。SMAC字段长度为6个字节,标识帧的发送者。
Type(类型字段)用于标识数据字段中包含的高层协议,该字段长度为2个字节。类型字段值为 0x0800的帧代表IP协议帧;类型字段值为0806的帧代表ARP协议帧。
Data(数据字段)是网络层数据,最小长度必须为46字节以保证帧长至少为64字节,数据字段的最大长度为1500字节。
FCS(循环冗余校验字段)提供了一种错误检测机制。该字段长度为4个字节。

 

类型只有2个,主要是由于目前的协议栈

  • 要么就是应用层发送数据时使用底层的ip协议
  • 要么就是在发送数据时未找到对方信息ip/mac,这时候就需要通过arp协议寻找对方信息,寻找到以后保存在协议栈中

下面是MAC头部信息的数据信息: (因为是调用ARP层函数接口拷贝硬件header 信息,目前还没有对arp 协议深入研究,只能通过下面头文件反推MAC头部信息)

#define ETH_ALEN        6               /* Octets in one ethernet addr   */
 

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

 组装后的包:

MAC头部信息,在IP层是如何添加的?

MAC头部信息是在arp 协议接口里面进行赋值的。下面展示一下具体源码:

ip_output->ip_finish_ouput->ip_finish_output2->neigh_output

static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{struct dst_entry *dst = skb_dst(skb);struct rtable *rt = (struct rtable *)dst;struct net_device *dev = dst->dev;unsigned int hh_len = LL_RESERVED_SPACE(dev);struct neighbour *neigh;bool is_v6gw = false;if (rt->rt_type == RTN_MULTICAST) {IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);} else if (rt->rt_type == RTN_BROADCAST)IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);/* Be paranoid, rather than too clever. */if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {struct sk_buff *skb2;skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));if (!skb2) {kfree_skb(skb);return -ENOMEM;}if (skb->sk)skb_set_owner_w(skb2, skb->sk);consume_skb(skb);skb = skb2;}if (lwtunnel_xmit_redirect(dst->lwtstate)) {int res = lwtunnel_xmit(skb);if (res < 0 || res == LWTUNNEL_XMIT_DONE)return res;}rcu_read_lock_bh();neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);if (!IS_ERR(neigh)) {int res;sock_confirm_neigh(skb, neigh);/* if crossing protocols, can not use the cached header */res = neigh_output(neigh, skb, is_v6gw); // 调用arp 层的函数接口rcu_read_unlock_bh();return res;}rcu_read_unlock_bh();net_dbg_ratelimited("%s: No header cache and no neighbour!\n",__func__);kfree_skb(skb);return -EINVAL;
}

在ip_finish_oupt 中,是调用了 GSO 的函数接口,在ip 层分段之后。接着开始在ip_finish_output2 中调用arp函数接口,把MAC头部信息添加上。因为ARP层有ARP表,可以查询目的地址。MAC头部信息添加之后,在调用 网络设备接口层函数,其实是回调加载驱动的函数。

接着看ARP 层的函数接口:neigh_output

static inline int neigh_output(struct neighbour *n, struct sk_buff *skb,bool skip_cache)
{const struct hh_cache *hh = &n->hh;  // 硬件头部信息缓存/* n->nud_state and hh->hh_len could be changed under us.* neigh_hh_output() is taking care of the race later.*/if (!skip_cache &&(READ_ONCE(n->nud_state) & NUD_CONNECTED) &&READ_ONCE(hh->hh_len))return neigh_hh_output(hh, skb); //调用硬件头部信息缓存return n->output(n, skb);
}

 接着neigh_hh_output

static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb)
{unsigned int hh_alen = 0;unsigned int seq;unsigned int hh_len;do {seq = read_seqbegin(&hh->hh_lock);hh_len = READ_ONCE(hh->hh_len);if (likely(hh_len <= HH_DATA_MOD)) {hh_alen = HH_DATA_MOD;/* skb_push() would proceed silently if we have room for* the unaligned size but not for the aligned size:* check headroom explicitly.*/if (likely(skb_headroom(skb) >= HH_DATA_MOD)) {/* this is inlined by gcc */memcpy(skb->data - HH_DATA_MOD, hh->hh_data,HH_DATA_MOD);// 拷贝硬件头部缓存信息}} else {hh_alen = HH_DATA_ALIGN(hh_len);if (likely(skb_headroom(skb) >= hh_alen)) {memcpy(skb->data - hh_alen, hh->hh_data,hh_alen);}}} while (read_seqretry(&hh->hh_lock, seq));if (WARN_ON_ONCE(skb_headroom(skb) < hh_alen)) {kfree_skb(skb);return NET_XMIT_DROP; //headroom 空间不够,会丢包}__skb_push(skb, hh_len); //移动data 的指针return dev_queue_xmit(skb);// 发送到网络设备接口层,这是发送到硬件驱动了
}

严格一点来说,MAC头部信息其实是在ARP协议层里完成的。

 (自己看代码发现,不一定准确)拿lo 网卡来说,调用ARP的函数接口如下:

第一种情况:

调用 neigh_output 函数中的 n->output(n, skb);
然后output 声明如下:

static const struct neigh_ops arp_direct_ops = {.family =               AF_INET,.output =               neigh_direct_output,.connected_output =     neigh_direct_output,
};

接着neigh_resolve_output

int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb)
{return dev_queue_xmit(skb);
}
EXPORT_SYMBOL(neigh_direct_output);

这样是直接本地回环发送数据包。

第二种情况:

output 声明如下:

static const struct neigh_ops arp_generic_ops = {.family =               AF_INET,.solicit =              arp_solicit,.error_report =         arp_error_report,.output =               neigh_resolve_output,.connected_output =     neigh_connected_output,
};

接着neigh_resolve_output

/* Slow and careful. */int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
{int rc = 0;if (!neigh_event_send(neigh, skb)) {int err;struct net_device *dev = neigh->dev;unsigned int seq;if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len))neigh_hh_init(neigh);do {__skb_pull(skb, skb_network_offset(skb));seq = read_seqbegin(&neigh->ha_lock);err = dev_hard_header(skb, dev, ntohs(skb->protocol),neigh->ha, NULL, skb->len);// 硬件设备头部信息MAC} while (read_seqretry(&neigh->ha_lock, seq));if (err >= 0)rc = dev_queue_xmit(skb);elsegoto out_kfree_skb;}
out:return rc;
out_kfree_skb:rc = -EINVAL;kfree_skb(skb);goto out;
}
EXPORT_SYMBOL(neigh_resolve_output);

接着dev_hard_header

static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,unsigned short type,const void *daddr, const void *saddr,unsigned int len)
{if (!dev->header_ops || !dev->header_ops->create)return 0;return dev->header_ops->create(skb, dev, type, daddr, saddr, len);
}

其实就是调用 header_ops 的定义好的函数接口,声明如下:

路径:kernel/net/ethernet/eth.c:347

const struct header_ops eth_header_ops ____cacheline_aligned = {.create         = eth_header,.parse          = eth_header_parse,.cache          = eth_header_cache,.cache_update   = eth_header_cache_update,.parse_protocol = eth_header_parse_protocol,
};

接着看eth_header

/*** eth_header - create the Ethernet header* @skb:        buffer to alter* @dev:        source device* @type:       Ethernet type field* @daddr: destination address (NULL leave destination address)* @saddr: source address (NULL use device source address)* @len:   packet length (<= skb->len)*** Set the protocol type. For a packet of type ETH_P_802_3/2 we put the length* in here instead.*/
int eth_header(struct sk_buff *skb, struct net_device *dev,unsigned short type,const void *daddr, const void *saddr, unsigned int len)
{struct ethhdr *eth = skb_push(skb, ETH_HLEN);if (type != ETH_P_802_3 && type != ETH_P_802_2)eth->h_proto = htons(type);elseeth->h_proto = htons(len);/**      Set the source hardware address.*/if (!saddr)saddr = dev->dev_addr;memcpy(eth->h_source, saddr, ETH_ALEN);if (daddr) {memcpy(eth->h_dest, daddr, ETH_ALEN);return ETH_HLEN;}/**      Anyway, the loopback-device should never use this function...*/if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {eth_zero_addr(eth->h_dest);return ETH_HLEN;}return -ETH_HLEN;
}
EXPORT_SYMBOL(eth_header);

这就完成了MAC头部信息的拷贝。

在IP层,只是把mac_header 空间保留出来,在后面arp 层,才是实际赋值数据的操作。

二、IP 头部结构 的定义

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)__u8    ihl:4,version:4; 
#elif defined (__BIG_ENDIAN_BITFIELD)__u8    version:4,ihl:4;版本信息(前4位),头长度(后4位)
#else
#error  "Please fix <asm/byteorder.h>"
#endif__u8    tos;//服务类型8位__be16  tot_len;//数据包长度__be16  id;//数据包标识__be16  frag_off;//分片使用__u8    ttl;//存活时间__u8    protocol;//协议类型__sum16 check;//校验和__be32  saddr; //源ip__be32  daddr;//目的ip/*The options start here. */
};

注意:ip报文头20个字节,但是在实际的数据包中可能长度大于20(有一些选项)

 三、tcp 头部结构定义

struct tcphdr {__be16  source; // 源端口号16bit__be16  dest; // 目的端口号16bit__be32  seq; // 序列号32bit__be32  ack_seq; // 确认号32bit
#if defined(__LITTLE_ENDIAN_BITFIELD)__u16   res1:4,doff:4,fin:1,syn:1,rst:1,psh:1,ack:1,urg:1,ece:1,cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)__u16   doff:4,res1:4,cwr:1,ece:1,urg:1,ack:1,psh:1,rst:1,syn:1,fin:1;// 前4位:TCP头长度;中6位:保留;后6位:标志位
#else
#error  "Adjust your <asm/byteorder.h> defines"
#endif__be16  window; // 窗口大小16bit__sum16 check; // 检验和16bit__be16  urg_ptr;  // 紧急数据偏移量16bit
};

注意:tcp报文头20个字节,但是在实际的数据包中可能长度大于20(有一些选项)

四、udp 头部结构定义

struct udphdr {__be16  source;  // 源端口号16bit__be16  dest;   // 目的端口号16bit__be16  len;    // 数据包长度16bit__sum16 check;   // 校验和16bit
};

注意:udp报文头8个字节

 五、arp 报文格式

 

上面28字节的具体内容:

 

  • 硬件类型:占两字节,表示ARP报文可以在哪种类型的网络上传输,值为1时表示为以太网地址。
  • 上层协议类型:占两字节,表示硬件地址要映射的协议地址类型,映射IP地址时的值为0x0800。
  • MAC地址长度:占一字节,标识MAC地址长度,以字节为单位,此处为6。
  • IP协议地址长度:占一字节,标识IP得知长度,以字节为单位,此处为4。
  • 操作类型:占2字节,指定本次ARP报文类型。1标识ARP请求报文,2标识ARP应答报文。
  • 源MAC地址:占6字节,标识发送设备的硬件地址。
  • 源IP地址:占4字节,标识发送方设备的IP地址。
  • 目的MAC地址:占6字节,表示接收方设备的硬件地址,在请求报文中该字段值全为0,即00-00-00-00-00-00,表示任意地址,因为现在不知道这个MAC地址。
  • 目的IP地址:占4字节,表示接受方的IP地址。

 arp报文 在组装,才能调用网卡驱动。

ARP报文不是直接在网络层上发送的,它还是需要向下传输到数据链路层,所以当ARP报文传输到数据链路层之后,需要再次进行封装。以以太网为例,ARP报文传输到以太网数据链路层后会形成ARP帧。ARP帧如下图所示,他就是在ARP报文前面加了一个以太网帧头。

封装之后,术语是ARP帧,才能调用网卡驱动进行发送。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1523451.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营_day34

题目信息 62. 不同路径 题目链接: https://leetcode.cn/problems/unique-paths/description/题目描述: 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角…

STM32G474之TIM1更新中断

STM32G474之TIM1能产生如下的中断&#xff1a; 1、捕获比较1个事件&#xff08;Capture compare 1 event&#xff09; 用来获取“捕获输入脉冲的时间”&#xff0c;其次用来输出“比较输出波形”&#xff1b; 2、捕获比较2个事件&#xff08;Capture compare 2 event&#x…

opencv实战项目十九:透射变换倾斜二维码校正

文章目录 前言一、实现方法二、实现代码三&#xff0c;效果 前言 随着科技的飞速发展&#xff0c;二维码作为一种信息载体&#xff0c;已经广泛应用于我们的日常生活中。无论是支付、身份验证还是信息传播&#xff0c;二维码都发挥着不可替代的作用。然而&#xff0c;在实际应…

TeamTalk消息服务器(群组相关)

具体的流程如下介绍&#xff0c;后续需要着重研究数据库相关表的结构设计。 群组信令和协议设计 enum GroupCmdID {CID_GROUP_NORMAL_LIST_REQUEST 1025,CID_GROUP_NORMAL_LIST_RESPONSE 1026,CID_GROUP_INFO_REQUEST 1027,CID_GROUP_INFO_RESPONSE 1028,// ...... 暂时省…

【Python】一文详细向您介绍 bisect_left 函数

【Python】一文详细向您介绍 bisect_left 函数 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#x…

项目管理:项目经理如何才能做好时间管理?

在项目管理中&#xff0c;时间管理是至关重要的环节。作为项目经理&#xff0c;有效的时间管理不仅关乎个人工作效率&#xff0c;更直接影响到项目的整体进度、成本控制和质量保证。 以下是一些建议&#xff0c;帮助项目经理更好地进行时间管理&#xff1a; 一、明确项目目标…

做Ozon的挣钱逻辑,选择高客单高利润的4点原因

以下是选择高客单高利润产品在 Ozon 上销售的四点原因&#xff1a; 一、利润空间更可观 高客单价产品单个利润高&#xff1a;比如销售一件高客单价的高端电子产品&#xff0c;其利润可能是低客单价产品的数倍甚至更多。假设一件低客单价的普通日用品利润为 5 元&#xff0c;而…

2024年9月7日(星期六)骑行滑草场

2024年9月7日 (星期六) 骑行滑草场&#xff0c;早8:30到9:00&#xff0c; 郊野公园西门集合&#xff0c;9:00准时出发【因迟到者&#xff0c;骑行速度快者&#xff0c;可自行追赶偶遇。】 偶遇地点:郊野公园西门集合 &#xff0c;家住东&#xff0c;南&#xff0c;北的骑友在…

护眼灯真的可以保护眼睛吗?曝光劣质护眼台灯常见的三个特征

护眼灯真的可以保护眼睛吗&#xff1f;随着时代的发展&#xff0c;我们注意到越来越多的孩子开始佩戴眼镜。这一趋势引起了许多细心家长的关注&#xff0c;他们认识到这不仅是个别情况&#xff0c;而是现代生活方式和环境对孩子视力健康的挑战。自然而然地&#xff0c;“儿童是…

springboot名著阅读网站

基于 springbootvue实现的名著阅读网站&#xff08;源码L文ppt&#xff09;4-035 4 系统设计 4.1 系统概述 名著阅读网站的设计与开发是指对该系统的各个功能模块进行详细设计&#xff0c;力求每个模块都能够满足用户的要求&#xff0c;系统开发完成后还需对系统进行单元…

C语言 ——— #define 定义宏

目录 何为宏 宏的声明及其使用方式 宏中的括号是否多余 何为宏 #define 机制包括了一个规定&#xff0c;允许把参数替换到文本中&#xff0c;这种实现通常称宏 宏的声明及其使用方式 声明代码演示&#xff1a; #define MAX(x,y) ((x)>(y)?(x):(y)) 使用代码演示&a…

第66期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

xxe漏洞靶场实战通过

1、用nmap扫描C段&#xff0c;找到靶场 2、打开网址&#xff0c;查看robots.txt文件 3、发现有两个目录&#xff0c;分别查看发现一个登录页面 4、使用BP抓包&#xff0c;发现是xml类型 5、尝试查看/etc/passwd 文件&#xff0c;在尝试查看xxe.php文件&#xff0c;发现是编码后…

VXLAN 为何采用UDP

VXLAN 简介 VXLAN是一种网络虚拟化技术&#xff0c;它通过在UDP数据包中封装MAC地址和IP信息&#xff0c;使得二层网络&#xff08;如以太网&#xff09;能够跨越三层网络&#xff08;如IP网络&#xff09;进行扩展。这种封装方式不仅支持TCP流量的传输&#xff0c;还能有效处…

合宙LuatOS开发板Core_Air780EP使用说明

Core-Air780EP 开发板是合宙通信推出的基于 Air780EP 模组所开发的&#xff0c; 包含电源&#xff0c;SIM卡&#xff0c;USB&#xff0c;天线&#xff0c;音频等必要功能的最小硬件系统。 以方便用户在设计前期对 Air780EP模块进行性能评估&#xff0c;功能调试&#xff0c;软…

辞职一年赚了50w才知道:上班真的不赚钱。

不知不觉就进入九月了&#xff0c;一年一度的苹果秋季发布会又准备开始了。 听说今年iPhone系列有重磅升级&#xff0c;会搭载苹果智能 「Apple Intelligence」&#xff0c;搓搓手等着以旧换新了&#xff01; 此前iPhone15 pro max系列用户已经可以享受部分AI功能&#xff0c;…

【C++模板初阶】

文章目录 一、泛型编程二、函数模板1.函数模板概念2.函数模板格式3.函数模板的原理4 函数模板的实例化1. 隐式实例化2. 显式实例化不同类型形参传参时的处理 5.模板参数的匹配原则 三、类模板1 类模板的定义格式2 类模板的实例化 一、泛型编程 首先大家先思考一个问题&#xff…

文字转视频软件哪个好用?揭秘创意新工具

最近&#xff0c;我在筹备一个小型的个人项目&#xff0c;需要制作一系列的教学视频&#xff0c;但我对视频编辑一窍不通。就在我快要放弃的时候&#xff0c;我发现了一些神奇的工具&#xff0c;它们能自动把文字变成视频&#xff01; 想知道自动生成视频的软件有哪些吗&#…

nginx配置白名单服务

http { # 其他配置… # 定义一个名为 whitelist 的共享内存区域 limit_zone whitelist $binary_remote_addr 10m;server {listen 80;server_name example.com;# 白名单配置location / {# 设置只允许特定 IP 访问allow 192.168.1.100; # 允许的 IPallow 192.168.1.10…

【嵌入式学习笔记】---- OLED屏幕工作原理

1 驱动芯片SSD1603简介 1.1 SSD1603芯片图 SSD1603是一款点阵显示屏控制器&#xff0c;可嵌入在屏幕中&#xff0c;用于执行接收数据、显示存储、扫描刷新等任务驱动接口&#xff1a;128个SEG引脚和64个COM引脚&#xff0c;对应 128 64 128\times 64 12864像素点阵显示屏内置…