阜阳网站是,网站建设遇到哪些攻击,网站建设分金手指专业十六,wordpress文章列表图片背景
在深入剖析cilium原理之前#xff0c;有两个关于epbf的基础内容需要先详细介绍一下#xff1a;
1. ebpf尾调用
尾调用类似于程序之间的相互跳转#xff0c;但它的功能更加强大。
2. trace
虽然之前使用trace_printk输出日志#xff0c;但这个函数不能多用#x…背景
在深入剖析cilium原理之前有两个关于epbf的基础内容需要先详细介绍一下
1. ebpf尾调用
尾调用类似于程序之间的相互跳转但它的功能更加强大。
2. trace
虽然之前使用trace_printk输出日志但这个函数不能多用会有性能问题。而且它的输出可读性差不利于程序进行分析。本文将着重讲讲如何进行分析方便程序后续跟踪。 1.ebpf尾调用介绍
尾调用能够从一个程序调到另一个程序提供了在运行时runtime原子地改变程序行为的灵活性。为了选择要跳转到哪个程序尾调用使用了程序数组map BPF_MAP_TYPE_PROG_ARRAY将map及其索引index传递给将要跳转到的程序。跳转动作一旦完成就没有办法返回到原来的程序但如果给定的map索引中没有程序无法跳转执行会继续在原来的程序中执行。
特殊在于由于函数的调用是由map来保存的可以使用TC更新map对应的函数为新的函数实现ebpf程序的局部更新。当然这就对功能编码也会有更高的要求需要明确更新可能的影响范围。因为ebpf的单个程序大小是有限制的而尾调用可以绕开这种限制。
以下是摘自cilium官方文档中的样例官方样例无法编译下面摘取的内容经过作者调整
#include bits/types.h
#include linux/bpf.h
#include linux/if_ether.h
#include linux/in.h
#include linux/ip.h
#include linux/pkt_cls.h
#include linux/tcp.h
#include errno.h
#include stdio.h
#include string.htypedef __uint32_t uint32_t;#define PIN_GLOBAL_NS 2struct bpf_elf_map {__u32 type;__u32 size_key;__u32 size_value;__u32 max_elem;__u32 flags;__u32 id;__u32 pinning;
};#ifndef __stringify
# define __stringify(X) #X
#endif#ifndef __section
# define __section(NAME) \__attribute__((section(NAME), used))
#endif#ifndef __section_tail
# define __section_tail(ID, KEY) \__section(__stringify(ID) / __stringify(KEY))
#endif#ifndef BPF_FUNC
# define BPF_FUNC(NAME, ...) \(*NAME)(__VA_ARGS__) (void *)BPF_FUNC_##NAME
#endif#define BPF_JMP_MAP_ID 1static int (*bpf_trace_printk)(const char *fmt, int fmt_size,...) (void *)BPF_FUNC_trace_printk;#define printk(fmt, ...) \do { \char _fmt[] fmt; \bpf_trace_printk(_fmt, sizeof(_fmt), ##__VA_ARGS__); \} while (0)static void BPF_FUNC(tail_call, struct __sk_buff *skb, void *map,uint32_t index);struct bpf_elf_map jmp_map __section(maps) {.type BPF_MAP_TYPE_PROG_ARRAY,.id BPF_JMP_MAP_ID,.size_key sizeof(uint32_t),.size_value sizeof(uint32_t),.pinning PIN_GLOBAL_NS,.max_elem 1,
};__section_tail(BPF_JMP_MAP_ID, 0)
int looper(struct __sk_buff *skb)
{printk(skb cb: %u\n, skb-cb[0]);tail_call(skb, jmp_map, 0);printk(skb end looper: cb: %u\n, skb-cb[0]);return TC_ACT_OK;
}__section(prog)
int entry(struct __sk_buff *skb)
{skb-cb[0] 0;tail_call(skb, jmp_map, 0);printk(skb end: cb: %u\n, skb-cb[0]);return TC_ACT_OK;
}char __license[] __section(license) GPL;
官方代码很清晰需要注意的是定义函数时__section_tail(BPF_JMP_MAP_ID, 0)使用的是BPF_JMP_MAP_ID而在bpf代码中调用的时候使用的是tail_call(skb, jmp_map, 0);中的jmp_map。而这两者之间的关系则是在定义jmp_map时指定。 功能测试演示 # 将上面文件保存为tail.c
# build
clang -g -c -O2 -target bpf -c tail.c -o tail.o# load
tc qdisc add dev enp1s0 ingress
tc filter replace dev enp1s0 ingress prio 1 handle 1 bpf da obj tail.o sec prog verbose# cat log
cat /sys/kernel/debug/tracing/trace_pipe
虽然上面的程序是死循环但测试后发现程序不是真的死循环会在looper中执行34次后结束。验证函数热更新功能。
idle-0 [007] ..s. 443032.404122: 0: skb cb: 29idle-0 [007] ..s. 443032.404122: 0: skb cb: 30idle-0 [007] ..s. 443032.404123: 0: skb cb: 31idle-0 [007] ..s. 443032.404123: 0: skb cb: 32idle-0 [007] ..s. 443032.404123: 0: skb cb: 33idle-0 [007] ..s. 443032.404124: 0: skb end looper: cb: 34idle-0 [007] ..s. 443032.404146: 0: skb cb: 0idle-0 [007] ..s. 443032.404148: 0: skb cb: 1idle-0 [007] ..s. 443032.404149: 0: skb cb: 2idle-0 [007] ..s. 443032.404149: 0: skb cb: 3idle-0 [007] ..s. 443032.404150: 0: skb cb: 4
在之前的源代码中添加如下函数 __section(newloop)
int newlooper(struct __sk_buff *skb)
{printk(skb end in new looper: cb: %u\n, skb-cb[0]);return TC_ACT_OK;
}# build
clang -g -c -O2 -target bpf -c tail.c -o tail.o# update func
tc exec bpf graft m:globals/jmp_map key 0 obj tail.o sec newloop
# 特别注意由于centos8自带的tc版本太低无法更新需要使用高版本的tc.可使用cilium自带的tc
# docker run -it --namemytest --networkhost --privileged -v $PWD:/hosts/ -v /sys/fs/bpf:/sys/fs/bpf -v /run/cilium/cgroupv2/:/run/cilium/cgroupv2 cilium:v1.12.7 bash 进入容器后执行上面的命令# 结果检查
cat /sys/kernel/debug/tracing/trace_pipe 2. send_trace_notify 跟踪
send_trace_notify – ctx_event_output – skb_event_outputevent_output // 调用样例
send_trace_notify(ctx, TRACE_FROM_LXC, SECLABEL, 0, 0, 0,TRACE_REASON_UNKNOWN, TRACE_PAYLOAD_LEN);// 函数定义
send_trace_notify(struct __ctx_buff *ctx, enum trace_point obs_point,__u32 src, __u32 dst, __u16 dst_id, __u32 ifindex,enum trace_reason reason, __u32 monitor)msg (typeof(msg)) {__notify_common_hdr(CILIUM_NOTIFY_TRACE, obs_point), // 事件大类CILIUM_NOTIFY_TRACE子类obs_point__notify_pktcap_hdr(ctx_len, (__u16)cap_len),.src_label src, // 源对象id cilium identity list, identity 全局唯一.dst_label dst, // 目的对象id.dst_id dst_id, // 不同的子类id取值不同.reason reason, // 原因标识.ifindex ifindex, // 不同的子类取值类型不同};ctx_event_output(ctx, EVENTS_MAP,(cap_len 32) | BPF_F_CURRENT_CPU,msg, sizeof(msg));
上报的信息就是harbor展示的数据来源。
信息说明 发送的信息会放在ring buffer中。如果未读取会进行覆盖写入。 这个buffer是cpu级别的每个cpu中都会有一个独立的。 读取时需要指定读取哪个cpu-1表示读取所有cpu中的信息。 读取event的具体细节可参考 https://man7.org/linux/manpages/man2/perf_event_open.2.html
下面为cilium使用go实现了perf读取库的使用说明github.com/cilium/ebpf/perf
cilium目前的信息结构说明
cilium monitor可以读取perf信息并且能基于identity进行过滤具体使用可参考该命令说明。 var err errorpath : oldBPF.MapPath(signalmap.MapName)
signalMap, err : ebpf.LoadPinnedMap(path, nil)
if err ! nil {log.WithError(err).Warningf(Failed to open signals map)return
}
events, err perf.NewReader(signalMap, os.Getpagesize())
if err ! nil {log.WithError(err).Warningf(Cannot open %s map! Ignoring signals!,signalmap.MapName)return
}go func() {log.Info(Datapath signal listener running)for {record, err : events.Read()switch {case err ! nil:signalCollectMetrics(nil, error)log.WithError(err).WithFields(logrus.Fields{logfields.BPFMapName: signalmap.MapName,}).Errorf(failed to read event)case record.LostSamples 0:signalCollectMetrics(nil, lost)default:signalReceive(record)}}
}()
消息结构如下
总体分两大类 lost count用于记数数据包丢弃数。 详细报文说明。 各种sample数据格式说明。 cilium monitor使用样例。 # 使用cilium monitor查看网络策略执行情况
# remoteID 表示针对对象
# ingress/egress表示入与出流量
# action表示策略结果
cilium monitor -t policy-verdict
Listening for events on 8 CPUs with 64x4096 of shared memory
Press Ctrl-C to quit
levelinfo msgInitializing dissection cache... subsysmonitor
Policy verdict log: flow 0x95e0951 local EP ID 2467, remote ID kube-apiserver, proto 6, ingress, action allow, auth: disabled, match L3-L4, 172.18.0.6:46866 - 172.18.0.8:2380 tcp SYN
Policy verdict log: flow 0x580738e1 local EP ID 2467, remote ID 16777218, proto 6, ingress, action allow, auth: disabled, match L3-L4, 172.18.0.2:40122 - 172.18.0.8:6443 tcp SYN
Policy verdict log: flow 0x4dfe98a0 local EP ID 2467, remote ID kube-apiserver, proto 6, ingress, action allow, auth: disabled, match L3-L4, 172.18.0.4:52314 - 172.18.0.8:2380 tcp SYN
Policy verdict log: flow 0xb3af0a31 local EP ID 2467, remote ID kube-apiserver, proto 6, egress, action allow, auth: disabled, match L3-L4, 172.18.0.8:42736 - 172.18.0.6:2380 tcp SYN
Policy verdict log: flow 0xfdb19557 local EP ID 2467, remote ID kube-apiserver, proto 6, egress, action allow, auth: disabled, match L3-L4, 172.18.0.8:46246 - 172.18.0.4:2380 tcp SYN
Policy verdict log: flow 0x5438a30a local EP ID 2467, remote ID 16777218, proto 6, ingress, action allow, auth: disabled, match L3-L4, 172.18.0.2:40164 - 172.18.0.8:6443 tcp SYN
endpoint id可通过这个方式查到。 展望
跟踪只允许基于endpointID进行跟踪无法基于ip、端口等信息当网络流量在多个节点中流转时无法有效的进行跟踪。
这一块可基于数据分析组件来实现它可以导出json结构数据采集统一汇总分析统计。
1.访问入口与目标pod在同节点上ip nat情况 2.访问入口与目标pod在同节点上流量跟踪情况 可以看到源端口在网络流转中是不变的可以基于此作为跟踪线索。
可基于eskibana,将cilium trace相关数据统一在es中存储并基于kibana展示如下图所示 红色框分别为源ip、目标ip、源端口、目标端口。而特殊的flb_host_ip是坐着在采集时添加的节点主机的ip用来分析不同主机间的流量。 作者沃趣科技产品研发部
更多技术干货请关注公号【云原生数据库】
squids.cn云数据库RDS迁移工具DBMotion云备份DBTwin等数据库生态工具。
irds.cn多数据库管理平台私有云。