MoonGen
 All Files Functions Variables Pages
utils.lua
Go to the documentation of this file.
1 ---------------------------------
2 --- @file utils.lua
3 --- @brief Defines general utility functions.
4 --- @todo TODO docu
5 --- @todo local unpackers ... crashes lua2dox parser
6 ---------------------------------
7 
8 local bor, band, bnot, rshift, lshift, bswap = bit.bor, bit.band, bit.bnot, bit.rshift, bit.lshift, bit.bswap
9 local write = io.write
10 local format = string.format
11 local random, log, floor = math.random, math.log, math.floor
12 
13 --- Print a formatted string.
14 --- @todo docu
15 --- @param str
16 --- @param args
17 --- @param return
18 function printf(str, ...)
19  return print(str:format(...))
20 end
21 
22 --- Print a formatted error string.
23 --- @todo docu
24 --- @param str
25 --- @param args
26 function errorf(str, ...)
27  error(str:format(...), 2)
28 end
29 
30 --- TODO
31 --- @todo docu
32 --- @param f
33 --- @param args
34 --- @return
35 function mapVarArg(f, ...)
36  local l = { ... }
37  for i, v in ipairs(l) do
38  l[i] = f(v)
39  end
40  return unpack(l)
41 end
42 
43 --- TODO
44 --- @todo docu
45 --- @param t
46 --- @param f
47 --- @return
48 function map(t, f)
49  for i, v in ipairs(t) do
50  t[i] = f(v)
51  end
52  return t
53 end
54 
55 --- TODO
56 --- @todo docu
57 --- @param args
58 --- @return
59 function tostringall(...)
60  return mapVarArg(tostring, ...)
61 end
62 
63 --- TODO
64 --- @todo docu
65 --- @param args
66 --- @return
67 function tonumberall(...)
68  return mapVarArg(tonumber, ...)
69 end
70 
71 --- TODO
72 --- @todo docu
73 --- @param args
74 --- @return
75 function toCsv(...)
76  local vals = { tostringall(...) }
77  for i, v in ipairs(vals) do
78  if v:find("\"") then
79  v = v:gsub("\"", "\"\"")
80  end
81  -- fields just containing \n or \r but not \n\r are not required to be quoted by RFC 4180...
82  -- but I doubt that most parser could handle this ;)
83  if v:find("\n") or v:find("\r") or v:find("\"") or v:find(",") then
84  vals[i] = ("\"%s\""):format(v)
85  end
86  end
87  return table.concat(vals, ",")
88 end
89 
90 --- TODO
91 --- @todo docu
92 --- @param args
93 --- @return
94 function printCsv(...)
95  return print(toCsv(...))
96 end
97 
98 --- Get the time to wait (in byte-times) for the next packet based on a poisson process.
99 --- @param average the average wait time between two packets
100 --- @returns the number of byte-times to wait to achieve the given average wait-time
101 function poissonDelay(average)
102  return floor(-log(1 - random()) / (1 / average) + 0.5)
103 end
104 
105 --- TODO
106 --- @todo docu
107 --- @param rate
108 --- @param size
109 --- @return
110 function rateToByteDelay(rate, size)
111  size = size or 60
112  return 10^10 / 8 / (rate * 10^6) - size - 24
113 end
114 
115 --- Byte swap for 16 bit integers
116 --- @param n 16 bit integer
117 --- @return Byte swapped integer
118 function bswap16(n)
119  return bor(rshift(n, 8), lshift(band(n, 0xFF), 8))
120 end
121 
122 hton16 = bswap16
123 ntoh16 = hton16
124 
125 _G.bswap = bswap -- export bit.bswap to global namespace to be consistent with bswap16
126 hton = bswap
127 ntoh = hton
128 
129 local ffi = require "ffi"
130 
131 ffi.cdef [[
132  struct timeval {
133  long tv_sec;
134  long tv_usec;
135  };
136  int gettimeofday(struct timeval* tv, void* tz);
137 ]]
138 
139 do
140  local tv = ffi.new("struct timeval")
141 
142  function time()
143  ffi.C.gettimeofday(tv, nil)
144  return tonumber(tv.tv_sec) + tonumber(tv.tv_usec) / 10^6
145  end
146 end
147 
148 
149 --- Calculate a 16 bit checksum
150 --- @param data cdata to calculate the checksum for.
151 --- @param len Number of bytes to calculate the checksum for.
152 --- @return 16 bit integer
153 function checksum(data, len)
154  data = ffi.cast("uint16_t*", data)
155  local cs = 0
156  for i = 0, len / 2 - 1 do
157  cs = cs + data[i]
158  if cs >= 2^16 then
159  cs = band(cs, 0xFFFF) + 1
160  end
161  end
162  return band(bnot(cs), 0xFFFF)
163 end
164 
165 --- Parse a string to a MAC address
166 --- @param mac address in string format
167 --- @return address in mac_address format or nil if invalid address
168 function parseMacAddress(mac)
169  local bytes = {string.match(mac, '(%x+)[-:](%x+)[-:](%x+)[-:](%x+)[-:](%x+)[-:](%x+)')}
170  if bytes == nil then
171  return
172  end
173  for i = 1, 6 do
174  if bytes[i] == nil then
175  return
176  end
177  bytes[i] = tonumber(bytes[i], 16)
178  if bytes[i] < 0 or bytes[i] > 0xFF then
179  return
180  end
181  end
182 
183  addr = ffi.new("struct mac_address")
184  for i = 0, 5 do
185  addr.uint8[i] = bytes[i + 1]
186  end
187  return addr
188 end
189 
190 --- Parse a string to an IP address
191 --- @return address ip address in ip4_address or ip6_address format or nil if invalid address
192 --- @return boolean true if IPv4 address, false otherwise
193 function parseIPAddress(ip)
194  ip = tostring(ip)
195  local address = parseIP4Address(ip)
196  if address == nil then
197  return parseIP6Address(ip), false
198  end
199  return address, true
200 end
201 
202 --- Parse a string to an IPv4 address
203 --- @param ip address in string format
204 --- @return address in uint32 format or nil if invalid address
205 function parseIP4Address(ip)
206  ip = tostring(ip)
207  local bytes = {string.match(ip, '(%d+)%.(%d+)%.(%d+)%.(%d+)')}
208  if bytes == nil then
209  return
210  end
211  for i = 1, 4 do
212  if bytes[i] == nil then
213  return
214  end
215  bytes[i] = tonumber(bytes[i])
216  if bytes[i] < 0 or bytes[i] > 255 then
217  return
218  end
219  end
220 
221  -- build a uint32
222  ip = bytes[1]
223  for i = 2, 4 do
224  ip = bor(lshift(ip, 8), bytes[i])
225  end
226  return ip
227 end
228 
229 ffi.cdef[[
230 int inet_pton(int af, const char *src, void *dst);
231 ]]
232 
233 --- Parse a string to an IPv6 address
234 --- @param ip address in string format
235 --- @return address in ip6_address format or nil if invalid address
236 function parseIP6Address(ip)
237  ip = tostring(ip)
238  local LINUX_AF_INET6 = 10 --preprocessor constant of Linux
239  local tmp_addr = ffi.new("union ip6_address")
240  local res = ffi.C.inet_pton(LINUX_AF_INET6, ip, tmp_addr)
241  if res == 0 then
242  return nil
243  end
244 
245  local addr = ffi.new("union ip6_address")
246  addr.uint32[0] = bswap(tmp_addr.uint32[3])
247  addr.uint32[1] = bswap(tmp_addr.uint32[2])
248  addr.uint32[2] = bswap(tmp_addr.uint32[1])
249  addr.uint32[3] = bswap(tmp_addr.uint32[0])
250 
251  return addr
252 end
253 
254 --- Retrieve the system time with microseconds accuracy.
255 --- @todo use some C function to get microseconds.
256 --- @return System time in hh:mm:ss.uuuuuu format.
257 function getTimeMicros()
258  local t = time()
259  local h, m, s, u
260  s = math.floor(t) -- round to seconds
261  u = t - s -- micro seconds
262  m = math.floor(s / 60) -- total minutes
263  s = s - m * 60 -- remaining seconds
264  h = math.floor(m / 60) -- total hours
265  m = m - h * 60 -- remaining minutes
266  h = h % 24 -- hour of the day
267  s = s + u -- seconds + micro seconds
268  return format("%02d:%02d:%02.6f", h, m, s)
269 end
270 
271 --- Print a hex dump of cdata.
272 --- @param data The cdata to be dumped.
273 --- @param bytes Number of bytes to dump.
274 function dumpHex(data, bytes)
275  local data = ffi.cast("uint8_t*", data)
276  for i = 0, bytes - 1 do
277  if i % 16 == 0 then -- new line
278  write(format(" 0x%04x: ", i))
279  end
280 
281  write(format("%02x", data[i]))
282 
283  if i % 2 == 1 then -- group 2 bytes
284  write(" ")
285  end
286  if i % 16 == 15 then -- end of 16 byte line
287  write("\n")
288  end
289  end
290  write("\n\n")
291 end
292 
293 --- Merge tables.
294 --- @param args Arbitrary amount of tables to get merged.
295 function mergeTables(...)
296  local table = {}
297  if select("#", ...) > 0 then
298  table = select(1, ...)
299  for i = 2, select("#", ...) do
300  for k,v in pairs(select(i, ...)) do
301  table[k] = v
302  end
303  end
304  end
305  return table
306 end
307 
308 --- Return all integerss in the range [start, max].
309 --- @param max upper bound
310 --- @param start lower bound, default = 1
311 function range(max, start, ...)
312  start = start or 1
313  if start > max then
314  return ...
315  end
316  return start, range(max, start + 1, select(2, ...))
317 end
318 
319 local band = bit.band
320 local sar = bit.arshift
321 
322 --- Increment a wrapping counter, i.e. (val + 1) % max
323 --- This function is optimized to generate branchless code and faster than a naive modulo-based implementation.
324 --- @note: all attempts to wrap this in a nice and simple class have failed (~30% performance impact).
325 --- @param val Current value (number)
326 --- @param max Maximum allowed value of val (number)
327 --- @return Incremented and wrapped number
328 function incAndWrap(val, max)
329  return band(val + 1, sar(val - max + 1, 31))
330 end
331 
332 local unpackers = setmetatable({}, { __index = function(self, n)
333  local func = loadstring(([[
334  return function(tbl)
335  return ]] .. ("tbl[%d], "):rep(n):sub(0, -3) .. [[
336  end
337  ]]):format(range(n)))()
338  rawset(self, n, func)
339  return func
340 end })
341 
342 --- unpack() with support for arrays with 'holes'.
343 --- @param tbl Table to unpack
344 --- @return Unpacked values
345 function unpackAll(tbl)
346  return unpackers[table.maxn(tbl)](tbl)
347 end
function rateToByteDelay(rate, size)
TODO.
local ffi
low-level dpdk wrapper
Definition: dpdkc.lua:6
function checksum(data, len)
Calculate a 16 bit checksum.
function getTimeMicros()
Retrieve the system time with microseconds accuracy.
function parseIPAddress(ip)
Parse a string to an IP address.
function mg_filter_5tuple build(numCategories)
Builds the filter with the currently added rules.
function bswap16(n)
Byte swap for 16 bit integers.
function mod band(mask1, mask2, result)
Bitwise and.
function toCsv(...)
TODO.
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 tostringall(...)
TODO.
function mod new(n)
function mod bor(mask1, mask2, result)
Bitwise or.
function mergeTables(...)
Merge tables.
function tonumberall(...)
TODO.
function printf(str,...)
Print a formatted string.
function printCsv(...)
TODO.
function dumpHex(data, bytes)
Print a hex dump of cdata.
function parseIP6Address(ip)
Parse a string to an IPv6 address.
function map(t, f)
TODO.
local ip
IP4 protocol constants.
Definition: ip4.lua:25
function incAndWrap(val, max)
Increment a wrapping counter, i.e.
function mod bnot(mask, result)
Bitwise not.
n
Create a new array of memory buffers (initialized to nil).
Definition: memory.lua:76
function errorf(str,...)
Print a formatted error string.
function poissonDelay(average)
Get the time to wait (in byte-times) for the next packet based on a poisson process.
function parseMacAddress(mac)
Parse a string to a MAC address.
function mapVarArg(f,...)
TODO.
function parseIP4Address(ip)
Parse a string to an IPv4 address.