1 ---------------------------------
3 --- @brief Utility functions
for packets (rte_mbuf).
5 --- - General functions (timestamping, rate control, ...)
7 --- - Create packet types
8 ---------------------------------
10 local
ffi = require
"ffi"
14 local dpdkc = require
"dpdkc"
15 local dpdk = require
"dpdk"
17 local
bor,
band,
bnot, rshift, lshift= bit.bor, bit.band, bit.bnot, bit.rshift, bit.lshift
18 local istype =
ffi.istype
19 local write = io.write
22 -------------------------------------------------------------------------------------------
23 ---- General functions
24 -------------------------------------------------------------------------------------------
26 --- Module
for packets (rte_mbuf)
30 --- Retrieve the time stamp information.
31 --- @
return The timestamp or nil
if the packet was not time stamped.
33 if bit.
bor(self.ol_flags, dpdk.PKT_RX_IEEE1588_TMST) ~= 0 then
34 -- TODO: support timestamps that are stored in registers instead of the rx buffer
35 local data =
ffi.cast("uint32_t* ", self.
pkt.data)
36 -- TODO: this is only tested with the Intel 82580 NIC at the moment
37 -- the datasheet claims that low and high are swapped, but this doesn't seem to be the case
38 -- TODO: check other NICs
41 return high * 2^32 + low
45 --- Check if the PKT_RX_IEEE1588_TMST flag is
set.
46 --- Turns out that this flag is pretty pointless, it does not indicate
47 --- if the packet was actually timestamped, just that it came from a
48 --- queue/filter with timestamping enabled.
49 --- You probably want to use device:
hasTimestamp() and check the sequence number.
51 return bit.
bor(self.ol_flags, dpdk.PKT_RX_IEEE1588_TMST) ~= 0
54 function
pkt:getSecFlags()
55 local secp = bit.rshift(bit.
band(self.ol_flags, dpdk.PKT_RX_IPSEC_SECP), 11)
56 local secerr = bit.rshift(bit.
band(self.ol_flags, bit.
bor(dpdk.PKT_RX_SECERR_MSB, dpdk.PKT_RX_SECERR_LSB)), 12)
60 --- Offload VLAN tagging to the NIC for this packet.
62 local tci = vlan + bit.lshift(pcp or 0, 13) + bit.lshift(cfi or 0, 12)
63 self.
pkt.vlan_tci = tci
64 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_VLAN_PKT)
67 local VLAN_VALID_MASK = bit.
bor(dpdk.PKT_RX_VLAN_PKT, dpdk.PKT_TX_VLAN_PKT)
69 --- Get the VLAN associated with a received packet.
71 if bit.
bor(self.ol_flags, VLAN_VALID_MASK) == 0 then
74 local tci = self.
pkt.vlan_tci
75 return bit.
band(tci, 0xFFF), bit.rshift(tci, 13), bit.
band(bit.rshift(tci, 12), 1)
79 --- Set the time to wait before the packet is sent for software rate-controlled send methods.
80 --- @param delay The time to wait before this packet \(in bytes, i.e. 1 == 0.8 nanoseconds on 10 GbE\)
82 self.
pkt.hash.rss = delay
87 self.
pkt.hash.rss = 10^10 / 8 / (rate * 10^6) - self.
pkt.pkt_len - 24
92 self.
pkt.pkt_len = size
93 self.
pkt.data_len = size
96 --- Returns the packet data cast to the best fitting packet struct.
97 --- Starting with ethernet header.
98 --- @return packet data as cdata of best fitting packet
103 --- Dumps the packet data cast to the best fitting packet struct.
105 function
pkt:dump(bytes)
106 self:
get():dump(bytes or self.
pkt.pkt_len)
109 -------------------------------------------------------------------------------------------------------
110 ---- IPSec offloading
111 -------------------------------------------------------------------------------------------------------
113 --- Use IPsec offloading.
114 --- @param idx SA_IDX to use
115 --- @param sec_type IPSec type to use ("esp"/"ah")
116 --- @param esp_mode ESP mode to use encrypt(1) or authenticate(0)
118 local mode = esp_mode or 0
120 if sec_type == "esp" then
122 elseif sec_type == "ah" then
125 error("Wrong IPSec type (esp/ah)")
128 -- Set IPSec offload flag in advanced data transmit descriptor.
129 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_IPSEC)
132 --if idx < 0 or idx > 1023 then
133 -- error("SA_IDX has to be in
range 0-2013")
135 --self.ol_ipsec.sec.sa_idx = idx
136 self.ol_ipsec.data = bit.
bor(self.ol_ipsec.data, bit.lshift(bit.
band(idx, 0x3FF), 0))
138 -- Set ESP enc/auth mode
139 --if mode ~= 0 and mode ~= 1 then
140 -- error("Wrong IPSec mode")
142 --self.ol_ipsec.sec.mode = mode
143 self.ol_ipsec.data = bit.
bor(self.ol_ipsec.data, bit.lshift(bit.
band(mode, 0x1), 20))
145 -- Set IPSec ESP/AH type
146 --if sec_type == "esp" then
147 -- self.ol_ipsec.sec.type = 1
148 --elseif sec_type == "ah" then
149 -- self.ol_ipsec.sec.type = 0
151 -- error("Wrong IPSec type (esp/ah)")
153 self.ol_ipsec.data = bit.
bor(self.ol_ipsec.data, bit.lshift(bit.
band(t, 0x1), 19))
156 --- Set the ESP trailer length
157 --- @param len ESP Trailer length in bytes
159 --Disable
range check for performance reasons
160 --if len < 0 or len > 511 then
161 -- error("ESP trailer length has to be in
range 0-511")
163 --self.ol_ipsec.sec.esp_len = len -- dont use bitfields
164 self.ol_ipsec.data = bit.
bor(self.ol_ipsec.data, bit.lshift(bit.
band(len, 0x1FF), 10))
167 -------------------------------------------------------------------------------------------------------
168 ---- Checksum offloading
169 -------------------------------------------------------------------------------------------------------
171 --- Instruct the NIC to calculate the IP
checksum for this packet.
172 --- @param ipv4 Boolean to decide whether the packet uses IPv4 (
set to nil/true) or IPv6 (
set to anything else).
173 --- In case it is an IPv6 packet, do nothing (the header has no
checksum).
174 --- @param l2_len Length of the layer 2 header in bytes (default 14 bytes for ethernet).
175 --- @param l3_len Length of the layer 3 header in bytes (default 20 bytes for IPv4).
177 -- NOTE: this method cannot be moved to the udpPacket class because it doesn't (and can't) know the pktbuf it belongs to
178 ipv4 = ipv4 == nil or ipv4
180 l2_len = l2_len or 14
181 l3_len = l3_len or 20
182 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_IPV4_CSUM)
183 self.
pkt.header_lengths = l2_len * 512 + l3_len
187 --- Instruct the NIC to calculate the IP and UDP
checksum for this packet.
188 --- @param ipv4 Boolean to decide whether the packet uses IPv4 (
set to nil/true) or IPv6 (
set to anything else).
189 --- @param l2_len Length of the layer 2 header in bytes (default 14 bytes for ethernet).
190 --- @param l3_len Length of the layer 3 header in bytes (default 20 bytes for IPv4, 40 bytes for IPv6).
192 -- NOTE: this method cannot be moved to the udpPacket class because it doesn't (and can't) know the pktbuf it belongs to
193 ipv4 = ipv4 == nil or ipv4
194 l2_len = l2_len or 14
196 l3_len = l3_len or 20
197 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_IPV4_CSUM, dpdk.PKT_TX_UDP_CKSUM)
198 self.
pkt.header_lengths = l2_len * 512 + l3_len
199 -- calculate pseudo header
checksum because the NIC doesn't do this...
200 dpdkc.calc_ipv4_pseudo_header_checksum(self.
pkt.data, 20)
202 l3_len = l3_len or 40
203 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_UDP_CKSUM)
204 self.
pkt.header_lengths = l2_len * 512 + l3_len
205 -- calculate pseudo header
checksum because the NIC doesn't do this...
206 dpdkc.calc_ipv6_pseudo_header_checksum(self.
pkt.data, 30)
210 --- Instruct the NIC to calculate the IP and TCP
checksum for this packet.
211 --- @param ipv4 Boolean to decide whether the packet uses IPv4 (
set to nil/true) or IPv6 (
set to anything else).
212 --- @param l2_len Length of the layer 2 header in bytes (default 14 bytes for ethernet).
213 --- @param l3_len Length of the layer 3 header in bytes (default 20 bytes for IPv4, 40 bytes for IPv6).
215 -- NOTE: this method cannot be moved to the udpPacket class because it doesn't (and can't) know the pktbuf it belongs to
216 ipv4 = ipv4 == nil or ipv4
217 l2_len = l2_len or 14
219 l3_len = l3_len or 20
220 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_IPV4_CSUM, dpdk.PKT_TX_TCP_CKSUM)
221 self.
pkt.header_lengths = l2_len * 512 + l3_len
222 -- calculate pseudo header
checksum because the NIC doesn't do this...
223 dpdkc.calc_ipv4_pseudo_header_checksum(self.
pkt.data, 25)
225 l3_len = l3_len or 40
226 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_TCP_CKSUM)
227 self.
pkt.header_lengths = l2_len * 512 + l3_len
228 -- calculate pseudo header
checksum because the NIC doesn't do this...
229 dpdkc.calc_ipv6_pseudo_header_checksum(self.
pkt.data, 35)
235 self.ol_flags = bit.
bor(self.ol_flags, dpdk.PKT_TX_IEEE1588_TMST)
239 ----------------------------------------------------------------------------------
240 ---- Create
new packet type
241 ----------------------------------------------------------------------------------
243 -- functions of the packet
253 --- Create struct and functions for a
new packet.
254 --- For implemented headers (see proto/) these packets are defined in the section 'Packet struct' of each protocol file
255 --- @param args list of keywords (see makeStruct)
256 --- @return returns the constructor/cast function for this packet
262 packet.__index = packet
266 if not packetName then
267 printf("WARNING: Failed to create
new packet type.")
271 -- functions of the packet
272 packet.getArgs = function() return args end
274 packet.getName = function() return packetName end
288 -- runtime critical function, load specific code during runtime
291 -- functions for manual (not offloaded)
checksum calculations
292 -- runtime critical function, load specific code during runtime
295 for _, v in ipairs(args) do
297 -- if the header has a
checksum,
add a function to calculate it
298 if header == "ip4" or header == "
icmp" then -- FIXME NYI or header == "
udp" or header == "tcp" then
299 local key = 'calculate' .. member:gsub("^.",
string.upper) .. 'Checksum'
305 --
add functions to packet
306 ffi.metatype(packetName, packet)
308 -- return '
get'/'cast' for this kind of packet
309 return function(self) return ctype(self.
pkt.data) end
312 --- Get the name of the header and the name of the respective member of a packet
313 --- @param v Either the name of the header (then the member has the same name), or a table { header, member }
314 --- @
return Name of the header
315 --- @
return Name of the member
317 if type(v) == "table" then
320 -- only the header name
321 -- special alias for ethernet
322 if v == "ethernet" or v == "
eth" then
323 return "ethernet", "
eth"
325 -- otherwise header name = member name
331 --- Get all headers of a packet as list.
332 --- @param self The packet
333 --- @return Table of members of the packet
336 for i, v in ipairs(
self:getArgs()) do
342 --- Get the specified header of a packet (e.g. self.
eth).
343 --- @param self the packet (cdata)
344 --- @param h header to be returned
345 --- @return The member of the packet
351 --- Print a hex
dump of a packet.
352 --- @param self the packet
353 --- @param bytes Number of bytes to
dump. If no size is specified the payload is truncated.
355 bytes = bytes or
ffi.sizeof(self:getName())
360 -- headers in cleartext
361 for i, v in ipairs(self:getHeaders()) do
363 if i == 1 then write(" " .. str .. "\
n") else print(str) end
370 --- Set all members of all headers.
371 --- Per default, all members are
set to default values specified in the respective
set function.
372 --- Optional named arguments can be used to
set a member to a user-provided value.
373 --- The argument 'pktLength' can be used to automatically calculate and
set the length member of headers (e.g.
ip header).
375 ---
fill() --- only default values
376 ---
fill{ ethSrc=
"12:23:34:45:56:67", ipTTL=100 } --- all members are
set to
default values with the exception of ethSrc and ipTTL
377 ---
fill{ pktLength=64 } --- only
default values, length members of the headers are adjusted
379 --- @param
self The packet
380 --- @param args Table of named arguments. For a list of available arguments see
"See also"
381 --- @note This
function is slow. If you want to modify members of a header during a time critical section of your script use the respective setters.
383 namedArgs = namedArgs or {}
384 local headers =
self:getHeaders()
385 local args = self:getArgs()
386 local accumulatedLength = 0
387 for i, v in ipairs(headers) do
392 v:
fill(namedArgs, curMember)
394 accumulatedLength = accumulatedLength +
ffi.sizeof(v)
398 --- Retrieve the values of all members as list of named arguments.
399 --- @param self The packet
400 --- @return Table of named arguments. For a list of arguments see "See also".
404 local args =
self:getArgs()
405 for i, v in ipairs(self:getHeaders()) do
412 --- Try to find out what the next header in the payload of this packet is.
413 --- This function is only used for buf:
get/buf:
dump
414 --- @param self The packet
416 local name = self:getName()
417 local headers = self:getHeaders()
418 local nextHeader = headers[
#headers]:resolveNextHeader()
420 -- unable to resolve: either there is no next header, or MoonGen does not support it yet
421 -- either
case, we
stop and
return current type of packet
422 if not nextHeader then
427 -- we know the next header, append it
428 name = name ..
"__" .. nextHeader ..
"_"
430 --
if simple
struct (headername = membername) already exists we can directly cast
431 nextMember = nextHeader
432 newName = name .. nextMember
434 if not
pkt.packetStructs[newName] then
435 -- check
if a similar
struct with
this header order exists
438 for k, v in pairs(
pkt.packetStructs)
do
439 if string.find(k, newName) and not
string.find(
string.gsub(k, newName,
""),
"__") then
440 -- the type matches and there are no further headers following (which would have been indicated by another
"__")
449 -- last resort:
build new packet type. However, one has to consider that one header
450 -- might occur multiple times! In
this case the member must
get a
new (unique!) name.
451 local args =
self:getArgs()
454 local newMember = nextMember
456 --
build new args information and in the meantime check
for duplicates
457 for i, v in ipairs(args) do
459 if member == newMember then
460 -- found duplicate, increase counter for newMember and keep checking for this one now
461 counter = counter + 1
462 newMember = nextMember .. "_" .. counter
463 -- TODO this assumes that there never will be a <member_X+1> before a <member_X>
468 --
add new header and member
469 newArgs[
#newArgs + 1] = { nextHeader, newMember }
471 -- create
new packet. It is unlikely that exactly
this packet type with
this made up naming scheme will be used
472 -- Therefore, we don
't really want to "safe" the cast function
473 pkt.TMP_PACKET = packetCreate(unpack(newArgs))
475 -- name of the new packet type
476 newName = newName .. newMember
480 -- finally, cast the packet to the next better fitting packet type and continue resolving
481 return ffi.cast(newName .. "*", self):resolveLastHeader()
485 --- Set length for all headers.
486 --- Necessary when sending variable sized packets.
487 --- @param self The packet
488 --- @param length Length of the packet. Value for respective length member of headers get calculated using this value.
489 function packetSetLength(args)
491 -- build the setLength functions for all the headers in this packet type
492 local accumulatedLength = 0
493 for _, v in ipairs(args) do
494 local header, member = getHeaderMember(v)
495 if header == "ip4" or header == "udp" or header == "ptp" then
497 self.]] .. member .. [[:setLength(length - ]] .. accumulatedLength .. [[)
499 elseif header == "ip6" then
501 self.]] .. member .. [[:setLength(length - ]] .. accumulatedLength + 40 .. [[)
504 accumulatedLength = accumulatedLength + ffi.sizeof("struct " .. header .. "_header")
507 -- build complete function
509 return function(self, length)]]
513 -- load new function and return it
514 local func = assert(loadstring(str))()
519 --- Calculate all checksums manually (not offloading them).
520 --- There also exist functions to calculate the checksum of only one header.
521 --- Naming convention: pkt:calculate<member>Checksum() (for all existing packets member = {Ip, Tcp, Udp, Icmp})
522 --- @note Calculating checksums manually is extremely slow compared to offloading this task to the NIC (~65% performance loss at the moment)
523 --- @todo Manual calculation of udp and tcp checksums NYI
524 function packetCalculateChecksums(args)
526 for _, v in ipairs(args) do
527 local header, member = getHeaderMember(v)
529 -- if the header has a checksum, call the function
530 if header == "ip4" or header == "icmp" then -- FIXME NYI or header == "udp" or header == "tcp" then
532 self.]] .. member .. [[:calculateChecksum()
537 -- build complete function
539 return function(self)]]
543 -- load new function and return it
544 local func = assert(loadstring(str))()
549 --- Table that contains the names and args of all created packet structs
550 pkt.packetStructs = {}
552 -- List all created packet structs enlisted in packetStructs
553 -- Debugging function
554 function listPacketStructs()
555 printf("All available packet structs:")
556 for k, v in pairs(pkt.packetStructs) do
561 --- Creates a packet struct (cdata) consisting of different headers.
562 --- Simply list the headers in the order you want them to be in a packet.
563 --- If you want the member to be named differently, use the following syntax:
564 --- normal: <header> ; different membername: { <header>, <member> }.
565 --- Supported keywords: eth, arp, ptp, ip, ip6, udp, tcp, icmp
567 --- makeStruct('eth', { 'ip4
', 'ip' }, 'udp') --- creates an UDP packet struct
568 --- --- the ip4 member of the packet is named 'ip'
570 --- The name of the created (internal) struct looks as follows:
571 --- struct __HEADER1_MEMBER1__HEADER2_MEMBER2 ...
572 --- Only the "__" (double underscore) has the special meaning of "a new header starts with name
573 --- <everything until "_" (single underscore)>, followed by the member name <everything after "_"
574 --- until next header starts (indicated by next __)>"
575 --- @param args list of keywords/tables of keyword-member pairs
576 --- @return name name of the struct
577 --- @return ctype ctype of the struct
578 function packetMakeStruct(...)
582 -- add the specified headers and build the name
583 for _, v in ipairs(...) do
584 local header, member = getHeaderMember(v)
588 struct ]] .. header .. '_header
' .. member .. [[;
592 name = name .. "__" .. header .. "_" .. member
595 -- add rest of the struct
597 struct __attribute__((__packed__)) ]]
603 union payload_t payload;
607 name = "struct " .. name
609 -- check uniqueness of packet type (name of struct)
610 if pkt.packetStructs[name] then
611 printf("WARNING: Struct with name \"" .. name .. "\" already exists. Skipping.")
614 -- add struct definition
617 -- add to list of existing structs
618 pkt.packetStructs[name] = {...}
620 -- return full name and typeof
621 return name, ffi.typeof(name .. "*")
626 ---------------------------------------------------------------------------
628 ---------------------------------------------------------------------------
630 ffi.metatype("struct rte_mbuf", pkt)
param n optional(default=2047)
Create a new memory pool.
local ffi
low-level dpdk wrapper
function pkt setVlan(vlan, pcp, cfi)
Offload VLAN tagging to the NIC for this packet.
function checksum(data, len)
Calculate a 16 bit checksum.
function getTimeMicros()
Retrieve the system time with microseconds accuracy.
function pkt offloadUdpChecksum(ipv4, l2_len, l3_len)
Instruct the NIC to calculate the IP and UDP checksum for this packet.
function mg_filter_5tuple build(numCategories)
Builds the filter with the currently added rules.
function ahHeader setLength(int)
Set the Length.
function packetCreate(...)
Create struct and functions for a new packet.
function ipsecICV getString(doByteSwap)
Get the IPsec string.
pkt getEthernetPacket
Cast the packet to an ethernet packet.
function mod band(mask1, mask2, result)
Bitwise and.
function range(max, start,...)
Return all integerss in the range [start, max].
function pkt dump(bytes)
Dumps the packet data cast to the best fitting packet struct.
function ipsecICV set(icv)
Set the IPsec ICV.
function pkt enableTimestamps()
local udp
Udp protocol constants.
function mod bor(mask1, mask2, result)
Bitwise or.
function pkt setDelay(delay)
Set the time to wait before the packet is sent for software rate-controlled send methods.
function icmpHeader calculateChecksum(len)
Calculate the checksum.
function packetFill(self, namedArgs)
Set all members of all headers.
function pkt get()
Returns the packet data cast to the best fitting packet struct.
function mergeTables(...)
Merge tables.
function getHeaderMember(v)
Get the name of the header and the name of the respective member of a packet.
function printf(str,...)
Print a formatted string.
function ahHeader fill(args, pre)
Set all members of the ah header.
function pkt hasTimestamp()
Check if the PKT_RX_IEEE1588_TMST flag is set.
function pkt setRate(rate)
function ip4Addr add(val)
Add a number to an IPv4 address in-place.
function dumpHex(data, bytes)
Print a hex dump of cdata.
function mod stop()
request all tasks to exit
function pkt setESPTrailerLength(len)
Set the ESP trailer length.
function packetDump(self, bytes)
Print a hex dump of a packet.
local eth
Ethernet protocol constants.
function packetMakeStruct(...)
Creates a packet struct (cdata) consisting of different headers.
function pkt offloadTcpChecksum(ipv4, l2_len, l3_len)
Instruct the NIC to calculate the IP and TCP checksum for this packet.
function pkt getTimestamp()
Retrieve the time stamp information.
local ip
IP4 protocol constants.
function packetGetHeader(self, h)
Get the specified header of a packet (e.g.
function packetResolveLastHeader(self)
Try to find out what the next header in the payload of this packet is.
function pkt offloadIPSec(idx, sec_type, esp_mode)
Use IPsec offloading.
function packetCalculateChecksums(args)
Calculate all checksums manually (not offloading them).
local pkt
Module for packets (rte_mbuf)
function mod bnot(mask, result)
Bitwise not.
function pkt getVlan()
Get the VLAN associated with a received packet.
n
Create a new array of memory buffers (initialized to nil).
function ahHeader setDefaultNamedArgs(pre, namedArgs, nextHeader, accumulatedLength)
Change the default values for namedArguments (for fill/get) This can be used to for instance calculat...
function packetGet(self)
Retrieve the values of all members as list of named arguments.
function pkt setSize(size)
function packetGetHeaders(self)
Get all headers of a packet as list.
local icmp
Icmp4 protocol constants.
function packetSetLength(args)
Set length for all headers.
function pkt offloadIPChecksum(ipv4, l2_len, l3_len)
Instruct the NIC to calculate the IP checksum for this packet.