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