• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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