Skip to content

Commit

Permalink
Merge PR snabbco#37 (ICMP support) into master
Browse files Browse the repository at this point in the history
  • Loading branch information
eugeneia committed Jun 5, 2018
2 parents 23390f9 + 48c33a9 commit dfa40d4
Show file tree
Hide file tree
Showing 23 changed files with 1,325 additions and 204 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ before_script:

script:
- make -j
- (cd src && sudo ./snabb snsh program/vita/test.snabb IMIX 1e6 3)
- (cd src && sudo program/vita/selftest.snabb)
- (cd src && sudo program/vita/conftest.snabb)
- (cd src && sudo program/vita/test.snabb IMIX 1e6 3)
5 changes: 4 additions & 1 deletion src/apps/test/match.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ function Match:push ()
elseif cmp.length ~= p.length
or C.memcmp(cmp.data, p.data, cmp.length) ~= 0 then
if not self.fuzzy then
table.insert(self.errs, "Mismatch:\n"..dump(cmp).."\n"..dump(p))
table.insert(self.errs,
"Mismatch at packet #"..(self.seen+1)..":\n"
..dump(cmp).."\n"
..dump(p))
end
else
self.seen = self.seen + 1
Expand Down
8 changes: 8 additions & 0 deletions src/lib/protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ Computes and sets the IPv4 header checksum. Its called automatically by
Predicate methods to test if *ip* is equal to the source or destination
addresses individually.

— Method **ipv4:swap**

Swaps the source and destination addresses.

— Method **ipv4:is_fragment**

Returns true if the header denotes an IP fragment and false otherwise.

— Function **ipv4:pton** *string*

Returns the binary representation of IPv4 address denoted by *string*.
Expand Down
45 changes: 41 additions & 4 deletions src/lib/protocol/ipv4.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ local header = require("lib.protocol.header")
local ipsum = require("lib.checksum").ipsum
local htons, ntohs, htonl, ntohl =
lib.htons, lib.ntohs, lib.htonl, lib.ntohl
local band, lshift, rshift, bnot =
bit.band, bit.lshift, bit.rshift, bit.bnot

