Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vpn #86

Closed
wants to merge 15 commits into from
Closed

Vpn #86

Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions src/apps/ipv6/ipv6.lua
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ function SimpleIPv6:push ()
ffi.copy(new_ipv6.smac, self.own_mac, 6)
new_ipv6.ethertype = 0xDD86
new_ipv6.flow_id = 0x60 -- version=6
new_ipv6.payload_length = htons(size.icmpv6expired + excerpt_len)
new_ipv6.payload_length = C.htons(size.icmpv6expired + excerpt_len)
new_ipv6.next_header = 58 -- icmpv6
new_ipv6.hop_limit = 255
ffi.copy(new_ipv6.src_ip, self.own_ip, 16)
Expand Down Expand Up @@ -161,16 +161,9 @@ function checksum_icmpv6 (ipv6, icmpv6, icmpv6_len)
csum = lib.update_csum(ipv6_ptr + ffi.offsetof(ipv6_t, 'payload_length'), 2, csum)
-- ICMPv6 checksum
icmpv6.checksum = 0
csum = lib.update_csum(icmpv6, htons(ipv6.payload_length), csum)
csum = lib.update_csum(icmpv6, C.htons(ipv6.payload_length), csum)
csum = csum + 58
icmpv6.checksum = htons(lib.finish_csum(csum))
end

function htons (n)
return bit.lshift(bit.band(n, 0xff), 8) + bit.rshift(n, 8)
end
function ntohs (n)
return htons(n)
icmpv6.checksum = C.htons(lib.finish_csum(csum))
end

function selftest ()
Expand Down
94 changes: 94 additions & 0 deletions src/apps/ipv6/ns_responder.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
-- This app acts as a responder for neighbor solicitaions for a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this design idea: to handle neighbor solicitation response as a small discrete app that "does one thing well" instead of adding it as a feature to the IPv6 next-hop-routing app.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the traffic going north->south is passed through untouched. Would it make sense to bypass the app in that direction to avoid the overhead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about that too. I suppose it will be worth doing that kind of stuff if the performance difference is significant in practice, once we are done with optimization. I hope we will have the basic app transmit/receive functionality so fast that we don't need to care.

-- specific target address and as a relay for all other packets. It
-- has two ports, north and south. The south port attaches to a port
-- on which NS messages are expected. Non-NS packets are sent on
-- north. All packets received on the north port are passed south.

require("class")
local ffi = require("ffi")
local app = require("core.app")
local packet = require("core.packet")
local datagram = require("lib.protocol.datagram")
local ethernet = require("lib.protocol.ethernet")
local ipv6 = require("lib.protocol.ipv6")
local icmp = require("lib.protocol.icmp.header")
local ns = require("lib.protocol.icmp.nd.ns")

local ns_responder = subClass(nil)

function ns_responder:_init_new(target, lladdr)
self._target = target
self._lladdr = lladdr
end

