OpenVPN
vlan.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2025 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 * Copyright (C) 2010 Fabian Knittel <fabian.knittel@lettink.de>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, see <https://www.gnu.org/licenses/>.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "syshead.h"
29
30#include "multi.h"
31#include "options.h"
32#include "vlan.h"
33
34/*
35 * Retrieve the VLAN Identifier (VID) from the IEEE 802.1Q header.
36 *
37 * @param hdr Pointer to the Ethernet header with IEEE 802.1Q tagging.
38 * @return Returns the VID in host byte order.
39 */
40static uint16_t
42{
43 return ntohs(hdr->pcp_cfi_vid & OPENVPN_8021Q_MASK_VID);
44}
45
46#if defined(__GNUC__) || defined(__clang__)
47#pragma GCC diagnostic push
48#pragma GCC diagnostic ignored "-Wconversion"
49#endif
50
51/*
52 * Set the VLAN Identifier (VID) in an IEEE 802.1Q header.
53 *
54 * @param hdr Pointer to the Ethernet header with IEEE 802.1Q tagging.
55 * @param vid The VID to set (in host byte order).
56 */
57static void
58vlanhdr_set_vid(struct openvpn_8021qhdr *hdr, const uint16_t vid)
59{
60 hdr->pcp_cfi_vid =
61 (hdr->pcp_cfi_vid & ~OPENVPN_8021Q_MASK_VID) | (htons(vid) & OPENVPN_8021Q_MASK_VID);
62}
63
64#if defined(__GNUC__) || defined(__clang__)
65#pragma GCC diagnostic pop
66#endif
67
68/*
69 * vlan_decapsulate - remove 802.1q header and return VID
70 *
71 * For vlan_accept == VLAN_ONLY_UNTAGGED_OR_PRIORITY:
72 * Only untagged frames and frames that are priority-tagged (VID == 0) are
73 * accepted. (This means that VLAN-tagged frames are dropped.) For frames
74 * that aren't dropped, the global vlan_pvid is returned as VID.
75 *
76 * For vlan_accept == VLAN_ONLY_TAGGED:
77 * If a frame is VLAN-tagged the tagging is removed and the embedded VID is
78 * returned. Any included priority information is lost.
79 * If a frame isn't VLAN-tagged, the frame is dropped.
80 *
81 * For vlan_accept == VLAN_ALL:
82 * Accepts both VLAN-tagged and untagged (or priority-tagged) frames and
83 * and handles them as described above.
84 *
85 * @param c The global context.
86 * @param buf The ethernet frame.
87 * @return Returns -1 if the frame is dropped or the VID if it is accepted.
88 */
89int16_t
90vlan_decapsulate(const struct context *c, struct buffer *buf)
91{
92 const struct openvpn_8021qhdr *vlanhdr;
93 struct openvpn_ethhdr *ethhdr;
94 uint16_t vid;
95
96 /* assume untagged frame */
97 if (BLEN(buf) < sizeof(*ethhdr))
98 {
99 goto drop;
100 }
101
102 ethhdr = (struct openvpn_ethhdr *)BPTR(buf);
103 if (ethhdr->proto != htons(OPENVPN_ETH_P_8021Q))
104 {
105 /* reject untagged frame */
107 {
108 msg(D_VLAN_DEBUG, "dropping frame without vlan-tag (proto/len 0x%04x)",
109 ntohs(ethhdr->proto));
110 goto drop;
111 }
112
113 /* untagged frame is accepted and associated with the global VID */
114 msg(D_VLAN_DEBUG, "assuming pvid for frame without vlan-tag, pvid: %u (proto/len 0x%04x)",
115 c->options.vlan_pvid, ntohs(ethhdr->proto));
116
117 return c->options.vlan_pvid;
118 }
119
120 /* tagged frame */
121 if (BLEN(buf) < sizeof(*vlanhdr))
122 {
123 goto drop;
124 }
125
126 vlanhdr = (const struct openvpn_8021qhdr *)BPTR(buf);
127 vid = vlanhdr_get_vid(vlanhdr);
128
129 switch (c->options.vlan_accept)
130 {
132 /* VLAN-tagged frame: drop packet */
133 if (vid != 0)
134 {
135 msg(D_VLAN_DEBUG, "dropping frame with vlan-tag, vid: %u (proto/len 0x%04x)", vid,
136 ntohs(vlanhdr->proto));
137 goto drop;
138 }
139
140 /* vid == 0 means prio-tagged packet: don't drop and fall-through */
141 case VLAN_ONLY_TAGGED:
142 case VLAN_ALL:
143 /* tagged frame can be accepted: extract vid and strip encapsulation */
144
145 /* in case of prio-tagged frame (vid == 0), assume the sender
146 * knows what he is doing and forward the packet as it is, so to
147 * keep the priority information intact.
148 */
149 if (vid == 0)
150 {
151 /* return the global VID for priority-tagged frames */
152 return c->options.vlan_pvid;
153 }
154
155 /* here we have a proper VLAN tagged frame: perform decapsulation
156 * and return embedded VID
157 */
158 msg(D_VLAN_DEBUG, "removing vlan-tag from frame: vid: %u, wrapped proto/len: 0x%04x",
159 vid, ntohs(vlanhdr->proto));
160
161 /* save inner protocol to be restored later after decapsulation */
162 uint16_t proto = vlanhdr->proto;
163 /* move the buffer head forward to adjust the headroom to a
164 * non-tagged frame
165 */
167 /* move the content of the 802.1q header to the new head, so that
168 * src/dst addresses are copied over
169 */
170 ethhdr = memmove(BPTR(buf), vlanhdr, sizeof(*ethhdr));
171 /* restore the inner protocol value */
172 ethhdr->proto = proto;
173
174 return vid;
175 }
176
177drop:
178 buf->len = 0;
179 return -1;
180}
181
182/*
183 * vlan_encapsulate - add 802.1q header and set the context related VID
184 *
185 * Assumes vlan_accept == VLAN_ONLY_TAGGED
186 *
187 * @param c The current context.
188 * @param buf The ethernet frame to encapsulate.
189 */
190void
191vlan_encapsulate(const struct context *c, struct buffer *buf)
192{
193 const struct openvpn_ethhdr *ethhdr;
194 struct openvpn_8021qhdr *vlanhdr;
195
196 if (BLEN(buf) < sizeof(*ethhdr))
197 {
198 goto drop;
199 }
200
201 ethhdr = (const struct openvpn_ethhdr *)BPTR(buf);
202 if (ethhdr->proto == htons(OPENVPN_ETH_P_8021Q))
203 {
204 /* Priority-tagged frame. (VLAN-tagged frames have been dropped before
205 * getting to this point)
206 */
207
208 /* Frame too small for header type? */
209 if (BLEN(buf) < sizeof(*vlanhdr))
210 {
211 goto drop;
212 }
213
214 vlanhdr = (struct openvpn_8021qhdr *)BPTR(buf);
215
216 /* sanity check: ensure this packet is really just prio-tagged */
217 uint16_t vid = vlanhdr_get_vid(vlanhdr);
218 if (vid != 0)
219 {
220 goto drop;
221 }
222 }
223 else
224 {
225 /* Untagged frame. */
226
227 /* Not enough head room for VLAN tag? */
229 {
230 goto drop;
231 }
232
233 vlanhdr = (struct openvpn_8021qhdr *)buf_prepend(buf, SIZE_ETH_TO_8021Q_HDR);
234
235 /* Initialise VLAN/802.1q header.
236 * Move the Eth header so to keep dst/src addresses the same and then
237 * assign the other fields.
238 *
239 * Also, save the inner protocol first, so that it can be restored later
240 * after the memmove()
241 */
242 uint16_t proto = ethhdr->proto;
243 memmove(vlanhdr, ethhdr, sizeof(*ethhdr));
244 vlanhdr->tpid = htons(OPENVPN_ETH_P_8021Q);
245 vlanhdr->pcp_cfi_vid = 0;
246 vlanhdr->proto = proto;
247 }
248
249 /* set the VID corresponding to the current context (client) */
250 vlanhdr_set_vid(vlanhdr, c->options.vlan_pvid);
251
252 msg(D_VLAN_DEBUG, "tagging frame: vid %u (wrapping proto/len: %04x)", c->options.vlan_pvid,
253 vlanhdr->proto);
254 return;
255
256drop:
257 /* Drop the frame. */
258 buf->len = 0;
259}
260
261/*
262 * vlan_is_tagged - check if a packet is VLAN-tagged
263 *
264 * Checks whether ethernet frame is VLAN-tagged.
265 *
266 * @param buf The ethernet frame.
267 * @return Returns true if the frame is VLAN-tagged, false otherwise.
268 */
269bool
270vlan_is_tagged(const struct buffer *buf)
271{
272 const struct openvpn_8021qhdr *vlanhdr;
273 uint16_t vid;
274
275 if (BLEN(buf) < sizeof(struct openvpn_8021qhdr))
276 {
277 /* frame too small to be VLAN-tagged */
278 return false;
279 }
280
281 vlanhdr = (const struct openvpn_8021qhdr *)BPTR(buf);
282
283 if (ntohs(vlanhdr->tpid) != OPENVPN_ETH_P_8021Q)
284 {
285 /* non tagged frame */
286 return false;
287 }
288
289 vid = vlanhdr_get_vid(vlanhdr);
290 if (vid == 0)
291 {
292 /* no vid: piority tagged only */
293 return false;
294 }
295
296 return true;
297}
298
299void
301{
302 if (!m->top.options.vlan_tagging)
303 {
304 return;
305 }
306
308 {
309 /* Packets forwarded to the TAP devices aren't VLAN-tagged. Only packets
310 * matching the PVID configured globally are allowed to be received
311 */
313 {
314 /* Packet is coming from the wrong VID, drop it. */
315 mi->context.c2.to_tun.len = 0;
316 }
317 }
318 else if (m->top.options.vlan_accept == VLAN_ALL)
319 {
320 /* Packets either need to be VLAN-tagged or not, depending on the
321 * packet's originating VID and the port's native VID (PVID). */
322
324 {
325 /* Packets need to be VLAN-tagged, because the packet's VID does not
326 * match the port's PVID. */
328 }
329 }
331 {
332 /* All packets on the port (the tap device) need to be VLAN-tagged. */
334 }
335}
#define BPTR(buf)
Definition buffer.h:123
static bool buf_advance(struct buffer *buf, int size)
Definition buffer.h:616
static uint8_t * buf_prepend(struct buffer *buf, int size)
Definition buffer.h:604
#define BLEN(buf)
Definition buffer.h:126
static int buf_reverse_capacity(const struct buffer *buf)
Definition buffer.h:575
#define D_VLAN_DEBUG
Definition errlevel.h:153
Header file for server-mode related structures and functions.
#define msg(flags,...)
Definition error.h:152
@ VLAN_ONLY_UNTAGGED_OR_PRIORITY
Definition options.h:222
@ VLAN_ALL
Definition options.h:223
@ VLAN_ONLY_TAGGED
Definition options.h:221
#define OPENVPN_ETH_P_8021Q
Definition proto.h:61
#define SIZE_ETH_TO_8021Q_HDR
Definition proto.h:82
#define OPENVPN_8021Q_MASK_VID
Definition proto.h:73
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
struct buffer to_tun
Definition openvpn.h:376
Contains all state information for one tunnel.
Definition openvpn.h:474
struct context_2 c2
Level 2 context.
Definition openvpn.h:517
struct options options
Options loaded from command line or configuration file.
Definition openvpn.h:475
Main OpenVPN server state structure.
Definition multi.h:164
struct context top
Storage structure for process-wide configuration.
Definition multi.h:203
Server-mode state structure for one single VPN tunnel.
Definition multi.h:103
struct context context
The context structure storing state for this VPN tunnel.
Definition multi.h:144
uint16_t tpid
Definition proto.h:70
uint16_t pcp_cfi_vid
Definition proto.h:74
uint16_t proto
Definition proto.h:75
uint16_t proto
Definition proto.h:62
enum vlan_acceptable_frames vlan_accept
Definition options.h:714
bool vlan_tagging
Definition options.h:713
uint16_t vlan_pvid
Definition options.h:715
int16_t vlan_decapsulate(const struct context *c, struct buffer *buf)
Definition vlan.c:90
void vlan_process_outgoing_tun(struct multi_context *m, struct multi_instance *mi)
Definition vlan.c:300
static void vlanhdr_set_vid(struct openvpn_8021qhdr *hdr, const uint16_t vid)
Definition vlan.c:58
static uint16_t vlanhdr_get_vid(const struct openvpn_8021qhdr *hdr)
Definition vlan.c:41
void vlan_encapsulate(const struct context *c, struct buffer *buf)
Definition vlan.c:191
bool vlan_is_tagged(const struct buffer *buf)
Definition vlan.c:270