1 /*
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 *
9 * Authors: J Hadi Salim (hadi@cyberus.ca)
10 * Johannes Berg (johannes@sipsolutions.net)
11 */
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
26 #define GENL_MAX_FAM_OPS 256
27 #define GENL_MAX_FAM_GRPS 256
28
usage(void)29 static 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
genl_ctrl_resolve_family(const char * family)42 int genl_ctrl_resolve_family(const char *family)
43 {
44 struct rtnl_handle rth;
45 int ret = 0;
46 struct {
47 struct nlmsghdr n;
48 struct genlmsghdr g;
49 char buf[4096];
50 } req = {
51 .n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
52 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
53 .n.nlmsg_type = GENL_ID_CTRL,
54 .g.cmd = CTRL_CMD_GETFAMILY,
55 };
56 struct nlmsghdr *nlh = &req.n;
57 struct genlmsghdr *ghdr = &req.g;
58
59 if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
60 fprintf(stderr, "Cannot open generic netlink socket\n");
61 exit(1);
62 }
63
64 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
65
66 if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
67 fprintf(stderr, "Error talking to the kernel\n");
68 goto errout;
69 }
70
71 {
72 struct rtattr *tb[CTRL_ATTR_MAX + 1];
73 int len = nlh->nlmsg_len;
74 struct rtattr *attrs;
75
76 if (nlh->nlmsg_type != GENL_ID_CTRL) {
77 fprintf(stderr, "Not a controller message, nlmsg_len=%d "
78 "nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type);
79 goto errout;
80 }
81
82 if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
83 fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
84 goto errout;
85 }
86
87 len -= NLMSG_LENGTH(GENL_HDRLEN);
88
89 if (len < 0) {
90 fprintf(stderr, "wrong controller message len %d\n", len);
91 return -1;
92 }
93
94 attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
95 parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
96
97 if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
98 fprintf(stderr, "Missing family id TLV\n");
99 goto errout;
100 }
101
102 ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
103 }
104
105 errout:
106 rtnl_close(&rth);
107 return ret;
108 }
109
print_ctrl_cmd_flags(FILE * fp,__u32 fl)110 static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
111 {
112 fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
113 if (!fl) {
114 fprintf(fp, "\n");
115 return;
116 }
117 fprintf(fp, "\t\t ");
118
119 if (fl & GENL_ADMIN_PERM)
120 fprintf(fp, " requires admin permission;");
121 if (fl & GENL_CMD_CAP_DO)
122 fprintf(fp, " can doit;");
123 if (fl & GENL_CMD_CAP_DUMP)
124 fprintf(fp, " can dumpit;");
125 if (fl & GENL_CMD_CAP_HASPOL)
126 fprintf(fp, " has policy");
127
128 fprintf(fp, "\n");
129 }
130
print_ctrl_cmds(FILE * fp,struct rtattr * arg,__u32 ctrl_ver)131 static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
132 {
133 struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
134
135 if (arg == NULL)
136 return -1;
137
138 parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg);
139 if (tb[CTRL_ATTR_OP_ID]) {
140 __u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]);
141 fprintf(fp, " ID-0x%x ",*id);
142 }
143 /* we are only gonna do this for newer version of the controller */
144 if (tb[CTRL_ATTR_OP_FLAGS] && ctrl_ver >= 0x2) {
145 __u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]);
146 print_ctrl_cmd_flags(fp, *fl);
147 }
148 return 0;
149
150 }
151
print_ctrl_grp(FILE * fp,struct rtattr * arg,__u32 ctrl_ver)152 static int print_ctrl_grp(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
153 {
154 struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
155
156 if (arg == NULL)
157 return -1;
158
159 parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
160 if (tb[2]) {
161 __u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]);
162 fprintf(fp, " ID-0x%x ",*id);
163 }
164 if (tb[1]) {
165 char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
166 fprintf(fp, " name: %s ", name);
167 }
168 return 0;
169
170 }
171
172 /*
173 * The controller sends one nlmsg per family
174 */
print_ctrl(const struct sockaddr_nl * who,struct rtnl_ctrl_data * ctrl,struct nlmsghdr * n,void * arg)175 static int print_ctrl(const struct sockaddr_nl *who,
176 struct rtnl_ctrl_data *ctrl,
177 struct nlmsghdr *n, void *arg)
178 {
179 struct rtattr *tb[CTRL_ATTR_MAX + 1];
180 struct genlmsghdr *ghdr = NLMSG_DATA(n);
181 int len = n->nlmsg_len;
182 struct rtattr *attrs;
183 FILE *fp = (FILE *) arg;
184 __u32 ctrl_v = 0x1;
185
186 if (n->nlmsg_type != GENL_ID_CTRL) {
187 fprintf(stderr, "Not a controller message, nlmsg_len=%d "
188 "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
189 return 0;
190 }
191
192 if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
193 ghdr->cmd != CTRL_CMD_DELFAMILY &&
194 ghdr->cmd != CTRL_CMD_NEWFAMILY &&
195 ghdr->cmd != CTRL_CMD_NEWMCAST_GRP &&
196 ghdr->cmd != CTRL_CMD_DELMCAST_GRP) {
197 fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
198 return 0;
199 }
200
201 len -= NLMSG_LENGTH(GENL_HDRLEN);
202
203 if (len < 0) {
204 fprintf(stderr, "wrong controller message len %d\n", len);
205 return -1;
206 }
207
208 attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
209 parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
210
211 if (tb[CTRL_ATTR_FAMILY_NAME]) {
212 char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
213 fprintf(fp, "\nName: %s\n",name);
214 }
215 if (tb[CTRL_ATTR_FAMILY_ID]) {
216 __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
217 fprintf(fp, "\tID: 0x%x ",*id);
218 }
219 if (tb[CTRL_ATTR_VERSION]) {
220 __u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
221 fprintf(fp, " Version: 0x%x ",*v);
222 ctrl_v = *v;
223 }
224 if (tb[CTRL_ATTR_HDRSIZE]) {
225 __u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
226 fprintf(fp, " header size: %d ",*h);
227 }
228 if (tb[CTRL_ATTR_MAXATTR]) {
229 __u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
230 fprintf(fp, " max attribs: %d ",*ma);
231 }
232 /* end of family definitions .. */
233 fprintf(fp,"\n");
234 if (tb[CTRL_ATTR_OPS]) {
235 struct rtattr *tb2[GENL_MAX_FAM_OPS];
236 int i=0;
237 parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
238 fprintf(fp, "\tcommands supported: \n");
239 for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
240 if (tb2[i]) {
241 fprintf(fp, "\t\t#%d: ", i);
242 if (0 > print_ctrl_cmds(fp, tb2[i], ctrl_v)) {
243 fprintf(fp, "Error printing command\n");
244 }
245 /* for next command */
246 fprintf(fp,"\n");
247 }
248 }
249
250 /* end of family::cmds definitions .. */
251 fprintf(fp,"\n");
252 }
253
254 if (tb[CTRL_ATTR_MCAST_GROUPS]) {
255 struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
256 int i;
257
258 parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
259 tb[CTRL_ATTR_MCAST_GROUPS]);
260 fprintf(fp, "\tmulticast groups:\n");
261
262 for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
263 if (tb2[i]) {
264 fprintf(fp, "\t\t#%d: ", i);
265 if (0 > print_ctrl_grp(fp, tb2[i], ctrl_v))
266 fprintf(fp, "Error printing group\n");
267 /* for next group */
268 fprintf(fp,"\n");
269 }
270 }
271
272 /* end of family::groups definitions .. */
273 fprintf(fp,"\n");
274 }
275
276 fflush(fp);
277 return 0;
278 }
279
print_ctrl2(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)280 static int print_ctrl2(const struct sockaddr_nl *who,
281 struct nlmsghdr *n, void *arg)
282 {
283 return print_ctrl(who, NULL, n, arg);
284 }
285
ctrl_list(int cmd,int argc,char ** argv)286 static int ctrl_list(int cmd, int argc, char **argv)
287 {
288 struct rtnl_handle rth;
289 int ret = -1;
290 char d[GENL_NAMSIZ];
291 struct {
292 struct nlmsghdr n;
293 struct genlmsghdr g;
294 char buf[4096];
295 } req = {
296 .n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
297 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
298 .n.nlmsg_type = GENL_ID_CTRL,
299 .g.cmd = CTRL_CMD_GETFAMILY,
300 };
301 struct nlmsghdr *nlh = &req.n;
302
303 if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
304 fprintf(stderr, "Cannot open generic netlink socket\n");
305 exit(1);
306 }
307
308 if (cmd == CTRL_CMD_GETFAMILY) {
309 if (argc != 2) {
310 fprintf(stderr, "Wrong number of params\n");
311 return -1;
312 }
313
314 if (matches(*argv, "name") == 0) {
315 NEXT_ARG();
316 strlcpy(d, *argv, sizeof(d));
317 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
318 d, strlen(d) + 1);
319 } else if (matches(*argv, "id") == 0) {
320 __u16 id;
321 NEXT_ARG();
322 if (get_u16(&id, *argv, 0)) {
323 fprintf(stderr, "Illegal \"id\"\n");
324 goto ctrl_done;
325 }
326
327 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2);
328
329 } else {
330 fprintf(stderr, "Wrong params\n");
331 goto ctrl_done;
332 }
333
334 if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
335 fprintf(stderr, "Error talking to the kernel\n");
336 goto ctrl_done;
337 }
338
339 if (print_ctrl2(NULL, nlh, (void *) stdout) < 0) {
340 fprintf(stderr, "Dump terminated\n");
341 goto ctrl_done;
342 }
343
344 }
345
346 if (cmd == CTRL_CMD_UNSPEC) {
347 nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
348 nlh->nlmsg_seq = rth.dump = ++rth.seq;
349
350 if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
351 perror("Failed to send dump request\n");
352 goto ctrl_done;
353 }
354
355 rtnl_dump_filter(&rth, print_ctrl2, stdout);
356
357 }
358
359 ret = 0;
360 ctrl_done:
361 rtnl_close(&rth);
362 return ret;
363 }
364
ctrl_listen(int argc,char ** argv)365 static int ctrl_listen(int argc, char **argv)
366 {
367 struct rtnl_handle rth;
368
369 if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) {
370 fprintf(stderr, "Canot open generic netlink socket\n");
371 return -1;
372 }
373
374 if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
375 return -1;
376
377 return 0;
378 }
379
parse_ctrl(struct genl_util * a,int argc,char ** argv)380 static int parse_ctrl(struct genl_util *a, int argc, char **argv)
381 {
382 argv++;
383 if (--argc <= 0) {
384 fprintf(stderr, "wrong controller params\n");
385 return -1;
386 }
387
388 if (matches(*argv, "monitor") == 0)
389 return ctrl_listen(argc-1, argv+1);
390 if (matches(*argv, "get") == 0)
391 return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1);
392 if (matches(*argv, "list") == 0 ||
393 matches(*argv, "show") == 0 ||
394 matches(*argv, "lst") == 0)
395 return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1);
396 if (matches(*argv, "help") == 0)
397 return usage();
398
399 fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl help\".\n",
400 *argv);
401
402 return -1;
403 }
404
405 struct genl_util ctrl_genl_util = {
406 .name = "ctrl",
407 .parse_genlopt = parse_ctrl,
408 .print_genlopt = print_ctrl2,
409 };
410