Skip to content

Commit

Permalink
Merge PR snabbco#70 'over-v6' into vita-next
Browse files Browse the repository at this point in the history
  • Loading branch information
eugeneia committed Jan 24, 2019
2 parents 1508813 + 954f767 commit db13296
Show file tree
Hide file tree
Showing 23 changed files with 1,327 additions and 197 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ before_script:
script:
- make -j
- (cd src && sudo program/vita/selftest.snabb)
- (cd src && sudo program/vita/selftest6.snabb)
- (cd src && sudo ./snabb snsh -t program.vita.exchange)
- (cd src && sudo program/vita/conftest.snabb)
- (cd src && sudo program/vita/test.snabb IMIX 1e6 3)
- (cd src && sudo program/vita/test6.snabb IMIX 1e6 3)
19 changes: 9 additions & 10 deletions src/apps/ipv6/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@ The `nd_light` app implements a small subset of IPv6 neighbor discovery
(RFC4861). It has two duplex ports, `north` and `south`. The `south`
port attaches to a network on which neighbor discovery (ND) must be
performed. The `north` port attaches to an app that processes IPv6
packets (including full ethernet frames). Packets transmitted to the
packets (without ethernet headers). Packets transmitted to the
`north` port must be wrapped in full Ethernet frames (which may be
empty).

The `nd_light` app replies to neighbor solicitations for which it is
configured as a target and performs rudimentary address resolution for
its configured *next-hop* address. If address resolution succeeds, the
Ethernet headers of packets from the `north` port will be overwritten
with headers containing the discovered destination address and the
configured source address before they are transmitted over the `south`
port. All packets from the `north` port are discarded as long as ND has
not yet succeeded. Packets received from the `south` port are transmitted
to the `north` port unaltered.
The `nd_light` app replies to neighbor solicitations for which it is configured
as a target and performs rudimentary address resolution for its configured
*next-hop* address. If address resolution succeeds, the Ethernet headers
containing the discovered destination address and the configured source address
are prepended to packets received from the `north` port before they are
transmitted over the `south` port. All packets from the `north` port are
discarded as long as ND has not yet succeeded. Packets received from the
`south` port are transmitted to the `north` port unaltered.

