<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          pwru: 一款基于 eBPF 的細(xì)粒度網(wǎng)絡(luò)數(shù)據(jù)包排查工具

          共 9311字,需瀏覽 19分鐘

           ·

          2022-07-01 06:08



          pwru 是 Cilium 推出的基于 eBPF 開(kāi)發(fā)的網(wǎng)絡(luò)數(shù)據(jù)包排查工具,它提供了更細(xì)粒度的網(wǎng)絡(luò)數(shù)據(jù)包排查方案。本文將介紹 pwru 的使用方法和經(jīng)典場(chǎng)景,并介紹其實(shí)現(xiàn)原理。


          安裝部署

          部署要求

          pwru 要求內(nèi)核代碼在 5.5 版本之上,--output-skb 要求內(nèi)核版本在 5.9 之上,并且要求內(nèi)核開(kāi)啟以下配置:

          OptionNote
          CONFIG_DEBUG_INFO_BTF=yAvailable since >= 5.3
          CONFIG_KPROBES=y
          CONFIG_PERF_EVENTS=y
          CONFIG_BPF=y
          CONFIG_BPF_SYSCALL=y

          使用方法

          Usage of ./pwru:
                --filter-dst-ip string        filter destination IP addr
                --filter-dst-port uint16      filter destination port
                --filter-func string          filter kernel functions to be probed by name (exact match, supports RE2 regular expression)
                --filter-mark uint32          filter skb mark
                --filter-netns uint32         filter netns inode
                --filter-proto string         filter L4 protocol (tcp, udp, icmp)
                --filter-src-ip string        filter source IP addr
                --filter-src-port uint16      filter source port
                --output-limit-lines uint     exit the program after the number of events has been received/printed
                --output-meta                 print skb metadata
                --output-relative-timestamp   print relative timestamp per skb
                --output-skb                  print skb
                --output-stack                print stack
                --output-tuple                print L4 tuple

          案例演示

          下圖案例演示了 pwru 展現(xiàn)出快速定位出數(shù)據(jù)包被 iptables 規(guī)則 drop 掉的原因:

          在不設(shè)置 iptables 規(guī)則之前:

          添加了 iptables 規(guī)則之后

          iptables -t filter -I OUTPUT 1 -m tcp --proto tcp --dst 1.1.1.1/32 -j DROP

          可以看到在 nf_hook_slow 函數(shù)后發(fā)生了變化:

          我們可以看到數(shù)據(jù)包在 nf_hook_slow 判決為 NF_DROP,調(diào)用了 kfree_skb

          int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
               const struct nf_hook_entries *e, unsigned int s)
          {
            unsigned int verdict;
            int ret;

            for (; s < e->num_hook_entries; s++) {
              verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
              switch (verdict & NF_VERDICT_MASK) {
              case NF_ACCEPT:
                break;
              case NF_DROP:
                kfree_skb(skb);
                ret = NF_DROP_GETERR(verdict);
                if (ret == 0)
                  ret = -EPERM;
                return ret;
              case NF_QUEUE:
                ret = nf_queue(skb, state, s, verdict);
                if (ret == 1)
                  continue;
                return ret;
              default:
                /* Implicit handling for NF_STOLEN, as well as any other
                 * non conventional verdicts.
                 */
                return 0;
              }
            }

            return 1;
          }

          原理實(shí)現(xiàn)

          pwru 本質(zhì)上是向 kprobe 注冊(cè)了一些 eBPF code,根據(jù) pwru 傳入的參數(shù)可以更新 eBPF Map,改變限制條件,從而更新輸出。


          比如在 FilterCfg 里面制定了過(guò)濾的 IP 地址和協(xié)議等條件

          type FilterCfg struct {
           FilterMark uint32

           //Filter l3
           FilterIPv6  uint8
           FilterSrcIP [16]byte
           FilterDstIP [16]byte

           //Filter l4
           FilterProto   uint8
           FilterSrcPort uint16
           FilterDstPort uint16

           //TODO: if there are more options later, then you can consider using a bit map
           OutputRelativeTS uint8
           OutputMeta       uint8
           OutputTuple      uint8
           OutputSkb        uint8
           OutputStack      uint8

           Pad byte
          }

          會(huì)根據(jù) pwru 傳入的參數(shù)更新這個(gè) eBPF Map

          func ConfigBPFMap(flags *Flags, cfgMap *ebpf.Map) {
           cfg := FilterCfg{
            FilterMark: flags.FilterMark,
           }

           if flags.FilterSrcPort > 0 {
            cfg.FilterSrcPort = byteorder.HostToNetwork16(flags.FilterSrcPort)
           }
           if flags.FilterDstPort > 0 {
            cfg.FilterDstPort = byteorder.HostToNetwork16(flags.FilterDstPort)
           }


           switch strings.ToLower(flags.FilterProto) {
           case "tcp":
            cfg.FilterProto = syscall.IPPROTO_TCP
           case "udp":
            cfg.FilterProto = syscall.IPPROTO_UDP
           case "icmp":
            cfg.FilterProto = syscall.IPPROTO_ICMP
           case "icmp6":
            cfg.FilterProto = syscall.IPPROTO_ICMPV6
           }

            // ... 
            
           if err := cfgMap.Update(uint32(0), cfg, 0); err != nil {
            log.Fatalf("Failed to set filter map: %v", err)
           }
          }

          在 eBPF code 中,可以看到會(huì)讀取配置 bpf_map_lookup_elem,然后進(jìn)而執(zhí)行真正的 filter:

          struct config {
           u32 mark;
           u8 ipv6;
           union addr saddr;
           union addr daddr;
           u8 l4_proto;
           u16 sport;
           u16 dport;
           u8 output_timestamp;
           u8 output_meta;
           u8 output_tuple;
           u8 output_skb;
           u8 output_stack;
           u8 pad;
          } __attribute__((packed));

          static __always_inline int
          handle_everything(struct sk_buff *skb, struct pt_regs *ctx) {
           struct event_t event = {};

           u32 index = 0;
           struct config *cfg = bpf_map_lookup_elem(&cfg_map, &index);

           if (cfg) {
            if (!filter(skb, cfg))
             return 0;

            set_output(ctx, skb, &event, cfg);
           }

           event.pid = bpf_get_current_pid_tgid();
           event.addr = PT_REGS_IP(ctx);
           event.skb_addr = (u64) skb;
           event.ts = bpf_ktime_get_ns();
           bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));

           return 0;
          }

          可以看到,這里通過(guò) bpf_perf_event_output 將過(guò)濾結(jié)果以 Perf event 傳遞上來(lái)。

          rd, err := perf.NewReader(events, os.Getpagesize())
          if err != nil {
           log.Fatalf("Creating perf event reader: %s", err)
          }
          defer rd.Close()

           // ...
           var event pwru.Event
          for {
           record, err := rd.Read()
           if err != nil {
            if perf.IsClosed(err) {
             return
            }
            log.Printf("Reading from perf event reader: %s", err)
           }

           if record.LostSamples != 0 {
            log.Printf("Perf event ring buffer full, dropped %d samples", record.LostSamples)
            continue
           }

           if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {
            log.Printf("Parsing perf event: %s", err)
            continue
           }

           output.Print(&event)

           select {
           case <-ctx.Done():
            break
           default:
            continue
           }
          }

          本文轉(zhuǎn)載自:「 Houmin 的博客 」,原文:https://url.hi-linux.com/et8wH ,版權(quán)歸原作者所有。歡迎投稿,投稿郵箱: [email protected]

          鏈接:https://houmin.cc/posts/6a8748a1/

          (版權(quán)歸原作者所有,侵刪)



          瀏覽 22
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色免费高清视频 | 欧美色图久久 | 看国产乱伦毛片 | 中国女人性交毛片 | 成人精品免费无码毛片 |