Home | History | Annotate | Download | only in genl
      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 (at) cyberus.ca)
     10  *		Johannes Berg (johannes (at) 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 
     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 
     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, NULL, NULL) < 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 = *(__u16 *) RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
    108 	}
    109 
    110 errout:
    111 	rtnl_close(&rth);
    112 	return ret;
    113 }
    114 
    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 
    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 
    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 */
    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 
    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, NULL, NULL) < 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, (const char *) 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, NULL, NULL);
    359 
    360         }
    361 
    362 	ret = 0;
    363 ctrl_done:
    364 	rtnl_close(&rth);
    365 	return ret;
    366 }
    367 
    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 
    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