local function process(self, dgram)
if dgram:parse(
{ { ethernet },
{ ipv6 },
{ icmp },
{ ns,
function(ns)
return(ns:target_eq(self._target))
end } }) then
local eth, ipv6, icmp, ns = unpack(dgram:stack())
local option = ns:options(dgram:payload())
if not (#option == 1 and option[1]:type() == 1) then
-- Invalid NS, ignore
return nil
end
-- Turn this message into a solicited neighbor
-- advertisement with target ll addr option

-- Ethernet
eth:swap()
eth:src(self._lladdr)

-- IPv6
ipv6:dst(ipv6:src())
ipv6:src(self._target)

-- ICMP
option[1]:type(2)
option[1]:option():addr(self._lladdr)
icmp:type(136)
-- Undo/redo icmp and ns headers to get
-- payload and set solicited flag
dgram:unparse(2)
dgram:parse() -- icmp
local payload, length = dgram:payload()
dgram:parse():solicited(1)
icmp:checksum(payload, length, ipv6)
return true
end
return false
end

function ns_responder:push()
local l_in = self.input.north
local l_out = self.output.south
assert(l_in and l_out)
while not app.empty(l_in) and not app.full(l_out) do
-- Pass everything on north -> south
app.transmit(l_out, app.receive(l_in))
end
l_in = self.input.south
l_out = self.output.north
local l_reply = self.output.south
while not app.empty(l_in) and not app.full(l_out) do
local p = app.receive(l_in)
local datagram = datagram:new(p, ethernet)
local status = process(self, datagram)
if status == nil then
-- Discard
packet.deref(p)
elseif status == true then
-- Send NA back south
app.transmit(l_reply, p)
else
-- Send transit traffic up north
app.transmit(l_out, p)
end
end
end

return ns_responder
41 changes: 41 additions & 0 deletions src/apps/socket/dev.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module(...,package.seeall)

local ffi = require("ffi")
local C = ffi.C

local buffer = require("core.buffer")
local packet = require("core.packet")
require("lib.raw.raw_h")
require("apps.socket.io_h")

dev = {}

function dev:new(ifname)
assert(ifname)
self.__index = self
local dev = { fd = C.open_raw(ifname) }
return setmetatable(dev, self)
end

function dev:transmit(p)
assert(self.fd)
assert(C.send_packet(self.fd, p) ~= -1)
end

function dev:can_transmit()
return C.can_transmit(self.fd) == 1
end

function dev:receive()
assert(self.fd)
local p = packet.allocate()
local b = buffer.allocate()
local s = C.receive_packet(self.fd, b)
assert(s ~= -1)
packet.add_iovec(p, b, s)
return p
end

function dev:can_receive()
return C.can_receive(self.fd) == 1
end
77 changes: 77 additions & 0 deletions src/apps/socket/io.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdint.h>
#include <strings.h>
#include <stdio.h>
#include <errno.h>
#include "core/packet.h"

int send_packet(int fd, struct packet *p) {
struct msghdr msg;
struct iovec iovecs[PACKET_IOVEC_MAX];
int i, n = p->niovecs;

bzero(&msg, sizeof(msg));
for (i=0; i < n; i++) {
struct packet_iovec *piov = &p->iovecs[i];
struct iovec *iov = &iovecs[i];

iov->iov_base = piov->buffer->pointer + piov->offset;
iov->iov_len = piov->length;
}
msg.msg_iov = &iovecs[0];
msg.msg_iovlen = n;

if (sendmsg(fd, &msg, 0) == -1) {
perror("sendmsg");
return(-1);
}
return(0);
}

int receive_packet(int fd, struct buffer *b) {
struct msghdr msg;
struct iovec iovec;
ssize_t s;

bzero(&msg, sizeof(msg));
iovec.iov_base = b->pointer;
iovec.iov_len = b->size;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
if ((s = recvmsg(fd, &msg, 0)) == -1) {
perror("recvmsg");
return(-1);
}
return(s);
}

int can_receive(int fd) {
fd_set fds;
struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
int result;

FD_ZERO(&fds);
FD_SET(fd, &fds);
if ((result = select(fd+1, &fds, NULL, NULL, &tv)) == -1) {
perror("select");
return(-1);
}
return(result);
}

int can_transmit(int fd) {
fd_set fds;
struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
int result;

FD_ZERO(&fds);
FD_SET(fd, &fds);
if ((result = select(fd+1, NULL, &fds, NULL, &tv)) == -1) {
perror("select");
return(-1);
}
return(result);
}
4 changes: 4 additions & 0 deletions src/apps/socket/io.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
int send_packet(int, struct packet *);
int receive_packet(int, struct buffer *);
int can_transmit(int);
int can_receive(int);
38 changes: 38 additions & 0 deletions src/apps/socket/raw.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module(...,package.seeall)

local app = require("core.app")
local packet = require("core.packet")
local dev = require("apps.socket.dev").dev

RawSocket = {}

function RawSocket:new (ifname)
assert(ifname)
self.__index = self
return setmetatable({ dev = dev:new(ifname) }, self)
end

function RawSocket:pull ()
assert(self.dev)
local l = self.output.tx
if l == nil then return end
while not app.full(l) and self.dev:can_receive() do
app.transmit(l, self.dev:receive())
end
end

function RawSocket:push ()
assert(self.dev)
local l = self.input.rx
if l == nil then return end
while not app.empty(l) and self.dev:can_transmit() do
local p = app.receive(l)
self.dev:transmit(p)
packet.deref(p)
end
end

function selftest ()
print("RawSocket selftest not implemented")
end

17 changes: 17 additions & 0 deletions src/apps/vhost/raw.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module(...,package.seeall)

local vhost = require("apps.vhost.vhost")
local TapVhost = require("apps.vhost.vhost_apps").TapVhost

RawVhost = TapVhost:new()

function RawVhost:open (ifname)
assert(ifname)
self.dev = vhost.new(ifname, "raw")
return self
end

function selftest ()
print("RawVhost selftest not implemented")
end

18 changes: 12 additions & 6 deletions src/apps/vhost/vhost.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ local buffer = require("core.buffer")
local packet = require("core.packet")
require("lib.virtio.virtio_vring_h")
require("lib.tuntap.tuntap_h")
require("lib.raw.raw_h")
require("apps.vhost.vhost_h")
require("apps.vhost.vhost_client_h")

vring_size = C.VHOST_VRING_SIZE
uint64_t = ffi.typeof("uint64_t")

function new (tapname)
function new (name, type)
local vhost = ffi.new("struct vhost")
local dev = { vhost = vhost,
rxring = vhost.vring[0], -- vring 0 is for receiving
Expand All @@ -32,15 +33,20 @@ function new (tapname)
-- Disable interrupts (eventfd "call"). We are polling anyway.
dev.rxring.avail.flags = C.VRING_F_NO_INTERRUPT
dev.txring.avail.flags = C.VRING_F_NO_INTERRUPT
open(dev, tapname)
open(dev, name, type)
setmetatable(dev, {__index = getfenv()})
return dev
end

function open (dev, tapname)
os.execute("modprobe tun; modprobe vhost_net")
local tapfd = C.open_tap(tapname);
assert(C.vhost_open(dev.vhost, tapfd, memory_regions()) == 0)
function open (dev, name, type)
local fd
if type == "raw" then
fd = C.open_raw(name)
else
os.execute("modprobe tun; modprobe vhost_net")
fd = C.open_tap(name);
end
assert(C.vhost_open(dev.vhost, fd, memory_regions()) == 0)
end

--- ### Data structures: buffer table and descriptor freelist
Expand Down
16 changes: 12 additions & 4 deletions src/apps/vhost/vhost_apps.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ local basic_apps = require("apps.basic.basic_apps")

TapVhost = {}

function TapVhost:new (ifname)
local dev = vhost.new(ifname)
return setmetatable({ dev = dev }, {__index = TapVhost})
function TapVhost:new ()
self.__index = self
return setmetatable({}, self)
end

function TapVhost:open (ifname)
assert(ifname)
self.dev = vhost.new(ifname)
return self
end

function TapVhost:pull ()
assert(self.dev)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is failing in selftest for me. I think the reason is that open() doesn't get called. Adding the open() call to new() looks like it would break RawVhost elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was an ill-fated attempt to subclass TapVhost in a simple-minded manner. I don't want to rewrite it and it's probably best to remove the entire RawVhost thing. I never used it anyway (RawSocket, OTOH, is useful for debugging, because you can attach wireshark to such an interface).

self.dev:sync_receive()
self.dev:sync_transmit()
local l = self.output.tx
Expand All @@ -28,6 +35,7 @@ function TapVhost:pull ()
end

function TapVhost:push ()
assert(self.dev)
local l = self.input.rx
if l == nil then return end
while not app.empty(l) and self.dev:can_transmit() do
Expand All @@ -39,7 +47,7 @@ end

function selftest ()
app.apps.source = app.new(basic_apps.Source)
app.apps.tapvhost = app.new(TapVhost:new("snabb%d"))
app.apps.tapvhost = app.new(TapVhost:new():open("snabb%d"))
app.apps.sink = app.new(basic_apps.Sink)
app.connect("source", "out", "tapvhost", "rx")
app.connect("tapvhost", "tx", "sink", "in")
Expand Down
Loading