1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | // Copyright (C) 2018 Facebook |
3 | |
4 | #include <stdlib.h> |
5 | #include <string.h> |
6 | #include <bpf/libbpf.h> |
7 | #include <linux/rtnetlink.h> |
8 | #include <linux/tc_act/tc_bpf.h> |
9 | |
10 | #include "bpf/nlattr.h" |
11 | #include "main.h" |
12 | #include "netlink_dumper.h" |
13 | |
14 | static void xdp_dump_prog_id(struct nlattr **tb, int attr, |
15 | const char *mode, |
16 | bool new_json_object) |
17 | { |
18 | if (!tb[attr]) |
19 | return; |
20 | |
21 | if (new_json_object) |
22 | NET_START_OBJECT |
23 | NET_DUMP_STR("mode" , " %s" , mode); |
24 | NET_DUMP_UINT("id" , " id %u" , libbpf_nla_getattr_u32(tb[attr])) |
25 | if (new_json_object) |
26 | NET_END_OBJECT |
27 | } |
28 | |
29 | static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex, |
30 | const char *name) |
31 | { |
32 | struct nlattr *tb[IFLA_XDP_MAX + 1]; |
33 | unsigned char mode; |
34 | |
35 | if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0) |
36 | return -1; |
37 | |
38 | if (!tb[IFLA_XDP_ATTACHED]) |
39 | return 0; |
40 | |
41 | mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); |
42 | if (mode == XDP_ATTACHED_NONE) |
43 | return 0; |
44 | |
45 | NET_START_OBJECT; |
46 | if (name) |
47 | NET_DUMP_STR("devname" , "%s" , name); |
48 | NET_DUMP_UINT("ifindex" , "(%d)" , ifindex); |
49 | |
50 | if (mode == XDP_ATTACHED_MULTI) { |
51 | if (json_output) { |
52 | jsonw_name(self: json_wtr, name: "multi_attachments" ); |
53 | jsonw_start_array(self: json_wtr); |
54 | } |
55 | xdp_dump_prog_id(tb, attr: IFLA_XDP_SKB_PROG_ID, mode: "generic" , new_json_object: true); |
56 | xdp_dump_prog_id(tb, attr: IFLA_XDP_DRV_PROG_ID, mode: "driver" , new_json_object: true); |
57 | xdp_dump_prog_id(tb, attr: IFLA_XDP_HW_PROG_ID, mode: "offload" , new_json_object: true); |
58 | if (json_output) |
59 | jsonw_end_array(self: json_wtr); |
60 | } else if (mode == XDP_ATTACHED_DRV) { |
61 | xdp_dump_prog_id(tb, attr: IFLA_XDP_PROG_ID, mode: "driver" , new_json_object: false); |
62 | } else if (mode == XDP_ATTACHED_SKB) { |
63 | xdp_dump_prog_id(tb, attr: IFLA_XDP_PROG_ID, mode: "generic" , new_json_object: false); |
64 | } else if (mode == XDP_ATTACHED_HW) { |
65 | xdp_dump_prog_id(tb, attr: IFLA_XDP_PROG_ID, mode: "offload" , new_json_object: false); |
66 | } |
67 | |
68 | NET_END_OBJECT_FINAL; |
69 | return 0; |
70 | } |
71 | |
72 | int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb) |
73 | { |
74 | if (!tb[IFLA_XDP]) |
75 | return 0; |
76 | |
77 | return do_xdp_dump_one(attr: tb[IFLA_XDP], ifindex: ifinfo->ifi_index, |
78 | name: libbpf_nla_getattr_str(tb[IFLA_IFNAME])); |
79 | } |
80 | |
81 | static int do_bpf_dump_one_act(struct nlattr *attr) |
82 | { |
83 | struct nlattr *tb[TCA_ACT_BPF_MAX + 1]; |
84 | |
85 | if (libbpf_nla_parse_nested(tb, TCA_ACT_BPF_MAX, attr, NULL) < 0) |
86 | return -LIBBPF_ERRNO__NLPARSE; |
87 | |
88 | if (!tb[TCA_ACT_BPF_PARMS]) |
89 | return -LIBBPF_ERRNO__NLPARSE; |
90 | |
91 | NET_START_OBJECT_NESTED2; |
92 | if (tb[TCA_ACT_BPF_NAME]) |
93 | NET_DUMP_STR("name" , "%s" , |
94 | libbpf_nla_getattr_str(tb[TCA_ACT_BPF_NAME])); |
95 | if (tb[TCA_ACT_BPF_ID]) |
96 | NET_DUMP_UINT("id" , " id %u" , |
97 | libbpf_nla_getattr_u32(tb[TCA_ACT_BPF_ID])); |
98 | NET_END_OBJECT_NESTED; |
99 | return 0; |
100 | } |
101 | |
102 | static int do_dump_one_act(struct nlattr *attr) |
103 | { |
104 | struct nlattr *tb[TCA_ACT_MAX + 1]; |
105 | |
106 | if (!attr) |
107 | return 0; |
108 | |
109 | if (libbpf_nla_parse_nested(tb, TCA_ACT_MAX, attr, NULL) < 0) |
110 | return -LIBBPF_ERRNO__NLPARSE; |
111 | |
112 | if (tb[TCA_ACT_KIND] && |
113 | strcmp(libbpf_nla_data(tb[TCA_ACT_KIND]), "bpf" ) == 0) |
114 | return do_bpf_dump_one_act(attr: tb[TCA_ACT_OPTIONS]); |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static int do_bpf_act_dump(struct nlattr *attr) |
120 | { |
121 | struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; |
122 | int act, ret; |
123 | |
124 | if (libbpf_nla_parse_nested(tb, TCA_ACT_MAX_PRIO, attr, NULL) < 0) |
125 | return -LIBBPF_ERRNO__NLPARSE; |
126 | |
127 | NET_START_ARRAY("act" , " %s [" ); |
128 | for (act = 0; act <= TCA_ACT_MAX_PRIO; act++) { |
129 | ret = do_dump_one_act(attr: tb[act]); |
130 | if (ret) |
131 | break; |
132 | } |
133 | NET_END_ARRAY("] " ); |
134 | |
135 | return ret; |
136 | } |
137 | |
138 | static int do_bpf_filter_dump(struct nlattr *attr) |
139 | { |
140 | struct nlattr *tb[TCA_BPF_MAX + 1]; |
141 | int ret; |
142 | |
143 | if (libbpf_nla_parse_nested(tb, TCA_BPF_MAX, attr, NULL) < 0) |
144 | return -LIBBPF_ERRNO__NLPARSE; |
145 | |
146 | if (tb[TCA_BPF_NAME]) |
147 | NET_DUMP_STR("name" , " %s" , |
148 | libbpf_nla_getattr_str(tb[TCA_BPF_NAME])); |
149 | if (tb[TCA_BPF_ID]) |
150 | NET_DUMP_UINT("id" , " id %u" , |
151 | libbpf_nla_getattr_u32(tb[TCA_BPF_ID])); |
152 | if (tb[TCA_BPF_ACT]) { |
153 | ret = do_bpf_act_dump(attr: tb[TCA_BPF_ACT]); |
154 | if (ret) |
155 | return ret; |
156 | } |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | int do_filter_dump(struct tcmsg *info, struct nlattr **tb, const char *kind, |
162 | const char *devname, int ifindex) |
163 | { |
164 | int ret = 0; |
165 | |
166 | if (tb[TCA_OPTIONS] && |
167 | strcmp(libbpf_nla_data(tb[TCA_KIND]), "bpf" ) == 0) { |
168 | NET_START_OBJECT; |
169 | if (devname[0] != '\0') |
170 | NET_DUMP_STR("devname" , "%s" , devname); |
171 | NET_DUMP_UINT("ifindex" , "(%u)" , ifindex); |
172 | NET_DUMP_STR("kind" , " %s" , kind); |
173 | ret = do_bpf_filter_dump(attr: tb[TCA_OPTIONS]); |
174 | NET_END_OBJECT_FINAL; |
175 | } |
176 | |
177 | return ret; |
178 | } |
179 | |