/* * Summary: ebt_vlan - IEEE 802.1Q extension module for userspace * * Description: 802.1Q Virtual LAN match support module for ebtables project. * Enables to match 802.1Q: * 1) VLAN-tagged frames by VLAN numeric identifier (12 - bits field) * 2) Priority-tagged frames by user_priority (3 bits field) * 3) Encapsulated Frame by ethernet protocol type/length * * Authors: * Bart De Schuymer * Nick Fedchik * June, 2002 * * License: GNU GPL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include "../include/ebtables_u.h" #include "../include/ethernetdb.h" #include #include #define GET_BITMASK(_MASK_) vlaninfo->bitmask & _MASK_ #define SET_BITMASK(_MASK_) vlaninfo->bitmask |= _MASK_ #define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "! " : "" #define CHECK_IF_MISSING_VALUE if (optind > argc) print_error ("Missing %s value", opts[c].name); #define CHECK_INV_FLAG(_INDEX_) if (check_inverse (optarg)) vlaninfo->invflags |= _INDEX_; #define CHECK_RANGE(_RANGE_) if (_RANGE_) print_error ("Invalid %s range", opts[c].name); #define NAME_VLAN_ID "id" #define NAME_VLAN_PRIO "prio" #define NAME_VLAN_ENCAP "encap" #define VLAN_ID 0 #define VLAN_PRIO 1 #define VLAN_ENCAP 2 static struct option opts[] = { {EBT_VLAN_MATCH "-" NAME_VLAN_ID, required_argument, NULL, VLAN_ID}, {EBT_VLAN_MATCH "-" NAME_VLAN_PRIO, required_argument, NULL, VLAN_PRIO}, {EBT_VLAN_MATCH "-" NAME_VLAN_ENCAP, required_argument, NULL, VLAN_ENCAP}, {NULL} }; /* * option inverse flags definition */ #define OPT_VLAN_ID 0x01 #define OPT_VLAN_PRIO 0x02 #define OPT_VLAN_ENCAP 0x04 #define OPT_VLAN_FLAGS (OPT_VLAN_ID | OPT_VLAN_PRIO | OPT_VLAN_ENCAP) struct ethertypeent *ethent; /* * Print out local help by "ebtables -h " */ static void print_help() { #define HELP_TITLE "802.1Q VLAN extension" printf(HELP_TITLE " options:\n"); printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_ID " %s" NAME_VLAN_ID " : VLAN-tagged frame identifier, 0,1-4096 (integer), default 1\n", OPT_VLAN_FLAGS & OPT_VLAN_ID ? "[!] " : ""); printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_PRIO " %s" NAME_VLAN_PRIO " : Priority-tagged frame user_priority, 0-7 (integer), default 0\n", OPT_VLAN_FLAGS & OPT_VLAN_PRIO ? "[!] " : ""); printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_ENCAP " %s" NAME_VLAN_ENCAP " : Encapsulated frame type (hexadecimal), default IP (0800)\n", OPT_VLAN_FLAGS & OPT_VLAN_ENCAP ? "[!] " : ""); } /* * Initialization function */ static void init(struct ebt_entry_match *match) { struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data; /* * Set initial values */ vlaninfo->id = 1; /* Default VID for VLAN-tagged 802.1Q frames */ vlaninfo->prio = 0; vlaninfo->encap = 0; vlaninfo->invflags = 0; vlaninfo->bitmask = 0; } /* * Parse passed arguments values (ranges, flags, etc...) * int c - parameter number from static struct option opts[] * int argc - total amout of arguments (std argc value) * int argv - arguments (std argv value) * const struct ebt_u_entry *entry - default ebtables entry set * unsigned int *flags - * struct ebt_entry_match **match - */ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) (*match)->data; char *end; struct ebt_vlan_info local; switch (c) { case VLAN_ID: check_option(flags, OPT_VLAN_ID); CHECK_INV_FLAG(EBT_VLAN_ID); CHECK_IF_MISSING_VALUE; (unsigned short) local.id = strtoul(argv[optind - 1], &end, 10); CHECK_RANGE(local.id > 4094 || *end != '\0'); vlaninfo->id = local.id; SET_BITMASK(EBT_VLAN_ID); break; case VLAN_PRIO: check_option(flags, OPT_VLAN_PRIO); CHECK_INV_FLAG(EBT_VLAN_PRIO); CHECK_IF_MISSING_VALUE; (unsigned char) local.prio = strtoul(argv[optind - 1], &end, 10); CHECK_RANGE(local.prio >= 8 || *end != '\0'); vlaninfo->prio = local.prio; SET_BITMASK(EBT_VLAN_PRIO); break; case VLAN_ENCAP: check_option(flags, OPT_VLAN_ENCAP); CHECK_INV_FLAG(EBT_VLAN_ENCAP); CHECK_IF_MISSING_VALUE; (unsigned short) local.encap = strtoul(argv[optind - 1], &end, 16); if (*end != '\0') { ethent = getethertypebyname(argv[optind - 1]); if (ethent == NULL) print_error("Unknown %s encap", opts[c].name); local.encap = ethent->e_ethertype; } CHECK_RANGE(local.encap < ETH_ZLEN); vlaninfo->encap = htons(local.encap); SET_BITMASK(EBT_VLAN_ENCAP); break; default: return 0; } return 1; } /* * Final check - logical conditions */ static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data; /* * Specified proto isn't 802.1Q? */ if (entry->ethproto != ETH_P_8021Q || entry->invflags & EBT_IPROTO) print_error ("For use 802.1Q extension the protocol must be specified as 802_1Q"); /* * Check if specified vlan-encap=0x8100 (802.1Q Frame) * when vlan-encap specified. */ if (GET_BITMASK(EBT_VLAN_ENCAP)) { if (vlaninfo->encap == htons(0x8100)) print_error ("Encapsulated frame type can not be 802.1Q (0x8100)"); } /* * Check if specified vlan-id=0 (priority-tagged frame condition) * when vlan-prio was specified. */ if (GET_BITMASK(EBT_VLAN_PRIO)) { if (vlaninfo->id && GET_BITMASK(EBT_VLAN_ID)) print_error ("For use user_priority the specified vlan-id must be 0"); } } /* * Print line when listing rules by ebtables -L */ static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data; /* * Print VLAN ID if they are specified */ if (GET_BITMASK(EBT_VLAN_ID)) { printf("--%s %s%d ", opts[VLAN_ID].name, INV_FLAG(EBT_VLAN_ID), vlaninfo->id); } /* * Print user priority if they are specified */ if (GET_BITMASK(EBT_VLAN_PRIO)) { printf("--%s %s%d ", opts[VLAN_PRIO].name, INV_FLAG(EBT_VLAN_PRIO), vlaninfo->prio); } /* * Print encapsulated frame type if they are specified */ if (GET_BITMASK(EBT_VLAN_ENCAP)) { printf("--%s %s", opts[VLAN_ENCAP].name, INV_FLAG(EBT_VLAN_ENCAP)); ethent = getethertypebynumber(ntohs(vlaninfo->encap)); if (ethent != NULL) { printf("%s ", ethent->e_name); } else { printf("%4.4X ", ntohs(vlaninfo->encap)); } } } static int compare(const struct ebt_entry_match *vlan1, const struct ebt_entry_match *vlan2) { struct ebt_vlan_info *vlaninfo1 = (struct ebt_vlan_info *) vlan1->data; struct ebt_vlan_info *vlaninfo2 = (struct ebt_vlan_info *) vlan2->data; /* * Compare argc */ if (vlaninfo1->bitmask != vlaninfo2->bitmask) return 0; /* * Compare inv flags */ if (vlaninfo1->invflags != vlaninfo2->invflags) return 0; /* * Compare VLAN ID if they are present */ if (vlaninfo1->bitmask & EBT_VLAN_ID) { if (vlaninfo1->id != vlaninfo2->id) return 0; }; /* * Compare VLAN Prio if they are present */ if (vlaninfo1->bitmask & EBT_VLAN_PRIO) { if (vlaninfo1->prio != vlaninfo2->prio) return 0; }; /* * Compare VLAN Encap if they are present */ if (vlaninfo1->bitmask & EBT_VLAN_ENCAP) { if (vlaninfo1->encap != vlaninfo2->encap) return 0; }; return 1; } static struct ebt_u_match vlan_match = { .name = EBT_VLAN_MATCH, .size = sizeof(struct ebt_vlan_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; static void _init(void) __attribute__ ((constructor)); static void _init(void) { register_match(&vlan_match); }