]>
git.proxmox.com Git - mirror_iproute2.git/blob - devlink/mnlg.c
2 * mnlg.c Generic Netlink helpers for libmnl
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.
9 * Authors: Jiri Pirko <jiri@mellanox.com>
18 #include <libmnl/libmnl.h>
19 #include <linux/genetlink.h>
21 #include "libnetlink.h"
26 struct mnl_socket
*nl
;
34 static struct nlmsghdr
*__mnlg_msg_prepare(struct mnlg_socket
*nlg
, uint8_t cmd
,
35 uint16_t flags
, uint32_t id
,
39 struct genlmsghdr
*genl
;
41 nlh
= mnl_nlmsg_put_header(nlg
->buf
);
43 nlh
->nlmsg_flags
= flags
;
44 nlg
->seq
= time(NULL
);
45 nlh
->nlmsg_seq
= nlg
->seq
;
47 genl
= mnl_nlmsg_put_extra_header(nlh
, sizeof(struct genlmsghdr
));
49 genl
->version
= version
;
54 struct nlmsghdr
*mnlg_msg_prepare(struct mnlg_socket
*nlg
, uint8_t cmd
,
57 return __mnlg_msg_prepare(nlg
, cmd
, flags
, nlg
->id
, nlg
->version
);
60 int mnlg_socket_send(struct mnlg_socket
*nlg
, const struct nlmsghdr
*nlh
)
62 return mnl_socket_sendto(nlg
->nl
, nlh
, nlh
->nlmsg_len
);
65 static int mnlg_cb_noop(const struct nlmsghdr
*nlh
, void *data
)
70 static int mnlg_cb_error(const struct nlmsghdr
*nlh
, void *data
)
72 const struct nlmsgerr
*err
= mnl_nlmsg_get_payload(nlh
);
74 /* Netlink subsystems returns the errno value with different signess */
80 if (nl_dump_ext_ack(nlh
, NULL
))
83 return err
->error
== 0 ? MNL_CB_STOP
: MNL_CB_ERROR
;
86 static int mnlg_cb_stop(const struct nlmsghdr
*nlh
, void *data
)
88 int len
= *(int *)NLMSG_DATA(nlh
);
92 nl_dump_ext_ack_done(nlh
, len
);
98 static mnl_cb_t mnlg_cb_array
[NLMSG_MIN_TYPE
] = {
99 [NLMSG_NOOP
] = mnlg_cb_noop
,
100 [NLMSG_ERROR
] = mnlg_cb_error
,
101 [NLMSG_DONE
] = mnlg_cb_stop
,
102 [NLMSG_OVERRUN
] = mnlg_cb_noop
,
105 int mnlg_socket_recv_run(struct mnlg_socket
*nlg
, mnl_cb_t data_cb
, void *data
)
110 err
= mnl_socket_recvfrom(nlg
->nl
, nlg
->buf
,
111 MNL_SOCKET_BUFFER_SIZE
);
114 err
= mnl_cb_run2(nlg
->buf
, err
, nlg
->seq
, nlg
->portid
,
115 data_cb
, data
, mnlg_cb_array
,
116 ARRAY_SIZE(mnlg_cb_array
));
128 static int parse_mc_grps_cb(const struct nlattr
*attr
, void *data
)
130 const struct nlattr
**tb
= data
;
131 int type
= mnl_attr_get_type(attr
);
133 if (mnl_attr_type_valid(attr
, CTRL_ATTR_MCAST_GRP_MAX
) < 0)
137 case CTRL_ATTR_MCAST_GRP_ID
:
138 if (mnl_attr_validate(attr
, MNL_TYPE_U32
) < 0)
141 case CTRL_ATTR_MCAST_GRP_NAME
:
142 if (mnl_attr_validate(attr
, MNL_TYPE_STRING
) < 0)
150 static void parse_genl_mc_grps(struct nlattr
*nested
,
151 struct group_info
*group_info
)
156 mnl_attr_for_each_nested(pos
, nested
) {
157 struct nlattr
*tb
[CTRL_ATTR_MCAST_GRP_MAX
+ 1] = {};
159 mnl_attr_parse_nested(pos
, parse_mc_grps_cb
, tb
);
160 if (!tb
[CTRL_ATTR_MCAST_GRP_NAME
] ||
161 !tb
[CTRL_ATTR_MCAST_GRP_ID
])
164 name
= mnl_attr_get_str(tb
[CTRL_ATTR_MCAST_GRP_NAME
]);
165 if (strcmp(name
, group_info
->name
) != 0)
168 group_info
->id
= mnl_attr_get_u32(tb
[CTRL_ATTR_MCAST_GRP_ID
]);
169 group_info
->found
= true;
173 static int get_group_id_attr_cb(const struct nlattr
*attr
, void *data
)
175 const struct nlattr
**tb
= data
;
176 int type
= mnl_attr_get_type(attr
);
178 if (mnl_attr_type_valid(attr
, CTRL_ATTR_MAX
) < 0)
181 if (type
== CTRL_ATTR_MCAST_GROUPS
&&
182 mnl_attr_validate(attr
, MNL_TYPE_NESTED
) < 0)
188 static int get_group_id_cb(const struct nlmsghdr
*nlh
, void *data
)
190 struct group_info
*group_info
= data
;
191 struct nlattr
*tb
[CTRL_ATTR_MAX
+ 1] = {};
192 struct genlmsghdr
*genl
= mnl_nlmsg_get_payload(nlh
);
194 mnl_attr_parse(nlh
, sizeof(*genl
), get_group_id_attr_cb
, tb
);
195 if (!tb
[CTRL_ATTR_MCAST_GROUPS
])
197 parse_genl_mc_grps(tb
[CTRL_ATTR_MCAST_GROUPS
], group_info
);
201 int mnlg_socket_group_add(struct mnlg_socket
*nlg
, const char *group_name
)
203 struct nlmsghdr
*nlh
;
204 struct group_info group_info
;
207 nlh
= __mnlg_msg_prepare(nlg
, CTRL_CMD_GETFAMILY
,
208 NLM_F_REQUEST
| NLM_F_ACK
, GENL_ID_CTRL
, 1);
209 mnl_attr_put_u16(nlh
, CTRL_ATTR_FAMILY_ID
, nlg
->id
);
211 err
= mnlg_socket_send(nlg
, nlh
);
215 group_info
.found
= false;
216 group_info
.name
= group_name
;
217 err
= mnlg_socket_recv_run(nlg
, get_group_id_cb
, &group_info
);
221 if (!group_info
.found
) {
226 err
= mnl_socket_setsockopt(nlg
->nl
, NETLINK_ADD_MEMBERSHIP
,
227 &group_info
.id
, sizeof(group_info
.id
));
234 static int get_family_id_attr_cb(const struct nlattr
*attr
, void *data
)
236 const struct nlattr
**tb
= data
;
237 int type
= mnl_attr_get_type(attr
);
239 if (mnl_attr_type_valid(attr
, CTRL_ATTR_MAX
) < 0)
242 if (type
== CTRL_ATTR_FAMILY_ID
&&
243 mnl_attr_validate(attr
, MNL_TYPE_U16
) < 0)
249 static int get_family_id_cb(const struct nlmsghdr
*nlh
, void *data
)
251 uint32_t *p_id
= data
;
252 struct nlattr
*tb
[CTRL_ATTR_MAX
+ 1] = {};
253 struct genlmsghdr
*genl
= mnl_nlmsg_get_payload(nlh
);
255 mnl_attr_parse(nlh
, sizeof(*genl
), get_family_id_attr_cb
, tb
);
256 if (!tb
[CTRL_ATTR_FAMILY_ID
])
258 *p_id
= mnl_attr_get_u16(tb
[CTRL_ATTR_FAMILY_ID
]);
262 struct mnlg_socket
*mnlg_socket_open(const char *family_name
, uint8_t version
)
264 struct mnlg_socket
*nlg
;
265 struct nlmsghdr
*nlh
;
269 nlg
= malloc(sizeof(*nlg
));
273 nlg
->buf
= malloc(MNL_SOCKET_BUFFER_SIZE
);
277 nlg
->nl
= mnl_socket_open(NETLINK_GENERIC
);
279 goto err_mnl_socket_open
;
281 /* Older kernels may no support capped/extended ACK reporting */
282 mnl_socket_setsockopt(nlg
->nl
, NETLINK_CAP_ACK
, &one
, sizeof(one
));
283 mnl_socket_setsockopt(nlg
->nl
, NETLINK_EXT_ACK
, &one
, sizeof(one
));
285 err
= mnl_socket_bind(nlg
->nl
, 0, MNL_SOCKET_AUTOPID
);
287 goto err_mnl_socket_bind
;
289 nlg
->portid
= mnl_socket_get_portid(nlg
->nl
);
291 nlh
= __mnlg_msg_prepare(nlg
, CTRL_CMD_GETFAMILY
,
292 NLM_F_REQUEST
| NLM_F_ACK
, GENL_ID_CTRL
, 1);
293 mnl_attr_put_strz(nlh
, CTRL_ATTR_FAMILY_NAME
, family_name
);
295 err
= mnlg_socket_send(nlg
, nlh
);
297 goto err_mnlg_socket_send
;
299 err
= mnlg_socket_recv_run(nlg
, get_family_id_cb
, &nlg
->id
);
301 goto err_mnlg_socket_recv_run
;
303 nlg
->version
= version
;
306 err_mnlg_socket_recv_run
:
307 err_mnlg_socket_send
:
309 mnl_socket_close(nlg
->nl
);
317 void mnlg_socket_close(struct mnlg_socket
*nlg
)
319 mnl_socket_close(nlg
->nl
);