blob: ffa34af56b246eee7129ab5a5d707c91a809377c [file] [log] [blame]
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -07001/*
2 * ctrl.c generic netlink controller
3 *
4 * This program is free software; you can distribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
Johannes Berg80c05b02007-07-19 11:13:18 +02009 * Authors: J Hadi Salim (hadi@cyberus.ca)
10 * Johannes Berg (johannes@sipsolutions.net)
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -070011 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21#include <string.h>
22
23#include "utils.h"
24#include "genl_utils.h"
25
jamal26328fc2006-12-06 12:35:52 -050026#define GENL_MAX_FAM_OPS 256
Johannes Berg80c05b02007-07-19 11:13:18 +020027#define GENL_MAX_FAM_GRPS 256
28
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -070029static int usage(void)
30{
31 fprintf(stderr,"Usage: ctrl <CMD>\n" \
32 "CMD := get <PARMS> | list | monitor\n" \
33 "PARMS := name <name> | id <id>\n" \
34 "Examples:\n" \
35 "\tctrl ls\n" \
36 "\tctrl monitor\n" \
37 "\tctrl get name foobar\n" \
38 "\tctrl get id 0xF\n");
39 return -1;
40}
41
42int genl_ctrl_resolve_family(const char *family)
43{
44 struct rtnl_handle rth;
45 struct nlmsghdr *nlh;
46 struct genlmsghdr *ghdr;
47 int ret = 0;
48 struct {
49 struct nlmsghdr n;
50 char buf[4096];
51 } req;
52
53 memset(&req, 0, sizeof(req));
54
55 nlh = &req.n;
56 nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
57 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
58 nlh->nlmsg_type = GENL_ID_CTRL;
59
60 ghdr = NLMSG_DATA(&req.n);
61 ghdr->cmd = CTRL_CMD_GETFAMILY;
62
63 if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
64 fprintf(stderr, "Cannot open generic netlink socket\n");
65 exit(1);
66 }
67
68 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
69
Stephen Hemmingerc079e122015-05-27 12:26:14 -070070 if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -070071 fprintf(stderr, "Error talking to the kernel\n");
72 goto errout;
73 }
74
75 {
76 struct rtattr *tb[CTRL_ATTR_MAX + 1];
77 struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
78 int len = nlh->nlmsg_len;
79 struct rtattr *attrs;
80
81 if (nlh->nlmsg_type != GENL_ID_CTRL) {
82 fprintf(stderr, "Not a controller message, nlmsg_len=%d "
83 "nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type);
84 goto errout;
85 }
86
87 if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
Johannes Berg80c05b02007-07-19 11:13:18 +020088 fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -070089 goto errout;
90 }
91
92 len -= NLMSG_LENGTH(GENL_HDRLEN);
93
94 if (len < 0) {
95 fprintf(stderr, "wrong controller message len %d\n", len);
96 return -1;
97 }
98
99 attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
100 parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
101
102 if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
103 fprintf(stderr, "Missing family id TLV\n");
104 goto errout;
105 }
106
Stephen Hemmingerff247462012-04-10 08:47:55 -0700107 ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700108 }
109
110errout:
111 rtnl_close(&rth);
112 return ret;
113}
114
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800115static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
jamal26328fc2006-12-06 12:35:52 -0500116{
117 fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
118 if (!fl) {
119 fprintf(fp, "\n");
120 return;
121 }
122 fprintf(fp, "\t\t ");
123
124 if (fl & GENL_ADMIN_PERM)
125 fprintf(fp, " requires admin permission;");
126 if (fl & GENL_CMD_CAP_DO)
127 fprintf(fp, " can doit;");
128 if (fl & GENL_CMD_CAP_DUMP)
129 fprintf(fp, " can dumpit;");
130 if (fl & GENL_CMD_CAP_HASPOL)
131 fprintf(fp, " has policy");
132
133 fprintf(fp, "\n");
134}
Stephen Hemmingere9e93652016-03-27 10:47:46 -0700135
jamal26328fc2006-12-06 12:35:52 -0500136static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
137{
138 struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
139
140 if (arg == NULL)
141 return -1;
142
143 parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg);
144 if (tb[CTRL_ATTR_OP_ID]) {
145 __u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]);
146 fprintf(fp, " ID-0x%x ",*id);
147 }
148 /* we are only gonna do this for newer version of the controller */
149 if (tb[CTRL_ATTR_OP_FLAGS] && ctrl_ver >= 0x2) {
150 __u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]);
151 print_ctrl_cmd_flags(fp, *fl);
152 }
153 return 0;
154
155}
156
Johannes Berg80c05b02007-07-19 11:13:18 +0200157static int print_ctrl_grp(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
158{
159 struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
160
161 if (arg == NULL)
162 return -1;
163
164 parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
165 if (tb[2]) {
166 __u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]);
167 fprintf(fp, " ID-0x%x ",*id);
168 }
169 if (tb[1]) {
170 char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
171 fprintf(fp, " name: %s ", name);
172 }
173 return 0;
174
175}
176
jamal26328fc2006-12-06 12:35:52 -0500177/*
178 * The controller sends one nlmsg per family
179*/
Nicolas Dichtel0628cdd2015-05-20 16:19:58 +0200180static int print_ctrl(const struct sockaddr_nl *who,
181 struct rtnl_ctrl_data *ctrl,
182 struct nlmsghdr *n, void *arg)
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700183{
184 struct rtattr *tb[CTRL_ATTR_MAX + 1];
185 struct genlmsghdr *ghdr = NLMSG_DATA(n);
186 int len = n->nlmsg_len;
187 struct rtattr *attrs;
188 FILE *fp = (FILE *) arg;
Stephen Hemminger81c61792006-12-13 17:05:50 -0800189 __u32 ctrl_v = 0x1;
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700190
191 if (n->nlmsg_type != GENL_ID_CTRL) {
192 fprintf(stderr, "Not a controller message, nlmsg_len=%d "
193 "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
194 return 0;
195 }
196
197 if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
198 ghdr->cmd != CTRL_CMD_DELFAMILY &&
Johannes Berg80c05b02007-07-19 11:13:18 +0200199 ghdr->cmd != CTRL_CMD_NEWFAMILY &&
200 ghdr->cmd != CTRL_CMD_NEWMCAST_GRP &&
201 ghdr->cmd != CTRL_CMD_DELMCAST_GRP) {
202 fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700203 return 0;
204 }
205
206 len -= NLMSG_LENGTH(GENL_HDRLEN);
207
208 if (len < 0) {
209 fprintf(stderr, "wrong controller message len %d\n", len);
210 return -1;
211 }
212
213 attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
214 parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
215
216 if (tb[CTRL_ATTR_FAMILY_NAME]) {
217 char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
jamal26328fc2006-12-06 12:35:52 -0500218 fprintf(fp, "\nName: %s\n",name);
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700219 }
220 if (tb[CTRL_ATTR_FAMILY_ID]) {
221 __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
jamal26328fc2006-12-06 12:35:52 -0500222 fprintf(fp, "\tID: 0x%x ",*id);
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700223 }
jamal26328fc2006-12-06 12:35:52 -0500224 if (tb[CTRL_ATTR_VERSION]) {
225 __u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
226 fprintf(fp, " Version: 0x%x ",*v);
227 ctrl_v = *v;
228 }
229 if (tb[CTRL_ATTR_HDRSIZE]) {
230 __u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
231 fprintf(fp, " header size: %d ",*h);
232 }
233 if (tb[CTRL_ATTR_MAXATTR]) {
234 __u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
235 fprintf(fp, " max attribs: %d ",*ma);
236 }
237 /* end of family definitions .. */
238 fprintf(fp,"\n");
239 if (tb[CTRL_ATTR_OPS]) {
240 struct rtattr *tb2[GENL_MAX_FAM_OPS];
241 int i=0;
242 parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
243 fprintf(fp, "\tcommands supported: \n");
244 for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
245 if (tb2[i]) {
246 fprintf(fp, "\t\t#%d: ", i);
247 if (0 > print_ctrl_cmds(fp, tb2[i], ctrl_v)) {
248 fprintf(fp, "Error printing command\n");
249 }
250 /* for next command */
251 fprintf(fp,"\n");
252 }
253 }
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700254
jamal26328fc2006-12-06 12:35:52 -0500255 /* end of family::cmds definitions .. */
256 fprintf(fp,"\n");
257 }
Johannes Berg80c05b02007-07-19 11:13:18 +0200258
259 if (tb[CTRL_ATTR_MCAST_GROUPS]) {
260 struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
261 int i;
262
263 parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
264 tb[CTRL_ATTR_MCAST_GROUPS]);
265 fprintf(fp, "\tmulticast groups:\n");
266
267 for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
268 if (tb2[i]) {
269 fprintf(fp, "\t\t#%d: ", i);
270 if (0 > print_ctrl_grp(fp, tb2[i], ctrl_v))
271 fprintf(fp, "Error printing group\n");
272 /* for next group */
273 fprintf(fp,"\n");
274 }
275 }
276
277 /* end of family::groups definitions .. */
278 fprintf(fp,"\n");
279 }
280
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700281 fflush(fp);
282 return 0;
283}
284
Nicolas Dichtel0628cdd2015-05-20 16:19:58 +0200285static int print_ctrl2(const struct sockaddr_nl *who,
286 struct nlmsghdr *n, void *arg)
287{
288 return print_ctrl(who, NULL, n, arg);
289}
290
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700291static int ctrl_list(int cmd, int argc, char **argv)
292{
293 struct rtnl_handle rth;
294 struct nlmsghdr *nlh;
295 struct genlmsghdr *ghdr;
296 int ret = -1;
297 char d[GENL_NAMSIZ];
298 struct {
299 struct nlmsghdr n;
300 char buf[4096];
301 } req;
302
303 memset(&req, 0, sizeof(req));
304
305 nlh = &req.n;
306 nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
307 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
308 nlh->nlmsg_type = GENL_ID_CTRL;
309
310 ghdr = NLMSG_DATA(&req.n);
311 ghdr->cmd = CTRL_CMD_GETFAMILY;
312
313 if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
314 fprintf(stderr, "Cannot open generic netlink socket\n");
315 exit(1);
316 }
317
318 if (cmd == CTRL_CMD_GETFAMILY) {
319 if (argc != 2) {
320 fprintf(stderr, "Wrong number of params\n");
321 return -1;
322 }
323
324 if (matches(*argv, "name") == 0) {
325 NEXT_ARG();
326 strncpy(d, *argv, sizeof (d) - 1);
327 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
328 d, strlen(d) + 1);
329 } else if (matches(*argv, "id") == 0) {
330 __u16 id;
331 NEXT_ARG();
332 if (get_u16(&id, *argv, 0)) {
333 fprintf(stderr, "Illegal \"id\"\n");
334 goto ctrl_done;
335 }
336
337 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2);
338
339 } else {
340 fprintf(stderr, "Wrong params\n");
341 goto ctrl_done;
342 }
343
Stephen Hemmingerc079e122015-05-27 12:26:14 -0700344 if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700345 fprintf(stderr, "Error talking to the kernel\n");
346 goto ctrl_done;
347 }
348
Nicolas Dichtel0628cdd2015-05-20 16:19:58 +0200349 if (print_ctrl2(NULL, nlh, (void *) stdout) < 0) {
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700350 fprintf(stderr, "Dump terminated\n");
351 goto ctrl_done;
352 }
353
354 }
355
356 if (cmd == CTRL_CMD_UNSPEC) {
357 nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
358 nlh->nlmsg_seq = rth.dump = ++rth.seq;
359
Stephen Hemminger6cf83982011-12-23 10:40:04 -0800360 if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700361 perror("Failed to send dump request\n");
362 goto ctrl_done;
363 }
364
Nicolas Dichtel0628cdd2015-05-20 16:19:58 +0200365 rtnl_dump_filter(&rth, print_ctrl2, stdout);
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700366
367 }
368
369 ret = 0;
370ctrl_done:
371 rtnl_close(&rth);
372 return ret;
373}
374
375static int ctrl_listen(int argc, char **argv)
376{
377 struct rtnl_handle rth;
378
379 if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) {
380 fprintf(stderr, "Canot open generic netlink socket\n");
381 return -1;
382 }
383
384 if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
385 return -1;
386
387 return 0;
388}
389
390static int parse_ctrl(struct genl_util *a, int argc, char **argv)
391{
392 argv++;
393 if (--argc <= 0) {
394 fprintf(stderr, "wrong controller params\n");
395 return -1;
396 }
397
398 if (matches(*argv, "monitor") == 0)
399 return ctrl_listen(argc-1, argv+1);
400 if (matches(*argv, "get") == 0)
401 return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1);
402 if (matches(*argv, "list") == 0 ||
403 matches(*argv, "show") == 0 ||
404 matches(*argv, "lst") == 0)
405 return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1);
406 if (matches(*argv, "help") == 0)
407 return usage();
408
Masatake YAMATO3669d212014-01-29 20:30:15 +0900409 fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl help\".\n",
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700410 *argv);
411
412 return -1;
413}
414
415struct genl_util ctrl_genl_util = {
416 .name = "ctrl",
417 .parse_genlopt = parse_ctrl,
Nicolas Dichtel0628cdd2015-05-20 16:19:58 +0200418 .print_genlopt = print_ctrl2,
Jamal Hadi Salim65018ae2006-08-08 12:13:34 -0700419};