From patchwork Wed Oct 9 03:34:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 856 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director8.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id yOdEAdbwnV0nEgAAIUCqbw for ; Wed, 09 Oct 2019 10:38:14 -0400 Received: from proxy3.mail.ord1d.rsapps.net ([172.30.191.6]) by director8.mail.ord1d.rsapps.net with LMTP id ID++ANbwnV3lEQAAfY0hYg ; Wed, 09 Oct 2019 10:38:14 -0400 Received: from smtp30.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy3.mail.ord1d.rsapps.net with LMTP id OBQJANbwnV1rfwAA7WKfLA ; Wed, 09 Oct 2019 10:38:14 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp30.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: 6be96f68-eaa2-11e9-bd5c-5254001e8e38-1-1 Received: from [216.105.38.7] ([216.105.38.7:43464] helo=lists.sourceforge.net) by smtp30.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id AC/26-23160-5D0FD9D5; Wed, 09 Oct 2019 10:38:13 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1iID5t-0001tF-Qd; Wed, 09 Oct 2019 14:37:37 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1iID5p-0001rs-Lf for openvpn-devel@lists.sourceforge.net; Wed, 09 Oct 2019 14:37:33 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=ofbytL23KAw13oNg1uxnlVAAfeALUlb7qwXv6B4ZhK0=; b=HtSS4MBAjVO81x3HX3L+5SxvCj D9gDufiaS9OEvJ+y8+XTknhnyxWsSkcmbgt6mrrsq8zL8GgZSBDE1Uj4O61q1Kvs7+C41M9IBw2rz 2jhilysUZO7+DoisgTCdq5D+4vDLkScRk+OG+bVW6lSV+1RXIga3XJ9SGqRleZ9Eex/g=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=ofbytL23KAw13oNg1uxnlVAAfeALUlb7qwXv6B4ZhK0=; b=Abu4n0m7DuzD8eIXBO9OYQO3Wq HRVpeMJ9Pvh38JEqcw2NrqHS0ZuV3He9q3HcpHF0UafDdVha+gv/oN0JugnWwkLEVQdnU0k5qQCU1 oKiIZ8ufMkGPWSwE2BkDRK/mobw4ThmLkia7fmmcprGIzThU3FNpef2EI/eWQZ7kfVsI=; Received: from [5.148.176.60] (helo=s2.neomailbox.net) by sfi-mx-3.v28.lw.sourceforge.com with esmtps (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.2) id 1iID5l-003zYo-Rp for openvpn-devel@lists.sourceforge.net; Wed, 09 Oct 2019 14:37:33 +0000 From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Wed, 9 Oct 2019 16:34:15 +0200 Message-Id: <20191009143422.9419-3-a@unstable.cc> In-Reply-To: <20191009143422.9419-1-a@unstable.cc> References: <20191009143422.9419-1-a@unstable.cc> MIME-Version: 1.0 X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: openvpn.net] -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [5.148.176.60 listed in list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record 1.0 RDNS_NONE Delivered to internal network by a host with no rDNS -0.3 AWL AWL: Adjusted score from AWL reputation of From: address X-Headers-End: 1iID5l-003zYo-Rp Subject: [Openvpn-devel] [PATCH 2/9] VLAN: add basic VLAN tagging support X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Antonio Quartulli Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox This patch introduces basic support for VLAN tagging on the server side. The introduced functionality consists in allowing the user to assign a VID to the server TAP device and a VID to each client port. Client specific VID are assigned by means of files in CCD (like for other client specific settings). Once VIDs have been assigned, everything works as before, except that communications are allowed only between hosts having the same VID. With this patch all broadcast and client-to-client traffic is yet separated by VLAN: only client-to-server unicasts are affected. Signed-off-by: Fabian Knittel Signed-off-by: Antonio Quartulli Acked-by: Gert Doering --- src/openvpn/Makefile.am | 1 + src/openvpn/errlevel.h | 2 + src/openvpn/multi.c | 27 +++++++--- src/openvpn/multi.h | 2 + src/openvpn/options.c | 54 ++++++++++++++++++++ src/openvpn/options.h | 9 ++++ src/openvpn/proto.h | 25 +++++++++ src/openvpn/vlan.c | 110 ++++++++++++++++++++++++++++++++++++++++ src/openvpn/vlan.h | 44 ++++++++++++++++ 9 files changed, 268 insertions(+), 6 deletions(-) create mode 100644 src/openvpn/vlan.c create mode 100644 src/openvpn/vlan.h diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 30caa01f..bc976019 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -123,6 +123,7 @@ openvpn_SOURCES = \ syshead.h \ tls_crypt.c tls_crypt.h \ tun.c tun.h \ + vlan.c vlan.h \ win32.h win32.c \ cryptoapi.h cryptoapi.c openvpn_LDADD = \ diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h index 60896c1f..e448fc37 100644 --- a/src/openvpn/errlevel.h +++ b/src/openvpn/errlevel.h @@ -148,6 +148,8 @@ #define D_PF_DEBUG LOGLEV(7, 72, M_DEBUG) /* packet filter debugging, must also define PF_DEBUG in pf.h */ #define D_PUSH_DEBUG LOGLEV(7, 73, M_DEBUG) /* show push/pull debugging info */ +#define D_VLAN_DEBUG LOGLEV(7, 74, M_DEBUG) /* show VLAN tagging/untagging debug info */ + #define D_HANDSHAKE_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show detailed description of each handshake */ #define D_TLS_DEBUG_MED LOGLEV(8, 70, M_DEBUG) /* limited info from tls_session routines */ #define D_INTERVAL LOGLEV(8, 70, M_DEBUG) /* show interval.h debugging info */ diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index d1f9c72e..8caaa868 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -45,6 +45,7 @@ #include "gremlin.h" #include "mstats.h" #include "ssl_verify.h" +#include "vlan.h" #include #include "memdbg.h" @@ -2208,7 +2209,8 @@ static void multi_bcast(struct multi_context *m, const struct buffer *buf, const struct multi_instance *sender_instance, - const struct mroute_addr *sender_addr) + const struct mroute_addr *sender_addr, + uint16_t vid) { struct hash_iterator hi; struct hash_element *he; @@ -2258,6 +2260,10 @@ multi_bcast(struct multi_context *m, } } #endif /* ifdef ENABLE_PF */ + if (vid != 0 && vid != mi->context.options.vlan_pvid) + { + continue; + } multi_add_mbuf(m, mi, mb); } } @@ -2595,7 +2601,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (mroute_flags & MROUTE_EXTRACT_MCAST) { /* for now, treat multicast as broadcast */ - multi_bcast(m, &c->c2.to_tun, m->pending, NULL); + multi_bcast(m, &c->c2.to_tun, m->pending, NULL, 0); } else /* possible client to client routing */ { @@ -2640,6 +2646,15 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst struct mroute_addr edest; mroute_addr_reset(&edest); #endif + if (m->top.options.vlan_tagging) + { + if (vlan_is_tagged(&c->c2.to_tun)) + { + /* Drop VLAN-tagged frame. */ + msg(D_VLAN_DEBUG, "dropping incoming VLAN-tagged frame"); + c->c2.to_tun.len = 0; + } + } /* extract packet source and dest addresses */ mroute_flags = mroute_extract_addr_from_packet(&src, &dest, @@ -2661,7 +2676,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst { if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST)) { - multi_bcast(m, &c->c2.to_tun, m->pending, NULL); + multi_bcast(m, &c->c2.to_tun, m->pending, NULL, 0); } else /* try client-to-client routing */ { @@ -2788,9 +2803,9 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags { /* for now, treat multicast as broadcast */ #ifdef ENABLE_PF - multi_bcast(m, &m->top.c2.buf, NULL, e2); + multi_bcast(m, &m->top.c2.buf, NULL, e2, 0); #else - multi_bcast(m, &m->top.c2.buf, NULL, NULL); + multi_bcast(m, &m->top.c2.buf, NULL, NULL, 0); #endif } else @@ -2972,7 +2987,7 @@ gremlin_flood_clients(struct multi_context *m) for (i = 0; i < parm.n_packets; ++i) { - multi_bcast(m, &buf, NULL, NULL); + multi_bcast(m, &buf, NULL, NULL, 0); } gc_free(&gc); diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index d87f21b9..cfd86bbe 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -40,6 +40,7 @@ #include "mudp.h" #include "mtcp.h" #include "perf.h" +#include "vlan.h" #define MULTI_PREFIX_MAX_LENGTH 256 @@ -620,6 +621,7 @@ multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags mi->context.c2.to_tun.len); #endif set_prefix(mi); + vlan_process_outgoing_tun(m, mi); process_outgoing_tun(&mi->context); ret = multi_process_post(m, mi, mpp_flags); clear_prefix(); diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 2d3865a6..caba6494 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -53,6 +53,7 @@ #include "win32.h" #include "push.h" #include "pool.h" +#include "proto.h" #include "helper.h" #include "manage.h" #include "forward.h" @@ -404,6 +405,8 @@ static const char usage_message[] = "--plugin m [str]: Load plug-in module m passing str as an argument\n" " to its initialization function.\n" #endif + "--vlan-tagging : Enable 802.1Q-based VLAN tagging.\n" + "--vlan-pvid v : Sets the Port VLAN Identifier. Defaults to 1.\n" #if P2MP #if P2MP_SERVER "\n" @@ -849,6 +852,8 @@ init_options(struct options *o, const bool init_gc) o->route_method = ROUTE_METHOD_ADAPTIVE; o->block_outside_dns = false; #endif + o->vlan_accept = VLAN_ONLY_UNTAGGED_OR_PRIORITY; + o->vlan_pvid = 1; #if P2MP_SERVER o->real_hash_size = 256; o->virtual_hash_size = 256; @@ -1224,6 +1229,17 @@ dhcp_option_address_parse(const char *name, const char *parm, in_addr_t *array, #endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */ +static const char * +print_vlan_accept(enum vlan_acceptable_frames mode) +{ + switch (mode) + { + case VLAN_ONLY_UNTAGGED_OR_PRIORITY: + return "untagged"; + } + return NULL; +} + #if P2MP #ifndef ENABLE_SMALL @@ -1293,6 +1309,9 @@ show_p2mp_parms(const struct options *o) SHOW_STR(port_share_host); SHOW_STR(port_share_port); #endif + SHOW_BOOL(vlan_tagging); + msg(D_SHOW_PARMS, " vlan_accept = %s", print_vlan_accept (o->vlan_accept)); + SHOW_INT(vlan_pvid); #endif /* P2MP_SERVER */ SHOW_BOOL(client); @@ -2355,6 +2374,18 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "--auth-user-pass-optional %s", postfix); } } + + if (options->vlan_tagging && dev != DEV_TYPE_TAP) + { + msg(M_USAGE, "--vlan-tagging must be used with --dev tap"); + } + if (!options->vlan_tagging) + { + if (options->vlan_pvid != defaults.vlan_pvid) + { + msg(M_USAGE, "--vlan-pvid requires --vlan-tagging"); + } + } } else { @@ -2444,6 +2475,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "--stale-routes-check requires --mode server"); } + + if (options->vlan_tagging) + { + msg(M_USAGE, "--vlan-tagging requires --mode server"); + } } #endif /* P2MP_SERVER */ @@ -8359,6 +8395,24 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->allow_recursive_routing = true; } + else if (streq(p[0], "vlan-tagging") && !p[1]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->vlan_tagging = true; + } + else if (streq(p[0], "vlan-pvid") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE); + options->vlan_pvid = positive_atoi(p[1]); + if (options->vlan_pvid < OPENVPN_8021Q_MIN_VID + || options->vlan_pvid > OPENVPN_8021Q_MAX_VID) + { + msg(msglevel, + "the parameter of --vlan-pvid parameters must be >= %u and <= %u", + OPENVPN_8021Q_MIN_VID, OPENVPN_8021Q_MAX_VID); + goto err; + } + } else { int i; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 63f0f4cb..3f5c5465 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -169,6 +169,11 @@ struct remote_list struct remote_entry *array[CONNECTION_LIST_SIZE]; }; +enum vlan_acceptable_frames +{ + VLAN_ONLY_UNTAGGED_OR_PRIORITY, +}; + struct remote_host_store { #define RH_HOST_LEN 80 @@ -632,6 +637,10 @@ struct options int keying_material_exporter_length; #endif + bool vlan_tagging; + enum vlan_acceptable_frames vlan_accept; + uint16_t vlan_pvid; + struct pull_filter_list *pull_filter_list; /* Useful when packets sent by openvpn itself are not subject diff --git a/src/openvpn/proto.h b/src/openvpn/proto.h index 4ddffc7d..c1ff3e14 100644 --- a/src/openvpn/proto.h +++ b/src/openvpn/proto.h @@ -60,9 +60,31 @@ struct openvpn_ethhdr #define OPENVPN_ETH_P_IPV4 0x0800 /* IPv4 protocol */ #define OPENVPN_ETH_P_IPV6 0x86DD /* IPv6 protocol */ #define OPENVPN_ETH_P_ARP 0x0806 /* ARP protocol */ +#define OPENVPN_ETH_P_8021Q 0x8100 /* 802.1Q protocol */ uint16_t proto; /* packet type ID field */ }; +struct openvpn_8021qhdr +{ + uint8_t dest[OPENVPN_ETH_ALEN]; /* destination ethernet addr */ + uint8_t source[OPENVPN_ETH_ALEN]; /* source ethernet addr */ + + uint16_t tpid; /* 802.1Q Tag Protocol Identifier */ +#define OPENVPN_8021Q_MASK_PCP htons(0xE000) /* mask PCP out of pcp_cfi_vid */ +#define OPENVPN_8021Q_MASK_CFI htons(0x1000) /* mask CFI out of pcp_cfi_vid */ +#define OPENVPN_8021Q_MASK_VID htons(0x0FFF) /* mask VID out of pcp_cfi_vid */ + uint16_t pcp_cfi_vid; /* bit fields, see IEEE 802.1Q */ + uint16_t proto; /* contained packet type ID field */ +}; + +/* + * Size difference between a regular Ethernet II header and an Ethernet II + * header with additional IEEE 802.1Q tagging. + */ +#define SIZE_ETH_TO_8021Q_HDR (sizeof(struct openvpn_8021qhdr) \ + - sizeof(struct openvpn_ethhdr)) + + struct openvpn_arp { #define ARP_MAC_ADDR_TYPE 0x0001 uint16_t mac_addr_type; /* 0x0001 */ @@ -311,4 +333,7 @@ void ipv4_packet_size_verify(const uint8_t *data, #endif +#define OPENVPN_8021Q_MIN_VID 1 +#define OPENVPN_8021Q_MAX_VID 4094 + #endif /* ifndef PROTO_H */ diff --git a/src/openvpn/vlan.c b/src/openvpn/vlan.c new file mode 100644 index 00000000..8e987277 --- /dev/null +++ b/src/openvpn/vlan.c @@ -0,0 +1,110 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2019 OpenVPN Technologies, Inc. + * Copyright (C) 2010 Fabian Knittel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if P2MP_SERVER + +#include "multi.h" +#include "options.h" +#include "vlan.h" + +/* + * Retrieve the VLAN Identifier (VID) from the IEEE 802.1Q header. + * + * @param hdr Pointer to the Ethernet header with IEEE 802.1Q tagging. + * @return Returns the VID in host byte order. + */ +static uint16_t +vlanhdr_get_vid(const struct openvpn_8021qhdr *hdr) +{ + return ntohs(hdr->pcp_cfi_vid & OPENVPN_8021Q_MASK_VID); +} + +/* + * vlan_is_tagged - check if a packet is VLAN-tagged + * + * Checks whether ethernet frame is VLAN-tagged. + * + * @param buf The ethernet frame. + * @return Returns true if the frame is VLAN-tagged, false otherwise. + */ +bool +vlan_is_tagged(const struct buffer *buf) +{ + const struct openvpn_8021qhdr *vlanhdr; + uint16_t vid; + + if (BLEN(buf) < sizeof(struct openvpn_8021qhdr)) + { + /* frame too small to be VLAN-tagged */ + return false; + } + + vlanhdr = (const struct openvpn_8021qhdr *)BPTR(buf); + + if (ntohs(vlanhdr->tpid) != OPENVPN_ETH_P_8021Q) + { + /* non tagged frame */ + return false; + } + + vid = vlanhdr_get_vid(vlanhdr); + if (vid == 0) + { + /* no vid: piority tagged only */ + return false; + } + + return true; +} + +void +vlan_process_outgoing_tun(struct multi_context *m, struct multi_instance *mi) +{ + if (!m->top.options.vlan_tagging) + { + return; + } + + if (m->top.options.vlan_accept == VLAN_ONLY_UNTAGGED_OR_PRIORITY) + { + /* Packets forwarded to the TAP devices aren't VLAN-tagged. Only packets + * matching the PVID configured globally are allowed to be received + */ + if (m->top.options.vlan_pvid != mi->context.options.vlan_pvid) + { + /* Packet is coming from the wrong VID, drop it. */ + mi->context.c2.to_tun.len = 0; + } + } +} + +#endif /* P2MP_SERVER */ diff --git a/src/openvpn/vlan.h b/src/openvpn/vlan.h new file mode 100644 index 00000000..1ef68813 --- /dev/null +++ b/src/openvpn/vlan.h @@ -0,0 +1,44 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2019 OpenVPN Technologies, Inc. + * Copyright (C) 2010 Fabian Knittel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef VLAN_H +#define VLAN_H + +#if P2MP_SERVER + +#include "buffer.h" +#include "mroute.h" + +struct multi_context; +struct multi_instance; + +bool +vlan_is_tagged(const struct buffer *buf); + +void +vlan_process_outgoing_tun(struct multi_context *m, struct multi_instance *mi); + +#endif /* P2MP_SERVER */ + +#endif /* VLAN_H */