-
Notifications
You must be signed in to change notification settings - Fork 299
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
Vpn #86
Changes from 6 commits
cce9cf5
f0613cd
ebd92d2
26c51e9
a4ac0fb
8d3b702
7e879f4
fc91865
51122bb
0e3116f
de1432b
411aabc
302f4ff
53aaaf6
40813ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
-- 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 |
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 |
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); | ||
} |
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); |
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 | ||
|
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
@@ -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 | ||
|
@@ -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") | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.