1
2
3
4
5
6
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12#include <errno.h>
13#include <inttypes.h>
14
15#include <linux/genetlink.h>
16#include <linux/ioam6_genl.h>
17
18#include "utils.h"
19#include "ip_common.h"
20#include "libgenl.h"
21#include "json_print.h"
22
23static void usage(void)
24{
25 fprintf(stderr,
26 "Usage: ip ioam { COMMAND | help }\n"
27 " ip ioam namespace show\n"
28 " ip ioam namespace add ID [ data DATA32 ] [ wide DATA64 ]\n"
29 " ip ioam namespace del ID\n"
30 " ip ioam schema show\n"
31 " ip ioam schema add ID DATA\n"
32 " ip ioam schema del ID\n"
33 " ip ioam namespace set ID schema { ID | none }\n");
34 exit(-1);
35}
36
37static struct rtnl_handle grth = { .fd = -1 };
38static int genl_family = -1;
39
40#define IOAM6_REQUEST(_req, _bufsiz, _cmd, _flags) \
41 GENL_REQUEST(_req, _bufsiz, genl_family, 0, \
42 IOAM6_GENL_VERSION, _cmd, _flags)
43
44static struct {
45 unsigned int cmd;
46 __u32 sc_id;
47 __u32 ns_data;
48 __u64 ns_data_wide;
49 __u16 ns_id;
50 bool has_ns_data;
51 bool has_ns_data_wide;
52 bool sc_none;
53 __u8 sc_data[IOAM6_MAX_SCHEMA_DATA_LEN];
54} opts;
55
56static void print_namespace(struct rtattr *attrs[])
57{
58 print_uint(PRINT_ANY, "namespace", "namespace %u",
59 rta_getattr_u16(attrs[IOAM6_ATTR_NS_ID]));
60
61 if (attrs[IOAM6_ATTR_SC_ID])
62 print_uint(PRINT_ANY, "schema", " [schema %u]",
63 rta_getattr_u32(attrs[IOAM6_ATTR_SC_ID]));
64
65 if (attrs[IOAM6_ATTR_NS_DATA])
66 print_hex(PRINT_ANY, "data", ", data %#010x",
67 rta_getattr_u32(attrs[IOAM6_ATTR_NS_DATA]));
68
69 if (attrs[IOAM6_ATTR_NS_DATA_WIDE])
70 print_0xhex(PRINT_ANY, "wide", ", wide %#018lx",
71 rta_getattr_u64(attrs[IOAM6_ATTR_NS_DATA_WIDE]));
72
73 print_nl();
74}
75
76static void print_schema(struct rtattr *attrs[])
77{
78 __u8 data[IOAM6_MAX_SCHEMA_DATA_LEN];
79 int len, i = 0;
80
81 print_uint(PRINT_ANY, "schema", "schema %u",
82 rta_getattr_u32(attrs[IOAM6_ATTR_SC_ID]));
83
84 if (attrs[IOAM6_ATTR_NS_ID])
85 print_uint(PRINT_ANY, "namespace", " [namespace %u]",
86 rta_getattr_u16(attrs[IOAM6_ATTR_NS_ID]));
87
88 len = RTA_PAYLOAD(attrs[IOAM6_ATTR_SC_DATA]);
89 memcpy(data, RTA_DATA(attrs[IOAM6_ATTR_SC_DATA]), len);
90
91 print_null(PRINT_ANY, "data", ", data:", NULL);
92 while (i < len) {
93 print_hhu(PRINT_ANY, "", " %02x", data[i]);
94 i++;
95 }
96 print_nl();
97}
98
99static int process_msg(struct nlmsghdr *n, void *arg)
100{
101 struct rtattr *attrs[IOAM6_ATTR_MAX + 1];
102 struct genlmsghdr *ghdr;
103 int len = n->nlmsg_len;
104
105 if (n->nlmsg_type != genl_family)
106 return -1;
107
108 len -= NLMSG_LENGTH(GENL_HDRLEN);
109 if (len < 0)
110 return -1;
111
112 ghdr = NLMSG_DATA(n);
113 parse_rtattr(attrs, IOAM6_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
114
115 open_json_object(NULL);
116 switch (ghdr->cmd) {
117 case IOAM6_CMD_DUMP_NAMESPACES:
118 print_namespace(attrs);
119 break;
120 case IOAM6_CMD_DUMP_SCHEMAS:
121 print_schema(attrs);
122 break;
123 }
124 close_json_object();
125
126 return 0;
127}
128
129static int ioam6_do_cmd(void)
130{
131 IOAM6_REQUEST(req, 1056, opts.cmd, NLM_F_REQUEST);
132 int dump = 0;
133
134 if (genl_init_handle(&grth, IOAM6_GENL_NAME, &genl_family))
135 exit(1);
136
137 req.n.nlmsg_type = genl_family;
138
139 switch (opts.cmd) {
140 case IOAM6_CMD_ADD_NAMESPACE:
141 addattr16(&req.n, sizeof(req), IOAM6_ATTR_NS_ID, opts.ns_id);
142 if (opts.has_ns_data)
143 addattr32(&req.n, sizeof(req), IOAM6_ATTR_NS_DATA,
144 opts.ns_data);
145 if (opts.has_ns_data_wide)
146 addattr64(&req.n, sizeof(req), IOAM6_ATTR_NS_DATA_WIDE,
147 opts.ns_data_wide);
148 break;
149 case IOAM6_CMD_DEL_NAMESPACE:
150 addattr16(&req.n, sizeof(req), IOAM6_ATTR_NS_ID, opts.ns_id);
151 break;
152 case IOAM6_CMD_DUMP_NAMESPACES:
153 case IOAM6_CMD_DUMP_SCHEMAS:
154 dump = 1;
155 break;
156 case IOAM6_CMD_ADD_SCHEMA:
157 addattr32(&req.n, sizeof(req), IOAM6_ATTR_SC_ID, opts.sc_id);
158 addattr_l(&req.n, sizeof(req), IOAM6_ATTR_SC_DATA, opts.sc_data,
159 strlen((const char *)opts.sc_data));
160 break;
161 case IOAM6_CMD_DEL_SCHEMA:
162 addattr32(&req.n, sizeof(req), IOAM6_ATTR_SC_ID, opts.sc_id);
163 break;
164 case IOAM6_CMD_NS_SET_SCHEMA:
165 addattr16(&req.n, sizeof(req), IOAM6_ATTR_NS_ID, opts.ns_id);
166 if (opts.sc_none)
167 addattr(&req.n, sizeof(req), IOAM6_ATTR_SC_NONE);
168 else
169 addattr32(&req.n, sizeof(req), IOAM6_ATTR_SC_ID,
170 opts.sc_id);
171 break;
172 }
173
174 if (!dump) {
175 if (rtnl_talk(&grth, &req.n, NULL) < 0)
176 return -1;
177 } else {
178 req.n.nlmsg_flags |= NLM_F_DUMP;
179 req.n.nlmsg_seq = grth.dump = ++grth.seq;
180 if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
181 perror("Failed to send dump request");
182 exit(1);
183 }
184
185 new_json_obj(json);
186 if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
187 fprintf(stderr, "Dump terminated\n");
188 exit(1);
189 }
190 delete_json_obj();
191 fflush(stdout);
192 }
193
194 return 0;
195}
196
197int do_ioam6(int argc, char **argv)
198{
199 bool maybe_wide = false;
200
201 if (argc < 1 || strcmp(*argv, "help") == 0)
202 usage();
203
204 memset(&opts, 0, sizeof(opts));
205
206 if (strcmp(*argv, "namespace") == 0) {
207 NEXT_ARG();
208
209 if (strcmp(*argv, "show") == 0) {
210 opts.cmd = IOAM6_CMD_DUMP_NAMESPACES;
211
212 } else if (strcmp(*argv, "add") == 0) {
213 NEXT_ARG();
214
215 if (get_u16(&opts.ns_id, *argv, 0))
216 invarg("Invalid namespace ID", *argv);
217
218 if (NEXT_ARG_OK()) {
219 NEXT_ARG_FWD();
220
221 if (strcmp(*argv, "data") == 0) {
222 NEXT_ARG();
223
224 if (get_u32(&opts.ns_data, *argv, 0))
225 invarg("Invalid data", *argv);
226
227 maybe_wide = true;
228 opts.has_ns_data = true;
229
230 } else if (strcmp(*argv, "wide") == 0) {
231 NEXT_ARG();
232
233 if (get_u64(&opts.ns_data_wide, *argv, 16))
234 invarg("Invalid wide data", *argv);
235
236 opts.has_ns_data_wide = true;
237
238 } else {
239 invarg("Invalid argument", *argv);
240 }
241 }
242
243 if (NEXT_ARG_OK()) {
244 NEXT_ARG_FWD();
245
246 if (!maybe_wide || strcmp(*argv, "wide") != 0)
247 invarg("Unexpected argument", *argv);
248
249 NEXT_ARG();
250
251 if (get_u64(&opts.ns_data_wide, *argv, 16))
252 invarg("Invalid wide data", *argv);
253
254 opts.has_ns_data_wide = true;
255 }
256
257 opts.cmd = IOAM6_CMD_ADD_NAMESPACE;
258
259 } else if (strcmp(*argv, "del") == 0) {
260 NEXT_ARG();
261
262 if (get_u16(&opts.ns_id, *argv, 0))
263 invarg("Invalid namespace ID", *argv);
264
265 opts.cmd = IOAM6_CMD_DEL_NAMESPACE;
266
267 } else if (strcmp(*argv, "set") == 0) {
268 NEXT_ARG();
269
270 if (get_u16(&opts.ns_id, *argv, 0))
271 invarg("Invalid namespace ID", *argv);
272
273 NEXT_ARG();
274
275 if (strcmp(*argv, "schema") != 0)
276 invarg("Unknown", *argv);
277
278 NEXT_ARG();
279
280 if (strcmp(*argv, "none") == 0) {
281 opts.sc_none = true;
282
283 } else {
284 if (get_u32(&opts.sc_id, *argv, 0))
285 invarg("Invalid schema ID", *argv);
286
287 opts.sc_none = false;
288 }
289
290 opts.cmd = IOAM6_CMD_NS_SET_SCHEMA;
291
292 } else {
293 invarg("Unknown", *argv);
294 }
295
296 } else if (strcmp(*argv, "schema") == 0) {
297 NEXT_ARG();
298
299 if (strcmp(*argv, "show") == 0) {
300 opts.cmd = IOAM6_CMD_DUMP_SCHEMAS;
301
302 } else if (strcmp(*argv, "add") == 0) {
303 NEXT_ARG();
304
305 if (get_u32(&opts.sc_id, *argv, 0))
306 invarg("Invalid schema ID", *argv);
307
308 NEXT_ARG();
309
310 if (strlen(*argv) > IOAM6_MAX_SCHEMA_DATA_LEN)
311 invarg("Schema DATA too big", *argv);
312
313 memcpy(opts.sc_data, *argv, strlen(*argv));
314 opts.cmd = IOAM6_CMD_ADD_SCHEMA;
315
316 } else if (strcmp(*argv, "del") == 0) {
317 NEXT_ARG();
318
319 if (get_u32(&opts.sc_id, *argv, 0))
320 invarg("Invalid schema ID", *argv);
321
322 opts.cmd = IOAM6_CMD_DEL_SCHEMA;
323
324 } else {
325 invarg("Unknown", *argv);
326 }
327
328 } else {
329 invarg("Unknown", *argv);
330 }
331
332 return ioam6_do_cmd();
333}
334