2

でIPアドレスを出力している2つの異なるタイプのBPFプログラムがありますbpf_printk("%pI4", &ipv4.s_addr);。xdp プログラムはループバック dev にロードされ、もう 1 つは bpf ソケット フィルターです。UDPパケットを送信するサーバーとクライアントプログラムがあります。以下のプログラムは IP アドレスを出力しますが、各プログラムは (/sys/kernel/debug/tracing/trace_pipe に) アドレスを異なる順序で出力します。1 つは (xdp プログラムで) 127.0.0.1 として出力され、もう 1 つは 1.0.0.127 として出力されます。ソケット フィルターはカーネルの skb_buff のミラーにアクセスでき、xdp プログラムは「以前の時点」でアタッチされているため、このデータにアクセスできないことを理解しています。私の質問: skb_buff データ フィールドのデータは、CPU エンディアンに「変更」されますか? この異なる動作の原因は何でしょうか?

前もって感謝します。

私の環境:

x86_64
clang-13
Little endian
Linux kernel 5.13
Ubuntu 21.10 
// socket filter program
unsigned long long load_word(void *skb, unsigned long long off) asm("llvm.bpf.load.word");

SEC("socket/ipv4")
int filter_ipv4(struct __sk_buff *skb)
{
    struct in_addr ipv4;
    ipv4.s_addr = load_word(skb, BPF_NET_OFF + offsetof(struct iphdr, saddr));
    bpf_printk("%pI4", &ipv4.s_addr); // prints 1.0.0.127 
    return -1;
}

// xdp program

SEC("xdp_prog")
int xdp_some_prog(struct xdp_md *ctx)
{
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *ethh;
    struct hdr_cursor nh;
    int nh_type;
    struct in_addr ipv4;

    nh.pos = data;
    nh_type = parse_ethhdr(&nh, data_end, &ethh);

    if (nh_type == bpf_htons(ETH_P_IP)) {
        struct iphdr *iph;

        nh_type = parse_iphdr(&nh, data_end, &iph);
        if (nh_type != IPPROTO_UDP)
            return XDP_PASS;

        ipv4.s_addr = iph->saddr;
        bpf_printk("%pI4", &ipv4.s_addr); // prints 127.0.0.1       
    }
    return XDP_PASS;
}

#define BPF_NET_OFF (-0x100000)
#define ETH_P_IP    0x0800  
#define IPPROTO_UDP    17

struct in_addr {
    __u32 s_addr;
};

struct ethhdr {
    unsigned char h_dest[6];
    unsigned char h_source[6];
    __be16 h_proto;
};

struct iphdr {
    __u8 ihl: 4;
    __u8 version: 4;
    __u8 tos;
    __be16 tot_len;
    __be16 id;
    __be16 frag_off;
    __u8 ttl;
    __u8 protocol;
    __sum16 check;
    __be32 saddr;
    __be32 daddr;
};


struct hdr_cursor {
    void *pos;
};

static __always_inline
int parse_ethhdr(struct hdr_cursor *nh, void *data_end, struct ethhdr **ethhdr)
{
    struct ethhdr *eth = nh->pos;

    if (eth + 1 > data_end)
        return -1;

    nh->pos = eth + 1;
    *ethhdr = eth;
    return eth->h_proto;
}

static __always_inline
int parse_iphdr(struct hdr_cursor *nh, void *data_end, struct iphdr **iphdr)
{
    struct iphdr *iph = nh->pos;
    int hdrsize;

    if (iph + 1 > data_end)
        return -1;

    hdrsize = iph->ihl * 4;

    /* ipv4 hdr is minimum 20 bytes */
    if (hdrsize  < sizeof(*iph))
        return -1;

    /* Variable-length IPv4 header, need to use byte-based arithmetic */
    if (nh->pos + hdrsize > data_end)
        return -1;

    nh->pos += hdrsize;
    *iphdr = iph;
    return iph->protocol;
}
4

0 に答える 0