我写了一个bpf代码,并用clang编译,试图加载时遇到错误。我无法理解为什么以及如何解决它,需要专家的建议。
我正在VM中运行此代码 操作系统:Ubuntu 18.04.2 内核:Linux 4.18.0-15-通用x86_64
我尝试了简单的程序,但无法加载该程序。
Product with Serializable
看到错误:
static __inline int clone_netflow_record (struct __sk_buff *skb, unsigned long dstIpAddr)
{
return XDP_PASS;
}
static __inline int process_netflow_records( struct __sk_buff *skb)
{
int i = 0;
#pragma clang loop unroll(full)
for (i = 0; i < MAX_REPLICATIONS; i++) {
clone_netflow_record (skb, ipAddr[i]);
}
return XDP_DROP;
}
__section("action")
static int probe_packets(struct __sk_buff *skb)
{
/* We will access all data through pointers to structs */
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
if (data > data_end)
return XDP_DROP;
/* for easy access we re-use the Kernel's struct definitions */
struct ethhdr *eth = data;
struct iphdr *ip = (data + sizeof(struct ethhdr));
/* Only actual IP packets are allowed */
if (eth->h_proto != __constant_htons(ETH_P_IP))
return XDP_DROP;
/* If Netflow packets process it */
if (ip->protocol != IPPROTO_ICMP)
{
process_netflow_records (skb);
}
return XDP_PASS;
}
答案 0 :(得分:0)
在Linux内核中对程序进行强制检查的内核验证程序可确保不尝试进行越界访问。您的程序被拒绝,因为它可能触发这种越界访问。
如果我们仔细查看您的代码段:
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
因此,我们在这里获得指向data
(数据包的开始)和data_end
的指针。
if (data > data_end)
return XDP_DROP;
上述检查是不必要的(data
不会高于data_end
)。但是,您应该在此处执行另一项检查。让我们看下面:
/* for easy access we re-use the Kernel's struct definitions */
struct ethhdr *eth = data;
struct iphdr *ip = (data + sizeof(struct ethhdr));
/* Only actual IP packets are allowed */
if (eth->h_proto != __constant_htons(ETH_P_IP))
return XDP_DROP;
您在这里要做的是,首先,使eth
和ip
指向数据包的开头,并(指向)IP头的开头。这一步很好。但是,然后,您尝试取消对eth
的引用以访问其h_proto
字段。
现在,如果数据包不是以太网,并且时间不够长,无法包含h_proto
字段,将会发生什么?您将尝试读取数据包范围内的某些数据,这是我之前提到的出站访问。请注意,这并不意味着您的程序实际上尝试读取此数据(事实上,我看不到如何获得小于14字节的数据包)。但是从验证者的角度来看,从技术上讲,这种禁止访问可能会发生,因此它会拒绝您的程序。这就是invalid bpf_context access
的含义:您的代码尝试以无效的方式访问上下文(对于XDP:数据包数据)。
那么我们该如何解决呢?在尝试取消引用指针之前应该进行的检查不应位于data > data_end
上,而应位于:
if (data + sizeof(struct ethhdr) > data_end)
return XDP_DROP;
因此,如果我们通过检查而未返回XDP_DROP
,则可以确保该数据包足够长,足以包含完整的struct ethhdr
(并因此包含h_proto
字段)。
请注意,出于相同的原因,在尝试取消引用data + sizeof(struct ethhdr) + sizeof(struct iphdr)
之前,有必要对ip
进行类似的检查。每次尝试从数据包(上下文)访问数据时,都应确保数据包足够长以安全地取消对指针的引用。