1 /*
2 * msgbuff.c - netlink message buffer
3 *
4 * Data structures and code for flexible message buffer abstraction.
5 */
6
7 #include <string.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11
12 #include "../internal.h"
13 #include "netlink.h"
14 #include "msgbuff.h"
15
16 #define MAX_MSG_SIZE (4 << 20) /* 4 MB */
17
18 /**
19 * msgbuff_realloc() - reallocate buffer if needed
20 * @msgbuff: message buffer
21 * @new_size: requested minimum size (add MNL_SOCKET_BUFFER_SIZE if zero)
22 *
23 * Make sure allocated buffer has size at least @new_size. If @new_size is
24 * shorter than current size, do nothing. If @new_size is 0, grow buffer by
25 * MNL_SOCKET_BUFFER_SIZE. Fail if new size would exceed MAX_MSG_SIZE.
26 *
27 * Return: 0 on success or negative error code
28 */
msgbuff_realloc(struct nl_msg_buff * msgbuff,unsigned int new_size)29 int msgbuff_realloc(struct nl_msg_buff *msgbuff, unsigned int new_size)
30 {
31 unsigned int nlhdr_off, genlhdr_off, payload_off;
32 unsigned int old_size = msgbuff->size;
33 char *nbuff;
34
35 nlhdr_off = (char *)msgbuff->nlhdr - msgbuff->buff;
36 genlhdr_off = (char *)msgbuff->genlhdr - msgbuff->buff;
37 payload_off = (char *)msgbuff->payload - msgbuff->buff;
38
39 if (!new_size)
40 new_size = old_size + MNL_SOCKET_BUFFER_SIZE;
41 if (new_size <= old_size)
42 return 0;
43 if (new_size > MAX_MSG_SIZE)
44 return -EMSGSIZE;
45 nbuff = realloc(msgbuff->buff, new_size);
46 if (!nbuff) {
47 msgbuff->buff = NULL;
48 msgbuff->size = 0;
49 msgbuff->left = 0;
50 return -ENOMEM;
51 }
52 if (nbuff != msgbuff->buff) {
53 if (new_size > old_size)
54 memset(nbuff + old_size, '\0', new_size - old_size);
55 msgbuff->nlhdr = (struct nlmsghdr *)(nbuff + nlhdr_off);
56 msgbuff->genlhdr = (struct genlmsghdr *)(nbuff + genlhdr_off);
57 msgbuff->payload = nbuff + payload_off;
58 msgbuff->buff = nbuff;
59 }
60 msgbuff->size = new_size;
61 msgbuff->left += (new_size - old_size);
62
63 return 0;
64 }
65
66 /**
67 * msgbuff_append() - add contents of another message buffer
68 * @dest: target message buffer
69 * @src: source message buffer
70 *
71 * Append contents of @src at the end of @dest. Fail if target buffer cannot
72 * be reallocated to sufficient size.
73 *
74 * Return: 0 on success or negative error code.
75 */
msgbuff_append(struct nl_msg_buff * dest,struct nl_msg_buff * src)76 int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src)
77 {
78 unsigned int src_len = mnl_nlmsg_get_payload_len(src->nlhdr);
79 unsigned int dest_len = MNL_ALIGN(msgbuff_len(dest));
80 int ret;
81
82 src_len -= GENL_HDRLEN;
83 ret = msgbuff_realloc(dest, dest_len + src_len);
84 if (ret < 0)
85 return ret;
86 memcpy(mnl_nlmsg_get_payload_tail(dest->nlhdr), src->payload, src_len);
87 msgbuff_reset(dest, dest_len + src_len);
88
89 return 0;
90 }
91
92 /**
93 * ethnla_put - write a netlink attribute to message buffer
94 * @msgbuff: message buffer
95 * @type: attribute type
96 * @len: attribute payload length
97 * @data: attribute payload
98 *
99 * Appends a netlink attribute with header to message buffer, reallocates
100 * if needed. This is mostly used via specific ethnla_put_* wrappers for
101 * basic data types.
102 *
103 * Return: false on success, true on error (reallocation failed)
104 */
ethnla_put(struct nl_msg_buff * msgbuff,uint16_t type,size_t len,const void * data)105 bool ethnla_put(struct nl_msg_buff *msgbuff, uint16_t type, size_t len,
106 const void *data)
107 {
108 struct nlmsghdr *nlhdr = msgbuff->nlhdr;
109
110 while (!mnl_attr_put_check(nlhdr, msgbuff->left, type, len, data)) {
111 int ret = msgbuff_realloc(msgbuff, 0);
112
113 if (ret < 0)
114 return true;
115 }
116
117 return false;
118 }
119
120 /**
121 * ethnla_nest_start - start a nested attribute
122 * @msgbuff: message buffer
123 * @type: nested attribute type (NLA_F_NESTED is added automatically)
124 *
125 * Return: pointer to the nest attribute or null of error
126 */
ethnla_nest_start(struct nl_msg_buff * msgbuff,uint16_t type)127 struct nlattr *ethnla_nest_start(struct nl_msg_buff *msgbuff, uint16_t type)
128 {
129 struct nlmsghdr *nlhdr = msgbuff->nlhdr;
130 struct nlattr *attr;
131
132 do {
133 attr = mnl_attr_nest_start_check(nlhdr, msgbuff->left, type);
134 if (attr)
135 return attr;
136 } while (msgbuff_realloc(msgbuff, 0) == 0);
137
138 return NULL;
139 }
140
141 /**
142 * ethnla_fill_header() - write standard ethtool request header to message
143 * @msgbuff: message buffer
144 * @type: attribute type for header nest
145 * @devname: device name (NULL to omit)
146 * @flags: request flags (omitted if 0)
147 *
148 * Return: pointer to the nest attribute or null of error
149 */
ethnla_fill_header(struct nl_msg_buff * msgbuff,uint16_t type,const char * devname,uint32_t flags)150 bool ethnla_fill_header(struct nl_msg_buff *msgbuff, uint16_t type,
151 const char *devname, uint32_t flags)
152 {
153 struct nlattr *nest;
154
155 nest = ethnla_nest_start(msgbuff, type);
156 if (!nest)
157 return true;
158
159 if ((devname &&
160 ethnla_put_strz(msgbuff, ETHTOOL_A_HEADER_DEV_NAME, devname)) ||
161 (flags &&
162 ethnla_put_u32(msgbuff, ETHTOOL_A_HEADER_FLAGS, flags)))
163 goto err;
164
165 ethnla_nest_end(msgbuff, nest);
166 return false;
167
168 err:
169 ethnla_nest_cancel(msgbuff, nest);
170 return true;
171 }
172
173 /**
174 * __msg_init() - init a genetlink message, fill netlink and genetlink header
175 * @msgbuff: message buffer
176 * @family: genetlink family
177 * @cmd: genetlink command (genlmsghdr::cmd)
178 * @flags: netlink flags (nlmsghdr::nlmsg_flags)
179 * @version: genetlink family version (genlmsghdr::version)
180 *
181 * Initialize a new genetlink message, fill netlink and genetlink header and
182 * set pointers in struct nl_msg_buff.
183 *
184 * Return: 0 on success or negative error code.
185 */
__msg_init(struct nl_msg_buff * msgbuff,int family,int cmd,unsigned int flags,int version)186 int __msg_init(struct nl_msg_buff *msgbuff, int family, int cmd,
187 unsigned int flags, int version)
188 {
189 struct nlmsghdr *nlhdr;
190 struct genlmsghdr *genlhdr;
191 int ret;
192
193 ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE);
194 if (ret < 0)
195 return ret;
196 memset(msgbuff->buff, '\0', NLMSG_HDRLEN + GENL_HDRLEN);
197
198 nlhdr = mnl_nlmsg_put_header(msgbuff->buff);
199 nlhdr->nlmsg_type = family;
200 nlhdr->nlmsg_flags = flags;
201 msgbuff->nlhdr = nlhdr;
202
203 genlhdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*genlhdr));
204 genlhdr->cmd = cmd;
205 genlhdr->version = version;
206 msgbuff->genlhdr = genlhdr;
207
208 msgbuff->payload = mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN);
209
210 return 0;
211 }
212
213 /**
214 * msg_init() - init an ethtool netlink message
215 * @msgbuff: message buffer
216 * @cmd: genetlink command (genlmsghdr::cmd)
217 * @flags: netlink flags (nlmsghdr::nlmsg_flags)
218 *
219 * Initialize a new ethtool netlink message, fill netlink and genetlink header
220 * and set pointers in struct nl_msg_buff.
221 *
222 * Return: 0 on success or negative error code.
223 */
msg_init(struct nl_context * nlctx,struct nl_msg_buff * msgbuff,int cmd,unsigned int flags)224 int msg_init(struct nl_context *nlctx, struct nl_msg_buff *msgbuff, int cmd,
225 unsigned int flags)
226 {
227 return __msg_init(msgbuff, nlctx->ethnl_fam, cmd, flags,
228 ETHTOOL_GENL_VERSION);
229 }
230
231 /**
232 * msgbuff_init() - initialize a message buffer
233 * @msgbuff: message buffer
234 *
235 * Initialize a message buffer structure before first use. Buffer length is
236 * set to zero and the buffer is not allocated until the first call to
237 * msgbuff_reallocate().
238 */
msgbuff_init(struct nl_msg_buff * msgbuff)239 void msgbuff_init(struct nl_msg_buff *msgbuff)
240 {
241 memset(msgbuff, '\0', sizeof(*msgbuff));
242 }
243
244 /**
245 * msg_done() - destroy a message buffer
246 * @msgbuff: message buffer
247 *
248 * Free the buffer and reset size and remaining size.
249 */
msgbuff_done(struct nl_msg_buff * msgbuff)250 void msgbuff_done(struct nl_msg_buff *msgbuff)
251 {
252 free(msgbuff->buff);
253 msgbuff->buff = NULL;
254 msgbuff->size = 0;
255 msgbuff->left = 0;
256 }
257