blob: 198f537391dde2ad6461ecd2610ae4273c88fd1a [file] [log] [blame]
Bart De Schuymer1abc55d2002-06-01 19:23:47 +00001/*
fnm391fc2be2002-06-25 16:37:52 +00002 * Summary: ebt_vlan - IEEE 802.1Q extension module for userspace
fnm3f794d5a2002-06-14 17:28:13 +00003 *
fnm391fc2be2002-06-25 16:37:52 +00004 * Description: 802.1Q Virtual LAN match support module for ebtables project.
5 * Enables to match 802.1Q:
6 * 1) VLAN-tagged frames by VLAN numeric identifier (12 - bits field)
7 * 2) Priority-tagged frames by user_priority (3 bits field)
8 * 3) Encapsulated Frame by ethernet protocol type/length
Bart De Schuymer1abc55d2002-06-01 19:23:47 +00009 *
10 * Authors:
11 * Bart De Schuymer <bart.de.schuymer@pandora.be>
fnm3f794d5a2002-06-14 17:28:13 +000012 * Nick Fedchik <nick@fedchik.org.ua>
13 * June, 2002
14 *
fnm391fc2be2002-06-25 16:37:52 +000015 * License: GNU GPL
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
fnm3f794d5a2002-06-14 17:28:13 +000030 *
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000031 */
Bart De Schuymere4b1fdf2002-06-16 09:20:22 +000032
fnm3f794d5a2002-06-14 17:28:13 +000033#include <stdio.h>
34#include <stdlib.h>
Bart De Schuymere4b1fdf2002-06-16 09:20:22 +000035#include <string.h>
Bart De Schuymere4b1fdf2002-06-16 09:20:22 +000036#include <getopt.h>
fnm38e011e62002-11-21 10:32:04 +000037#include <ctype.h>
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000038#include "../include/ebtables_u.h"
Bart De Schuymerc1939b12002-11-20 19:41:54 +000039#include "../include/ethernetdb.h"
Bart De Schuymere4b1fdf2002-06-16 09:20:22 +000040#include <linux/netfilter_bridge/ebt_vlan.h>
fnm38e011e62002-11-21 10:32:04 +000041#include <linux/if_ether.h>
42
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000043
fnm391fc2be2002-06-25 16:37:52 +000044#define GET_BITMASK(_MASK_) vlaninfo->bitmask & _MASK_
45#define SET_BITMASK(_MASK_) vlaninfo->bitmask |= _MASK_
Bart De Schuymer2ab59ea2002-07-24 10:14:02 +000046#define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "! " : ""
fnm38e011e62002-11-21 10:32:04 +000047#define CHECK_IF_MISSING_VALUE if (optind > argc) print_error ("Missing %s value", opts[c].name);
48#define CHECK_INV_FLAG(_INDEX_) if (check_inverse (optarg)) vlaninfo->invflags |= _INDEX_;
49#define CHECK_RANGE(_RANGE_) if (_RANGE_) print_error ("Invalid %s range", opts[c].name);
50
51#define NAME_VLAN_ID "id"
52#define NAME_VLAN_PRIO "prio"
53#define NAME_VLAN_ENCAP "encap"
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000054
fnm391fc2be2002-06-25 16:37:52 +000055#define VLAN_ID 0
56#define VLAN_PRIO 1
57#define VLAN_ENCAP 2
fnm38e011e62002-11-21 10:32:04 +000058
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000059static struct option opts[] = {
fnm38e011e62002-11-21 10:32:04 +000060 {EBT_VLAN_MATCH "-" NAME_VLAN_ID, required_argument, NULL,
61 VLAN_ID},
62 {EBT_VLAN_MATCH "-" NAME_VLAN_PRIO, required_argument, NULL,
63 VLAN_PRIO},
64 {EBT_VLAN_MATCH "-" NAME_VLAN_ENCAP, required_argument, NULL,
65 VLAN_ENCAP},
fnm391fc2be2002-06-25 16:37:52 +000066 {NULL}
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000067};
68
fnm38e011e62002-11-21 10:32:04 +000069/*
70 * option inverse flags definition
71 */
72#define OPT_VLAN_ID 0x01
73#define OPT_VLAN_PRIO 0x02
74#define OPT_VLAN_ENCAP 0x04
75#define OPT_VLAN_FLAGS (OPT_VLAN_ID | OPT_VLAN_PRIO | OPT_VLAN_ENCAP)
76
77struct ethertypeent *ethent;
fnm391fc2be2002-06-25 16:37:52 +000078
Bart De Schuymer2f0d7482003-06-04 06:23:49 +000079/*
80 * Print out local help by "ebtables -h <match name>"
81 */
82
fnm38e011e62002-11-21 10:32:04 +000083static void print_help()
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000084{
fnm38e011e62002-11-21 10:32:04 +000085#define HELP_TITLE "802.1Q VLAN extension"
86
87 printf(HELP_TITLE " options:\n");
88 printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_ID " %s" NAME_VLAN_ID
89 " : VLAN-tagged frame identifier, 0,1-4096 (integer), default 1\n",
90 OPT_VLAN_FLAGS & OPT_VLAN_ID ? "[!] " : "");
91 printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_PRIO " %s" NAME_VLAN_PRIO
92 " : Priority-tagged frame user_priority, 0-7 (integer), default 0\n",
93 OPT_VLAN_FLAGS & OPT_VLAN_PRIO ? "[!] " : "");
94 printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_ENCAP " %s"
95 NAME_VLAN_ENCAP
96 " : Encapsulated frame type (hexadecimal), default IP (0800)\n",
97 OPT_VLAN_FLAGS & OPT_VLAN_ENCAP ? "[!] " : "");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000098}
99
Bart De Schuymer2f0d7482003-06-04 06:23:49 +0000100/*
101 * Initialization function
102 */
fnm38e011e62002-11-21 10:32:04 +0000103static void init(struct ebt_entry_match *match)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000104{
105 struct ebt_vlan_info *vlaninfo =
106 (struct ebt_vlan_info *) match->data;
Bart De Schuymer2f0d7482003-06-04 06:23:49 +0000107 /*
108 * Set initial values
109 */
fnm391fc2be2002-06-25 16:37:52 +0000110 vlaninfo->id = 1; /* Default VID for VLAN-tagged 802.1Q frames */
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000111 vlaninfo->prio = 0;
fnm3f794d5a2002-06-14 17:28:13 +0000112 vlaninfo->encap = 0;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000113 vlaninfo->invflags = 0;
114 vlaninfo->bitmask = 0;
115}
116
Bart De Schuymer2f0d7482003-06-04 06:23:49 +0000117
118/*
119 * Parse passed arguments values (ranges, flags, etc...)
120 * int c - parameter number from static struct option opts[]
121 * int argc - total amout of arguments (std argc value)
122 * int argv - arguments (std argv value)
123 * const struct ebt_u_entry *entry - default ebtables entry set
124 * unsigned int *flags -
125 * struct ebt_entry_match **match -
126 */
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000127static int
fnm38e011e62002-11-21 10:32:04 +0000128parse(int c,
129 char **argv,
130 int argc,
131 const struct ebt_u_entry *entry,
132 unsigned int *flags, struct ebt_entry_match **match)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000133{
134 struct ebt_vlan_info *vlaninfo =
135 (struct ebt_vlan_info *) (*match)->data;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000136 char *end;
fnm38e011e62002-11-21 10:32:04 +0000137 struct ebt_vlan_info local;
138
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000139 switch (c) {
140 case VLAN_ID:
fnm38e011e62002-11-21 10:32:04 +0000141 check_option(flags, OPT_VLAN_ID);
142 CHECK_INV_FLAG(EBT_VLAN_ID);
143 CHECK_IF_MISSING_VALUE;
144 (unsigned short) local.id =
145 strtoul(argv[optind - 1], &end, 10);
146 CHECK_RANGE(local.id > 4094 || *end != '\0');
147 vlaninfo->id = local.id;
148 SET_BITMASK(EBT_VLAN_ID);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000149 break;
150
151 case VLAN_PRIO:
fnm38e011e62002-11-21 10:32:04 +0000152 check_option(flags, OPT_VLAN_PRIO);
153 CHECK_INV_FLAG(EBT_VLAN_PRIO);
154 CHECK_IF_MISSING_VALUE;
155 (unsigned char) local.prio =
156 strtoul(argv[optind - 1], &end, 10);
157 CHECK_RANGE(local.prio >= 8 || *end != '\0');
158 vlaninfo->prio = local.prio;
159 SET_BITMASK(EBT_VLAN_PRIO);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000160 break;
161
fnm3f794d5a2002-06-14 17:28:13 +0000162 case VLAN_ENCAP:
fnm38e011e62002-11-21 10:32:04 +0000163 check_option(flags, OPT_VLAN_ENCAP);
164 CHECK_INV_FLAG(EBT_VLAN_ENCAP);
165 CHECK_IF_MISSING_VALUE;
166 (unsigned short) local.encap =
167 strtoul(argv[optind - 1], &end, 16);
Bart De Schuymerc1939b12002-11-20 19:41:54 +0000168 if (*end != '\0') {
fnm38e011e62002-11-21 10:32:04 +0000169 ethent = getethertypebyname(argv[optind - 1]);
170 if (ethent == NULL)
171 print_error("Unknown %s encap",
172 opts[c].name);
173 local.encap = ethent->e_ethertype;
Bart De Schuymerc1939b12002-11-20 19:41:54 +0000174 }
fnm38e011e62002-11-21 10:32:04 +0000175 CHECK_RANGE(local.encap < ETH_ZLEN);
176 vlaninfo->encap = htons(local.encap);
177 SET_BITMASK(EBT_VLAN_ENCAP);
fnm3f794d5a2002-06-14 17:28:13 +0000178 break;
fnm38e011e62002-11-21 10:32:04 +0000179
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000180 default:
181 return 0;
fnm38e011e62002-11-21 10:32:04 +0000182
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000183 }
184 return 1;
185}
186
Bart De Schuymer2f0d7482003-06-04 06:23:49 +0000187/*
188 * Final check - logical conditions
189 */
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000190static void
fnm38e011e62002-11-21 10:32:04 +0000191final_check(const struct ebt_u_entry *entry,
192 const struct ebt_entry_match *match,
193 const char *name, unsigned int hookmask, unsigned int time)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000194{
fnm391fc2be2002-06-25 16:37:52 +0000195
196 struct ebt_vlan_info *vlaninfo =
197 (struct ebt_vlan_info *) match->data;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000198 /*
Bart De Schuymer40573192002-08-29 16:48:36 +0000199 * Specified proto isn't 802.1Q?
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000200 */
fnm38e011e62002-11-21 10:32:04 +0000201 if (entry->ethproto != ETH_P_8021Q || entry->invflags & EBT_IPROTO)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000202 print_error
Bart De Schuymer2ab59ea2002-07-24 10:14:02 +0000203 ("For use 802.1Q extension the protocol must be specified as 802_1Q");
fnm391fc2be2002-06-25 16:37:52 +0000204 /*
fnm3cd338272002-11-09 13:27:31 +0000205 * Check if specified vlan-encap=0x8100 (802.1Q Frame)
206 * when vlan-encap specified.
207 */
fnm38e011e62002-11-21 10:32:04 +0000208 if (GET_BITMASK(EBT_VLAN_ENCAP)) {
209 if (vlaninfo->encap == htons(0x8100))
fnm3cd338272002-11-09 13:27:31 +0000210 print_error
211 ("Encapsulated frame type can not be 802.1Q (0x8100)");
212 }
213
214 /*
fnm391fc2be2002-06-25 16:37:52 +0000215 * Check if specified vlan-id=0 (priority-tagged frame condition)
216 * when vlan-prio was specified.
217 */
fnm38e011e62002-11-21 10:32:04 +0000218 if (GET_BITMASK(EBT_VLAN_PRIO)) {
219 if (vlaninfo->id && GET_BITMASK(EBT_VLAN_ID))
fnm391fc2be2002-06-25 16:37:52 +0000220 print_error
Bart De Schuymer2ab59ea2002-07-24 10:14:02 +0000221 ("For use user_priority the specified vlan-id must be 0");
fnm391fc2be2002-06-25 16:37:52 +0000222 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000223}
224
Bart De Schuymer2f0d7482003-06-04 06:23:49 +0000225/*
226 * Print line when listing rules by ebtables -L
227 */
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000228static void
fnm38e011e62002-11-21 10:32:04 +0000229print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000230{
231 struct ebt_vlan_info *vlaninfo =
232 (struct ebt_vlan_info *) match->data;
233
234 /*
235 * Print VLAN ID if they are specified
236 */
fnm38e011e62002-11-21 10:32:04 +0000237 if (GET_BITMASK(EBT_VLAN_ID)) {
238 printf("--%s %s%d ",
239 opts[VLAN_ID].name,
240 INV_FLAG(EBT_VLAN_ID), vlaninfo->id);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000241 }
242 /*
fnm391fc2be2002-06-25 16:37:52 +0000243 * Print user priority if they are specified
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000244 */
fnm38e011e62002-11-21 10:32:04 +0000245 if (GET_BITMASK(EBT_VLAN_PRIO)) {
246 printf("--%s %s%d ",
247 opts[VLAN_PRIO].name,
248 INV_FLAG(EBT_VLAN_PRIO), vlaninfo->prio);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000249 }
fnm3f794d5a2002-06-14 17:28:13 +0000250 /*
fnm391fc2be2002-06-25 16:37:52 +0000251 * Print encapsulated frame type if they are specified
fnm3f794d5a2002-06-14 17:28:13 +0000252 */
fnm38e011e62002-11-21 10:32:04 +0000253 if (GET_BITMASK(EBT_VLAN_ENCAP)) {
254 printf("--%s %s",
255 opts[VLAN_ENCAP].name, INV_FLAG(EBT_VLAN_ENCAP));
fnm38e011e62002-11-21 10:32:04 +0000256 ethent = getethertypebynumber(ntohs(vlaninfo->encap));
257 if (ethent != NULL) {
258 printf("%s ", ethent->e_name);
259 } else {
260 printf("%4.4X ", ntohs(vlaninfo->encap));
261 }
fnm3f794d5a2002-06-14 17:28:13 +0000262 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000263}
264
265
266static int
fnm38e011e62002-11-21 10:32:04 +0000267compare(const struct ebt_entry_match *vlan1,
268 const struct ebt_entry_match *vlan2)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000269{
270 struct ebt_vlan_info *vlaninfo1 =
271 (struct ebt_vlan_info *) vlan1->data;
272 struct ebt_vlan_info *vlaninfo2 =
273 (struct ebt_vlan_info *) vlan2->data;
274 /*
275 * Compare argc
276 */
277 if (vlaninfo1->bitmask != vlaninfo2->bitmask)
278 return 0;
279 /*
280 * Compare inv flags
281 */
282 if (vlaninfo1->invflags != vlaninfo2->invflags)
283 return 0;
284 /*
285 * Compare VLAN ID if they are present
286 */
287 if (vlaninfo1->bitmask & EBT_VLAN_ID) {
288 if (vlaninfo1->id != vlaninfo2->id)
289 return 0;
290 };
291 /*
292 * Compare VLAN Prio if they are present
293 */
294 if (vlaninfo1->bitmask & EBT_VLAN_PRIO) {
295 if (vlaninfo1->prio != vlaninfo2->prio)
296 return 0;
297 };
fnm3f794d5a2002-06-14 17:28:13 +0000298 /*
299 * Compare VLAN Encap if they are present
300 */
301 if (vlaninfo1->bitmask & EBT_VLAN_ENCAP) {
302 if (vlaninfo1->encap != vlaninfo2->encap)
303 return 0;
304 };
fnm38e011e62002-11-21 10:32:04 +0000305
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000306 return 1;
307}
308
309static struct ebt_u_match vlan_match = {
Bart De Schuymer7cf1cca2003-08-30 16:20:19 +0000310 .name = EBT_VLAN_MATCH,
311 .size = sizeof(struct ebt_vlan_info),
312 .help = print_help,
313 .init = init,
314 .parse = parse,
315 .final_check = final_check,
316 .print = print,
317 .compare = compare,
318 .extra_ops = opts,
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000319};
320
fnm38e011e62002-11-21 10:32:04 +0000321static void _init(void) __attribute__ ((constructor));
322static void _init(void)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000323{
fnm38e011e62002-11-21 10:32:04 +0000324 register_match(&vlan_match);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000325}