MoonGen
 All Files Functions Variables Pages
ipsec.lua
Go to the documentation of this file.
1 ---------------------------------
2 --- @file ipsec.lua
3 --- @brief IPsec (ESP/AH) offloading.
4 --- @todo Documentation
5 ---------------------------------
6 
7 local mod = {}
8 
9 local dpdkc = require "dpdkc"
10 local dpdk = require "dpdk"
11 local ffi = require "ffi"
12 
13 -- Intel X540 registers
14 local SECTXCTRL = 0x00008800
15 local SECRXCTRL = 0x00008D00
16 local SECTXSTAT = 0x00008804
17 local SECRXSTAT = 0x00008D04
18 local SECTXMINIFG = 0x00008810
19 local SECTXBUFFAF = 0x00008808
20 
21 local IPSTXIDX = 0x00008900
22 local IPSTXKEY_3 = 0x00008914 --MSB of key
23 local IPSTXKEY_2 = 0x00008910
24 local IPSTXKEY_1 = 0x0000890C
25 local IPSTXKEY_0 = 0x00008908 --LSB of key
26 local IPSTXSALT = 0x00008904
27 
28 local IPSRXIDX = 0x00008E00
29 local IPSRXKEY_3 = 0x00008E28 --MSB of key
30 local IPSRXKEY_2 = 0x00008E24
31 local IPSRXKEY_1 = 0x00008E20
32 local IPSRXKEY_0 = 0x00008E1C --LSB ofkey
33 local IPSRXSALT = 0x00008E2C
34 local IPSRXMOD = 0x00008E30
35 
36 --Note: Field is defined in Big Endian (LS byte is first on the wire).
37 local IPSRXIPADDR_3 = 0x00008E10 --LSB of IPv6 or IPv4
38 local IPSRXIPADDR_2 = 0x00008E0C
39 local IPSRXIPADDR_1 = 0x00008E08
40 local IPSRXIPADDR_0 = 0x00008E04 --MSB of IPv6 or 0
41 
42 --Note: Field is defined in Big Endian (LS byte is first on the wire).
43 local IPSRXSPI = 0x00008E14
44 local IPSRXIPIDX = 0x00008E18
45 
46 -- Helper function to return padded, unsigned 32bit hex string.
47 function uhex32(x)
48  return bit.tohex(x, 8)
49 end
50 
51 -- Helper function to clear a single bit
52 function clear_bit32(reg32, idx)
53  if idx < 0 or idx > 31 then
54  error("Idx must be in range 0-31")
55  end
56  local mask = bit.bnot(bit.lshift(0x1, idx))
57  return bit.band(reg32, mask)
58 end
59 
60 -- Helper function to set a single bit
61 function set_bit32(reg32, idx)
62  if idx < 0 or idx > 31 then
63  error("Idx must be in range 0-31")
64  end
65  return bit.bor(reg32, bit.lshift(0x1, idx))
66 end
67 
68 -- Helper function to clear the bits (MSB) from..to (LSB)
69 function clear_bits32(reg32, from, to)
70  local tmp = reg32
71  for i=from,to,-1 do
72  tmp = clear_bit32(tmp, i)
73  end
74  return tmp
75 end
76 
77 -- Helper function to set the bits (MSB) from..to (LSB)
78 function set_bits32(reg32, from, to, value)
79  local upper_limit = math.pow(2, ((from-to)+1))-1 --i.e. (2^num_bits)-1
80  if value < 0 or value > upper_limit then
81  error("Value must be in range 0-"..upper_limit)
82  end
83  local tmp = clear_bits32(reg32, from, to)
84  return bit.bor(tmp, bit.lshift(value, to))
85 end
86 
87 function dump_regs(port)
88  --print("===== DUMP REGS =====")
89  --local reg = dpdkc.read_reg32(port, SECTXCTRL)
90  --print("SECTXCTRL: 0x"..uhex32(reg))
91  --local reg = dpdkc.read_reg32(port, SECRXCTRL)
92  --print("SECRXCTRL: 0x"..uhex32(reg))
93  --local reg = dpdkc.read_reg32(port, SECTXSTAT)
94  --print("SECTXSTAT: 0x"..uhex32(reg))
95  --local reg = dpdkc.read_reg32(port, SECRXSTAT)
96  --print("SECRXSTAT: 0x"..uhex32(reg))
97  --local reg = dpdkc.read_reg32(port, SECTXMINIFG)
98  --print("SECTXMINIFG: 0x"..uhex32(reg))
99  --local reg = dpdkc.read_reg32(port, SECTXBUFFAF)
100  --print("SECTXBUFFAF: 0x"..uhex32(reg))
101 end
102 
103 --- Enable the Hardware Crypto Engine.
104 --- This function must be called before using any other IPSec functions
105 --- @param port The port/interface to use
106 function mod.enable(port)
107  --print("IPsec enable, port: "..port)
108  --dump_regs(port)
109 
110  -- Stop TX data path (set TX_DIS bit)
111  local SECTXCTRL__VALUE = set_bit32(dpdkc.read_reg32(port, SECTXCTRL), 1) --set TX_DIS
112  dpdkc.write_reg32(port, SECTXCTRL, SECTXCTRL__VALUE)
113 
114  -- Stop RX data path (set RX_DIS bit)
115  local SECRXCTRL__VALUE = set_bit32(dpdkc.read_reg32(port, SECRXCTRL), 1) --set RX_DIS
116  dpdkc.write_reg32(port, SECRXCTRL, SECRXCTRL__VALUE)
117 
118  -- Wait for the data paths to be emptied by hardware (check SECTX/RX_RDY bits).
119  repeat
120  --print("Waiting for registers to be asserted by hardware...")
121  dpdk.sleepMillis(100) -- wait 100ms before poll
122  local SECTXSTAT__SECTX_RDY = bit.band(dpdkc.read_reg32(port, SECTXSTAT), 0x1)
123  local SECRXSTAT__SECRX_RDY = bit.band(dpdkc.read_reg32(port, SECRXSTAT), 0x1)
124  --print("SECTX_RDY: "..SECTXSTAT__SECTX_RDY..", SECRX_RDY: "..SECRXSTAT__SECRX_RDY)
125  until SECTXSTAT__SECTX_RDY == 0x1 and SECRXSTAT__SECRX_RDY == 0x1
126 
127  -- Set MINSECIFG to 0x3
128  local SECTXMINIFG__VALUE = set_bits32(dpdkc.read_reg32(port, SECTXMINIFG), 3, 0, 0x3) --set MINSECIFG
129  dpdkc.write_reg32(port, SECTXMINIFG, SECTXMINIFG__VALUE)
130 
131  -- Set FULLTHRESH to 0x15
132  local SECTXBUFFAF__VALUE = set_bits32(dpdkc.read_reg32(port, SECTXBUFFAF), 9, 0, 0x15) --set FULLTHRESH
133  dpdkc.write_reg32(port, SECTXBUFFAF, SECTXBUFFAF__VALUE)
134 
135  -- Enable TX crypto engine
136  local SECTXCTRL__VALUE = clear_bit32(dpdkc.read_reg32(port, SECTXCTRL), 0) --clear SECTX_DIS
137  SECTXCTRL__VALUE = set_bit32(SECTXCTRL__VALUE, 2) --set STORE_FORWARD
138  dpdkc.write_reg32(port, SECTXCTRL, SECTXCTRL__VALUE)
139 
140  -- Enable RX crypto engine
141  local SECRXCTRL__VALUE = clear_bit32(dpdkc.read_reg32(port, SECRXCTRL), 0) --clear SECRX_DIS
142  dpdkc.write_reg32(port, SECRXCTRL, SECRXCTRL__VALUE)
143 
144  -- Enable IPsec TX SA lookup
145  local IPSTXIDX__VALUE = set_bit32(dpdkc.read_reg32(port, IPSTXIDX), 0) --set IPS_TX_EN
146  dpdkc.write_reg32(port, IPSTXIDX, IPSTXIDX__VALUE)
147 
148  -- Enable IPsec RX SA lookup
149  local IPSRXIDX__VALUE = set_bit32(dpdkc.read_reg32(port, IPSRXIDX), 0) --set IPS_RX_EN
150  dpdkc.write_reg32(port, IPSRXIDX, IPSRXIDX__VALUE)
151 
152  -- Restart TX data path (clear TX_DIS bit)
153  local SECTXCTRL__VALUE = clear_bit32(dpdkc.read_reg32(port, SECTXCTRL), 1) --clear TX_DIS
154  dpdkc.write_reg32(port, SECTXCTRL, SECTXCTRL__VALUE)
155 
156  -- Restart RX data path (clear RX_DIS bit)
157  local SECRXCTRL__VALUE = clear_bit32(dpdkc.read_reg32(port, SECRXCTRL), 1) --clear RX_DIS
158  dpdkc.write_reg32(port, SECRXCTRL, SECRXCTRL__VALUE)
159 
160  --dump_regs(port)
161 end
162 
163 --- Disable the Hardware Crypto Engine.
164 --- This function should be called after using the other IPSec functions
165 --- @param port The port/interface to use
166 function mod.disable(port)
167  --print("IPsec disable, port: "..port)
168  dump_regs(port)
169 
170  -- Stop TX data path (set TX_DIS bit)
171  local SECTXCTRL__VALUE = set_bit32(dpdkc.read_reg32(port, SECTXCTRL), 1) --set TX_DIS
172  dpdkc.write_reg32(port, SECTXCTRL, SECTXCTRL__VALUE)
173 
174  -- Stop RX data path (set RX_DIS bit)
175  local SECRXCTRL__VALUE = set_bit32(dpdkc.read_reg32(port, SECRXCTRL), 1) --set RX_DIS
176  dpdkc.write_reg32(port, SECRXCTRL, SECRXCTRL__VALUE)
177 
178  -- Wait for the data paths to be emptied by hardware (check SECTX/RX_RDY bits).
179  repeat
180  --print("Waiting for registers to be asserted by hardware...")
181  dpdk.sleepMillis(100) -- wait 100ms before poll
182  local SECTXSTAT__SECTX_RDY = bit.band(dpdkc.read_reg32(port, SECTXSTAT), 0x1)
183  local SECRXSTAT__SECRX_RDY = bit.band(dpdkc.read_reg32(port, SECRXSTAT), 0x1)
184  --print("SECTX_RDY: "..SECTXSTAT__SECTX_RDY..", SECRX_RDY: "..SECRXSTAT__SECRX_RDY)
185  until SECTXSTAT__SECTX_RDY == 0x1 and SECRXSTAT__SECRX_RDY == 0x1
186 
187  -- Disable IPsec TX SA lookup
188  local IPSTXIDX__VALUE = clear_bit32(dpdkc.read_reg32(port, IPSTXIDX), 0) --clear IPS_TX_EN
189  dpdkc.write_reg32(port, IPSTXIDX, IPSTXIDX__VALUE)
190 
191  -- Disable IPsec RX SA lookup
192  local IPSRXIDX__VALUE = clear_bit32(dpdkc.read_reg32(port, IPSRXIDX), 0) --clear IPS_RX_EN
193  dpdkc.write_reg32(port, IPSRXIDX, IPSRXIDX__VALUE)
194 
195  --TODO: what about MINSECIFG?
196 
197  -- Set FULLTHRESH to 0x250
198  local SECTXBUFFAF__VALUE = set_bits32(dpdkc.read_reg32(port, SECTXBUFFAF), 9, 0, 0x250) --set FULLTHRESH
199  dpdkc.write_reg32(port, SECTXBUFFAF, SECTXBUFFAF__VALUE)
200 
201  -- Disable TX crypto engine
202  local SECTXCTRL__VALUE = set_bit32(dpdkc.read_reg32(port, SECTXCTRL), 0) --set SECTX_DIS
203  SECTXCTRL__VALUE = clear_bit32(SECTXCTRL__VALUE, 2) --clear STORE_FORWARD
204  dpdkc.write_reg32(port, SECTXCTRL, SECTXCTRL__VALUE)
205 
206  -- Disable RX crypto engine
207  local SECRXCTRL__VALUE = set_bit32(dpdkc.read_reg32(port, SECRXCTRL), 0) --set SECRX_DIS
208  dpdkc.write_reg32(port, SECRXCTRL, SECRXCTRL__VALUE)
209 
210  -- Restart TX data path (clear TX_DIS bit)
211  local SECTXCTRL__VALUE = clear_bit32(dpdkc.read_reg32(port, SECTXCTRL), 1) --clear TX_DIS
212  dpdkc.write_reg32(port, SECTXCTRL, SECTXCTRL__VALUE)
213 
214  -- Restart RX data path (clear RX_DIS bit)
215  local SECRXCTRL__VALUE = clear_bit32(dpdkc.read_reg32(port, SECRXCTRL), 1) --clear RX_DIS
216  dpdkc.write_reg32(port, SECRXCTRL, SECRXCTRL__VALUE)
217 
218  dump_regs(port)
219 end
220 
221 --- Write AES 128 bit Key and Salt into the Hardware TX SA table
222 --- @param port The port/interface to use
223 --- @parma idx Index into TX SA table (0-1023)
224 --- @param key 128 bit AES key (as hex string)
225 --- @param salt 32 bit AES salt (as hex string)
226 function mod.tx_set_key(port, idx, key, salt)
227  if idx > 1023 or idx < 0 then
228  error("Idx must be in range 0-1023")
229  end
230  if string.len(key) ~= 32 then
231  error("Key must be 128 bit (hex string).")
232  end
233  if string.len(salt) ~= 8 then
234  error("Salt must be 32 bit (hex string).")
235  end
236 
237  local key_3 = tonumber(string.sub(key, 1, 8), 16) --MSB
238  local key_2 = tonumber(string.sub(key, 9, 16), 16)
239  local key_1 = tonumber(string.sub(key, 17, 24), 16)
240  local key_0 = tonumber(string.sub(key, 25, 32), 16) --LSB
241  local _salt = tonumber(salt, 16)
242 
243  -- Prepare command to write key to SA_IDX
244  local value = set_bits32(dpdkc.read_reg32(port, IPSTXIDX), 12, 3, idx) --set SA_IDX
245  value = clear_bit32(value, 30) --clear READ
246  value = set_bit32(value, 31) --set WRITE
247  --print("IPSTXIDX: 0x"..uhex32(value))
248 
249  --prepare registers
250  dpdkc.write_reg32(port, IPSTXKEY_3, key_3)
251  dpdkc.write_reg32(port, IPSTXKEY_2, key_2)
252  dpdkc.write_reg32(port, IPSTXKEY_1, key_1)
253  dpdkc.write_reg32(port, IPSTXKEY_0, key_0)
254  dpdkc.write_reg32(port, IPSTXSALT, _salt)
255  --push to hw
256  dpdkc.write_reg32(port, IPSTXIDX, value)
257  --pass SA_IDX via 'TX context descriptor' to use this SA!
258 end
259 
260 --- Read AES 128 bit Key and Salt from the Hardware TX SA table
261 --- @param port The port/interface to use
262 --- @param idx Index into the TX SA table (0-1023)
263 --- @return Key and Salt (as hex string)
264 function mod.tx_get_key(port, idx)
265  if idx > 1023 or idx < 0 then
266  error("Idx must be in range 0-1023")
267  end
268 
269  -- Prepare command to read key from SA_IDX
270  local value = set_bits32(dpdkc.read_reg32(port, IPSTXIDX), 12, 3, idx) --set SA_IDX
271  value = set_bit32(value, 30) --set READ
272  value = clear_bit32(value, 31) --clear WRITE
273  --print("IPSTXIDX: 0x"..uhex32(value))
274 
275  --pull from hw
276  dpdkc.write_reg32(port, IPSTXIDX, value)
277 
278  --fetch result
279  local key_3 = dpdkc.read_reg32(port, IPSTXKEY_3)
280  local key_2 = dpdkc.read_reg32(port, IPSTXKEY_2)
281  local key_1 = dpdkc.read_reg32(port, IPSTXKEY_1)
282  local key_0 = dpdkc.read_reg32(port, IPSTXKEY_0)
283  local _salt = dpdkc.read_reg32(port, IPSTXSALT)
284 
285  local key = uhex32(key_3)..uhex32(key_2)..uhex32(key_1)..uhex32(key_0)
286 
287  return key, uhex32(_salt)
288 end
289 
290 --- Write AES 128 bit Key and Salt into the Hardware RX SA table
291 --- @param port the port/interface to use
292 --- @param idx Index into SA RX table (0-1023)
293 --- @param key 128 bit AES key (as hex string)
294 --- @param salt 32 bit AES salt (as hex string)
295 --- @param ip_ver IP Version for which this SA is valid (4 or 6)
296 --- @param proto IPSec protocol type to use ("esp" or "ah")
297 --- @param decrypt ESP mode (1=ESP decrypt and authenticate, 0=ESP authenticate only)
298 function mod.rx_set_key(port, idx, key, salt, ip_ver, proto, decrypt)
299  if idx > 1023 or idx < 0 then
300  error("Idx must be in range 0-1023")
301  end
302  if string.len(key) ~= 32 then
303  error("Key must be 128 bit (hex string).")
304  end
305  if string.len(salt) ~= 8 then
306  error("Salt must be 32 bit (hex string).")
307  end
308 
309  local ipv6 = nil
310  if ip_ver == 4 then
311  ipv6 = 0
312  elseif ip_ver == 6 then
313  ipv6 = 1
314  else
315  error("IP version must be either 4 or 6")
316  end
317 
318  local esp = nil
319  if proto == "esp" then
320  esp = 1
321  elseif proto == "ah" then
322  esp = 0
323  else
324  error("Protocol must be either 'esp' or 'ah'")
325  end
326 
327  local esp_mode = decrypt or 0
328  if esp_mode ~= 1 and esp_mode ~= 0 then
329  error("ESP Decrypt must be either 0 or 1")
330  end
331 
332  local key_3 = tonumber(string.sub(key, 1, 8), 16) --MSB
333  local key_2 = tonumber(string.sub(key, 9, 16), 16)
334  local key_1 = tonumber(string.sub(key, 17, 24), 16)
335  local key_0 = tonumber(string.sub(key, 25, 32), 16) --LSB
336  local _salt = tonumber(salt, 16)
337 
338  -- Prepare command to write KEY_TABLE at TB_IDX
339  local value = set_bits32(dpdkc.read_reg32(port, IPSRXIDX), 2, 1, 0x3) --set TABLE to KEY(0x3)
340  value = set_bits32(value, 12, 3, idx) --set TB_IDX
341  value = clear_bit32(value, 30) --clear READ
342  value = set_bit32(value, 31) --set WRITE
343  --print("IPSRXIDX: 0x"..uhex32(value))
344 
345  -- Prepare security mode register
346  local mode = set_bit32(dpdkc.read_reg32(port, IPSRXMOD), 0) --set VALID, 1=valid, 0=invalid SA
347  mode = set_bits32(mode, 2, 2, esp) --set PROTO, 1=ESP, 0=AH
348  mode = set_bits32(mode, 3, 3, esp_mode) --set DECRYPT, 1=decrypt(ESP), 0=authenticate(ESP)
349  mode = set_bits32(mode, 4, 4, ipv6) --set IPV6, 1=IPv6, 0=IPv4
350 
351  --prepare registers
352  dpdkc.write_reg32(port, IPSRXKEY_3, key_3)
353  dpdkc.write_reg32(port, IPSRXKEY_2, key_2)
354  dpdkc.write_reg32(port, IPSRXKEY_1, key_1)
355  dpdkc.write_reg32(port, IPSRXKEY_0, key_0)
356  dpdkc.write_reg32(port, IPSRXSALT, _salt)
357  dpdkc.write_reg32(port, IPSRXMOD, mode)
358  --push to hw
359  dpdkc.write_reg32(port, IPSRXIDX, value)
360 end
361 
362 --- Read AES 128 bit Key and Salt from the Hardware RX SA table
363 --- @param port The port/interface to use
364 --- @param idx Index into the RX SA table (0-1023)
365 --- @return Key and Salt (as hex string),
366 --- Valid Flag (1=SA is valid, 0=SA is invalid),
367 --- Proto Flag (1=ESP, 0=AH),
368 --- Decrypt Flag (1=ESP decrypt and authenticate, 0=ESP authenticate only),
369 --- IPv6 Flag (1=SA is valid for IPv6, 0=SA is valid for IPv4)
370 function mod.rx_get_key(port, idx)
371  if idx > 1023 or idx < 0 then
372  error("Idx must be in range 0-1023")
373  end
374 
375  -- Prepare command to read from KEY_TABLE at TB_IDX
376  local value = set_bits32(dpdkc.read_reg32(port, IPSRXIDX), 2, 1, 0x3) --set TABLE to KEY(0x3)
377  value = set_bits32(value, 12, 3, idx) --set TB_IDX
378  value = set_bit32(value, 30) --set READ
379  value = clear_bit32(value, 31), --clear WRITE
380  --print("IPSRXIDX: 0x"..uhex32(value))
381 
382  --pull from hw
383  dpdkc.write_reg32(port, IPSRXIDX, value)
384 
385  --fetch result
386  local key_3 = dpdkc.read_reg32(port, IPSRXKEY_3)
387  local key_2 = dpdkc.read_reg32(port, IPSRXKEY_2)
388  local key_1 = dpdkc.read_reg32(port, IPSRXKEY_1)
389  local key_0 = dpdkc.read_reg32(port, IPSRXKEY_0)
390  local _salt = dpdkc.read_reg32(port, IPSRXSALT)
391  local _mode = dpdkc.read_reg32(port, IPSRXMOD)
392 
393  local valid = bit.rshift(bit.band(_mode, 0x01), 0)
394  local proto = bit.rshift(bit.band(_mode, 0x04), 2)
395  local decrypt = bit.rshift(bit.band(_mode, 0x08), 3)
396  local ipv6 = bit.rshift(bit.band(_mode, 0x10), 4)
397  local key = uhex32(key_3)..uhex32(key_2)..uhex32(key_1)..uhex32(key_0)
398 
399  return key, uhex32(_salt), valid, proto, decrypt, ipv6
400 end
401 
402 --- Write IP-Address into the Hardware RX IP table
403 --- @param port The port/interface to use
404 --- @param idx Index into the RX IP table (0-127).
405 --- @param ip_addr IP(v4/v6)-Address to set (as string)
406 function mod.rx_set_ip(port, idx, ip_addr)
407  if idx > 127 or idx < 0 then
408  error("Idx must be in range 0-127")
409  end
410 
411  local ip, is_ipv4 = parseIPAddress(ip_addr)
412 
413  local ip_3 = 0x0
414  local ip_2 = 0x0
415  local ip_1 = 0x0
416  local ip_0 = 0x0
417 
418  if is_ipv4 == true then
419  ip_3 = bswap(ip)
420  else
421  ip_3 = bswap(ip.uint32[0])
422  ip_2 = bswap(ip.uint32[1])
423  ip_1 = bswap(ip.uint32[2])
424  ip_0 = bswap(ip.uint32[3])
425  end
426 
427  -- Prepare command to write to IP_TABLE at TB_IDX
428  local value = set_bits32(dpdkc.read_reg32(port, IPSRXIDX), 2, 1, 0x1) --set TABLE to IP(0x1)
429  value = set_bits32(value, 12, 3, idx) --set TB_IDX
430  value = clear_bit32(value, 30) --clear READ
431  value = set_bit32(value, 31) --set WRITE
432  --print("IPSRXIDX: 0x"..uhex32(value))
433 
434  --prepare registers
435  dpdkc.write_reg32(port, IPSRXIPADDR_3, ip_3)
436  dpdkc.write_reg32(port, IPSRXIPADDR_2, ip_2)
437  dpdkc.write_reg32(port, IPSRXIPADDR_1, ip_1)
438  dpdkc.write_reg32(port, IPSRXIPADDR_0, ip_0)
439  --push to hw
440  dpdkc.write_reg32(port, IPSRXIDX, value)
441 end
442 
443 --- Read IP-Address from the Hardware RX IP table
444 --- @param port The port/interface to use
445 --- @param idx Index into the RX IP table (0-127)
446 --- @param is_ipv4 IP Version expected (true/false)
447 --- @return The IP(v4/v6)-Address (as string) and a IP Version Flag (true=IPv4, false=IPv6)
448 function mod.rx_get_ip(port, idx, is_ipv4)
449  if idx > 127 or idx < 0 then
450  error("Idx must be in range 0-127")
451  end
452  if is_ipv4 == nil then
453  is_ipv4 = true
454  end
455 
456  -- Prepare command to read from IP_TABLE at TB_IDX
457  local value = set_bits32(dpdkc.read_reg32(port, IPSRXIDX), 2, 1, 0x1) --set TABLE to IP(0x1)
458  value = set_bits32(value, 12, 3, idx) --set TB_IDX
459  value = set_bit32(value, 30) --set READ
460  value = clear_bit32(value, 31) --clear WRITE
461  --print("IPSRXIDX: 0x"..uhex32(value))
462 
463  --pull from hw
464  dpdkc.write_reg32(port, IPSRXIDX, value)
465  --fetch result
466  local ip_3 = dpdkc.read_reg32(port, IPSRXIPADDR_3)
467  local ip_2 = dpdkc.read_reg32(port, IPSRXIPADDR_2)
468  local ip_1 = dpdkc.read_reg32(port, IPSRXIPADDR_1)
469  local ip_0 = dpdkc.read_reg32(port, IPSRXIPADDR_0)
470 
471  local ip = nil
472  if is_ipv4 == true then
473  local ip4 = ffi.new("union ip4_address")
474  ip4.uint32 = ip_3
475  ip = ip4:getString()
476  else
477  local ip6 = ffi.new("union ip6_address")
478  ip6.uint32[3] = ip_3
479  ip6.uint32[2] = ip_2
480  ip6.uint32[1] = ip_1
481  ip6.uint32[0] = ip_0
482  ip = ip6:getString()
483  end
484 
485  return ip, is_ipv4
486 end
487 
488 --- Write SPI into the Hardware RX SPI table
489 --- This table functions as 'glue' between the received packet (SPI), IP and KEY table.
490 --- @param port The port/interface to use
491 --- @param idx Index into the RX SPI table (0-1023). This must match the idx of the corresponding KEY table entry.
492 --- @param spi SPI to write (0-0xFFFFFFFF)
493 --- @param ip_idx Reference to the IP table. This must match the idx of the corresponding IP table entry.
494 function mod.rx_set_spi(port, idx, spi, ip_idx)
495  if idx > 1023 or idx < 0 then
496  error("Idx must be in range 0-1023")
497  end
498  if spi > 0xFFFFFFFF or spi < 0 then
499  error("Spi must be in range 0x0-0xFFFFFFFF")
500  end
501  if ip_idx > 127 or ip_idx < 0 then
502  error("IP_Idx must be in range 0-127")
503  end
504 
505  -- Prepare command to write SPI_TABLE at TB_IDX
506  local value = set_bits32(dpdkc.read_reg32(port, IPSRXIDX), 2, 1, 0x2) --set TABLE to SPI(0x2)
507  value = set_bits32(value, 12, 3, idx) --set TB_IDX
508  value = clear_bit32(value, 30) --clear READ
509  value = set_bit32(value, 31) --set WRITE
510  --print("IPSRXIDX: 0x"..uhex32(value))
511 
512  -- Prepare ip_idx field
513  ip_idx = set_bits32(dpdkc.read_reg32(port, IPSRXIPIDX), 6, 0, ip_idx) --set IP_IDX
514 
515  --prepare registers
516  dpdkc.write_reg32(port, IPSRXSPI, bswap(spi)) --network byte order!
517  dpdkc.write_reg32(port, IPSRXIPIDX, ip_idx)
518  --push to hw
519  dpdkc.write_reg32(port, IPSRXIDX, value)
520 end
521 
522 --- Read SPI from the Hardware RX SPI table
523 --- @param port The port/interface to use
524 --- @param idx Index into the RX SPI table (0-1023)
525 --- @return The SPI and the corresponding Index into the IP table
526 function mod.rx_get_spi(port, idx)
527  if idx > 1023 or idx < 0 then
528  error("Idx must be in range 0-1023")
529  end
530 
531  -- Prepare command to read from SPI_TABLE at TB_IDX
532  local value = set_bits32(dpdkc.read_reg32(port, IPSRXIDX), 2, 1, 0x2) --set TABLE to SPI(0x2)
533  value = set_bits32(value, 12, 3, idx) --set TB_IDX
534  value = set_bit32(value, 30) --set READ
535  value = clear_bit32(value, 31) --clear WRITE
536  --print("IPSRXIDX: 0x"..uhex32(value))
537 
538  --pull from hw
539  dpdkc.write_reg32(port, IPSRXIDX, value)
540  --fetch result
541  local spi = dpdkc.read_reg32(port, IPSRXSPI) --network byte order!
542  local ip_idx = bit.band(dpdkc.read_reg32(port, IPSRXIPIDX), 0x7f) --IP_IDX in bits 6:0
543 
544  return bswap(spi), ip_idx
545 end
546 
547 --- Calculate the length of extra padding needed, in order to achive a 4 byte alignment.
548 --- @param payload_len The lenght of the original IP packet
549 --- @return The number of extra padding bytes needed
550 function mod.calc_extra_pad(payload_len)
551  local idx = math.ceil(payload_len/4)
552  local idx8 = idx * 4
553  local extra_pad = idx8 - payload_len
554  return extra_pad
555 end
556 
557 --- Calculate the length of extra padding included in this packet for 4 byte alignment.
558 --- @param buf The rte_mbuf containing the hw-decrypted ESP packet
559 --- @return The number of extra padding bytes included in this packet
560 function mod.get_extra_pad(buf)
561  local pkt = buf:getIPPacket()
562  local payload_len = pkt.ip4:getLength()-20 --IP4 Length less 20 bytes IP4 Header
563  --ESP_ICV(16), ESP_next_hdr(1), array_offset(1)
564  local esp_padding_len = pkt.payload.uint8[payload_len-16-1-1]
565  return esp_padding_len-2 --subtract default padding of 2 bytes, which is always there
566 end
567 
568 --- Calculate a ESP Trailer and the corresponding Padding and append to the packet payload.
569 --- Only relevant for ESP/Ecryption mode
570 --- @param buf rte_mbuf to add esp trailer to
571 --- @param payload_len real payload length in bytes
572 --- @param next_hdr type of encapsulated packet
573 function mod.add_esp_trailer(buf, payload_len, next_hdr)
574  local pkt = buf:getEspPacket()
575  local idx = math.ceil(payload_len/4)
576  local idx8 = idx * 4
577  local extra_pad = idx8 - payload_len
578  local pad_len = 2 + extra_pad
579  local esp_trailer_len = 20 + extra_pad
580 
581  pkt.payload.uint8[idx8+0] = 0x00
582  pkt.payload.uint8[idx8+1] = 0x00
583  pkt.payload.uint8[idx8+2] = pad_len
584  pkt.payload.uint8[idx8+3] = next_hdr
585 
586  pkt.payload.uint32[idx+1] = 0x00 -- ICV n-3
587  pkt.payload.uint32[idx+2] = 0x00 -- ICV n-2
588  pkt.payload.uint32[idx+3] = 0x00 -- ICV n-1
589  pkt.payload.uint32[idx+4] = 0x00 -- ICV n-0
590 
591  buf:setESPTrailerLength(esp_trailer_len)
592 end
593 
594 --- Decapsulate a hw-decrypted ESP packet
595 --- @param buf The rte_mbuf containing the hw-decrypted ESP packet
596 --- @param len Length of the hw-decrypted IP/ESP packet
597 --- @param eth_mem memoryPool to allocate new ethernet packets from
598 --- @return A new rte_mbuf containing the original (inner) IP packet
599 function mod.esp_vpn_decapsulate(buf, len, eth_mem)
600  local extra_pad = mod.get_extra_pad(buf)
601  -- eth(14), pkt(len), pad(extra_pad), outer_ip(20), esp_header(16), esp_trailer(20)
602  local new_len = 14+len-extra_pad-20-16-20
603 
604  local mybuf = eth_mem:alloc(new_len)
605  local new_pkt = mybuf:getEthPacket()
606 
607  local esp_pkt = buf:getEspPacket()
608  ffi.copy(new_pkt.payload, esp_pkt.payload, new_len-14)
609 
610  return mybuf
611 end
612 
613 --- Encapsulate an IP packet into a new IP header and ESP header and trailer
614 --- @param buf The rte_mbuf containing the original IP packet
615 --- @param len Length of the original IP packet
616 --- @param esp_mem memoryPool to allocate new esp packets from
617 --- @return A new rte_mbuf containing the encapsulated IP/ESP packet
618 function mod.esp_vpn_encapsulate(buf, len, esp_mem)
619  local extra_pad = mod.calc_extra_pad(len) --for 4 byte alignment
620  -- eth(14), ip4(20), esp(16), pkt(len), pad(extra_pad), esp_trailer(20)
621  local new_len = 14+20+16+len+extra_pad+20
622 
623  local mybuf = esp_mem:alloc(new_len)
624  local new_pkt = mybuf:getEspPacket()
625 
626  local eth_pkt = buf:getEthPacket()
627  new_pkt.ip4:setLength(new_len-14)
628  --for i = 0, len-1 do
629  -- new_pkt.payload.uint8[i] = eth_pkt.payload.uint8[i]
630  --end
631  ffi.copy(new_pkt.payload, eth_pkt.payload, len)
632 
633  mod.add_esp_trailer(mybuf, len, 0x4) -- Tunnel mode: next_header = 0x4 (IPv4)
634 
635  return mybuf
636 end
637 
638 return mod
function mod sleepMillis(t)
Delay by t milliseconds.
function mod disable(port)
Disable the Hardware Crypto Engine.
local ffi
low-level dpdk wrapper
Definition: dpdkc.lua:6
function parseIPAddress(ip)
Parse a string to an IP address.
function ahHeader setLength(int)
Set the Length.
function ipsecICV getString(doByteSwap)
Get the IPsec string.
function mod band(mask1, mask2, result)
Bitwise and.
function range(max, start,...)
Return all integerss in the range [start, max].
function ipsecICV set(icv)
Set the IPsec ICV.
function mod new(n)
function mod rx_get_key(port, idx)
Read AES 128 bit Key and Salt from the Hardware RX SA table.
function mod add_esp_trailer(buf, payload_len, next_hdr)
Calculate a ESP Trailer and the corresponding Padding and append to the packet payload.
function mod rx_set_key(port, idx, key, salt, ip_ver, proto, decrypt)
Write AES 128 bit Key and Salt into the Hardware RX SA table.
function mod bor(mask1, mask2, result)
Bitwise or.
function mod enable(port)
Enable the Hardware Crypto Engine.
function mod get_extra_pad(buf)
Calculate the length of extra padding included in this packet for 4 byte alignment.
function mod tx_set_key(port, idx, key, salt)
Write AES 128 bit Key and Salt into the Hardware TX SA table.
local mod
high-level dpdk wrapper
Definition: dpdk.lua:6
function mod calc_extra_pad(payload_len)
Calculate the length of extra padding needed, in order to achive a 4 byte alignment.
function mod esp_vpn_decapsulate(buf, len, eth_mem)
Decapsulate a hw-decrypted ESP packet.
pkt getEthPacket
Cast the packet to an ethernet packet (alias for pkt.getEthernetPacket)
Definition: ethernet.lua:176
function ip4Addr add(val)
Add a number to an IPv4 address in-place.
function mod rx_get_ip(port, idx, is_ipv4)
Read IP-Address from the Hardware RX IP table.
function pkt setESPTrailerLength(len)
Set the ESP trailer length.
local eth
Ethernet protocol constants.
Definition: ethernet.lua:24
local ip6
IP6 protocol constants.
Definition: ip6.lua:25
local ip
IP4 protocol constants.
Definition: ip4.lua:25
function mod esp_vpn_encapsulate(buf, len, esp_mem)
Encapsulate an IP packet into a new IP header and ESP header and trailer.
function mod tx_get_key(port, idx)
Read AES 128 bit Key and Salt from the Hardware TX SA table.
function arp lookup(ip)
Perform a lookup in the ARP table.
local pkt
Module for packets (rte_mbuf)
Definition: packet.lua:20
function mod bnot(mask, result)
Bitwise not.
n
Create a new array of memory buffers (initialized to nil).
Definition: memory.lua:76
function mod rx_set_spi(port, idx, spi, ip_idx)
Write SPI into the Hardware RX SPI table This table functions as 'glue' between the received packet (...
function mod rx_get_spi(port, idx)
Read SPI from the Hardware RX SPI table.
function ahHeader getLength()
Retrieve the Length.
pkt getIPPacket
Cast the packet to either an IP4 (nil/true) or IP6 (false) packet, depending on the passed boolean...
Definition: ip4.lua:321
function mod rx_set_ip(port, idx, ip_addr)
Write IP-Address into the Hardware RX IP table.