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
36 static struct nla_policy veth_policy[VETH_INFO_MAX+1] = {
37 [VETH_INFO_PEER] = { .minlen = sizeof(struct ifinfomsg) },
38 };
39
veth_parse(struct rtnl_link * link,struct nlattr * data,struct nlattr * xstats)40 static 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
79 errout:
80 return err;
81 }
82
veth_dump_line(struct rtnl_link * link,struct nl_dump_params * p)83 static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
84 {
85 }
86
veth_dump_details(struct rtnl_link * link,struct nl_dump_params * p)87 static 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
veth_clone(struct rtnl_link * dst,struct rtnl_link * src)99 static 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
veth_put_attrs(struct nl_msg * msg,struct rtnl_link * link)117 static 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
veth_alloc(struct rtnl_link * link)143 static 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
veth_free(struct rtnl_link * link)168 static 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
180 static 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 */
rtnl_link_veth_alloc(void)212 struct 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 */
rtnl_link_veth_get_peer(struct rtnl_link * link)232 struct 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 */
rtnl_link_veth_release(struct rtnl_link * link)243 void 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 */
rtnl_link_is_veth(struct rtnl_link * link)255 int 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 */
rtnl_link_veth_add(struct nl_sock * sock,const char * name,const char * peer_name,pid_t pid)274 int 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
veth_init(void)298 static void __init veth_init(void)
299 {
300 rtnl_link_register_info(&veth_info_ops);
301 }
302
veth_exit(void)303 static void __exit veth_exit(void)
304 {
305 rtnl_link_unregister_info(&veth_info_ops);
306 }
307
308 /** @} */
309