1/*
2 * lib/route/link/macvlan.c	MACVLAN Link Info
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 Michael Braun <michael-dev@fami-braun.de>
10 */
11
12/**
13 * @ingroup link
14 * @defgroup macvlan MACVLAN
15 * MAC-based Virtual LAN link module
16 *
17 * @details
18 * \b Link Type Name: "macvlan"
19 *
20 * @route_doc{link_macvlan, MACVLAN 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/macvlan.h>
33
34#include <linux/if_link.h>
35
36/** @cond SKIP */
37#define MACVLAN_HAS_MODE	(1<<0)
38#define MACVLAN_HAS_FLAGS	(1<<1)
39
40struct macvlan_info
41{
42	uint32_t		mvi_mode;
43	uint16_t		mvi_flags; // there currently is only one flag and kernel has no flags_mask yet
44	uint32_t		mvi_mask;
45};
46
47/** @endcond */
48
49static struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX+1] = {
50	[IFLA_MACVLAN_MODE]	= { .type = NLA_U32 },
51	[IFLA_MACVLAN_FLAGS]	= { .type = NLA_U16 },
52};
53
54static int macvlan_alloc(struct rtnl_link *link)
55{
56	struct macvlan_info *mvi;
57
58	if ((mvi = calloc(1, sizeof(*mvi))) == NULL)
59		return -NLE_NOMEM;
60
61	link->l_info = mvi;
62
63	return 0;
64}
65
66static int macvlan_parse(struct rtnl_link *link, struct nlattr *data,
67                         struct nlattr *xstats)
68{
69	struct nlattr *tb[IFLA_MACVLAN_MAX+1];
70	struct macvlan_info *mvi;
71	int err;
72
73	NL_DBG(3, "Parsing MACVLAN link info");
74
75	if ((err = nla_parse_nested(tb, IFLA_MACVLAN_MAX, data, macvlan_policy)) < 0)
76		goto errout;
77
78	if ((err = macvlan_alloc(link)) < 0)
79		goto errout;
80
81	mvi = link->l_info;
82
83	if (tb[IFLA_MACVLAN_MODE]) {
84		mvi->mvi_mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]);
85		mvi->mvi_mask |= MACVLAN_HAS_MODE;
86	}
87
88	if (tb[IFLA_MACVLAN_FLAGS]) {
89		mvi->mvi_mode = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]);
90		mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
91	}
92
93	err = 0;
94errout:
95	return err;
96}
97
98static void macvlan_free(struct rtnl_link *link)
99{
100	free(link->l_info);
101	link->l_info = NULL;
102}
103
104static void macvlan_dump(struct rtnl_link *link, struct nl_dump_params *p)
105{
106	char buf[64];
107	struct macvlan_info *mvi = link->l_info;
108
109	if (mvi->mvi_mask & MACVLAN_HAS_MODE) {
110		rtnl_link_macvlan_mode2str(mvi->mvi_mode, buf, sizeof(buf));
111		nl_dump(p, "macvlan-mode %s", buf);
112	}
113
114	if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) {
115		rtnl_link_macvlan_flags2str(mvi->mvi_flags, buf, sizeof(buf));
116		nl_dump(p, "macvlan-flags %s", buf);
117	}
118}
119
120static int macvlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
121{
122	struct macvlan_info *vdst, *vsrc = src->l_info;
123	int err;
124
125	dst->l_info = NULL;
126	if ((err = rtnl_link_set_type(dst, "macvlan")) < 0)
127		return err;
128	vdst = dst->l_info;
129
130	if (!vdst || !vsrc)
131		return -NLE_NOMEM;
132
133	memcpy(vdst, vsrc, sizeof(struct macvlan_info));
134
135	return 0;
136}
137
138static int macvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
139{
140	struct macvlan_info *mvi = link->l_info;
141	struct nlattr *data;
142
143	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
144		return -NLE_MSGSIZE;
145
146	if (mvi->mvi_mask & MACVLAN_HAS_MODE)
147		NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, mvi->mvi_mode);
148
149	if (mvi->mvi_mask & MACVLAN_HAS_FLAGS)
150		NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, mvi->mvi_flags);
151
152	nla_nest_end(msg, data);
153
154nla_put_failure:
155
156	return 0;
157}
158
159static struct rtnl_link_info_ops macvlan_info_ops = {
160	.io_name		= "macvlan",
161	.io_alloc		= macvlan_alloc,
162	.io_parse		= macvlan_parse,
163	.io_dump = {
164	    [NL_DUMP_LINE]	= macvlan_dump,
165	    [NL_DUMP_DETAILS]	= macvlan_dump,
166	},
167	.io_clone		= macvlan_clone,
168	.io_put_attrs		= macvlan_put_attrs,
169	.io_free		= macvlan_free,
170};
171
172/** @cond SKIP */
173#define IS_MACVLAN_LINK_ASSERT(link) \
174	if ((link)->l_info_ops != &macvlan_info_ops) { \
175		APPBUG("Link is not a macvlan link. set type \"macvlan\" first."); \
176		return -NLE_OPNOTSUPP; \
177	}
178/** @endcond */
179
180/**
181 * @name MACVLAN Object
182 * @{
183 */
184
185/**
186 * Allocate link object of type MACVLAN
187 *
188 * @return Allocated link object or NULL.
189 */
190struct rtnl_link *rtnl_link_macvlan_alloc(void)
191{
192	struct rtnl_link *link;
193	int err;
194
195	if (!(link = rtnl_link_alloc()))
196		return NULL;
197
198	if ((err = rtnl_link_set_type(link, "macvlan")) < 0) {
199		rtnl_link_put(link);
200		return NULL;
201	}
202
203	return link;
204}
205
206/**
207 * Check if link is a MACVLAN link
208 * @arg link		Link object
209 *
210 * @return True if link is a MACVLAN link, otherwise false is returned.
211 */
212int rtnl_link_is_macvlan(struct rtnl_link *link)
213{
214	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvlan");
215}
216
217/**
218 * Set MACVLAN MODE
219 * @arg link		Link object
220 * @arg mode		MACVLAN mode
221 *
222 * @return 0 on success or a negative error code
223 */
224int rtnl_link_macvlan_set_mode(struct rtnl_link *link, uint32_t mode)
225{
226	struct macvlan_info *mvi = link->l_info;
227
228	IS_MACVLAN_LINK_ASSERT(link);
229
230	mvi->mvi_mode = mode;
231	mvi->mvi_mask |= MACVLAN_HAS_MODE;
232
233	return 0;
234}
235
236/**
237 * Get MACVLAN Mode
238 * @arg link		Link object
239 *
240 * @return MACVLAN mode, 0 if not set or a negative error code.
241 */
242uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *link)
243{
244	struct macvlan_info *mvi = link->l_info;
245
246	IS_MACVLAN_LINK_ASSERT(link);
247
248	if (mvi->mvi_mask & MACVLAN_HAS_MODE)
249		return mvi->mvi_mode;
250	else
251		return 0;
252}
253
254/**
255 * Set MACVLAN flags
256 * @arg link		Link object
257 * @arg flags		MACVLAN flags
258 *
259 * @return 0 on success or a negative error code.
260 */
261int rtnl_link_macvlan_set_flags(struct rtnl_link *link, uint16_t flags)
262{
263	struct macvlan_info *mvi = link->l_info;
264
265	IS_MACVLAN_LINK_ASSERT(link);
266
267	mvi->mvi_flags |= flags;
268	mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
269
270	return 0;
271}
272
273/**
274 * Unset MACVLAN flags
275 * @arg link		Link object
276 * @arg flags		MACVLAN flags
277 *
278 * Note: kernel currently only has a single flag and lacks flags_mask to
279 * indicate which flags shall be changed (it always all).
280 *
281 * @return 0 on success or a negative error code.
282 */
283int rtnl_link_macvlan_unset_flags(struct rtnl_link *link, uint16_t flags)
284{
285	struct macvlan_info *mvi = link->l_info;
286
287	IS_MACVLAN_LINK_ASSERT(link);
288
289	mvi->mvi_flags &= ~flags;
290	mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
291
292	return 0;
293}
294
295/**
296 * Get MACVLAN flags
297 * @arg link		Link object
298 *
299 * @return MACVLAN flags, 0 if none set, or a negative error code.
300 */
301uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *link)
302{
303	struct macvlan_info *mvi = link->l_info;
304
305	IS_MACVLAN_LINK_ASSERT(link);
306
307	return mvi->mvi_flags;
308}
309
310/** @} */
311
312static const struct trans_tbl macvlan_flags[] = {
313	__ADD(MACVLAN_FLAG_NOPROMISC, nopromisc)
314};
315
316static const struct trans_tbl macvlan_modes[] = {
317	__ADD(MACVLAN_MODE_PRIVATE, private)
318	__ADD(MACVLAN_MODE_VEPA, vepa)
319	__ADD(MACVLAN_MODE_BRIDGE, bridge)
320	__ADD(MACVLAN_MODE_PASSTHRU, passthru)
321};
322
323/**
324 * @name Flag Translation
325 * @{
326 */
327
328char *rtnl_link_macvlan_flags2str(int flags, char *buf, size_t len)
329{
330	return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags));
331}
332
333int rtnl_link_macvlan_str2flags(const char *name)
334{
335	return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags));
336}
337
338/** @} */
339
340/**
341 * @name Mode Translation
342 * @{
343 */
344
345char *rtnl_link_macvlan_mode2str(int mode, char *buf, size_t len)
346{
347	return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes));
348}
349
350int rtnl_link_macvlan_str2mode(const char *name)
351{
352	return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes));
353}
354
355/** @} */
356
357static void __init macvlan_init(void)
358{
359	rtnl_link_register_info(&macvlan_info_ops);
360}
361
362static void __exit macvlan_exit(void)
363{
364	rtnl_link_unregister_info(&macvlan_info_ops);
365}
366
367/** @} */
368