XDP程序ipheader,数据,nh_off混乱
XDP program ipheader, data, nh_off confusion
我现在正在研究 XDP 代码,我对程序如何处理数据包的某些部分感到困惑 header。
所以!当我查看获取数据包 IP 地址的代码时,它是这样的:
static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
struct iphdr *iph = data + nh_off;
if ((void*)&iph[1] > data_end)
return 0;
return iph->protocol;
}
下面是一些让我困惑的事情:
struct iphdr *iph = data + nh_off;
我认为nh_off
是到下一个header的偏移值,所以如果你添加data + nh_off
,那不应该带你到下一个数据包?
因为据我理解,如果给数据加上nextheader偏移量,应该还有下一个数据包等待处理!
什么意思
(void*)&iph[1]
具体怎么做?我猜了几天这行代码是干什么的,还是一点头绪都没有。
如果我的问题太吸引人或含糊不清,我深表歉意。这件事困扰了我一段时间,如果有人能与我分享他们的知识,我将不胜感激。非常感谢您。
这完全取决于您的代码,因为我看不到 nh_off
在您的案例中是如何定义的。但大多数时候,它确实指向下一个header,所以我们会:
nh_off
是 next header 在以太网 header 被解析后的偏移量,即 nh_off
是IPheader在数据包中的偏移量(一般情况下,现阶段设置为14,以太网中的字节数header,如果不使用VLAN/encap) .
设置 struct iphdr *iph = data + nh_off;
将 iph
声明并初始化为 struct iphdr
指针,因此我们可以在之后重用它以轻松从 IPv4 header 访问每个字段。它指向 data + nh_off
,即数据包的开头加上 IPv4 header 在数据包中开始的偏移量。
无法从您的 eBPF 程序中访问下一个要处理的数据包;当那个新数据包通过对BPF程序的新调用处理时,你会得到一个新的ctx
和一个data
指针指向它,但你只看到一次只有一包。
因此 iph
指向您的 IPv4 header 的开头。我们可以使用该指针轻松到达各个字段(例如 iph->protocol
以获得 L4 协议)。但在我们这样做之前,我们必须确保数据包足够长并且实际上包含这些字段。否则我们可以进行 out-of-bound 访问(因此验证者首先会拒绝该程序)。这是我们在这里做的检查:if ((void*)&iph[1] > data_end) return 0;
在那次验证中,(void*)&iph[1]
意味着:i) 考虑一个struct iphdr *
数组(&iph
,一个指向struct iphdr
的指针)。 ii) 取该数组的第二个单元格,例如第二个 struct iphdr *
指向的结构的地址,例如数据包中第一个 struct iphdr
之后开始的字节的地址。 iii) 将其转换为 void *
,以便我们可以将其与 data_end
进行比较。换句话说,这是一种比较 data_end
(数据包最后一个字节之后的内存地址)和 IPv4 header 之后的字节地址(可能是 L4 的第一个字节)数据包是否足够长)。如果 (void*)&iph[1]
大于 data_end
,那么我们考虑的 IPv4 header 比我们得到的实际数据包更长,我们不能取消引用 iph
来尝试到达例如protocol
字段。
有了图表,也许:
Packet data
| Ethernet | IPv4 | IPv4 data (e.g. L4, data) |
+--------------+--------------------+------ ... ----------------------+
^ ^ ^ ^
data data + nh_off | data_end
iph |
&iph[0] &iph[1]
如果我们有以下内容,我们将无法访问 iph->protocol
(如果比较成功,这就是我们 return 0
的原因):
Packet data
| Ethernet | <something> | End of packet
+--------------+---------------- +
^ ^ ^ ^
data data + nh_off | |
iph | |
&iph[0] | &iph[1]
data_end
我现在正在研究 XDP 代码,我对程序如何处理数据包的某些部分感到困惑 header。 所以!当我查看获取数据包 IP 地址的代码时,它是这样的:
static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
struct iphdr *iph = data + nh_off;
if ((void*)&iph[1] > data_end)
return 0;
return iph->protocol;
}
下面是一些让我困惑的事情:
struct iphdr *iph = data + nh_off;
我认为
nh_off
是到下一个header的偏移值,所以如果你添加data + nh_off
,那不应该带你到下一个数据包? 因为据我理解,如果给数据加上nextheader偏移量,应该还有下一个数据包等待处理!什么意思
(void*)&iph[1]
具体怎么做?我猜了几天这行代码是干什么的,还是一点头绪都没有。
如果我的问题太吸引人或含糊不清,我深表歉意。这件事困扰了我一段时间,如果有人能与我分享他们的知识,我将不胜感激。非常感谢您。
这完全取决于您的代码,因为我看不到 nh_off
在您的案例中是如何定义的。但大多数时候,它确实指向下一个header,所以我们会:
nh_off
是 next header 在以太网 header 被解析后的偏移量,即nh_off
是IPheader在数据包中的偏移量(一般情况下,现阶段设置为14,以太网中的字节数header,如果不使用VLAN/encap) .设置
struct iphdr *iph = data + nh_off;
将iph
声明并初始化为struct iphdr
指针,因此我们可以在之后重用它以轻松从 IPv4 header 访问每个字段。它指向data + nh_off
,即数据包的开头加上 IPv4 header 在数据包中开始的偏移量。无法从您的 eBPF 程序中访问下一个要处理的数据包;当那个新数据包通过对BPF程序的新调用处理时,你会得到一个新的
ctx
和一个data
指针指向它,但你只看到一次只有一包。因此
iph
指向您的 IPv4 header 的开头。我们可以使用该指针轻松到达各个字段(例如iph->protocol
以获得 L4 协议)。但在我们这样做之前,我们必须确保数据包足够长并且实际上包含这些字段。否则我们可以进行 out-of-bound 访问(因此验证者首先会拒绝该程序)。这是我们在这里做的检查:if ((void*)&iph[1] > data_end) return 0;
在那次验证中,
(void*)&iph[1]
意味着:i) 考虑一个struct iphdr *
数组(&iph
,一个指向struct iphdr
的指针)。 ii) 取该数组的第二个单元格,例如第二个struct iphdr *
指向的结构的地址,例如数据包中第一个struct iphdr
之后开始的字节的地址。 iii) 将其转换为void *
,以便我们可以将其与data_end
进行比较。换句话说,这是一种比较data_end
(数据包最后一个字节之后的内存地址)和 IPv4 header 之后的字节地址(可能是 L4 的第一个字节)数据包是否足够长)。如果(void*)&iph[1]
大于data_end
,那么我们考虑的 IPv4 header 比我们得到的实际数据包更长,我们不能取消引用iph
来尝试到达例如protocol
字段。
有了图表,也许:
Packet data
| Ethernet | IPv4 | IPv4 data (e.g. L4, data) |
+--------------+--------------------+------ ... ----------------------+
^ ^ ^ ^
data data + nh_off | data_end
iph |
&iph[0] &iph[1]
如果我们有以下内容,我们将无法访问 iph->protocol
(如果比较成功,这就是我们 return 0
的原因):
Packet data
| Ethernet | <something> | End of packet
+--------------+---------------- +
^ ^ ^ ^
data data + nh_off | |
iph | |
&iph[0] | &iph[1]
data_end