1/*
2 * m_vlan.c		vlan manipulation module
3 *
4 *              This program is free software; you can redistribute it and/or
5 *              modify it under the terms of the GNU General Public License
6 *              as published by the Free Software Foundation; either version
7 *              2 of the License, or (at your option) any later version.
8 *
9 * Authors:     Jiri Pirko <jiri@resnulli.us>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <string.h>
16#include <linux/if_ether.h>
17#include "utils.h"
18#include "rt_names.h"
19#include "tc_util.h"
20#include <linux/tc_act/tc_vlan.h>
21
22static void explain(void)
23{
24	fprintf(stderr, "Usage: vlan pop\n");
25	fprintf(stderr, "       vlan push [ protocol VLANPROTO ] id VLANID\n");
26	fprintf(stderr, "       VLANPROTO is one of 802.1Q or 802.1AD\n");
27	fprintf(stderr, "            with default: 802.1Q\n");
28}
29
30static void usage(void)
31{
32	explain();
33	exit(-1);
34}
35
36static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
37		      int tca_id, struct nlmsghdr *n)
38{
39	int argc = *argc_p;
40	char **argv = *argv_p;
41	struct rtattr *tail;
42	int action = 0;
43	__u16 id;
44	int id_set = 0;
45	__u16 proto;
46	int proto_set = 0;
47	struct tc_vlan parm = { 0 };
48
49	if (matches(*argv, "vlan") != 0)
50		return -1;
51
52	NEXT_ARG();
53
54	while (argc > 0) {
55		if (matches(*argv, "pop") == 0) {
56			if (action) {
57				fprintf(stderr, "unexpected \"%s\" - action already specified\n",
58					*argv);
59				explain();
60				return -1;
61			}
62			action = TCA_VLAN_ACT_POP;
63		} else if (matches(*argv, "push") == 0) {
64			if (action) {
65				fprintf(stderr, "unexpected \"%s\" - action already specified\n",
66					*argv);
67				explain();
68				return -1;
69			}
70			action = TCA_VLAN_ACT_PUSH;
71		} else if (matches(*argv, "id") == 0) {
72			if (action != TCA_VLAN_ACT_PUSH) {
73				fprintf(stderr, "\"%s\" is only valid for push\n",
74					*argv);
75				explain();
76				return -1;
77			}
78			NEXT_ARG();
79			if (get_u16(&id, *argv, 0))
80				invarg("id is invalid", *argv);
81			id_set = 1;
82		} else if (matches(*argv, "protocol") == 0) {
83			if (action != TCA_VLAN_ACT_PUSH) {
84				fprintf(stderr, "\"%s\" is only valid for push\n",
85					*argv);
86				explain();
87				return -1;
88			}
89			NEXT_ARG();
90			if (ll_proto_a2n(&proto, *argv))
91				invarg("protocol is invalid", *argv);
92			proto_set = 1;
93		} else if (matches(*argv, "help") == 0) {
94			usage();
95		} else {
96			break;
97		}
98		argc--;
99		argv++;
100	}
101
102	parm.action = TC_ACT_PIPE;
103	if (argc) {
104		if (matches(*argv, "reclassify") == 0) {
105			parm.action = TC_ACT_RECLASSIFY;
106			argc--;
107			argv++;
108		} else if (matches(*argv, "pipe") == 0) {
109			parm.action = TC_ACT_PIPE;
110			argc--;
111			argv++;
112		} else if (matches(*argv, "drop") == 0 ||
113			   matches(*argv, "shot") == 0) {
114			parm.action = TC_ACT_SHOT;
115			argc--;
116			argv++;
117		} else if (matches(*argv, "continue") == 0) {
118			parm.action = TC_ACT_UNSPEC;
119			argc--;
120			argv++;
121		} else if (matches(*argv, "pass") == 0) {
122			parm.action = TC_ACT_OK;
123			argc--;
124			argv++;
125		}
126	}
127
128	if (argc) {
129		if (matches(*argv, "index") == 0) {
130			NEXT_ARG();
131			if (get_u32(&parm.index, *argv, 10)) {
132				fprintf(stderr, "vlan: Illegal \"index\"\n");
133				return -1;
134			}
135			argc--;
136			argv++;
137		}
138	}
139
140	if (action == TCA_VLAN_ACT_PUSH && !id_set) {
141		fprintf(stderr, "id needs to be set for push\n");
142		explain();
143		return -1;
144	}
145
146	parm.v_action = action;
147	tail = NLMSG_TAIL(n);
148	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
149	addattr_l(n, MAX_MSG, TCA_VLAN_PARMS, &parm, sizeof(parm));
150	if (id_set)
151		addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_ID, &id, 2);
152	if (proto_set) {
153		if (proto != htons(ETH_P_8021Q) &&
154		    proto != htons(ETH_P_8021AD)) {
155			fprintf(stderr, "protocol not supported\n");
156			explain();
157			return -1;
158		}
159
160		addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PROTOCOL, &proto, 2);
161	}
162	tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
163
164	*argc_p = argc;
165	*argv_p = argv;
166	return 0;
167}
168
169static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
170{
171	SPRINT_BUF(b1);
172	struct rtattr *tb[TCA_VLAN_MAX + 1];
173	__u16 val;
174	struct tc_vlan *parm;
175
176	if (arg == NULL)
177		return -1;
178
179	parse_rtattr_nested(tb, TCA_VLAN_MAX, arg);
180
181	if (!tb[TCA_VLAN_PARMS]) {
182		fprintf(f, "[NULL vlan parameters]");
183		return -1;
184	}
185	parm = RTA_DATA(tb[TCA_VLAN_PARMS]);
186
187	fprintf(f, " vlan");
188
189	switch(parm->v_action) {
190	case TCA_VLAN_ACT_POP:
191		fprintf(f, " pop");
192		break;
193	case TCA_VLAN_ACT_PUSH:
194		fprintf(f, " push");
195		if (tb[TCA_VLAN_PUSH_VLAN_ID]) {
196			val = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
197			fprintf(f, " id %u", val);
198		}
199		if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
200			fprintf(f, " protocol %s",
201				ll_proto_n2a(rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]),
202					     b1, sizeof(b1)));
203		}
204		break;
205	}
206	fprintf(f, " %s", action_n2a(parm->action, b1, sizeof (b1)));
207
208	fprintf(f, "\n\t index %d ref %d bind %d", parm->index, parm->refcnt,
209		parm->bindcnt);
210
211	if (show_stats) {
212		if (tb[TCA_VLAN_TM]) {
213			struct tcf_t *tm = RTA_DATA(tb[TCA_VLAN_TM]);
214			print_tm(f, tm);
215		}
216	}
217
218	fprintf(f, "\n ");
219
220	return 0;
221}
222
223struct action_util vlan_action_util = {
224	.id = "vlan",
225	.parse_aopt = parse_vlan,
226	.print_aopt = print_vlan,
227};
228