1/*
2 * lib/route/cls/mirred.c		mirred action
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 act
14 * @defgroup act_mirred Mirror and Redirect
15 *
16 * @{
17 */
18
19#include <netlink-private/netlink.h>
20#include <netlink-private/tc.h>
21#include <netlink/netlink.h>
22#include <netlink/attr.h>
23#include <netlink/utils.h>
24#include <netlink-private/route/tc-api.h>
25#include <netlink/route/act/mirred.h>
26
27static struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
28	[TCA_MIRRED_PARMS]      = { .minlen = sizeof(struct tc_mirred) },
29};
30
31static int mirred_msg_parser(struct rtnl_tc *tc, void *data)
32{
33	struct rtnl_mirred *u = data;
34	struct nlattr *tb[TCA_MIRRED_MAX + 1];
35	int err;
36
37	err = tca_parse(tb, TCA_MIRRED_MAX, tc, mirred_policy);
38	if (err < 0)
39		return err;
40
41	if (!tb[TCA_MIRRED_PARMS])
42		return -NLE_MISSING_ATTR;
43
44	nla_memcpy(&u->m_parm, tb[TCA_MIRRED_PARMS], sizeof(u->m_parm));
45	return 0;
46}
47
48static void mirred_free_data(struct rtnl_tc *tc, void *data)
49{
50}
51
52static int mirred_clone(void *_dst, void *_src)
53{
54	struct rtnl_mirred *dst = _dst, *src = _src;
55
56	memcpy(&dst->m_parm, &src->m_parm, sizeof(src->m_parm));
57	return 0;
58}
59
60static void mirred_dump_line(struct rtnl_tc *tc, void *data,
61			  struct nl_dump_params *p)
62{
63	struct rtnl_mirred *u = data;
64	if (!u)
65		return;
66
67	nl_dump(p, " index %u", u->m_parm.ifindex);
68
69	if (u->m_parm.eaction == TCA_EGRESS_MIRROR)
70		nl_dump(p, " egress mirror");
71	else if (u->m_parm.eaction == TCA_EGRESS_REDIR)
72		nl_dump(p, " egress redirect");
73
74	switch(u->m_parm.action) {
75	case TC_ACT_UNSPEC:
76		nl_dump(p, " unspecified");
77		break;
78	case TC_ACT_PIPE:
79		nl_dump(p, " pipe");
80		break;
81	case TC_ACT_STOLEN:
82		nl_dump(p, " stolen");
83		break;
84	case TC_ACT_SHOT:
85		nl_dump(p, " shot");
86		break;
87	case TC_ACT_QUEUED:
88		nl_dump(p, " queued");
89		break;
90	case TC_ACT_REPEAT:
91		nl_dump(p, " repeat");
92		break;
93	}
94}
95
96static void mirred_dump_details(struct rtnl_tc *tc, void *data,
97			     struct nl_dump_params *p)
98{
99}
100
101static void mirred_dump_stats(struct rtnl_tc *tc, void *data,
102			   struct nl_dump_params *p)
103{
104	struct rtnl_mirred *u = data;
105
106	if (!u)
107		return;
108	/* TODO */
109}
110
111
112static int mirred_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
113{
114	struct rtnl_mirred *u = data;
115
116	if (!u)
117		return 0;
118
119	NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(u->m_parm), &u->m_parm);
120	return 0;
121
122nla_put_failure:
123	return -NLE_NOMEM;
124}
125
126/**
127 * @name Attribute Modifications
128 * @{
129 */
130
131int rtnl_mirred_set_action(struct rtnl_act *act, int action)
132{
133	struct rtnl_mirred *u;
134
135	if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
136		return -NLE_NOMEM;
137
138	if (action > TCA_INGRESS_MIRROR || action < TCA_EGRESS_REDIR)
139		return -NLE_INVAL;
140
141	switch (action) {
142	case TCA_EGRESS_MIRROR:
143	case TCA_EGRESS_REDIR:
144		u->m_parm.eaction = action;
145		break;
146	case TCA_INGRESS_REDIR:
147	case TCA_INGRESS_MIRROR:
148	default:
149		return NLE_OPNOTSUPP;
150	}
151	return 0;
152}
153
154int rtnl_mirred_get_action(struct rtnl_act *act)
155{
156	struct rtnl_mirred *u;
157
158	if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
159		return -NLE_NOMEM;
160	return u->m_parm.eaction;
161}
162
163int rtnl_mirred_set_ifindex(struct rtnl_act *act, uint32_t ifindex)
164{
165	struct rtnl_mirred *u;
166
167	if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
168		return -NLE_NOMEM;
169
170	u->m_parm.ifindex = ifindex;
171	return 0;
172}
173
174uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *act)
175{
176	struct rtnl_mirred *u;
177
178	if ((u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
179		return u->m_parm.ifindex;
180	return 0;
181}
182
183int rtnl_mirred_set_policy(struct rtnl_act *act, int policy)
184{
185	struct rtnl_mirred *u;
186
187	if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
188		return -NLE_NOMEM;
189
190	if (policy > TC_ACT_REPEAT || policy < TC_ACT_OK)
191		return -NLE_INVAL;
192
193	switch (u->m_parm.eaction) {
194	case TCA_EGRESS_MIRROR:
195	case TCA_EGRESS_REDIR:
196		u->m_parm.action = policy;
197		break;
198	case TCA_INGRESS_REDIR:
199	case TCA_INGRESS_MIRROR:
200	default:
201		return NLE_OPNOTSUPP;
202	}
203	return 0;
204}
205
206int rtnl_mirred_get_policy(struct rtnl_act *act)
207{
208	struct rtnl_mirred *u;
209
210	if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
211		return -NLE_NOMEM;
212	return u->m_parm.action;
213}
214
215/** @} */
216
217static struct rtnl_tc_ops mirred_ops = {
218	.to_kind		= "mirred",
219	.to_type		= RTNL_TC_TYPE_ACT,
220	.to_size		= sizeof(struct rtnl_mirred),
221	.to_msg_parser		= mirred_msg_parser,
222	.to_free_data		= mirred_free_data,
223	.to_clone		= mirred_clone,
224	.to_msg_fill		= mirred_msg_fill,
225	.to_dump = {
226	    [NL_DUMP_LINE]	= mirred_dump_line,
227	    [NL_DUMP_DETAILS]	= mirred_dump_details,
228	    [NL_DUMP_STATS]	= mirred_dump_stats,
229	},
230};
231
232static void __init mirred_init(void)
233{
234	rtnl_tc_register(&mirred_ops);
235}
236
237static void __exit mirred_exit(void)
238{
239	rtnl_tc_unregister(&mirred_ops);
240}
241
242/** @} */
243