安装部署需要pwru要求内核代码在5.5以上版本,--output-skb要求内核版本在5.9以上,需要内核启用以下配置:OptionNoteCONFIG_DEBUG_INFO_BTF=yAvailablesince>=5.3CONFIG_KPROBES=yCONFIG_PERF_EVENTS=yCONFIG_BPF=yCONFIG_BPF_SYSCALL=y./pwru的用法:--filter-dst-ipstringfilterdestinationIPaddr--filter-intdtinst-portportufilter-funcstringfilter内核函数名称探测(精确匹配,支持RE2正则表达式)--filter-markuint32filterskbmark--filter-netnsuint32filternetnsinode--filter-protostringfilterL4协议(tcp,udp,icmp)--filter-src-ipstringfiltersourceIPaddr--filter-src-portuint16filtersourceport--output-limit-linesuint接收/打印事件数后退出程序--output-meta打印skb元数据--output-relative-timestamp打印每个skb的相对时间戳--output-skb打印skb--output-stack打印堆栈--output-tuple打印L4元组案例演示:未设置iptables规则之前:添加iptables规则之后iptables-tfilter-IOUTPUT1-mtcp--prototcp--dst1.1.1.1/32-jDROP可以看到nf_hook_slow函数发生了变化:我们可以看到数据包在nf_hook_slow中判断为NF_DROP,kfree_skbintnf_hook_slow(structsk_buff*skb,structnf_hook_state*state,conststructnf_hook_entries*e,unsignedints){unsignedintverdict;诠释;对于(;snum_hook_entries;s++){verdict=nf_hook_entry_hookfn(&e->hooks[s],skb,state);switch(verdict&NF_VERDICT_MASK){caseNF_ACCEPT:break;}案例NF_DROP:kfree_skb(skb);ret=NF_DROP_GETERR(判决);如果(ret==0)ret=-EPERM;返还;caseNF_QUEUE:ret=nf_queue(skb,state,s,verdict);如果(ret==1)继续;返还;默认值:/*NF_STOLEN的隐式处理,以及任何其他*非传统判断。*/返回0;}}返回1;pwru传入的参数可以更新eBPFMap,改变约束,更新输出例如,在FilterCfg中,制定了过滤的IP地址和协议等条件。typeFilterCfgstruct{FilterMarkuint32//Filterl3FilterIPv6uint8FilterSrcIP[16]byteFilterDstIP[16]byte//Filterl4FilterProtouint8FilterSrcPortuint16FilterDstPortuint16//TODO:if,thenyoucanconsiderusingabitmapOutputRelativeTSuint8OutputMetauint8OutputTupleuint8OutputSkbuint8OutputStackuint8Padbyte}willupdatethiseBPFMapfunc根据pwru传入的参数。,}ifflags.FilterSrcPort>0{cfg.FilterSrcPort=byteorder.HostToNetwork16(flags.FilterSrcPort)}ifflags.FilterDstPort>0{cfg.FilterDstPort=byteorder.HostToNetwork16(flags.FilterDstPort)}开关字符串.ToLower(flags.FilterPro){case"tcp":cfg.FilterProto=syscall.IPPROTO_TCPcase"udp":cfg.FilterProto=syscall.IPPROTO_UDPcase"icmp":cfg.FilterProto=syscall.IPPROTO_ICMPcase"icmp6":cfg.FilterProto=syscall.IPPROTO_ICMPV6}//...如果错误:=cfgMap.Update(uint32(0),cfg,0);err!=nil{log.Fatalf("Failedtosetfiltermap:%v",err)}}eBPF代码中可以看到会读取配置bpf_map_lookup_elem,然后执行真正的filter:struct配置{u32mark;u8ipv6;unionaddrsaddr;unionaddrdaddr;u8l4_proto;u16sport;u16dport;u8output_timestamp;u8output_meta;u8output_tuple;u8output_skb;u8output_stack;u8pad;}__attribute__((packed));static__always_inlineinthandle_everything(structsk_buff*skb,structpt_regs*ctx){structevent_texevent={ind}=u032;structconfig*cfg=bpf_map_lookup_elem(&cfg_map,&index);if(cfg){if(!filter(skb),cfg))返回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));return0;}可以看出,这里通过bpf_perf_event_output将过滤结果作为Perf事件传递接下来,err:=perf.NewReader(events,os.Getpagesize())iferr!=nil{log.Fatalf("Creatingperfeventreader:%s",err)}deferrd.Close()//。..vareventpwru.Eventfor{record,err:=rd.Read()iferr!=nil{ifperf.IsClosed(err){return}log.Printf("Readingfromperfeventreader:%s",err)}ifrecord.LostSamples!=0{log.Printf("Perfeventringbufferfull,dropped%dsamples",record.LostSamples)continue}iferr:=binary.Read(bytes.NewBuffer(record.RawSample),binary.LittleEndian,&event);err!=nil{log.Printf("Parsingperfevent:%s",err)continue}output.Print(&event)select{case<-ctx.Done():breakdefault:continue}}