Hi, changes since the last round of patches:
- Added Signed-Off-By to all patches. - Properly mentioned "--vlan-accept all" mode in options.c (it was missing in a few places). - Split out '--vlan-accept raw' to '--vlan-tagging' boolean switch. Quoting from the patch description: > As '--vlan-accept raw' is a global behavioural modifier, it makes more > sense > to break it out into a global '--vlan-tagging' boolean switch. (Which > matches the approach chosen in the initial patch-set.) > > '--vlan-accept tagged|untagged|all' now only specifies the behaviour of > the > tap device, while '--vlan-tagging' specifies whether OpenVPN should take a > closer look at the tagging of packets. > > The patch is only a configuration option change. The behaviour should > remain the same. > > New Old > ------------------------------------------------------------------ > > not specifying anything is equivalent to not specifying anything > > not specifying anything is equivalent to '--vlan-accept raw' > > '--vlan-tagging' + > '--vlan-accept tagged' is equivalent to '--vlan-accept tagged' > > '--vlan-tagging' + > '--vlan-accept untagged' is equivalent to '--vlan-accept untagged' > > '--vlan-tagging' is equivalent to '--vlan-accept all' > > '--vlan-tagging' + > '--vlan-accept all' is equivalent to '--vlan-accept all' All further work will be bug-fix only. The rebased changes are available from my git repo [1] on branch 'feat_vlan'. On branch 'feat_vlan_tagging' the same changes are available as incremental patches to the 'feat_vlan_tagging' branch on openvpn-testing.git. (Only the non-incremental patches provide all missing Signed-Off-Bys). For reviewers who haven't had a look at the other patches yet, I've attached a diff containing all changes introduced by the current patch-set. Cheers Fabian 1: git://fsmi-dev.fsmi.uni-karlsruhe.de/openvpn.git
diff --git a/configure.ac b/configure.ac index 116ff7c..b99bc11 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,12 @@ AC_ARG_ENABLE(selinux, [SELINUX="yes"] ) +AC_ARG_ENABLE(vlan-tagging, + [ --disable-vlan-tagging Disable support for VLAN tagging/untagging], + [VLAN_TAGGING="$enableval"], + [VLAN_TAGGING="yes"] +) + AC_ARG_WITH(ssl-headers, [ --with-ssl-headers=DIR Crypto/SSL Include files location], [CS_HDR_DIR="$withval"] @@ -886,6 +892,12 @@ if test "$SELINUX" = "yes"; then ) fi +dnl enable --vlan-tagging +if test "$VLAN_TAGGING" = "yes"; then + AC_DEFINE(ENABLE_VLAN_TAGGING, 1, [Enable VLAN tagging/untagging]) +fi + + TAP_ID="PRODUCT_TAP_ID" TAP_WIN32_MIN_MAJOR="PRODUCT_TAP_WIN32_MIN_MAJOR" TAP_WIN32_MIN_MINOR="PRODUCT_TAP_WIN32_MIN_MINOR" diff --git a/errlevel.h b/errlevel.h index a092d3f..68128b9 100644 --- a/errlevel.h +++ b/errlevel.h @@ -144,6 +144,8 @@ #define D_PF_DROPPED_BCAST LOGLEV(7, 71, M_DEBUG) /* packet filter dropped a broadcast packet */ #define D_PF_DEBUG LOGLEV(7, 72, M_DEBUG) /* packet filter debugging, must also define PF_DEBUG in pf.h */ +#define D_VLAN_DEBUG LOGLEV(7, 72, 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/mroute.c b/mroute.c index 9d8fa66..72ef91a 100644 --- a/mroute.c +++ b/mroute.c @@ -164,12 +164,28 @@ mroute_extract_addr_ipv4 (struct mroute_addr *src, return ret; } +static void mroute_copy_ether_to_addr(struct mroute_addr *maddr, + const uint8_t *eth_addr, + int16_t vid) +{ + maddr->type = MR_ADDR_ETHER; + maddr->netbits = 0; + memcpy (maddr->addr, eth_addr, 6); +#ifdef ENABLE_VLAN_TAGGING + maddr->len = 8; + memcpy (maddr->addr + 6, &vid, 2); +#else + maddr->len = 6; +#endif +} + unsigned int mroute_extract_addr_ether (struct mroute_addr *src, struct mroute_addr *dest, struct mroute_addr *esrc, struct mroute_addr *edest, - const struct buffer *buf) + const struct buffer *buf, + int16_t vid) { unsigned int ret = 0; if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr)) @@ -177,17 +193,11 @@ mroute_extract_addr_ether (struct mroute_addr *src, const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR (buf); if (src) { - src->type = MR_ADDR_ETHER; - src->netbits = 0; - src->len = 6; - memcpy (src->addr, eth->source, 6); + mroute_copy_ether_to_addr(src, eth->source, vid); } if (dest) { - dest->type = MR_ADDR_ETHER; - dest->netbits = 0; - dest->len = 6; - memcpy (dest->addr, eth->dest, 6); + mroute_copy_ether_to_addr(dest, eth->dest, vid); /* ethernet broadcast/multicast packet? */ if (is_mac_mcast_addr (eth->dest)) @@ -202,7 +212,16 @@ mroute_extract_addr_ether (struct mroute_addr *src, struct buffer b = *buf; if (buf_advance (&b, sizeof (struct openvpn_ethhdr))) { - switch (ntohs (eth->proto)) + uint16_t proto = ntohs (eth->proto); + if (proto == OPENVPN_ETH_P_8021Q && + BLEN (buf) >= (int) sizeof (struct openvpn_8021qhdr)) + { + const struct openvpn_8021qhdr *tag = (const struct openvpn_8021qhdr *) BPTR (buf); + proto = ntohs (tag->proto); + buf_advance (&b, SIZE_ETH_TO_8021Q_HDR); + } + + switch (proto) { case OPENVPN_ETH_P_IPV4: ret |= (mroute_extract_addr_ipv4 (esrc, edest, &b) << MROUTE_SEC_SHIFT); @@ -303,6 +322,9 @@ mroute_addr_print_ex (const struct mroute_addr *ma, { case MR_ADDR_ETHER: buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc)); +#ifdef ENABLE_VLAN_TAGGING + buf_printf (&out, "@%d", *(int16_t*)(ma->addr + 6)); +#endif break; case MR_ADDR_IPV4: { diff --git a/mroute.h b/mroute.h index 9b9087d..ccf79ec 100644 --- a/mroute.h +++ b/mroute.h @@ -139,7 +139,8 @@ mroute_extract_addr_from_packet (struct mroute_addr *src, struct mroute_addr *esrc, struct mroute_addr *edest, const struct buffer *buf, - int tunnel_type) + int tunnel_type, + int16_t bcast_domain) { unsigned int mroute_extract_addr_ipv4 (struct mroute_addr *src, struct mroute_addr *dest, @@ -149,13 +150,14 @@ mroute_extract_addr_from_packet (struct mroute_addr *src, struct mroute_addr *dest, struct mroute_addr *esrc, struct mroute_addr *edest, - const struct buffer *buf); + const struct buffer *buf, + int16_t vid); unsigned int ret = 0; verify_align_4 (buf); if (tunnel_type == DEV_TYPE_TUN) ret = mroute_extract_addr_ipv4 (src, dest, buf); else if (tunnel_type == DEV_TYPE_TAP) - ret = mroute_extract_addr_ether (src, dest, esrc, edest, buf); + ret = mroute_extract_addr_ether (src, dest, esrc, edest, buf, bcast_domain); return ret; } diff --git a/multi.c b/multi.c index 342871a..dfd23a3 100644 --- a/multi.c +++ b/multi.c @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2010 Fabian Knittel <fabian.knit...@avona.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -1759,7 +1760,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, + int16_t vid) { struct hash_iterator hi; struct hash_element *he; @@ -1804,6 +1806,10 @@ multi_bcast (struct multi_context *m, } } #endif +#ifdef ENABLE_VLAN_TAGGING + if (vid != 0 && vid != mi->context.options.vlan_pvid) + continue; +#endif multi_add_mbuf (m, mi, mb); } } @@ -1918,6 +1924,28 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un return ret; } +#ifdef ENABLE_VLAN_TAGGING +bool +buf_filter_incoming_vlan_tags (const struct buffer *buf) +{ + if (BLEN (buf) >= (int) sizeof (struct openvpn_8021qhdr)) + { + const struct openvpn_8021qhdr *vlanhdr = (const struct openvpn_8021qhdr *) BPTR (buf); + + if (ntohs (vlanhdr->tpid) == OPENVPN_ETH_P_8021Q) + { + const int16_t vid = vlanhdr_get_vid(vlanhdr); + if (vid != 0) + { + msg (D_VLAN_DEBUG, "dropping tagged incoming frame, vid: %d", vid); + return true; + } + } + } + return false; +} +#endif + /* * Process packets in the TCP/UDP socket -> TUN/TAP interface direction, * i.e. client -> server direction. @@ -1975,7 +2003,8 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins NULL, NULL, &c->c2.to_tun, - DEV_TYPE_TUN); + DEV_TYPE_TUN, + 0); /* drop packet if extract failed */ if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED)) @@ -1996,7 +2025,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins 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 */ { @@ -2033,10 +2062,27 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins } else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP) { +#ifdef ENABLE_VLAN_TAGGING + int16_t vid = 0; +#else + const int16_t vid = 0; +#endif #ifdef ENABLE_PF struct mroute_addr edest; mroute_addr_reset (&edest); #endif +#ifdef ENABLE_VLAN_TAGGING + if (m->top.options.vlan_tagging) + { + if (buf_filter_incoming_vlan_tags (&c->c2.to_tun)) + { + /* Drop tagged frames. */ + c->c2.to_tun.len = 0; + } + else + vid = c->options.vlan_pvid; + } +#endif /* extract packet source and dest addresses */ mroute_flags = mroute_extract_addr_from_packet (&src, &dest, @@ -2047,7 +2093,8 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins NULL, #endif &c->c2.to_tun, - DEV_TYPE_TAP); + DEV_TYPE_TAP, + vid); if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED) { @@ -2058,7 +2105,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins { 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, vid); } else /* try client-to-client routing */ { @@ -2116,6 +2163,159 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins return ret; } +#ifdef ENABLE_VLAN_TAGGING +/* + * For vlan_accept == VAF_ONLY_UNTAGGED_OR_PRIORITY: + * If a frame is VLAN-tagged, it is dropped. Otherwise, the global + * vlan_pvid is returned as VID. + * + * For vlan_accept == VAF_ONLY_VLAN_TAGGED: + * If a frame is VLAN-tagged and contains no priority information, the + * tagging is removed and the embedded VID is returned. + * If a frame is VLAN-tagged and contains priority information, the frame + * is turned into a priority frame and the embedded VID is returned. + * If a frame isn't VLAN-tagged, the frame is dropped. + * + * For vlan_accept == VAF_ALL: + * Accepts both VLAN-tagged and untagged (or priority-tagged) frames and + * and handles them as described above. + */ +static int16_t +remove_vlan_tag (const struct context *c, struct buffer *buf) +{ + struct openvpn_ethhdr eth; + struct openvpn_8021qhdr vlanhdr; + int16_t vid; + int16_t pcp; + + if (BLEN (buf) < (sizeof (struct openvpn_8021qhdr))) + goto drop; + + vlanhdr = *(const struct openvpn_8021qhdr *) BPTR (buf); + + if (ntohs (vlanhdr.tpid) != OPENVPN_ETH_P_8021Q) + { + /* Untagged packet. */ + + if (c->options.vlan_accept == VAF_ONLY_VLAN_TAGGED) + { + /* We only accept vlan-tagged frames, so drop frames without vlan-tag + */ + msg (D_VLAN_DEBUG, "dropping frame without vlan-tag (proto/len 0x%04x)", + ntohs (vlanhdr.tpid)); + goto drop; + } + + msg (D_VLAN_DEBUG, "assuming pvid for frame without vlan-tag, pvid: %d (proto/len 0x%04x)", + c->options.vlan_pvid, ntohs (vlanhdr.tpid)); + /* We return the global PVID as the VID for the untagged frame. */ + return c->options.vlan_pvid; + } + + /* Tagged packet. */ + + vid = ntohs (vlanhdr_get_vid (&vlanhdr)); + pcp = ntohs (vlanhdr_get_pcp (&vlanhdr)); + + if (c->options.vlan_accept == VAF_ONLY_UNTAGGED_OR_PRIORITY) + { + if (vid != 0) + { + /* We only accept untagged frames or priority-tagged frames. So drop + VLAN-tagged frames. */ + msg (D_VLAN_DEBUG, "dropping frame with vlan-tag, vid: %d (proto/len 0x%04x)", + vid, ntohs (vlanhdr.proto)); + goto drop; + } + + /* We return the global PVID as the VID for the priority-tagged frame. */ + return c->options.vlan_pvid; + } + + if (pcp == 0) + { + /* VLAN-tagged without priority information. */ + + msg (D_VLAN_DEBUG, "removing vlan-tag from frame: vid: %d, wrapped proto/len: 0x%04x", + vid, ntohs (vlanhdr.proto)); + memcpy (ð, &vlanhdr, sizeof (eth)); + eth.proto = vlanhdr.proto; + + buf_advance (buf, SIZE_ETH_TO_8021Q_HDR); + memcpy (BPTR (buf), ð, sizeof eth); + } + else + { + /* VLAN-tagged _with_ priority information. We turn this frame into + a pure priority frame. I.e. we clear out the VID but leave the rest + of the header intact. */ + msg (D_VLAN_DEBUG, "removing vlan-tag from priority frame: vid: %d, wrapped proto/len: 0x%04x, prio: %d", + vid, ntohs (vlanhdr.proto), pcp); + vlanhdr_set_vid (&vlanhdr, htons (0)); + memcpy (BPTR (buf), &vlanhdr, sizeof vlanhdr); + } + + return vid; +drop: + /* Drop the frame. */ + buf->len = 0; + return -1; +} + +/* + * Adds VLAN tagging to a frame. Assumes vlan_accept == VAF_ONLY_VLAN_TAGGED + * or VAF_ALL and a matching PVID. + */ +void +multi_prepend_vlan_tag (const struct context *c, struct buffer *buf) +{ + struct openvpn_ethhdr eth; + struct openvpn_8021qhdr *vlanhdr; + + /* Frame too small? */ + if (BLEN (buf) < (int) sizeof (struct openvpn_ethhdr)) + goto drop; + + eth = *(const struct openvpn_ethhdr *) BPTR (buf); + if (ntohs (eth.proto) == OPENVPN_ETH_P_8021Q) + { + /* Priority-tagged frame. */ + + /* Frame too small for header type? */ + if (BLEN (buf) < (int) (sizeof (struct openvpn_8021qhdr))) + goto drop; + + vlanhdr = (struct openvpn_8021qhdr *) BPTR (buf); + } + else + { + /* Untagged frame. */ + + /* Not enough head room for VLAN tag? */ + if (buf_reverse_capacity (buf) < SIZE_ETH_TO_8021Q_HDR) + goto drop; + + vlanhdr = (struct openvpn_8021qhdr *) buf_prepend (buf, SIZE_ETH_TO_8021Q_HDR); + + /* Initialise VLAN-tag ... */ + memcpy (vlanhdr, ð, sizeof eth); + vlanhdr->tpid = htons (OPENVPN_ETH_P_8021Q); + vlanhdr->proto = eth.proto; + vlanhdr_set_pcp (vlanhdr, htons (0)); + vlanhdr_set_cfi (vlanhdr, htons (0)); + } + + vlanhdr_set_vid (vlanhdr, htons (c->options.vlan_pvid)); + + msg (D_VLAN_DEBUG, "tagging frame: vid %d (wrapping proto/len: %04x)", + c->options.vlan_pvid, vlanhdr->proto); + return; +drop: + /* Drop the frame. */ + buf->len = 0; +} +#endif /* ENABLE_VLAN_TAGGING */ + /* * Process packets in the TUN/TAP interface -> TCP/UDP socket direction, * i.e. server -> client direction. @@ -2131,6 +2331,11 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag unsigned int mroute_flags; struct mroute_addr src, dest; const int dev_type = TUNNEL_TYPE (m->top.c1.tuntap); +#ifdef ENABLE_VLAN_TAGGING + int16_t vid = 0; +#else + const int16_t vid = 0; +#endif #ifdef ENABLE_PF struct mroute_addr esrc, *e1, *e2; @@ -2158,6 +2363,16 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag * the appropriate multi_instance object. */ +#ifdef ENABLE_VLAN_TAGGING + if (dev_type == DEV_TYPE_TAP && m->top.options.vlan_tagging) + { + if ((vid = remove_vlan_tag (&m->top, &m->top.c2.buf)) == -1) + { + return false; + } + } +#endif + mroute_flags = mroute_extract_addr_from_packet (&src, &dest, #ifdef ENABLE_PF @@ -2167,7 +2382,8 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag #endif NULL, &m->top.c2.buf, - dev_type); + dev_type, + vid); if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED) { @@ -2178,9 +2394,9 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag { /* 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, vid); #else - multi_bcast (m, &m->top.c2.buf, NULL, NULL); + multi_bcast (m, &m->top.c2.buf, NULL, NULL, vid); #endif } else @@ -2349,7 +2565,7 @@ gremlin_flood_clients (struct multi_context *m) ASSERT (buf_write_u8 (&buf, get_random () & 0xFF)); 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/multi.h b/multi.h index ad26c12..3ed1981 100644 --- a/multi.h +++ b/multi.h @@ -405,6 +405,9 @@ multi_get_timeout (struct multi_context *m, struct timeval *dest) static inline bool multi_process_outgoing_tun (struct multi_context *m, const unsigned int mpp_flags) { +#ifdef ENABLE_VLAN_TAGGING + void multi_prepend_vlan_tag (const struct context *c, struct buffer *buf); +#endif struct multi_instance *mi = m->pending; bool ret = true; @@ -415,6 +418,26 @@ multi_process_outgoing_tun (struct multi_context *m, const unsigned int mpp_flag mi->context.c2.to_tun.len); #endif set_prefix (mi); +#ifdef ENABLE_VLAN_TAGGING + if (m->top.options.vlan_accept == VAF_ONLY_UNTAGGED_OR_PRIORITY) + { + /* Packets aren't tagged on the tap device. */ + + 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; + } + } + else if (m->top.options.vlan_accept == VAF_ONLY_VLAN_TAGGED || + (m->top.options.vlan_accept == VAF_ALL && + m->top.options.vlan_pvid != mi->context.options.vlan_pvid)) + { + /* Packets need to be tagged. Either because all packets are tagged or + because the vid matches the port's pvid. */ + multi_prepend_vlan_tag (&mi->context, &mi->context.c2.to_tun); + } +#endif process_outgoing_tun (&mi->context); ret = multi_process_post (m, mi, mpp_flags); clear_prefix (); diff --git a/openvpn.8 b/openvpn.8 index 45e61fa..b5b98f9 100644 --- a/openvpn.8 +++ b/openvpn.8 @@ -3194,6 +3194,94 @@ other protocols such as ssh. Not implemented on Windows. .\"********************************************************* +.TP +.B \-\-vlan\-tagging +Turns the OpenVPN server instance into a switch that understands VLAN-tagging. +The tap device and each of the connecting clients is seen as a port of the +switch. All client ports are in untagged mode and the tap device is +VLAN-tagged, untagged or accepts both, depending on the +.B \-\-vlan\-accept +setting. + +Using the +.B \-\-vlan\-pvid v +option once per client, each port can be associated with a certain VLAN +Identifier (VID). Packets can only be distributed between ports with a +matching VID. Therefore, clients with differing VIDs are completely separated +from one-another, even if +.B \-\-client-to-client +is activated. + +The filtering of packets takes place in the OpenVPN server. Clients do not +need support for VLAN tagging. + +The +.B \-\-vlan\-tagging +option is off by default. While turned off, OpenVPN +does no parsing and accepts any Ethernet frames. + +The option can only be activated in +.B \-\-dev tap +mode. + +.\"********************************************************* +.TP +.B \-\-vlan\-accept tagged | untagged | all +Allows the tap device's VLAN tagging policy to be configured. You can choose +between +.B tagged +("Admit Only VLAN-tagged frames"), +.B untagged +("Admit Only Untagged and Priority-tagged frames") or +.B all +("Admit All frames") mode. + +Incoming untagged packets from clients are assigned with the client's Port +VLAN Identifier (PVID) as their VID. In +.B untagged +mode, incoming untagged packets on the tap device are associated with the +global +.B \-\-vlan\-pvid +setting. In +.B tagged +mode, any incoming untagged packets are dropped. + +In +.B tagged +mode, packets going out through the tap device are VLAN-tagged with the +originating client's VID. + +In +.B all +mode, incoming tagged packets are handled the same way as in +.B tagged +mode. Incoming untagged packets are handled as in +.B untagged +mode. Outgoing packets are tagged, unless the VID matches the global PVID, in +which case the packets go out untagged. + +Defaults to \fB\-\-vlan\-accept all\fR. +.\"********************************************************* +.TP +.B \-\-vlan\-pvid v +Specifies which VLAN identifier a "port" is associated with. Not valid without +\fB\-\-vlan\-tagging\fR. + +In client context, the setting specifies which VLAN identifier a client is +associated with. In global context, the tap device's VLAN identifier is set. +The latter only makes sense in +.B \-\-vlan\-accept untagged +and +.B \-\-vlan\-accept all +mode. + +Valid values for +.B v +go from 1 through to 4094. Defaults to 1. + +In some switch implementations, the Port VLAN Identifier is also referred +to as Native VLAN. +.\"********************************************************* .SS Client Mode Use client mode when connecting to an OpenVPN server which has diff --git a/options.c b/options.c index 36b9913..bf8b3a1 100644 --- a/options.c +++ b/options.c @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2010 Fabian Knittel <fabian.knit...@avona.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -420,6 +421,11 @@ static const char usage_message[] = "--port-share host port : When run in TCP mode, proxy incoming HTTPS sessions\n" " to a web server at host:port.\n" #endif +#ifdef ENABLE_VLAN_TAGGING + "--vlan-tagging : Enable VLAN tagging.\n" + "--vlan-accept tagged|untagged|all : Set VLAN tagging mode. Default is 'all'.\n" + "--vlan-pvid v : Sets the Port VLAN Identifier. Defaults to 1.\n" +#endif #endif "\n" "Client options (when connecting to a multi-client server):\n" @@ -754,6 +760,10 @@ init_options (struct options *o, const bool init_gc) #ifdef ENABLE_PKCS11 o->pkcs11_pin_cache_period = -1; #endif /* ENABLE_PKCS11 */ +#ifdef ENABLE_VLAN_TAGGING + o->vlan_accept = VAF_ALL; + o->vlan_pvid = 1; +#endif } void @@ -948,6 +958,23 @@ dhcp_option_address_parse (const char *name, const char *parm, in_addr_t *array, #endif +#ifdef ENABLE_VLAN_TAGGING +static const char * +print_vlan_accept (enum vlan_acceptable_frames mode) +{ + switch (mode) + { + case VAF_ONLY_VLAN_TAGGED: + return "tagged"; + case VAF_ONLY_UNTAGGED_OR_PRIORITY: + return "untagged"; + case VAF_ALL: + return "all"; + } + return NULL; +} +#endif + #if P2MP #ifdef ENABLE_DEBUG @@ -1006,6 +1033,11 @@ show_p2mp_parms (const struct options *o) SHOW_STR (port_share_host); SHOW_INT (port_share_port); #endif +#ifdef ENABLE_VLAN_TAGGING + SHOW_BOOL (vlan_tagging); + msg (D_SHOW_PARMS, " vlan_accept = %s", print_vlan_accept (o->vlan_accept)); + SHOW_INT (vlan_pvid); +#endif #endif /* P2MP_SERVER */ SHOW_BOOL (client); @@ -1742,6 +1774,17 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if ((options->ssl_flags & SSLF_NO_NAME_REMAPPING) && script_method == SM_SYSTEM) msg (M_USAGE, "--script-security method='system' cannot be combined with --no-name-remapping"); +#ifdef ENABLE_VLAN_TAGGING + 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_accept != defaults.vlan_accept) + msg (M_USAGE, "--vlan-accept requires --vlan-tagging"); + if (options->vlan_pvid != defaults.vlan_pvid) + msg (M_USAGE, "--vlan-pvid requires --vlan-tagging"); + } +#endif } else { @@ -1788,7 +1831,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (options->port_share_host || options->port_share_port) msg (M_USAGE, "--port-share requires TCP server mode (--mode server --proto tcp-server)"); #endif - +#ifdef ENABLE_VLAN_TAGGING + if (options->vlan_tagging) + msg (M_USAGE, "--vlan-tagging requires --mode server"); +#endif } #endif /* P2MP_SERVER */ @@ -5730,6 +5776,45 @@ add_option (struct options *options, options->persist_mode = 1; } #endif +#ifdef ENABLE_VLAN_TAGGING + else if (streq (p[0], "vlan-tagging")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->vlan_tagging = true; + } + else if (streq (p[0], "vlan-accept") && p[1]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + if (streq (p[1], "tagged")) + { + options->vlan_accept = VAF_ONLY_VLAN_TAGGED; + } + else if (streq (p[1], "untagged")) + { + options->vlan_accept = VAF_ONLY_UNTAGGED_OR_PRIORITY; + } + else if (streq (p[1], "all")) + { + options->vlan_accept = VAF_ALL; + } + else + { + msg (msglevel, "--vlan-accept must be 'tagged', 'untagged' or 'all'"); + goto err; + } + } + else if (streq (p[0], "vlan-pvid") && p[1]) + { + 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 >= %d and <= %d", OPENVPN_8021Q_MIN_VID, OPENVPN_8021Q_MAX_VID); + goto err; + } + } +#endif else { if (file) diff --git a/options.h b/options.h index 740e18e..5c4851f 100644 --- a/options.h +++ b/options.h @@ -126,6 +126,15 @@ struct remote_list #endif +#ifdef ENABLE_VLAN_TAGGING +enum vlan_acceptable_frames +{ + VAF_ONLY_VLAN_TAGGED, + VAF_ONLY_UNTAGGED_OR_PRIORITY, + VAF_ALL, +}; +#endif + /* Command line options */ struct options { @@ -509,6 +518,12 @@ struct options bool show_net_up; int route_method; #endif + +#ifdef ENABLE_VLAN_TAGGING + bool vlan_tagging; + enum vlan_acceptable_frames vlan_accept; + int vlan_pvid; +#endif }; #define streq(x, y) (!strcmp((x), (y))) diff --git a/proto.c b/proto.c index 1f582ce..64486de 100644 --- a/proto.c +++ b/proto.c @@ -54,6 +54,9 @@ is_ipv4 (int tunnel_type, struct buffer *buf) return false; eh = (const struct openvpn_ethhdr *) BPTR (buf); if (ntohs (eh->proto) == OPENVPN_ETH_P_8021Q) { + if (BLEN (buf) < (int)(sizeof (struct openvpn_8021qhdr) + + sizeof (struct openvpn_iphdr))) + return false; const struct openvpn_8021qhdr *evh; evh = (const struct openvpn_8021qhdr *) BPTR (buf); if (ntohs (evh->proto) != OPENVPN_ETH_P_IPV4) diff --git a/proto.h b/proto.h index 628e991..5c641ff 100644 --- a/proto.h +++ b/proto.h @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2010 Fabian Knittel <fabian.knit...@avona.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -61,20 +62,28 @@ 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 */ }; -# define OPENVPN_ETH_P_8021Q 0x8100 /* 802.1Q protocol */ - struct openvpn_8021qhdr { uint8_t dest[OPENVPN_ETH_ALEN]; /* destination ethernet addr */ uint8_t source[OPENVPN_ETH_ALEN]; /* source ethernet addr */ - uint32_t tag; /* packet 802.1Q Vlan Tag */ - uint16_t proto; /* packet type ID field */ + uint16_t tpid; /* 802.1Q Tag Protocol Identifier */ +# define OPENVPN_8021Q_MASK_VID htons (0x0FFF) /* mask VID out of pcp_cfi_vid */ +# 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 */ + 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 @@ -211,4 +220,59 @@ void ipv4_packet_size_verify (const uint8_t *data, counter_type *errors); #endif +#ifdef ENABLE_VLAN_TAGGING +# define OPENVPN_8021Q_MIN_VID 1 +# define OPENVPN_8021Q_MAX_VID 4094 + +/* + * Retrieve the Priority Code Point (PCP) from the IEEE 802.1Q header. + */ +static inline int +vlanhdr_get_pcp (const struct openvpn_8021qhdr *hdr) +{ + return hdr->pcp_cfi_vid & OPENVPN_8021Q_MASK_PCP; +} +/* + * Retrieve the Canonical Format Indicator (CFI) from the IEEE 802.1Q header. + */ +static inline int +vlanhdr_get_cfi (const struct openvpn_8021qhdr *hdr) +{ + return hdr->pcp_cfi_vid & OPENVPN_8021Q_MASK_CFI; +} +/* + * Retrieve the VLAN Identifier (VID) from the IEEE 802.1Q header. + */ +static inline int +vlanhdr_get_vid (const struct openvpn_8021qhdr *hdr) +{ + return hdr->pcp_cfi_vid & OPENVPN_8021Q_MASK_VID; +} + +/* + * Set the Priority Code Point (PCP) in an IEEE 802.1Q header. + */ +static inline void +vlanhdr_set_pcp (struct openvpn_8021qhdr *hdr, const int pcp) +{ + hdr->pcp_cfi_vid = (hdr->pcp_cfi_vid & ~OPENVPN_8021Q_MASK_PCP) | (pcp & OPENVPN_8021Q_MASK_PCP); +} +/* + * Set the Canonical Format Indicator (CFI) in an IEEE 802.1Q header. + */ +static inline void +vlanhdr_set_cfi (struct openvpn_8021qhdr *hdr, const int cfi) +{ + hdr->pcp_cfi_vid = (hdr->pcp_cfi_vid & ~OPENVPN_8021Q_MASK_CFI) | (cfi & OPENVPN_8021Q_MASK_CFI); +} +/* + * Set the VLAN Identifier (VID) in an IEEE 802.1Q header. + */ +static inline void +vlanhdr_set_vid (struct openvpn_8021qhdr *hdr, const int vid) +{ + hdr->pcp_cfi_vid = (hdr->pcp_cfi_vid & ~OPENVPN_8021Q_MASK_VID) | (vid & OPENVPN_8021Q_MASK_VID); +} +#endif + #endif