• 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 	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, 0, 0, nlh) < 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 
110 errout:
111 	rtnl_close(&rth);
112 	return ret;
113 }
114 
print_ctrl_cmd_flags(FILE * fp,__u32 fl)115 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 
print_ctrl_cmds(FILE * fp,struct rtattr * arg,__u32 ctrl_ver)136 static 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 
print_ctrl_grp(FILE * fp,struct rtattr * arg,__u32 ctrl_ver)157 static 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 */
print_ctrl(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)180 static int print_ctrl(const struct sockaddr_nl *who, struct nlmsghdr *n,
181 		      void *arg)
182 {
183 	struct rtattr *tb[CTRL_ATTR_MAX + 1];
184 	struct genlmsghdr *ghdr = NLMSG_DATA(n);
185 	int len = n->nlmsg_len;
186 	struct rtattr *attrs;
187 	FILE *fp = (FILE *) arg;
188 	__u32 ctrl_v = 0x1;
189 
190 	if (n->nlmsg_type !=  GENL_ID_CTRL) {
191 		fprintf(stderr, "Not a controller message, nlmsg_len=%d "
192 			"nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
193 		return 0;
194 	}
195 
196 	if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
197 	    ghdr->cmd != CTRL_CMD_DELFAMILY &&
198 	    ghdr->cmd != CTRL_CMD_NEWFAMILY &&
199 	    ghdr->cmd != CTRL_CMD_NEWMCAST_GRP &&
200 	    ghdr->cmd != CTRL_CMD_DELMCAST_GRP) {
201 		fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
202 		return 0;
203 	}
204 
205 	len -= NLMSG_LENGTH(GENL_HDRLEN);
206 
207 	if (len < 0) {
208 		fprintf(stderr, "wrong controller message len %d\n", len);
209 		return -1;
210 	}
211 
212 	attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
213 	parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
214 
215 	if (tb[CTRL_ATTR_FAMILY_NAME]) {
216 		char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
217 		fprintf(fp, "\nName: %s\n",name);
218 	}
219 	if (tb[CTRL_ATTR_FAMILY_ID]) {
220 		__u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
221 		fprintf(fp, "\tID: 0x%x ",*id);
222 	}
223 	if (tb[CTRL_ATTR_VERSION]) {
224 		__u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
225 		fprintf(fp, " Version: 0x%x ",*v);
226 		ctrl_v = *v;
227 	}
228 	if (tb[CTRL_ATTR_HDRSIZE]) {
229 		__u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
230 		fprintf(fp, " header size: %d ",*h);
231 	}
232 	if (tb[CTRL_ATTR_MAXATTR]) {
233 		__u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
234 		fprintf(fp, " max attribs: %d ",*ma);
235 	}
236 	/* end of family definitions .. */
237 	fprintf(fp,"\n");
238 	if (tb[CTRL_ATTR_OPS]) {
239 		struct rtattr *tb2[GENL_MAX_FAM_OPS];
240 		int i=0;
241 		parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
242 		fprintf(fp, "\tcommands supported: \n");
243 		for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
244 			if (tb2[i]) {
245 				fprintf(fp, "\t\t#%d: ", i);
246 				if (0 > print_ctrl_cmds(fp, tb2[i], ctrl_v)) {
247 					fprintf(fp, "Error printing command\n");
248 				}
249 				/* for next command */
250 				fprintf(fp,"\n");
251 			}
252 		}
253 
254 		/* end of family::cmds definitions .. */
255 		fprintf(fp,"\n");
256 	}
257 
258 	if (tb[CTRL_ATTR_MCAST_GROUPS]) {
259 		struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
260 		int i;
261 
262 		parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
263 				    tb[CTRL_ATTR_MCAST_GROUPS]);
264 		fprintf(fp, "\tmulticast groups:\n");
265 
266 		for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
267 			if (tb2[i]) {
268 				fprintf(fp, "\t\t#%d: ", i);
269 				if (0 > print_ctrl_grp(fp, tb2[i], ctrl_v))
270 					fprintf(fp, "Error printing group\n");
271 				/* for next group */
272 				fprintf(fp,"\n");
273 			}
274 		}
275 
276 		/* end of family::groups definitions .. */
277 		fprintf(fp,"\n");
278 	}
279 
280 	fflush(fp);
281 	return 0;
282 }
283 
ctrl_list(int cmd,int argc,char ** argv)284 static int ctrl_list(int cmd, int argc, char **argv)
285 {
286 	struct rtnl_handle rth;
287 	struct nlmsghdr *nlh;
288 	struct genlmsghdr *ghdr;
289 	int ret = -1;
290 	char d[GENL_NAMSIZ];
291 	struct {
292 		struct nlmsghdr         n;
293 		char                    buf[4096];
294 	} req;
295 
296 	memset(&req, 0, sizeof(req));
297 
298 	nlh = &req.n;
299 	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
300 	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
301 	nlh->nlmsg_type = GENL_ID_CTRL;
302 
303 	ghdr = NLMSG_DATA(&req.n);
304 	ghdr->cmd = CTRL_CMD_GETFAMILY;
305 
306 	if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
307 		fprintf(stderr, "Cannot open generic netlink socket\n");
308 		exit(1);
309 	}
310 
311 	if (cmd == CTRL_CMD_GETFAMILY) {
312 		if (argc != 2) {
313 			fprintf(stderr, "Wrong number of params\n");
314 			return -1;
315 		}
316 
317 		if (matches(*argv, "name") == 0) {
318 			NEXT_ARG();
319 			strncpy(d, *argv, sizeof (d) - 1);
320 			addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
321 				  d, strlen(d) + 1);
322 		} else if (matches(*argv, "id") == 0) {
323 			__u16 id;
324 			NEXT_ARG();
325 			if (get_u16(&id, *argv, 0)) {
326 				fprintf(stderr, "Illegal \"id\"\n");
327 				goto ctrl_done;
328 			}
329 
330 			addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2);
331 
332 		} else {
333 			fprintf(stderr, "Wrong params\n");
334 			goto ctrl_done;
335 		}
336 
337 		if (rtnl_talk(&rth, nlh, 0, 0, nlh) < 0) {
338 			fprintf(stderr, "Error talking to the kernel\n");
339 			goto ctrl_done;
340 		}
341 
342 		if (print_ctrl(NULL, nlh, (void *) stdout) < 0) {
343 			fprintf(stderr, "Dump terminated\n");
344 			goto ctrl_done;
345 		}
346 
347 	}
348 
349 	if (cmd == CTRL_CMD_UNSPEC) {
350 		nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
351 		nlh->nlmsg_seq = rth.dump = ++rth.seq;
352 
353 		if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
354 			perror("Failed to send dump request\n");
355 			goto ctrl_done;
356 		}
357 
358 		rtnl_dump_filter(&rth, print_ctrl, stdout);
359 
360         }
361 
362 	ret = 0;
363 ctrl_done:
364 	rtnl_close(&rth);
365 	return ret;
366 }
367 
ctrl_listen(int argc,char ** argv)368 static int ctrl_listen(int argc, char **argv)
369 {
370 	struct rtnl_handle rth;
371 
372 	if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) {
373 		fprintf(stderr, "Canot open generic netlink socket\n");
374 		return -1;
375 	}
376 
377 	if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
378 		return -1;
379 
380 	return 0;
381 }
382 
parse_ctrl(struct genl_util * a,int argc,char ** argv)383 static int parse_ctrl(struct genl_util *a, int argc, char **argv)
384 {
385 	argv++;
386 	if (--argc <= 0) {
387 		fprintf(stderr, "wrong controller params\n");
388 		return -1;
389 	}
390 
391 	if (matches(*argv, "monitor") == 0)
392 		return ctrl_listen(argc-1, argv+1);
393 	if (matches(*argv, "get") == 0)
394 		return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1);
395 	if (matches(*argv, "list") == 0 ||
396 	    matches(*argv, "show") == 0 ||
397 	    matches(*argv, "lst") == 0)
398 		return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1);
399 	if (matches(*argv, "help") == 0)
400 		return usage();
401 
402 	fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl -help\".\n",
403 		*argv);
404 
405 	return -1;
406 }
407 
408 struct genl_util ctrl_genl_util = {
409 	.name = "ctrl",
410 	.parse_genlopt = parse_ctrl,
411 	.print_genlopt = print_ctrl,
412 };
413