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) < 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 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) < 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 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