xref: /aosp_15_r20/external/ethtool/netlink/msgbuff.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
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