1 /* 2 * mnlg.c Generic Netlink helpers for libmnl 3 * 4 * This program is free software; you can redistribute 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: Jiri Pirko <jiri (at) mellanox.com> 10 */ 11 12 #include <stdlib.h> 13 #include <stdbool.h> 14 #include <string.h> 15 #include <errno.h> 16 #include <unistd.h> 17 #include <time.h> 18 #include <libmnl/libmnl.h> 19 #include <linux/genetlink.h> 20 21 #include "mnlg.h" 22 23 struct mnlg_socket { 24 struct mnl_socket *nl; 25 char *buf; 26 uint32_t id; 27 uint8_t version; 28 unsigned int seq; 29 unsigned int portid; 30 }; 31 32 static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, 33 uint16_t flags, uint32_t id, 34 uint8_t version) 35 { 36 struct nlmsghdr *nlh; 37 struct genlmsghdr *genl; 38 39 nlh = mnl_nlmsg_put_header(nlg->buf); 40 nlh->nlmsg_type = id; 41 nlh->nlmsg_flags = flags; 42 nlg->seq = time(NULL); 43 nlh->nlmsg_seq = nlg->seq; 44 45 genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); 46 genl->cmd = cmd; 47 genl->version = version; 48 49 return nlh; 50 } 51 52 struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, 53 uint16_t flags) 54 { 55 return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version); 56 } 57 58 int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh) 59 { 60 return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); 61 } 62 63 int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) 64 { 65 int err; 66 67 do { 68 err = mnl_socket_recvfrom(nlg->nl, nlg->buf, 69 MNL_SOCKET_BUFFER_SIZE); 70 if (err <= 0) 71 break; 72 err = mnl_cb_run(nlg->buf, err, nlg->seq, nlg->portid, 73 data_cb, data); 74 } while (err > 0); 75 76 return err; 77 } 78 79 struct group_info { 80 bool found; 81 uint32_t id; 82 const char *name; 83 }; 84 85 static int parse_mc_grps_cb(const struct nlattr *attr, void *data) 86 { 87 const struct nlattr **tb = data; 88 int type = mnl_attr_get_type(attr); 89 90 if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) 91 return MNL_CB_OK; 92 93 switch (type) { 94 case CTRL_ATTR_MCAST_GRP_ID: 95 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) 96 return MNL_CB_ERROR; 97 break; 98 case CTRL_ATTR_MCAST_GRP_NAME: 99 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) 100 return MNL_CB_ERROR; 101 break; 102 } 103 tb[type] = attr; 104 return MNL_CB_OK; 105 } 106 107 static void parse_genl_mc_grps(struct nlattr *nested, 108 struct group_info *group_info) 109 { 110 struct nlattr *pos; 111 const char *name; 112 113 mnl_attr_for_each_nested(pos, nested) { 114 struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; 115 116 mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb); 117 if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || 118 !tb[CTRL_ATTR_MCAST_GRP_ID]) 119 continue; 120 121 name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]); 122 if (strcmp(name, group_info->name) != 0) 123 continue; 124 125 group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); 126 group_info->found = true; 127 } 128 } 129 130 static int get_group_id_attr_cb(const struct nlattr *attr, void *data) 131 { 132 const struct nlattr **tb = data; 133 int type = mnl_attr_get_type(attr); 134 135 if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) 136 return MNL_CB_ERROR; 137 138 if (type == CTRL_ATTR_MCAST_GROUPS && 139 mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) 140 return MNL_CB_ERROR; 141 tb[type] = attr; 142 return MNL_CB_OK; 143 } 144 145 static int get_group_id_cb(const struct nlmsghdr *nlh, void *data) 146 { 147 struct group_info *group_info = data; 148 struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; 149 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); 150 151 mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb); 152 if (!tb[CTRL_ATTR_MCAST_GROUPS]) 153 return MNL_CB_ERROR; 154 parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info); 155 return MNL_CB_OK; 156 } 157 158 int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name) 159 { 160 struct nlmsghdr *nlh; 161 struct group_info group_info; 162 int err; 163 164 nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, 165 NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); 166 mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, nlg->id); 167 168 err = mnlg_socket_send(nlg, nlh); 169 if (err < 0) 170 return err; 171 172 group_info.found = false; 173 group_info.name = group_name; 174 err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info); 175 if (err < 0) 176 return err; 177 178 if (!group_info.found) { 179 errno = ENOENT; 180 return -1; 181 } 182 183 err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP, 184 &group_info.id, sizeof(group_info.id)); 185 if (err < 0) 186 return err; 187 188 return 0; 189 } 190 191 static int get_family_id_attr_cb(const struct nlattr *attr, void *data) 192 { 193 const struct nlattr **tb = data; 194 int type = mnl_attr_get_type(attr); 195 196 if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) 197 return MNL_CB_ERROR; 198 199 if (type == CTRL_ATTR_FAMILY_ID && 200 mnl_attr_validate(attr, MNL_TYPE_U16) < 0) 201 return MNL_CB_ERROR; 202 tb[type] = attr; 203 return MNL_CB_OK; 204 } 205 206 static int get_family_id_cb(const struct nlmsghdr *nlh, void *data) 207 { 208 uint32_t *p_id = data; 209 struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; 210 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); 211 212 mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb); 213 if (!tb[CTRL_ATTR_FAMILY_ID]) 214 return MNL_CB_ERROR; 215 *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); 216 return MNL_CB_OK; 217 } 218 219 struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) 220 { 221 struct mnlg_socket *nlg; 222 struct nlmsghdr *nlh; 223 int err; 224 225 nlg = malloc(sizeof(*nlg)); 226 if (!nlg) 227 return NULL; 228 229 nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE); 230 if (!nlg->buf) 231 goto err_buf_alloc; 232 233 nlg->nl = mnl_socket_open(NETLINK_GENERIC); 234 if (!nlg->nl) 235 goto err_mnl_socket_open; 236 237 err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); 238 if (err < 0) 239 goto err_mnl_socket_bind; 240 241 nlg->portid = mnl_socket_get_portid(nlg->nl); 242 243 nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, 244 NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); 245 mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); 246 247 err = mnlg_socket_send(nlg, nlh); 248 if (err < 0) 249 goto err_mnlg_socket_send; 250 251 err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id); 252 if (err < 0) 253 goto err_mnlg_socket_recv_run; 254 255 nlg->version = version; 256 return nlg; 257 258 err_mnlg_socket_recv_run: 259 err_mnlg_socket_send: 260 err_mnl_socket_bind: 261 mnl_socket_close(nlg->nl); 262 err_mnl_socket_open: 263 free(nlg->buf); 264 err_buf_alloc: 265 free(nlg); 266 return NULL; 267 } 268 269 void mnlg_socket_close(struct mnlg_socket *nlg) 270 { 271 mnl_socket_close(nlg->nl); 272 free(nlg->buf); 273 free(nlg); 274 } 275