-- TODO: generalize
local AF_INET = 2
Expand All @@ -31,10 +33,10 @@ local ipv4 = subClass(header)
ipv4._name = "ipv4"
ipv4._ulp = {
class_map = {
[1] = "lib.protocol.icmp.header",
[6] = "lib.protocol.tcp",
[17] = "lib.protocol.udp",
[47] = "lib.protocol.gre",
[58] = "lib.protocol.icmp.header",
},
method = 'protocol' }
ipv4:init(
Expand Down Expand Up @@ -62,7 +64,7 @@ function ipv4:new (config)
o:ihl(o:sizeof() / 4)
o:dscp(config.dscp or 0)
o:ecn(config.ecn or 0)
o:total_length(o:sizeof()) -- default to header only
o:total_length(config.total_length or o:sizeof()) -- default to header only
o:id(config.id or 0)
o:flags(config.flags or 0)
o:frag_off(config.frag_off or 0)
Expand Down Expand Up @@ -139,6 +141,10 @@ function ipv4:frag_off (frag_off)
return lib.bitfield(16, self:header(), 'frag_off', 3, 13, frag_off)
end

function ipv4:is_fragment ()
return self:frag_off() ~= 0 or band(self:flags(), 0x01) == 1
end

function ipv4:ttl (ttl)
if ttl ~= nil then
self:header().ttl = ttl
Expand All @@ -147,6 +153,25 @@ function ipv4:ttl (ttl)
end
end

-- Adopted from lwAFTR with love
function ipv4:ttl_decrement ()
local old_ttl = self:ttl()
local new_ttl = band(old_ttl - 1, 0xff)
self:ttl(new_ttl)
local chksum = bnot(ntohs(self:header().checksum))
-- Now fix up the checksum. The ttl field is the first byte in the
-- 16-bit big-endian word, so the difference to the overall sum is
-- multiplied by 0xff.
chksum = chksum + lshift(new_ttl - old_ttl, 8)
-- Now do the one's complement 16-bit addition of the 16-bit words of
-- the checksum, which necessarily is a 32-bit value. Two carry
-- iterations will suffice.
chksum = band(chksum, 0xffff) + rshift(chksum, 16)
chksum = band(chksum, 0xffff) + rshift(chksum, 16)
self:header().checksum = htons(bnot(chksum))
return new_ttl
end

function ipv4:protocol (protocol)
if protocol ~= nil then
self:header().protocol = protocol
Expand Down Expand Up @@ -174,8 +199,12 @@ function ipv4:src (ip)
end
end

local function ip_eq (x, y)
return ffi.cast("uint32_t *", x)[0] == ffi.cast("uint32_t *", y)[0]
end

function ipv4:src_eq (ip)
return C.memcmp(ip, self:header().src_ip, ipv4_addr_t_size) == 0
return ip_eq(ip, self:header().src_ip, ipv4_addr_t_size)
end

function ipv4:dst (ip)
Expand All @@ -187,7 +216,7 @@ function ipv4:dst (ip)
end

function ipv4:dst_eq (ip)
return C.memcmp(ip, self:header().dst_ip, ipv4_addr_t_size) == 0
return ip_eq(ip, self:header().dst_ip, ipv4_addr_t_size)
end

-- override the default equality method
Expand All @@ -199,6 +228,14 @@ function ipv4:eq (other)
self:src_eq(other:src()) and self:dst_eq(other:dst())
end

function ipv4:swap ()
local tmp = ipv4_addr_t()
local h = self:header()
ffi.copy(tmp, h.src_ip, ipv4_addr_t_size)
ffi.copy(h.dst_ip, h.src_ip, ipv4_addr_t_size)
ffi.copy(h.src_ip, tmp, ipv4_addr_t_size)
end

-- Return a pseudo header for checksum calculation in a upper-layer
-- protocol (e.g. icmp). Note that the payload length and next-header
-- values in the pseudo-header refer to the effective upper-layer
Expand Down
23 changes: 16 additions & 7 deletions src/program/vita/README.config
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ CONFIGURATION SYNTAX
interface:=
macaddr <hwaddr>;
[ vlan <tag>; ]
[ mtu <size>; ]
pciaddr <busaddr>;

route:=
Expand All @@ -23,6 +22,7 @@ CONFIGURATION SYNTAX
gw_ip4 <ipv4addr>;
preshared_key <string>;
spi <spi>;
[ private_mtu <size>; ]

NOTES

Expand All @@ -32,10 +32,10 @@ NOTES
Vita nodes.

Each interface is identified by a Linux PCI bus address, and assigned an
Ethernet (MAC) address. Optionally, the MTU can be specified in bytes, and a
IEEE 802.1Q VLAN Identifier can be set. Given that the underlying hardware
device supports VMDq, it is possible to pass the same PCI bus address for
both interfaces to have them share a single physical port.
Ethernet (MAC) address. Optionally, a IEEE 802.1Q VLAN Identifier can be set.
Given that the underlying hardware device supports VMDq, it is possible to
pass the same PCI bus address for both interfaces to have them share a single
physical port.

Each route is given a unique, human readable identifier that must satisfy the
pattern [a-zA-Z0-9_]+ (i.e., one or more alphanumeric ASCII and underscore
Expand All @@ -48,13 +48,20 @@ NOTES
associating encrypted traffic for a given route. Like the pre-shared key, the
SPI must be the same for both ends of a route.

Optionally, the private MTU can be specified in bytes. The default and
maximum permitted value is 8937. Since Vita performs neither fragmentation
nor reassembly it may be necessary to adjust the next-hop MTU accordingly.
Note that packets leaving the public interface will have an added packet size
overhead due to encapsulation (up to 57 bytes for IPv4 and up to 77 bytes for
IPv6.)

While the default configuration should be generally applicable, the
negotiation timeout and lifetime of Security Associations (SA) can be
specified in seconds.

EXAMPLE

private_interface { macaddr 52:54:00:00:00:00; mtu 1280; pciaddr 0c:00.0; }
private_interface { macaddr 52:54:00:00:00:00; pciaddr 0c:00.0; }
public_interface { macaddr 52:54:00:00:00:FF; pciaddr 0c:00.1; }

private_ip4 192.168.10.10;
Expand All @@ -76,7 +83,9 @@ EXAMPLE
net_cidr4 192.168.30.0/24;
gw_ip4 203.0.113.3;
preshared_key CF0BDD7A058BE55C12B7F2AA30D23FF01BDF8BE6571F2019ED7F7EBD3DA97B47;
spi 1002;
spi 1223;
}

private_mtu 1280;

sa_ttl 86400;
39 changes: 22 additions & 17 deletions src/program/vita/conftest.snabb
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,26 @@ local S = require("syscall")
local confpath = shm.root.."/"..shm.resolve("group/testconf")

local function commit_conf (conf)
local f = assert(io.open(confpath, "w"), "Unable to open file: "..confpath)
yang.print_config_for_schema(vita.schemata['esp-gateway'], conf, f)
f:close()
vita.save_config(vita.schemata['esp-gateway'], confpath, conf)
end

worker.set_exit_on_worker_death(true)

worker.start(
"PublicRouterLoopback",
([[require("program.vita.vita").public_router_loopback_worker(%q)]])
:format(confpath)
)
worker.start(
"Exchange",
([[require("program.vita.vita").exchange_worker(%q)]])
:format(confpath)
)
S.sleep(1)

local conf0 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.1",
private_nexthop_ip4 = "192.168.10.1",
Expand All @@ -37,8 +42,8 @@ commit_conf(conf0)
S.sleep(3)

local conf1 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.1",
private_nexthop_ip4 = "192.168.10.1",
Expand All @@ -61,8 +66,8 @@ commit_conf(conf1)
S.sleep(3)

local conf2 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.1",
private_nexthop_ip4 = "192.168.10.1",
Expand All @@ -81,8 +86,8 @@ commit_conf(conf2)
S.sleep(3)

local conf3 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.1",
private_nexthop_ip4 = "192.168.10.1",
Expand All @@ -101,8 +106,8 @@ commit_conf(conf3)
S.sleep(3)

local conf4 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.2",
private_nexthop_ip4 = "192.168.10.1",
Expand All @@ -121,8 +126,8 @@ commit_conf(conf4)
S.sleep(3)

local conf5 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.2",
private_nexthop_ip4 = "192.168.10.1",
Expand All @@ -141,8 +146,8 @@ commit_conf(conf5)
S.sleep(3)

local conf6 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:00" },
public_interface = { pciaddr = "00:00.0", macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.2",
private_nexthop_ip4 = "192.168.10.1",
Expand Down
Loading

0 comments on commit dfa40d4

Please sign in to comment.