1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2023 Baptiste Daroussin <bapt@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted providing that the following conditions~ 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/module.h> 30 31 #include <stdint.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <err.h> 35 #include <stdio.h> 36 37 #include <netlink/netlink.h> 38 #include <netlink/netlink_generic.h> 39 #include <netlink/netlink_snl.h> 40 #include <netlink/netlink_snl_generic.h> 41 42 struct genl_ctrl_op { 43 uint32_t id; 44 uint32_t flags; 45 }; 46 47 struct genl_ctrl_ops { 48 uint32_t num_ops; 49 struct genl_ctrl_op **ops; 50 }; 51 52 #define _OUT(_field) offsetof(struct genl_ctrl_op, _field) 53 static struct snl_attr_parser _nla_p_getops[] = { 54 { .type = CTRL_ATTR_OP_ID, .off = _OUT(id), .cb = snl_attr_get_uint32}, 55 { .type = CTRL_ATTR_OP_FLAGS, .off = _OUT(flags), .cb = snl_attr_get_uint32 }, 56 }; 57 #undef _OUT 58 SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser, 59 sizeof(struct genl_ctrl_op), 60 _nla_p_getops, NULL); 61 62 struct genl_family { 63 uint16_t id; 64 char *name; 65 uint32_t version; 66 uint32_t hdrsize; 67 uint32_t max_attr; 68 struct snl_genl_ctrl_mcast_groups mcast_groups; 69 struct genl_ctrl_ops ops; 70 }; 71 72 #define _OUT(_field) offsetof(struct genl_family, _field) 73 static struct snl_attr_parser _nla_p_getfamily[] = { 74 { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(id), .cb = snl_attr_get_uint16 }, 75 { .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(name), .cb = snl_attr_get_string }, 76 { .type = CTRL_ATTR_VERSION, .off = _OUT(version), .cb = snl_attr_get_uint32 }, 77 { .type = CTRL_ATTR_VERSION, .off = _OUT(hdrsize), .cb = snl_attr_get_uint32 }, 78 { .type = CTRL_ATTR_MAXATTR, .off = _OUT(max_attr), .cb = snl_attr_get_uint32 }, 79 { 80 .type = CTRL_ATTR_OPS, 81 .off = _OUT(ops), 82 .cb = snl_attr_get_parray, 83 .arg = &genl_ctrl_op_parser, 84 }, 85 { 86 .type = CTRL_ATTR_MCAST_GROUPS, 87 .off = _OUT(mcast_groups), 88 .cb = snl_attr_get_parray, 89 .arg = &_genl_ctrl_mc_parser, 90 }, 91 }; 92 #undef _OUT 93 SNL_DECLARE_GENL_PARSER(genl_family_parser, _nla_p_getfamily); 94 95 static struct op_capability { 96 uint32_t flag; 97 const char *str; 98 } op_caps[] = { 99 { GENL_ADMIN_PERM, "requires admin permission" }, 100 { GENL_CMD_CAP_DO, "can modify" }, 101 { GENL_CMD_CAP_DUMP, "can get/dump" }, 102 { GENL_CMD_CAP_HASPOL, "has policy" }, 103 }; 104 105 static void 106 dump_operations(struct genl_ctrl_ops *ops) 107 { 108 if (ops->num_ops == 0) 109 return; 110 printf("\tsupported operations: \n"); 111 for (uint32_t i = 0; i < ops->num_ops; i++) { 112 printf("\t - ID: %#02x, Capabilities: %#02x (", 113 ops->ops[i]->id, 114 ops->ops[i]->flags); 115 for (size_t j = 0; j < nitems(op_caps); j++) 116 if ((ops->ops[i]->flags & op_caps[j].flag) == op_caps[j].flag) 117 printf("%s; ", op_caps[j].str); 118 printf("\b\b)\n"); 119 } 120 } 121 122 static void 123 dump_mcast_groups( struct snl_genl_ctrl_mcast_groups *mcast_groups) 124 { 125 if (mcast_groups->num_groups == 0) 126 return; 127 printf("\tmulticast groups: \n"); 128 for (uint32_t i = 0; i < mcast_groups->num_groups; i++) 129 printf("\t - ID: %#02x, Name: %s\n", 130 mcast_groups->groups[i]->mcast_grp_id, 131 mcast_groups->groups[i]->mcast_grp_name); 132 } 133 134 135 static void 136 dump_family(struct genl_family *family) 137 { 138 printf("Name: %s\n\tID: %#02hx, Version: %#02x, " 139 "header size: %d, max attributes: %d\n", 140 family->name, family->id, family->version, 141 family->hdrsize, family->max_attr); 142 dump_operations(&family->ops); 143 dump_mcast_groups(&family->mcast_groups); 144 } 145 146 int 147 main(int argc, char **argv __unused) 148 { 149 struct snl_state ss; 150 struct snl_writer nw; 151 struct nlmsghdr *hdr; 152 struct snl_errmsg_data e = {}; 153 uint32_t seq_id; 154 155 if (argc > 1) 156 errx(EXIT_FAILURE, "usage: genl does not accept any argument"); 157 if (modfind("netlink") == -1) 158 err(EXIT_FAILURE, "require netlink module to be loaded"); 159 160 if (!snl_init(&ss, NETLINK_GENERIC)) 161 err(EXIT_FAILURE, "snl_init()"); 162 163 snl_init_writer(&ss, &nw); 164 hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY); 165 if ((hdr = snl_finalize_msg(&nw)) == NULL) 166 err(EXIT_FAILURE, "snl_finalize_msg"); 167 seq_id = hdr->nlmsg_seq; 168 if (!snl_send_message(&ss, hdr)) 169 err(EXIT_FAILURE, "snl_send_message"); 170 171 while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) { 172 if (e.error != 0) { 173 err(EXIT_FAILURE, "Error reading generic netlink"); 174 } 175 struct genl_family family = {}; 176 if (snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &family)) 177 dump_family(&family); 178 } 179 180 return (EXIT_SUCCESS); 181 } 182