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