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
29static 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
70	if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
71		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) {
88			fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
89			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
107		ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
108	}
109
110errout:
111	rtnl_close(&rth);
112	return ret;
113}
114
115static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
116{
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}
135
136static 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
157static 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
177/*
178 * The controller sends one nlmsg per family
179*/
180static int print_ctrl(const struct sockaddr_nl *who,
181		      struct rtnl_ctrl_data *ctrl,
182		      struct nlmsghdr *n, void *arg)
183{
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;
189	__u32 ctrl_v = 0x1;
190
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 &&
199	    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);
203		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]);
218		fprintf(fp, "\nName: %s\n",name);
219	}
220	if (tb[CTRL_ATTR_FAMILY_ID]) {
221		__u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
222		fprintf(fp, "\tID: 0x%x ",*id);
223	}
224	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		}
254
255		/* end of family::cmds definitions .. */
256		fprintf(fp,"\n");
257	}
258
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
281	fflush(fp);
282	return 0;
283}
284
285static 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
291static 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
344		if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
345			fprintf(stderr, "Error talking to the kernel\n");
346			goto ctrl_done;
347		}
348
349		if (print_ctrl2(NULL, nlh, (void *) stdout) < 0) {
350			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
360		if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
361			perror("Failed to send dump request\n");
362			goto ctrl_done;
363		}
364
365		rtnl_dump_filter(&rth, print_ctrl2, stdout);
366
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
409	fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl help\".\n",
410		*argv);
411
412	return -1;
413}
414
415struct genl_util ctrl_genl_util = {
416	.name = "ctrl",
417	.parse_genlopt = parse_ctrl,
418	.print_genlopt = print_ctrl2,
419};
420