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, 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 110 errout: 111 rtnl_close(&rth); 112 return ret; 113 } 114 115 static 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, 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 285 static 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 291 static 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; 370 ctrl_done: 371 rtnl_close(&rth); 372 return ret; 373 } 374 375 static 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 390 static 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 415 struct genl_util ctrl_genl_util = { 416 .name = "ctrl", 417 .parse_genlopt = parse_ctrl, 418 .print_genlopt = print_ctrl2, 419 }; 420