お腹.ヘッタ。

関数型とかセキュリティとか勉強したい。進捗つらぽよ

XDP bpf_fib_lookup

XDPでルーティングの技術を使うということが多いがとても取っ付きづらくあまり情報が明文化されたまとまりがない。そこでこの文章ではlookup周りに関してまとめてみる。

bpf_fib_lookup

実装としてはここが始まり。 https://patchwork.ozlabs.org/patch/908451/ Fib(forwarding infomation baseつまりルーティングテーブル)をチェックする関数

 * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags)
 *  Description
 *      Do FIB lookup in kernel tables using parameters in *params*.
 *      If lookup is successful and result shows packet is to be
 *      forwarded, the neighbor tables are searched for the nexthop.
 *      If successful (ie., FIB lookup shows forwarding and nexthop
 *      is resolved), the nexthop address is returned in ipv4_dst
 *      or ipv6_dst based on family, smac is set to mac address of
 *      egress device, dmac is set to nexthop mac address, rt_metric
 *      is set to metric from route (IPv4/IPv6 only), and ifindex
 *      is set to the device index of the nexthop from the FIB lookup.
 *
 *      *plen* argument is the size of the passed in struct.
 *      *flags* argument can be a combination of one or more of the
 *      following values:
 *
 *      **BPF_FIB_LOOKUP_DIRECT**
 *          Do a direct table lookup vs full lookup using FIB
 *          rules.
 *      **BPF_FIB_LOOKUP_OUTPUT**
 *          Perform lookup from an egress perspective (default is
 *          ingress).
 *
 *      *ctx* is either **struct xdp_md** for XDP programs or
 *      **struct sk_buff** tc cls_act programs.
 *  Return
 *      * < 0 if any input argument is invalid
 *      *   0 on success (packet is forwarded, nexthop neighbor exists)
 *      * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
 *        packet is not forwarded or needs assist from full stack

入力

入力パラメーターはfibのパラメーターとルックアップの参照方法のフラグを入れることになる。

雑に適当にパラメーターを突っ込むとよい。こんな感じ

fib_params.family = AF_INET;
fib_params.tos = iph->tos;
fib_params.l4_protocol = iph->protocol;
fib_params.sport = 0;
fib_params.dport = 0;
fib_params.tot_len = bpf_ntohs(iph->tot_len);
fib_params.ipv4_src = iph->saddr;
fib_params.ipv4_dst = iph->daddr;

L4が含まれてる点については先ほどのfibruleにL4を紐付かせているケースがあるのでそれを考慮した結果。

  • ルックアップの参照方法
BPF_FIB_LOOKUP_*
DIRECT: Skip the FIB rules and go to FIB table associated with device
OUTPUT: Do lookup from egress perspective; default is ingress

以上に書いてる以上のことはないのですが、もう少し具体的な話は以下のコードを読むとわかる。

出力

lookupした時の結果と、結果に基づく情報が受け取れる。前者は関数の返り値、後者はfib_paramに書き込まれて実行が終わるのでこんな感じで書き込むだけで良い。

memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
memcpy(eth->h_source, fib_params.smac, ETH_ALEN);

前者の話は以下のあたいが帰ってくる。

    case BPF_FIB_LKUP_RET_SUCCESS:         /* lookup successful */
    case BPF_FIB_LKUP_RET_BLACKHOLE:    /* dest is blackholed; can be dropped */
    case BPF_FIB_LKUP_RET_UNREACHABLE:  /* dest is unreachable; can be dropped */
    case BPF_FIB_LKUP_RET_PROHIBIT:     /* dest not allowed; can be dropped */
    case BPF_FIB_LKUP_RET_NOT_FWDED:    /* packet is not forwarded */
    case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */
    case BPF_FIB_LKUP_RET_UNSUPP_LWT:   /* fwd requires encapsulation */
    case BPF_FIB_LKUP_RET_NO_NEIGH:     /* no neighbor entry for nh */
    case BPF_FIB_LKUP_RET_FRAG_NEEDED:  /* fragmentation required to fwd */

失敗した場合は雑にxdp_passをするとkernelが雑にarpを解決するのでそれを使うと良い。

参考

github.com