The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
ethernet.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 of the
4 * License as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14 */
15
16/**
17 * $Id: 45774a04e8cb082659d1382098d822e65fc40410 $
18 * @file ethernet.c
19 * @brief Functions to parse and construct ethernet headers.
20 *
21 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 */
23#include "ethernet.h"
24#include <string.h>
25#include <arpa/inet.h>
26#include <freeradius-devel/io/proto.h>
27
28/** Decodes an ethernet header with up to two levels of VLAN nesting
29 *
30 * Technically this should be a .1Q protocol, but because of how .1Q subsumes the
31 * ether_type field, it's just easier to munge it together with the ethernet
32 * decoder.
33 *
34 * @param[out] proto_ctx Header information extracted from the ethernet frame,
35 * and any additional VLAN tags discovered.
36 * Must point to memory of the size indicated by the
37 * #fr_proto_lib_t struct exported by this library.
38 * @param[in] data Start of packet data.
39 * @param[in] data_len of the data.
40 * @return
41 * - >0 Length of the header.
42 * - <=0 on failure.
43 */
44static ssize_t fr_ethernet_decode(void *proto_ctx, uint8_t const *data, size_t data_len)
45{
46 uint8_t const *p = data, *end = p + data_len;
47 ethernet_header_t const *ether_hdr = (void const *)p;
48 vlan_header_t const *vlan_hdr;
49 int i = 0;
50 uint16_t ether_type;
51 fr_ethernet_proto_ctx_t *ether_ctx = proto_ctx;
52
53 p += sizeof(*ether_hdr);
54 if (unlikely(p >= end)) {
55 ood:
56 fr_strerror_printf("Ethernet header length (%zu bytes) is greater than remaining "
57 "data in buffer (%zu bytes)", sizeof(*ether_hdr), data_len);
58 return 0;
59 }
60
61 memcpy(ether_ctx->dst_addr.addr, ether_hdr->dst_addr, sizeof(ether_ctx->dst_addr));
62 memcpy(ether_ctx->src_addr.addr, ether_hdr->src_addr, sizeof(ether_ctx->src_addr));
63 ether_type = ntohs(ether_hdr->ether_type);
64
65 p -= sizeof(ether_hdr->ether_type); /* reverse */
66 vlan_hdr = (void const *)p;
67 for (i = 0; i < 3; i++) {
68 switch (ether_type) {
69 /*
70 * There are a number of devices out there which
71 * double tag with 0x8100 *sigh*
72 */
73 case 0x8100: /* CVLAN */
74 case 0x9100: /* SVLAN */
75 case 0x9200: /* SVLAN */
76 case 0x9300: /* SVLAN */
77 if ((uint8_t const *)(++vlan_hdr) >= end) goto ood;
78 ether_type = ntohs(vlan_hdr->tag_type);
79 continue;
80
81 default:
82 break;
83 }
84
85 break;
86 }
87 vlan_hdr = (void const *)p; /* reset */
88
89 /*
90 * We don't explicitly memset the ctx
91 * so se these to zero now.
92 */
93 ether_ctx->svlan_tpid = 0;
94 ether_ctx->cvlan_tpid = 0;
95
96 switch (i) {
97 /*
98 * SVLAN
99 */
100 case 2:
101 ether_ctx->svlan_tpid = ntohs(vlan_hdr->tag_type);
102 ether_ctx->svlan_pcp = VLAN_PCP_UNPACK(vlan_hdr);
103 ether_ctx->svlan_dei = VLAN_DEI_UNPACK(vlan_hdr);
104 ether_ctx->svlan_vid = VLAN_VID_UNPACK(vlan_hdr);
105 vlan_hdr++;
107
108 /*
109 * CVLAN
110 */
111 case 1:
112 ether_ctx->cvlan_tpid = ntohs(vlan_hdr->tag_type);
113 ether_ctx->cvlan_pcp = VLAN_PCP_UNPACK(vlan_hdr);
114 ether_ctx->cvlan_dei = VLAN_DEI_UNPACK(vlan_hdr);
115 ether_ctx->cvlan_vid = VLAN_VID_UNPACK(vlan_hdr);
116 vlan_hdr++;
118
119 /*
120 * Naked
121 */
122 case 0:
123 ether_ctx->ether_type = ether_type; /* Always ends up being the payload type */
124 break;
125
126 default:
127 fr_strerror_const("Exceeded maximum level of VLAN tag nesting (2)");
128 break;
129 }
130 p = ((uint8_t const *)vlan_hdr) + sizeof(ether_hdr->ether_type);
131
132 ether_ctx->payload_len = data_len - (p - data);
133
134 return p - data;
135}
136
137/** Encodes an ethernet header and up to two levels of VLAN nesting
138 *
139 * @param[in] proto_ctx produced by #fr_ethernet_decode, or by the code
140 * creating a new packet.
141 * @param[out] data Where to write output data.
142 * @param[in] data_len Length of the output buffer.
143 * @return
144 * - >0 The length of data written to the buffer.
145 * - 0 an error occurred.
146 * - <0 The amount of buffer space we would have needed (as a negative integer).
147 */
148static ssize_t fr_ethernet_encode(void *proto_ctx, uint8_t *data, size_t data_len)
149{
150 fr_ethernet_proto_ctx_t *ether_ctx = proto_ctx;
151
152 uint8_t *p = data, *end = p + data_len;
153
154 ethernet_header_t *ether_hdr = (void *)p;
155
156 p += sizeof(ether_hdr->src_addr) + sizeof(ether_hdr->dst_addr);
157 if (unlikely(p >= end)) {
158 oob:
159 fr_strerror_printf("insufficient buffer space, needed %zu bytes, have %zu bytes",
160 p - data, data_len);
161 return data - p;
162 }
163
164 memcpy(ether_hdr->dst_addr, ether_ctx->dst_addr.addr, sizeof(ether_hdr->dst_addr));
165 memcpy(ether_hdr->src_addr, ether_ctx->src_addr.addr, sizeof(ether_hdr->src_addr));
166
167 /*
168 * Encode the SVLAN, CVLAN and ether type.
169 */
170 if (ether_ctx->svlan_tpid) {
171 vlan_header_t *svlan_hdr, *cvlan_hdr;
172 uint16_t *ether_type;
173
174 svlan_hdr = (void *)p;
175 p += sizeof(*svlan_hdr);
176
177 cvlan_hdr = (void *)p;
178 p += sizeof(*cvlan_hdr);
179
180 ether_type = (void *)p;
181 p += sizeof(*ether_type);
182
183 if (unlikely(p >= end)) goto oob;
184
185 svlan_hdr->tag_type = htons(ether_ctx->svlan_tpid);
186 svlan_hdr->tag_control = VLAN_TCI_PACK(ether_ctx->svlan_pcp, ether_ctx->svlan_dei,
187 ether_ctx->svlan_vid);
188
189 cvlan_hdr->tag_type = htons(ether_ctx->cvlan_tpid);
190 cvlan_hdr->tag_control = VLAN_TCI_PACK(ether_ctx->cvlan_pcp, ether_ctx->cvlan_dei,
191 ether_ctx->cvlan_vid);
192
193 *ether_type = htons(ether_ctx->ether_type);
194
195 return p - data;
196 }
197
198 /*
199 * Just encode the CVLAN and ether type.
200 */
201 if (ether_ctx->cvlan_tpid) {
202 vlan_header_t *cvlan_hdr;
203 uint16_t *ether_type;
204
205 cvlan_hdr = (void *)p;
206 p += sizeof(*cvlan_hdr);
207
208 ether_type = (void *)p;
209 p += sizeof(*ether_type);
210
211 if (unlikely(p >= end)) goto oob;
212
213 cvlan_hdr->tag_type = htons(ether_ctx->cvlan_tpid);
214 cvlan_hdr->tag_control = VLAN_TCI_PACK(ether_ctx->cvlan_pcp, ether_ctx->cvlan_dei,
215 ether_ctx->cvlan_vid);
216
217 *ether_type = htons(ether_ctx->ether_type);
218
219 return p - data;
220 }
221
222 /*
223 * Just encode the ether type.
224 */
225 p += sizeof(ether_hdr->ether_type);
226 if (unlikely(p >= end)) goto oob;
227
228 ether_hdr->ether_type = htons(ether_ctx->ether_type);
229
230 return p - data;
231}
232
233/** Inverts addresses, so that a decoder proto_ctx can be used for encoding
234 *
235 * @param[in] proto_ctx created by the user or decoder.
236 */
237static void fr_ethernet_invert(void *proto_ctx)
238{
239 fr_ethernet_proto_ctx_t *ether_ctx = proto_ctx;
240 uint8_t tmp_addr[ETHER_ADDR_LEN];
241
242 /*
243 * VLANs stay the same, we just need to swap the mac addresses
244 */
245 memcpy(tmp_addr, ether_ctx->dst_addr.addr, sizeof(tmp_addr));
246 memcpy(&ether_ctx->dst_addr, &ether_ctx->src_addr, sizeof(ether_ctx->dst_addr));
247 memcpy(ether_ctx->src_addr.addr, tmp_addr, sizeof(ether_ctx->src_addr));
248}
249
250/** Retrieve an option value from the proto_ctx
251 *
252 * @param[out] out value box to place option value into.
253 * @param[in] proto_ctx to retrieve value from.
254 * @param[in] group Option group. Which collection of options to query.
255 * @param[in] opt Option to retrieve.
256 * @return
257 * - 0 on success.
258 * - -1 on failure.
259 */
260static int fr_ethernet_get_option(fr_value_box_t *out, void const *proto_ctx, fr_proto_opt_group_t group, int opt)
261{
262 fr_ethernet_proto_ctx_t const *ether_ctx = proto_ctx;
263
264 switch (group) {
266 switch (opt) {
268 return fr_value_box(out, ether_ctx->svlan_tpid, true);
269
271 return fr_value_box(out, ether_ctx->svlan_pcp, true);
272
274 return fr_value_box(out, ether_ctx->svlan_dei, true);
275
277 return fr_value_box(out, ether_ctx->svlan_vid, true);
278
280 return fr_value_box(out, ether_ctx->cvlan_tpid, true);
281
283 return fr_value_box(out, ether_ctx->cvlan_pcp, true);
284
286 return fr_value_box(out, ether_ctx->cvlan_dei, true);
287
289 return fr_value_box(out, ether_ctx->cvlan_vid, true);
290
291 default:
292 fr_strerror_printf("Option %i group %i not implemented", opt, group);
293 return -1;
294 }
295
297 switch (opt) {
299 fr_value_box_init(out, FR_TYPE_SIZE, NULL, true);
300 out->vb_size = ether_ctx->payload_len;
301 return 0;
302
304 return fr_value_box_ethernet_addr(out, NULL, &ether_ctx->src_addr, true);
305
307 return fr_value_box_ethernet_addr(out, NULL, &ether_ctx->dst_addr, true);
308
310 return fr_value_box(out, ether_ctx->ether_type, true);
311
312 default:
313 fr_strerror_printf("Option %i group %i not implemented", opt, group);
314 return -1;
315 }
316
317 default:
318 fr_strerror_printf("Option group %i not implemented", group);
319 return -1;
320 }
321}
322
323/** Set an option in the proto_ctx
324 *
325 * @param[in] proto_ctx to set value in.
326 * @param[in] group Option group. Which collection of options opt exists in.
327 * @param[in] opt Option to set.
328 * @param[in] in value to set.
329 * @return
330 * - 0 on success.
331 * - -1 on failure.
332 */
333static int fr_ethernet_set_option(void *proto_ctx, fr_proto_opt_group_t group, int opt, fr_value_box_t *in)
334{
335 fr_ethernet_proto_ctx_t *ether_ctx = proto_ctx;
336
337 switch (group) {
339 switch (opt) {
341 return fr_value_unbox_shallow(&ether_ctx->svlan_tpid, in);
342
344 return fr_value_unbox_shallow(&ether_ctx->svlan_pcp, in);
345
347 return fr_value_unbox_shallow(&ether_ctx->svlan_dei, in);
348
350 return fr_value_unbox_shallow(&ether_ctx->svlan_vid, in);
351
353 return fr_value_unbox_shallow(&ether_ctx->cvlan_tpid, in);
354
356 return fr_value_unbox_shallow(&ether_ctx->cvlan_pcp, in);
357
359 return fr_value_unbox_shallow(&ether_ctx->cvlan_dei, in);
360
362 return fr_value_unbox_shallow(&ether_ctx->cvlan_vid, in);
363
364 default:
365 fr_strerror_printf("Option %i group %i not implemented", opt, group);
366 return -1;
367 }
368
370 switch (opt) {
372 if (in->type != FR_TYPE_SIZE) {
373 fr_strerror_printf("Unboxing failed. Needed type %s, had type %s",
375 fr_type_to_str(in->type));
376 return -1;
377 }
378 ether_ctx->payload_len = in->vb_size;
379 return 0;
380
382 return fr_value_unbox_ethernet_addr(&ether_ctx->src_addr, in);
383
385 return fr_value_unbox_ethernet_addr(&ether_ctx->dst_addr, in);
386
388 return fr_value_unbox_shallow(&ether_ctx->ether_type, in);
389
390 default:
391 fr_strerror_printf("Option %i group %i not implemented", opt, group);
392 return -1;
393 }
394
395 default:
396 fr_strerror_printf("Option group %i not implemented", group);
397 return -1;
398 }
399
400}
401
404 .magic = MODULE_MAGIC_INIT,
405 .name = "ethernet",
407
408 .decode = fr_ethernet_decode,
409 .encode = fr_ethernet_encode,
410 .invert = fr_ethernet_invert,
411 .get_option = fr_ethernet_get_option,
412 .set_option = fr_ethernet_set_option
413};
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define unlikely(_x)
Definition build.h:381
static fr_slen_t in
Definition dict.h:824
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static ssize_t fr_ethernet_decode(void *proto_ctx, uint8_t const *data, size_t data_len)
Decodes an ethernet header with up to two levels of VLAN nesting.
Definition ethernet.c:44
static int fr_ethernet_get_option(fr_value_box_t *out, void const *proto_ctx, fr_proto_opt_group_t group, int opt)
Retrieve an option value from the proto_ctx.
Definition ethernet.c:260
static ssize_t fr_ethernet_encode(void *proto_ctx, uint8_t *data, size_t data_len)
Encodes an ethernet header and up to two levels of VLAN nesting.
Definition ethernet.c:148
static void fr_ethernet_invert(void *proto_ctx)
Inverts addresses, so that a decoder proto_ctx can be used for encoding.
Definition ethernet.c:237
static int fr_ethernet_set_option(void *proto_ctx, fr_proto_opt_group_t group, int opt, fr_value_box_t *in)
Set an option in the proto_ctx.
Definition ethernet.c:333
fr_proto_lib_t const libfreeradius_ethernet
Definition ethernet.c:403
Structures and functions for parsing ethernet headers.
uint16_t ether_type
Ether type. Usually 0x0800 (IPv4) 0x086DD (IPv6).
Definition ethernet.h:90
fr_ethernet_t src_addr
Definition ethernet.h:88
uint8_t dst_addr[ETHER_ADDR_LEN]
Definition ethernet.h:79
uint16_t svlan_tpid
SVLAN tag type. If 0, no SVLAN present.
Definition ethernet.h:97
uint16_t cvlan_tpid
CVLAN tag type. If 0, no CVLAN/SVLAN present.
Definition ethernet.h:92
uint16_t cvlan_vid
CVLAN vlan ID.
Definition ethernet.h:95
fr_ethernet_t dst_addr
Definition ethernet.h:89
uint16_t svlan_vid
SVLAN vlan ID.
Definition ethernet.h:100
#define VLAN_VID_UNPACK(_vlan)
Unpack the VLAN ID from the TCI.
Definition ethernet.h:48
uint8_t cvlan_dei
CVLAN drop eligible indicator.
Definition ethernet.h:94
size_t payload_len
Remaining bytes after the ethernet header has been parsed.
Definition ethernet.h:102
uint8_t svlan_dei
SVLAN drop eligible indicator.
Definition ethernet.h:99
uint16_t tag_control
Definition ethernet.h:70
#define VLAN_DEI_UNPACK(_vlan)
Unpack the Drop Eligible Indicator from the TCI.
Definition ethernet.h:43
#define VLAN_TCI_PACK(_pcp, _dei, _vid)
Pack the PCP (Priority Code Point) DEI (Drop Eligible Indicator) and VID (VLAN ID)
Definition ethernet.h:61
@ PROTO_OPT_ETHERNET_SVLAN_PCP
Outer VLAN priority code point.
Definition ethernet.h:110
@ PROTO_OPT_ETHERNET_CVLAN_PCP
Inner VLAN priority code point.
Definition ethernet.h:114
@ PROTO_OPT_ETHERNET_CVLAN_VID
Inner VLAN ID.
Definition ethernet.h:116
@ PROTO_OPT_ETHERNET_SVLAN_VID
Outer VLAN ID.
Definition ethernet.h:112
@ PROTO_OPT_ETHERNET_CVLAN_DEI
Inner VLAN drop eligible indicator.
Definition ethernet.h:115
@ PROTO_OPT_ETHERNET_SVLAN_DEI
Outer VLAN drop eligible indicator.
Definition ethernet.h:111
@ PROTO_OPT_ETHERNET_SVLAN_TPID
Outer VLAN tag type.
Definition ethernet.h:109
@ PROTO_OPT_ETHERNET_CVLAN_TPID
Inner VLAN tag type.
Definition ethernet.h:113
uint16_t tag_type
Tag type.
Definition ethernet.h:68
#define VLAN_PCP_UNPACK(_vlan)
Unpack the Priority Code Point from the TCI.
Definition ethernet.h:38
uint16_t ether_type
Definition ethernet.h:81
uint8_t svlan_pcp
SVLAN priority code point 0-6.
Definition ethernet.h:98
uint8_t src_addr[ETHER_ADDR_LEN]
Definition ethernet.h:80
uint8_t cvlan_pcp
CVLAN priority code point 0-6.
Definition ethernet.h:93
Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
Definition ethernet.h:78
Src/dst link layer information.
Definition ethernet.h:87
A VLAN header.
Definition ethernet.h:67
uint8_t addr[6]
Ethernet address.
Definition inet.h:46
fr_proto_opt_group_t
Option contexts.
Definition proto.h:36
@ PROTO_OPT_GROUP_L2
Generic layer 2 options.
Definition proto.h:39
@ PROTO_OPT_GROUP_CUSTOM
Custom options exported by the library.
Definition proto.h:37
int opt_group
Option groups implemented by proto lib.
Definition proto.h:162
@ PROTO_OPT_L2_NEXT_PROTOCOL
Next protocol (if available).
Definition proto.h:52
@ PROTO_OPT_L2_SRC_ADDRESS
Source address.
Definition proto.h:50
@ PROTO_OPT_L2_DST_ADDRESS
Destination address.
Definition proto.h:51
@ PROTO_OPT_L2_PAYLOAD_LEN
Definition proto.h:49
The public structure exported by protocol encoding/decoding libraries.
Definition proto.h:157
unsigned short uint16_t
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
long int ssize_t
unsigned char uint8_t
#define ETHER_ADDR_LEN
Definition net.h:64
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
static fr_slen_t data
Definition value.h:1265
#define fr_value_box(_box, _var, _tainted)
Automagically fill in a box, determining the value type from the type of the C variable.
Definition value.h:871
static always_inline int fr_value_box_ethernet_addr(fr_value_box_t *dst, fr_dict_attr_t const *enumv, fr_ethernet_t const *src, bool tainted)
Definition value.h:823
static int fr_value_unbox_ethernet_addr(fr_ethernet_t *dst, fr_value_box_t *src)
Unbox an ethernet value (6 bytes, network byte order)
Definition value.h:915
#define fr_value_unbox_shallow(_var, _box)
Unbox simple types performing type checks.
Definition value.h:960
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:587
static size_t char ** out
Definition value.h:997