xref: /aosp_15_r20/external/libnl/lib/route/link/bonding.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2011-2013 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup link
8  * @defgroup bonding Bonding
9  *
10  * @details
11  * \b Link Type Name: "bond"
12  *
13  * @route_doc{link_bonding, Bonding Documentation}
14  * @{
15  */
16 
17 #include "nl-default.h"
18 
19 #include <netlink/netlink.h>
20 #include <netlink/route/link/bonding.h>
21 
22 #include "nl-route.h"
23 #include "link-api.h"
24 
25 #define BOND_HAS_MODE		(1 << 0)
26 #define BOND_HAS_ACTIVE_SLAVE	(1 << 1)
27 #define BOND_HAS_HASHING_TYPE	(1 << 2)
28 #define BOND_HAS_MIIMON		(1 << 3)
29 #define BOND_HAS_MIN_LINKS	(1 << 4)
30 
31 struct bond_info {
32 	uint8_t bn_mode;
33 	uint8_t hashing_type;
34 	uint32_t ifindex;
35 	uint32_t bn_mask;
36 	uint32_t miimon;
37 	uint32_t min_links;
38 };
39 
bond_info_alloc(struct rtnl_link * link)40 static int bond_info_alloc(struct rtnl_link *link)
41 {
42 	struct bond_info *bn;
43 
44 	if (link->l_info)
45 		memset(link->l_info, 0, sizeof(*bn));
46 	else {
47 		bn = calloc(1, sizeof(*bn));
48 		if (!bn)
49 			return -NLE_NOMEM;
50 
51 		link->l_info = bn;
52 	}
53 
54 	return 0;
55 }
56 
bond_info_free(struct rtnl_link * link)57 static void bond_info_free(struct rtnl_link *link)
58 {
59 	_nl_clear_free(&link->l_info);
60 }
61 
bond_put_attrs(struct nl_msg * msg,struct rtnl_link * link)62 static int bond_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
63 {
64 	struct bond_info *bn = link->l_info;
65 	struct nlattr *data;
66 
67 	data = nla_nest_start(msg, IFLA_INFO_DATA);
68 	if (!data)
69 		return -NLE_MSGSIZE;
70 	if (bn->bn_mask & BOND_HAS_MODE)
71 		NLA_PUT_U8(msg, IFLA_BOND_MODE, bn->bn_mode);
72 
73 	if (bn->bn_mask & BOND_HAS_ACTIVE_SLAVE)
74 		NLA_PUT_U32(msg, IFLA_BOND_ACTIVE_SLAVE, bn->ifindex);
75 
76 	if (bn->bn_mask & BOND_HAS_HASHING_TYPE)
77 		NLA_PUT_U8(msg, IFLA_BOND_XMIT_HASH_POLICY, bn->hashing_type);
78 
79 	if (bn->bn_mask & BOND_HAS_MIIMON)
80 		NLA_PUT_U32(msg, IFLA_BOND_MIIMON, bn->miimon);
81 
82 	if (bn->bn_mask & BOND_HAS_MIN_LINKS)
83 		NLA_PUT_U32(msg, IFLA_BOND_MIN_LINKS, bn->min_links);
84 
85 	nla_nest_end(msg, data);
86 	return 0;
87 
88 nla_put_failure:
89 	nla_nest_cancel(msg, data);
90 	return -NLE_MSGSIZE;
91 }
92 
93 static struct rtnl_link_info_ops bonding_info_ops = {
94 	.io_name		= "bond",
95 	.io_alloc		= bond_info_alloc,
96 	.io_put_attrs		= bond_put_attrs,
97 	.io_free		= bond_info_free,
98 };
99 
100 #define IS_BOND_INFO_ASSERT(link)                                                    \
101 	do {                                                                         \
102 		if (link->l_info_ops != &bonding_info_ops) {                         \
103 			APPBUG("Link is not a bond link. Set type \"bond\" first."); \
104 		}                                                                    \
105 	} while (0)
106 
107 /**
108  * Set active slave for bond
109  * @arg link            Link object of type bond
110  * @arg active          ifindex of active slave to set
111  *
112  * @return void
113  */
rtnl_link_bond_set_activeslave(struct rtnl_link * link,int active_slave)114 void rtnl_link_bond_set_activeslave(struct rtnl_link *link, int active_slave)
115 {
116 	struct bond_info *bn = link->l_info;
117 
118 	IS_BOND_INFO_ASSERT(link);
119 
120 	bn->ifindex = active_slave;
121 
122 	bn->bn_mask |= BOND_HAS_ACTIVE_SLAVE;
123 }
124 
125 /**
126  * Set bond mode
127  * @arg link            Link object of type bond
128  * @arg mode            bond mode to set
129  *
130  * @return void
131  */
rtnl_link_bond_set_mode(struct rtnl_link * link,uint8_t mode)132 void rtnl_link_bond_set_mode(struct rtnl_link *link, uint8_t mode)
133 {
134 	struct bond_info *bn = link->l_info;
135 
136 	IS_BOND_INFO_ASSERT(link);
137 
138 	bn->bn_mode = mode;
139 
140 	bn->bn_mask |= BOND_HAS_MODE;
141 }
142 
143 /**
144  * Set hashing type
145  * @arg link            Link object of type bond
146  * @arg type            bond hashing type to set
147  *
148  * @return void
149  */
rtnl_link_bond_set_hashing_type(struct rtnl_link * link,uint8_t type)150 void rtnl_link_bond_set_hashing_type (struct rtnl_link *link, uint8_t type)
151 {
152 	struct bond_info *bn = link->l_info;
153 
154 	IS_BOND_INFO_ASSERT(link);
155 
156 	bn->hashing_type = type;
157 
158 	bn->bn_mask |= BOND_HAS_HASHING_TYPE;
159 }
160 
161 /**
162  * Set MII monitoring interval
163  * @arg link            Link object of type bond
164  * @arg miimon          interval in milliseconds
165  *
166  * @return void
167  */
rtnl_link_bond_set_miimon(struct rtnl_link * link,uint32_t miimon)168 void rtnl_link_bond_set_miimon (struct rtnl_link *link, uint32_t miimon)
169 {
170 	struct bond_info *bn = link->l_info;
171 
172 	IS_BOND_INFO_ASSERT(link);
173 
174 	bn->miimon = miimon;
175 
176 	bn->bn_mask |= BOND_HAS_MIIMON;
177 }
178 
179 /**
180  * Set the minimum number of member ports that must be up before
181  * marking the bond device as up
182  * @arg link            Link object of type bond
183  * @arg min_links       Number of links
184  *
185  * @return void
186  */
rtnl_link_bond_set_min_links(struct rtnl_link * link,uint32_t min_links)187 void rtnl_link_bond_set_min_links (struct rtnl_link *link, uint32_t min_links)
188 {
189 	struct bond_info *bn = link->l_info;
190 
191 	IS_BOND_INFO_ASSERT(link);
192 
193 	bn->min_links = min_links;
194 
195 	bn->bn_mask |= BOND_HAS_MIN_LINKS;
196 }
197 
198 /**
199  * Allocate link object of type bond
200  *
201  * @return Allocated link object or NULL.
202  */
rtnl_link_bond_alloc(void)203 struct rtnl_link *rtnl_link_bond_alloc(void)
204 {
205 	struct rtnl_link *link;
206 
207 	if (!(link = rtnl_link_alloc()))
208 		return NULL;
209 
210 	if (rtnl_link_set_type(link, "bond") < 0) {
211 		rtnl_link_put(link);
212 		return NULL;
213 	}
214 
215 	return link;
216 }
217 
218 /**
219  * Create a new kernel bonding device
220  * @arg sock		netlink socket
221  * @arg name		name of bonding device or NULL
222  * @arg opts		bonding options (currently unused)
223  *
224  * Creates a new bonding device in the kernel. If no name is
225  * provided, the kernel will automatically pick a name of the
226  * form "type%d" (e.g. bond0, vlan1, etc.)
227  *
228  * The \a opts argument is currently unused. In the future, it
229  * may be used to carry additional bonding options to be set
230  * when creating the bonding device.
231  *
232  * @note When letting the kernel assign a name, it will become
233  *       difficult to retrieve the interface afterwards because
234  *       you have to guess the name the kernel has chosen. It is
235  *       therefore not recommended to not provide a device name.
236  *
237  * @see rtnl_link_bond_enslave()
238  * @see rtnl_link_bond_release()
239  *
240  * @return 0 on success or a negative error code
241  */
rtnl_link_bond_add(struct nl_sock * sock,const char * name,struct rtnl_link * opts)242 int rtnl_link_bond_add(struct nl_sock *sock, const char *name,
243 		       struct rtnl_link *opts)
244 {
245 	struct rtnl_link *link;
246 	int err;
247 
248 	if (!(link = rtnl_link_bond_alloc()))
249 		return -NLE_NOMEM;
250 
251 	if (!name && opts)
252 		name = rtnl_link_get_name(opts);
253 
254 	if (name)
255 		rtnl_link_set_name(link, name);
256 
257 	err = rtnl_link_add(sock, link, NLM_F_CREATE);
258 
259 	rtnl_link_put(link);
260 
261 	return err;
262 }
263 
264 /**
265  * Add a link to a bond (enslave)
266  * @arg sock		netlink socket
267  * @arg master		ifindex of bonding master
268  * @arg slave		ifindex of slave link to add to bond
269  *
270  * This function is identical to rtnl_link_bond_enslave() except that
271  * it takes interface indices instead of rtnl_link objcets.
272  *
273  * @see rtnl_link_bond_enslave()
274  *
275  * @return 0 on success or a negative error code.
276  */
rtnl_link_bond_enslave_ifindex(struct nl_sock * sock,int master,int slave)277 int rtnl_link_bond_enslave_ifindex(struct nl_sock *sock, int master,
278 				   int slave)
279 {
280 	struct rtnl_link *link;
281 	int err;
282 
283 	if (!(link = rtnl_link_bond_alloc()))
284 		return -NLE_NOMEM;
285 
286 	rtnl_link_set_ifindex(link, slave);
287 	rtnl_link_set_master(link, master);
288 
289 	if ((err = rtnl_link_change(sock, link, link, 0)) < 0)
290 		goto errout;
291 
292 	rtnl_link_put(link);
293 
294 	/*
295 	 * Due to the kernel not signaling whether this opertion is
296 	 * supported or not, we will retrieve the attribute to see  if the
297 	 * request was successful. If the master assigned remains unchanged
298 	 * we will return NLE_OPNOTSUPP to allow performing backwards
299 	 * compatibility of some sort.
300 	 */
301 	if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0)
302 		return err;
303 
304 	if (rtnl_link_get_master(link) != master)
305 		err = -NLE_OPNOTSUPP;
306 
307 errout:
308 	rtnl_link_put(link);
309 
310 	return err;
311 }
312 
313 /**
314  * Add a link to a bond (enslave)
315  * @arg sock		netlink socket
316  * @arg master		bonding master
317  * @arg slave		slave link to add to bond
318  *
319  * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to
320  * the master and sends the request via the specified netlink socket.
321  *
322  * @note The feature of enslaving/releasing via netlink has only been added
323  *       recently to the kernel (Feb 2011). Also, the kernel does not signal
324  *       if the operation is not supported. Therefore this function will
325  *       verify if the master assignment has changed and will return
326  *       -NLE_OPNOTSUPP if it did not.
327  *
328  * @see rtnl_link_bond_enslave_ifindex()
329  * @see rtnl_link_bond_release()
330  *
331  * @return 0 on success or a negative error code.
332  */
rtnl_link_bond_enslave(struct nl_sock * sock,struct rtnl_link * master,struct rtnl_link * slave)333 int rtnl_link_bond_enslave(struct nl_sock *sock, struct rtnl_link *master,
334 			   struct rtnl_link *slave)
335 {
336 	return rtnl_link_bond_enslave_ifindex(sock,
337 				rtnl_link_get_ifindex(master),
338 				rtnl_link_get_ifindex(slave));
339 }
340 
341 /**
342  * Release a link from a bond
343  * @arg sock		netlink socket
344  * @arg slave		slave link to be released
345  *
346  * This function is identical to rtnl_link_bond_release() except that
347  * it takes an interface index instead of a rtnl_link object.
348  *
349  * @see rtnl_link_bond_release()
350  *
351  * @return 0 on success or a negative error code.
352  */
rtnl_link_bond_release_ifindex(struct nl_sock * sock,int slave)353 int rtnl_link_bond_release_ifindex(struct nl_sock *sock, int slave)
354 {
355 	return rtnl_link_bond_enslave_ifindex(sock, 0, slave);
356 }
357 
358 /**
359  * Release a link from a bond
360  * @arg sock		netlink socket
361  * @arg slave		slave link to be released
362  *
363  * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from
364  * its master and sends the request via the specified netlink socket.
365  *
366  * @note The feature of enslaving/releasing via netlink has only been added
367  *       recently to the kernel (Feb 2011). Also, the kernel does not signal
368  *       if the operation is not supported. Therefore this function will
369  *       verify if the master assignment has changed and will return
370  *       -NLE_OPNOTSUPP if it did not.
371  *
372  * @see rtnl_link_bond_release_ifindex()
373  * @see rtnl_link_bond_enslave()
374  *
375  * @return 0 on success or a negative error code.
376  */
rtnl_link_bond_release(struct nl_sock * sock,struct rtnl_link * slave)377 int rtnl_link_bond_release(struct nl_sock *sock, struct rtnl_link *slave)
378 {
379 	return rtnl_link_bond_release_ifindex(sock,
380 				rtnl_link_get_ifindex(slave));
381 }
382 
bonding_init(void)383 static void _nl_init bonding_init(void)
384 {
385 	rtnl_link_register_info(&bonding_info_ops);
386 }
387 
bonding_exit(void)388 static void _nl_exit bonding_exit(void)
389 {
390 	rtnl_link_unregister_info(&bonding_info_ops);
391 }
392 
393 /** @} */
394