DIAGRAM: nd_light
+----------+
Expand Down
27 changes: 9 additions & 18 deletions src/apps/ipv6/nd_light.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ nd_light.config = {
nd_light.shm = {
status = {counter, 2}, -- Link down
rxerrors = {counter},
txerrors = {counter},
txdrop = {counter},
ns_checksum_errors = {counter},
ns_target_address_errors = {counter},
na_duplicate_errors = {counter},
Expand Down Expand Up @@ -362,23 +360,16 @@ function nd_light:push ()

l_in = self.input.north
l_out = self.output.south
-- Do not forward packets from north until ND for the next-hop has
-- completed.
if not self._eth_header then
return
end
while not link.empty(l_in) do
if not self._eth_header then
-- Drop packets until ND for the next-hop
-- has completed.
packet.free(link.receive(l_in))
counter.add(self.shm.txdrop)
else
local p = cache.p
p[0] = link.receive(l_in)
if p[0].length >= self._eth_header:sizeof() then
self._eth_header:copy(p[0].data)
link.transmit(l_out, p[0])
else
packet.free(p[0])
counter.add(self.shm.txerrors)
end
end
local p = packet.prepend(
link.receive(l_in), self._eth_header:header(), ethernet:sizeof()
)
link.transmit(l_out, p)
end
end

Expand Down
1 change: 1 addition & 0 deletions src/lib/protocol/ipv6.lua
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ function ipv6:new (config)
o:version(6)
o:traffic_class(config.traffic_class or defaults.traffic_class)
o:flow_label(config.flow_label or defaults.flow_label)
o:payload_length(config.payload_length or 0)
o:next_header(config.next_header or defaults.next_header)
o:hop_limit(config.hop_limit or defaults.hop_limit)
o:src(config.src)
Expand Down
52 changes: 28 additions & 24 deletions src/program/vita/README.config
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
CONFIGURATION SYNTAX

configuration:=
private-interface { <interface> }
public-interface { <interface> }
[ route { <route> } ]*
private-interface4 { <interface> }
public-interface4|public-interface6 { <interface> }
[ route4|route46 { <route> } ]*
[ mtu <size>; ]
[ negotiation-ttl <seconds>; ]
[ sa-ttl <seconds>; ]
[ data-pane <boolean>; ]
Expand All @@ -12,18 +13,17 @@ CONFIGURATION SYNTAX

interface:=
pci <busaddr>;
ip4 <ipv4addr>;
nexthop-ip4 <ipv4addr>;
ip4|ip6 <addr>;
nexthop-ip4 <addr>;
[ mac <hwaddr>; ]
[ nexthop-mac <hwaddr>; ]

route:=
id <string>;
net-cidr4 <ipv4prefix>;
gw-ip4 <ipv4addr>;
net-cidr4 <prefix>;
gw-ip4|gw-ip6 <addr>;
preshared-key <string>;
spi <spi>;
[ private-mtu <size>; ]

sa:=
route <string>;
Expand All @@ -34,19 +34,19 @@ CONFIGURATION SYNTAX

NOTES

A Vita configuration defines the a private and a public network interface for
use by the gateway, a set of routes to other Vita nodes, and the gateway’s
MTU among other miscellaneous operational parameters.
A Vita configuration defines a private and a public network interface for use
by the gateway, a set of routes to other Vita nodes, and the gateway’s MTU
among other miscellaneous operational parameters.

Each interface is identified by a Linux PCI bus address, and assigned an
IPv4 address as well as the IPv4 address of the next hop through which all
IP address as well as the IP address of the next hop through which all
packets leaving the interface will be routed. The IP addresses assigned to
both interfaces may be the same.

If the Ethernet (MAC) address of the interface is not explicitly specified,
it is automatically derived from its assigned IPv4 address by prepending the
bytes 2a:bb: or 3a:bb: depending on whether it is the private or public
interface respectively.
it is automatically derived from its assigned IP address by prepending the
bytes 2a:bb: or 3a:bb: to its least significant 32 bits depending on whether
it is the private or public interface respectively.

Given that the NIC driver in use supports VMDq, it is possible to pass the
same PCI bus address for both interfaces to have them share a single physical
Expand All @@ -58,11 +58,10 @@ NOTES

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
characters.) The route’s destination IPv4 subnet and gateway are specified
with an IPv4 prefix in CIDR notation, and an IPv4 address respectively.
Packets that arrive on the private interface and which are destined to a
route’s specified IPv4 subnetwork are tunneled to the destination gateway of
that route.
characters.) The route’s destination IP subnet and gateway are specified with
an IP prefix in CIDR notation, and an IP address respectively. Packets that
arrive on the private interface and which are destined to a route’s specified
IP subnetwork are tunneled to the destination gateway of that route.

