|
28 | 28 | :bidirectional = require"ipparse.fun" |
29 | 29 | :format, pack: sp, unpack: su = require "ipparse.lib.pack_compat" |
30 | 30 | {:need_bytes} = require "ipparse" |
| 31 | +{:band} = require "ipparse.lib.bit_compat" |
31 | 32 | unpack or= table.unpack |
32 | 33 |
|
| 34 | +--- EtherType for IEEE 802.1Q VLAN-tagged frames. |
| 35 | +ETH_P_8021Q = 0x8100 |
| 36 | + |
33 | 37 | --- Packs the Ethernet frame fields into a binary string. |
34 | | --- Constructs the binary representation of the Ethernet frame, including destination MAC, source MAC, EtherType, and optional payload data. |
| 38 | +-- If the `vlan` field is set and non-zero, inserts a 4-byte 802.1Q tag (TPID=0x8100, TCI=VID) |
| 39 | +-- between source MAC and EtherType. Otherwise produces a plain Ethernet frame. |
35 | 40 | -- @tparam table self The Ethernet frame object. |
36 | 41 | -- @treturn string The packed Ethernet frame as a binary string. |
37 | | -pack = => sp("c6 c6 >H", @dst, @src, @protocol) .. "#{@data or ''}" |
| 42 | +pack = => |
| 43 | + if @vlan and @vlan != 0 |
| 44 | + sp("c6 c6 >HHH", @dst, @src, ETH_P_8021Q, @vlan, @protocol) .. "#{@data or ''}" |
| 45 | + else |
| 46 | + sp("c6 c6 >H", @dst, @src, @protocol) .. "#{@data or ''}" |
38 | 47 |
|
39 | 48 | _mt = |
40 | 49 | --- Converts the Ethernet frame object to a binary string. |
41 | 50 | -- @treturn string Binary string representing the Ethernet frame. |
42 | 51 | __tostring: pack |
43 | 52 |
|
44 | 53 | --- Parses an Ethernet frame header from a data string. |
45 | | --- Extracts the destination MAC, source MAC, EtherType, and calculates offsets for the payload. |
| 54 | +-- Transparently handles 802.1Q VLAN-tagged frames: if EtherType is 0x8100, the 4-byte tag is |
| 55 | +-- consumed and the `vlan` field (VID, 12-bit) is set on the result; `protocol` reflects the |
| 56 | +-- inner EtherType and `data_off` points past the full header (18 bytes instead of 14). |
46 | 57 | -- @tparam string self The binary string containing the Ethernet frame. |
47 | 58 | -- @tparam[opt=1] number off Offset in the data string to start parsing from. Defaults to 1. |
48 | | --- @treturn table A table containing the Ethernet header fields: `dst` (destination MAC), `src` (source MAC), `protocol` (EtherType), `off` (input offset), `data_off` (offset after header). |
49 | | --- @treturn number The offset after the Ethernet header (data_off). |
| 59 | +-- @treturn table Fields: `dst`, `src`, `protocol` (inner EtherType), `vlan` (nil if untagged), |
| 60 | +-- `off` (input offset), `data_off` (offset of payload). |
| 61 | +-- @treturn number The offset after the header. |
50 | 62 | parse = (off=1) => |
51 | 63 | return nil, off unless need_bytes @, off, 14 |
52 | 64 | dst, src, protocol, data_off = su "c6 c6 >H", @, off |
53 | | - setmetatable({:dst, :src, :protocol, :off, :data_off}, _mt), data_off |
| 65 | + vlan = nil |
| 66 | + if protocol == ETH_P_8021Q |
| 67 | + return nil, off unless need_bytes @, data_off, 4 |
| 68 | + tci, protocol, data_off = su ">HH", @, data_off |
| 69 | + vlan = band tci, 0xFFF |
| 70 | + setmetatable({:dst, :src, :protocol, :vlan, :off, :data_off}, _mt), data_off |
54 | 71 |
|
55 | 72 | --- Creates a new instance of the Ethernet frame object and sets its metatable. |
56 | 73 | -- @tparam table self The Ethernet frame object. |
@@ -81,4 +98,4 @@ proto = |
81 | 98 | IP4: 0x800 |
82 | 99 | proto = bidirectional proto |
83 | 100 |
|
84 | | -:parse, :new, :pack, :proto, :mac2s, :s2mac |
| 101 | +:parse, :new, :pack, :proto, :mac2s, :s2mac, :ETH_P_8021Q |
0 commit comments