xref: /aosp_15_r20/external/libnl/lib/route/link/veth.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2013 Cong Wang <[email protected]>
4  */
5 
6 /**
7  * @ingroup link
8  * @defgroup veth VETH
9  * Virtual Ethernet
10  *
11  * @details
12  * \b Link Type Name: "veth"
13  *
14  * @route_doc{link_veth, VETH Documentation}
15  *
16  * @{
17  */
18 
19 #include "nl-default.h"
20 
21 #include <linux/if_link.h>
22 #include <linux/veth.h>
23 
24 #include <netlink/netlink.h>
25 #include <netlink/attr.h>
26 #include <netlink/utils.h>
27 #include <netlink/object.h>
28 #include <netlink/route/rtnl.h>
29 #include <netlink/route/link/veth.h>
30 
31 #include "nl-route.h"
32 #include "link-api.h"
33 
34 static struct nla_policy veth_policy[VETH_INFO_MAX+1] = {
35 	[VETH_INFO_PEER]	= { .minlen = sizeof(struct ifinfomsg) },
36 };
37 
veth_parse(struct rtnl_link * link,struct nlattr * data,struct nlattr * xstats)38 static int veth_parse(struct rtnl_link *link, struct nlattr *data,
39 		      struct nlattr *xstats)
40 {
41 	struct nlattr *tb[VETH_INFO_MAX+1];
42 	struct nlattr *peer_tb[IFLA_MAX + 1];
43 	struct rtnl_link *peer = link->l_info;
44 	int err;
45 
46 	NL_DBG(3, "Parsing veth link info\n");
47 
48 	if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0)
49 		goto errout;
50 
51 	if (tb[VETH_INFO_PEER]) {
52 		struct nlattr *nla_peer;
53 		struct ifinfomsg *ifi;
54 
55 		nla_peer = tb[VETH_INFO_PEER];
56 		ifi = nla_data(nla_peer);
57 
58 		peer->l_family = ifi->ifi_family;
59 		peer->l_arptype = ifi->ifi_type;
60 		peer->l_index = ifi->ifi_index;
61 		peer->l_flags = ifi->ifi_flags;
62 		peer->l_change = ifi->ifi_change;
63 		err = nla_parse(peer_tb, IFLA_MAX, (struct nlattr *)
64 		                ((char *) nla_data(nla_peer) + sizeof(struct ifinfomsg)),
65 				nla_len(nla_peer) - sizeof(struct ifinfomsg),
66 				rtln_link_policy);
67 		if (err < 0)
68 			goto errout;
69 
70 		err = rtnl_link_info_parse(peer, peer_tb);
71 		if (err < 0)
72 			goto errout;
73 	}
74 
75 	err = 0;
76 
77 errout:
78 	return err;
79 }
80 
veth_dump_line(struct rtnl_link * link,struct nl_dump_params * p)81 static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
82 {
83 }
84 
veth_dump_details(struct rtnl_link * link,struct nl_dump_params * p)85 static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
86 {
87 	struct rtnl_link *peer = link->l_info;
88 	char *name;
89 	name = rtnl_link_get_name(peer);
90 	nl_dump(p, "      peer ");
91 	if (name)
92 		nl_dump_line(p, "%s\n", name);
93 	else
94 		nl_dump_line(p, "%u\n", peer->l_index);
95 }
96 
veth_clone(struct rtnl_link * dst,struct rtnl_link * src)97 static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src)
98 {
99 	struct rtnl_link *dst_peer = NULL, *src_peer = src->l_info;
100 
101 	/* we are calling nl_object_clone() recursively, this should
102 	 * happen only once */
103 	if (src_peer) {
104 		src_peer->l_info = NULL;
105 		dst_peer = (struct rtnl_link *)nl_object_clone(OBJ_CAST(src_peer));
106 		if (!dst_peer)
107 			return -NLE_NOMEM;
108 		src_peer->l_info = src;
109 		dst_peer->l_info = dst;
110 	}
111 	dst->l_info = dst_peer;
112 	return 0;
113 }
114 
veth_put_attrs(struct nl_msg * msg,struct rtnl_link * link)115 static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
116 {
117 	struct rtnl_link *peer = link->l_info;
118 	struct ifinfomsg ifi;
119 	struct nlattr *data, *info_peer;
120 
121 	memset(&ifi, 0, sizeof ifi);
122 	ifi.ifi_family = peer->l_family;
123 	ifi.ifi_type = peer->l_arptype;
124 	ifi.ifi_index = peer->l_index;
125 	ifi.ifi_flags = peer->l_flags;
126 	ifi.ifi_change = peer->l_change;
127 
128 	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
129 		return -NLE_MSGSIZE;
130 	if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER)))
131 		return -NLE_MSGSIZE;
132 	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
133 		return -NLE_MSGSIZE;
134 	rtnl_link_fill_info(msg, peer);
135 	nla_nest_end(msg, info_peer);
136 	nla_nest_end(msg, data);
137 
138 	return 0;
139 }
140 
veth_alloc(struct rtnl_link * link)141 static int veth_alloc(struct rtnl_link *link)
142 {
143 	struct rtnl_link *peer;
144 	int err;
145 
146 	/* return early if we are in recursion */
147 	if (link->l_info)
148 		return 0;
149 
150 	if (!(peer = rtnl_link_alloc()))
151 		return -NLE_NOMEM;
152 
153 	/* We don't need to hold a reference here, as link and
154 	 * its peer should always be freed together.
155 	 */
156 	peer->l_info = link;
157 	if ((err = rtnl_link_set_type(peer, "veth")) < 0) {
158 		rtnl_link_put(peer);
159 		return err;
160 	}
161 
162 	link->l_info = peer;
163 	return 0;
164 }
165 
veth_free(struct rtnl_link * link)166 static void veth_free(struct rtnl_link *link)
167 {
168 	struct rtnl_link *peer = link->l_info;
169 	if (peer) {
170 		link->l_info = NULL;
171 		/* avoid calling this recursively */
172 		peer->l_info = NULL;
173 		rtnl_link_put(peer);
174 	}
175 	/* the caller should finally free link */
176 }
177 
178 static struct rtnl_link_info_ops veth_info_ops = {
179 	.io_name		= "veth",
180 	.io_parse		= veth_parse,
181 	.io_dump = {
182 	    [NL_DUMP_LINE]	= veth_dump_line,
183 	    [NL_DUMP_DETAILS]	= veth_dump_details,
184 	},
185 	.io_alloc		= veth_alloc,
186 	.io_clone		= veth_clone,
187 	.io_put_attrs		= veth_put_attrs,
188 	.io_free		= veth_free,
189 };
190 
191 /** @cond SKIP */
192 
193 #define IS_VETH_LINK_ASSERT(link) \
194 	if ((link)->l_info_ops != &veth_info_ops) { \
195 		APPBUG("Link is not a veth link. set type \"veth\" first."); \
196 		return NULL; \
197 	}
198 /** @endcond */
199 
200 /**
201  * @name VETH Object
202  * @{
203  */
204 
205 /**
206  * Allocate link object of type veth
207  *
208  * @return Allocated link object or NULL.
209  */
rtnl_link_veth_alloc(void)210 struct rtnl_link *rtnl_link_veth_alloc(void)
211 {
212 	struct rtnl_link *link;
213 
214 	if (!(link = rtnl_link_alloc()))
215 		return NULL;
216 	if (rtnl_link_set_type(link, "veth") < 0) {
217 		rtnl_link_put(link);
218 		return NULL;
219 	}
220 
221 	return link;
222 }
223 
224 /**
225  * Get the peer link of a veth link
226  *
227  * @return the peer link object.
228  */
rtnl_link_veth_get_peer(struct rtnl_link * link)229 struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link)
230 {
231 	IS_VETH_LINK_ASSERT(link);
232 	nl_object_get(OBJ_CAST(link->l_info));
233 	return link->l_info;
234 }
235 
236 /**
237  * Release a veth link and its peer
238  *
239  */
rtnl_link_veth_release(struct rtnl_link * link)240 void rtnl_link_veth_release(struct rtnl_link *link)
241 {
242 	veth_free(link);
243 	rtnl_link_put(link);
244 }
245 
246 /**
247  * Check if link is a veth link
248  * @arg link		Link object
249  *
250  * @return True if link is a veth link, otherwise false is returned.
251  */
rtnl_link_is_veth(struct rtnl_link * link)252 int rtnl_link_is_veth(struct rtnl_link *link)
253 {
254 	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth");
255 }
256 
257 /**
258  * Create a new kernel veth device
259  * @arg sock		netlink socket
260  * @arg name		name of the veth device or NULL
261  * @arg peer_name	name of its peer or NULL
262  * @arg pid		pid of the process in the new netns
263  *
264  * Creates a new veth device pair in the kernel and move the peer
265  * to the network namespace where the process is. If no name is
266  * provided, the kernel will automatically pick a name of the
267  * form "veth%d" (e.g. veth0, veth1, etc.)
268  *
269  * @return 0 on success or a negative error code
270  */
rtnl_link_veth_add(struct nl_sock * sock,const char * name,const char * peer_name,pid_t pid)271 int rtnl_link_veth_add(struct nl_sock *sock, const char *name,
272                        const char *peer_name, pid_t pid)
273 {
274 	struct rtnl_link *link, *peer;
275 	int err = -NLE_NOMEM;
276 
277 	if (!(link = rtnl_link_veth_alloc()))
278 		return -NLE_NOMEM;
279 	peer = link->l_info;
280 
281 	if (name)
282 		rtnl_link_set_name(link, name);
283 	if (peer_name)
284 		rtnl_link_set_name(peer, peer_name);
285 
286 	rtnl_link_set_ns_pid(peer, pid);
287 	err = rtnl_link_add(sock, link, NLM_F_CREATE | NLM_F_EXCL);
288 
289 	rtnl_link_put(link);
290 	return err;
291 }
292 
293 /** @} */
294 
veth_init(void)295 static void _nl_init veth_init(void)
296 {
297 	rtnl_link_register_info(&veth_info_ops);
298 }
299 
veth_exit(void)300 static void _nl_exit veth_exit(void)
301 {
302 	rtnl_link_unregister_info(&veth_info_ops);
303 }
304 
305 /** @} */
306