For authentication, each route is assigned a unique, pre-shared 256-bit key,
encoded as a hexadecimal string (two digits for each octet, most significant
Expand Down Expand Up @@ -94,29 +93,34 @@ NOTES
32-bit salt respectively, encoded as hexadecimal strings (two digits for each
octet, most significant digit first).

REFERENCES

Refer to vita-esp-gateway.yang for a formal YANG model that defines Vita’s
configuration as well as the statistics counters queryable at runtime.

EXAMPLE

private-interface {
private-interface4 {
pci 0c:00.0;
ip4 172.16.1.10;
nexthop-ip4 172.16.1.1;
}

public-interface {
public-interface4 {
pci 0c:00.1;
ip4 203.0.113.10;
nexthop-ip4 172.16.1.1;
}

route {
route4 {
id site2;
net-cidr4 172.16.2.0/24;
gw-ip4 203.0.113.2;
preshared-key 91440DA06376A668AC4959A840A125D75FB544E8AA25A08B813E49F0A4B2E270;
spi 1001;
}

route {
route4 {
id site3;
net-cidr4 172.16.3.0/24;
gw-ip4 203.0.113.3;
Expand Down
16 changes: 8 additions & 8 deletions src/program/vita/conftest.snabb
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,22 @@ test_conf(vita_test.gen_configuration({nroutes=1}))

print("Change route key...")
local conf = vita_test.gen_configuration({nroutes=1})
conf.route.test1.preshared_key = string.rep("FF", 32)
conf.route4.test1.preshared_key = string.rep("FF", 32)
test_conf(conf)

print("Change route gw_ip4...")
conf.route.test1.gw_ip4 = "172.17.1.10"
conf.route4.test1.gw_ip4 = "172.17.1.10"
test_conf(conf)

print("Change public_ip4 and route net_cidr4...")
conf.public_interface.ip4 = "172.16.0.11"
conf.public_interface.nexthop_ip4 = conf.public_interface.ip4
conf.route.test1.net_cidr4 = "172.16.0.0/16"
conf.public_interface4.ip4 = "172.16.0.11"
conf.public_interface4.nexthop_ip4 = conf.public_interface4.ip4
conf.route4.test1.net_cidr4 = "172.16.0.0/16"
test_conf(conf)

print("Change route id...")
conf.route.test2 = conf.route.test1
conf.route.test1 = nil
conf.route4.test2 = conf.route4.test1
conf.route4.test1 = nil
test_conf(conf)

print("Change negotiation_ttl...")
Expand All @@ -53,5 +53,5 @@ conf.sa_ttl = 42
test_conf(conf)

print("Remove route...")
conf.route.test2 = nil
conf.route4.test2 = nil
test_conf(conf)
48 changes: 44 additions & 4 deletions src/program/vita/dispatch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local icmp = require("program.vita.icmp")
local counter = require("core.counter")
local ethernet = require("lib.protocol.ethernet")
local ipv4 = require("lib.protocol.ipv4")
local ipv6 = require("lib.protocol.ipv6")
local pf_match = require("pf.match")
local ffi = require("ffi")

Expand Down Expand Up @@ -86,7 +87,8 @@ end
PublicDispatch = {
name = "PublicDispatch",
config = {
node_ip4 = {required=true}
node_ip4 = {},
node_ip6 = {}
},
shm = {
rxerrors = {counter},
Expand All @@ -98,8 +100,10 @@ PublicDispatch = {

function PublicDispatch:new (conf)
local o = {
p_box = ffi.new("struct packet *[1]"),
dispatch = pf_match.compile(([[match {
p_box = ffi.new("struct packet *[1]")
}
if conf.node_ip4 then
o.dispatch = pf_match.compile(([[match {
ip[6:2] & 0x3FFF != 0 => reject_fragment
ip proto esp => forward4
ip proto %d => protocol
Expand All @@ -109,7 +113,17 @@ function PublicDispatch:new (conf)
arp => arp
otherwise => reject_ethertype
}]]):format(exchange.PROTOCOL, conf.node_ip4, conf.node_ip4))
}
elseif conf.node_ip6 then
o.dispatch = pf_match.compile(([[match {
ip6 proto esp => forward6
ip6 proto %d => protocol6
ip6 and icmp6 and (ip6[40] = 135 or ip6[40] = 136) => nd
ip6 dst host %s and icmp6 => icmp6
ip6 dst host %s => protocol6_unreachable
ip6 => reject_protocol
otherwise => reject_ethertype
}]]):format(exchange.PROTOCOL, conf.node_ip6, conf.node_ip6, conf.node_ip6))
else error("Need either node_ip4 or node_ip6.") end
return setmetatable(o, {__index=PublicDispatch})
end

Expand All @@ -120,26 +134,52 @@ function PublicDispatch:forward4 ()
link.transmit(self.output.forward4, p)
end

function PublicDispatch:forward6 ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof() + ipv6:sizeof())
-- NB: Ignore potential differences between IP datagram and Ethernet size
-- since the minimum ESP packet exceeds 60 bytes in payload.
link.transmit(self.output.forward6, p)
end

function PublicDispatch:protocol ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof() + ipv4:sizeof())
link.transmit(self.output.protocol, p)
end

function PublicDispatch:protocol6 ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof() + ipv6:sizeof())
link.transmit(self.output.protocol, p)
end

function PublicDispatch:icmp4 ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof())
link.transmit(self.output.icmp4, p)
end

function PublicDispatch:icmp6 ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof())
link.transmit(self.output.icmp6, p)
end

function PublicDispatch:arp ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof())
link.transmit(self.output.arp, p)
end

function PublicDispatch:nd ()
link.transmit(self.output.nd, self.p_box[0])
end

function PublicDispatch:protocol4_unreachable ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof())
link.transmit(self.output.protocol4_unreachable, p)
end

function PublicDispatch:protocol6_unreachable ()
local p = packet.shiftleft(self.p_box[0], ethernet:sizeof())
link.transmit(self.output.protocol6_unreachable, p)
end

function PublicDispatch:reject_fragment ()
packet.free(self.p_box[0])
counter.add(self.shm.rxerrors)
Expand Down
Loading

0 comments on commit db13296

Please sign in to comment.