* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Authors: J Hadi Salim (hadi@cyberus.ca)
+ * Authors: J Hadi Salim (hadi@cyberus.ca)
+ * Johannes Berg (johannes@sipsolutions.net)
*/
#include <stdio.h>
#include "utils.h"
#include "genl_utils.h"
+#define GENL_MAX_FAM_OPS 256
+#define GENL_MAX_FAM_GRPS 256
+
static int usage(void)
{
fprintf(stderr,"Usage: ctrl <CMD>\n" \
addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
- if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
goto errout;
}
}
if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
- fprintf(stderr, "Unkown controller command %d\n", ghdr->cmd);
+ fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
goto errout;
}
goto errout;
}
- ret = *(__u16 *) RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
+ ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
}
errout:
return ret;
}
+void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
+{
+ fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
+ if (!fl) {
+ fprintf(fp, "\n");
+ return;
+ }
+ fprintf(fp, "\t\t ");
+
+ if (fl & GENL_ADMIN_PERM)
+ fprintf(fp, " requires admin permission;");
+ if (fl & GENL_CMD_CAP_DO)
+ fprintf(fp, " can doit;");
+ if (fl & GENL_CMD_CAP_DUMP)
+ fprintf(fp, " can dumpit;");
+ if (fl & GENL_CMD_CAP_HASPOL)
+ fprintf(fp, " has policy");
+
+ fprintf(fp, "\n");
+}
+
+static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
+{
+ struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg);
+ if (tb[CTRL_ATTR_OP_ID]) {
+ __u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]);
+ fprintf(fp, " ID-0x%x ",*id);
+ }
+ /* we are only gonna do this for newer version of the controller */
+ if (tb[CTRL_ATTR_OP_FLAGS] && ctrl_ver >= 0x2) {
+ __u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]);
+ print_ctrl_cmd_flags(fp, *fl);
+ }
+ return 0;
+
+}
+
+static int print_ctrl_grp(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
+{
+ struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
+ if (tb[2]) {
+ __u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]);
+ fprintf(fp, " ID-0x%x ",*id);
+ }
+ if (tb[1]) {
+ char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+ fprintf(fp, " name: %s ", name);
+ }
+ return 0;
+
+}
+
+/*
+ * The controller sends one nlmsg per family
+*/
static int print_ctrl(const struct sockaddr_nl *who, struct nlmsghdr *n,
void *arg)
{
int len = n->nlmsg_len;
struct rtattr *attrs;
FILE *fp = (FILE *) arg;
+ __u32 ctrl_v = 0x1;
if (n->nlmsg_type != GENL_ID_CTRL) {
fprintf(stderr, "Not a controller message, nlmsg_len=%d "
if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
ghdr->cmd != CTRL_CMD_DELFAMILY &&
- ghdr->cmd != CTRL_CMD_NEWFAMILY) {
- fprintf(stderr, "Unkown controller command %d\n", ghdr->cmd);
+ ghdr->cmd != CTRL_CMD_NEWFAMILY &&
+ ghdr->cmd != CTRL_CMD_NEWMCAST_GRP &&
+ ghdr->cmd != CTRL_CMD_DELMCAST_GRP) {
+ fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
return 0;
}
if (tb[CTRL_ATTR_FAMILY_NAME]) {
char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
- fprintf(fp, "Name: %s\n",name);
+ fprintf(fp, "\nName: %s\n",name);
}
if (tb[CTRL_ATTR_FAMILY_ID]) {
__u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
- fprintf(fp, "ID: 0x%x\n",*id);
+ fprintf(fp, "\tID: 0x%x ",*id);
+ }
+ if (tb[CTRL_ATTR_VERSION]) {
+ __u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
+ fprintf(fp, " Version: 0x%x ",*v);
+ ctrl_v = *v;
+ }
+ if (tb[CTRL_ATTR_HDRSIZE]) {
+ __u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
+ fprintf(fp, " header size: %d ",*h);
+ }
+ if (tb[CTRL_ATTR_MAXATTR]) {
+ __u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
+ fprintf(fp, " max attribs: %d ",*ma);
+ }
+ /* end of family definitions .. */
+ fprintf(fp,"\n");
+ if (tb[CTRL_ATTR_OPS]) {
+ struct rtattr *tb2[GENL_MAX_FAM_OPS];
+ int i=0;
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
+ fprintf(fp, "\tcommands supported: \n");
+ for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
+ if (tb2[i]) {
+ fprintf(fp, "\t\t#%d: ", i);
+ if (0 > print_ctrl_cmds(fp, tb2[i], ctrl_v)) {
+ fprintf(fp, "Error printing command\n");
+ }
+ /* for next command */
+ fprintf(fp,"\n");
+ }
+ }
+
+ /* end of family::cmds definitions .. */
+ fprintf(fp,"\n");
+ }
+
+ if (tb[CTRL_ATTR_MCAST_GROUPS]) {
+ struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
+ int i;
+
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
+ tb[CTRL_ATTR_MCAST_GROUPS]);
+ fprintf(fp, "\tmulticast groups:\n");
+
+ for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
+ if (tb2[i]) {
+ fprintf(fp, "\t\t#%d: ", i);
+ if (0 > print_ctrl_grp(fp, tb2[i], ctrl_v))
+ fprintf(fp, "Error printing group\n");
+ /* for next group */
+ fprintf(fp,"\n");
+ }
+ }
+
+ /* end of family::groups definitions .. */
+ fprintf(fp,"\n");
}
fflush(fp);
goto ctrl_done;
}
- if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
goto ctrl_done;
}
nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
nlh->nlmsg_seq = rth.dump = ++rth.seq;
- if (rtnl_send(&rth, (const char *) nlh, nlh->nlmsg_len) < 0) {
+ if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
perror("Failed to send dump request\n");
goto ctrl_done;
}
- rtnl_dump_filter(&rth, print_ctrl, stdout, NULL, NULL);
+ rtnl_dump_filter(&rth, print_ctrl, stdout);
}