Skip to content

Instantly share code, notes, and snippets.

@msantos
Created March 29, 2012 20:30
Show Gist options
  • Save msantos/2243441 to your computer and use it in GitHub Desktop.
Save msantos/2243441 to your computer and use it in GitHub Desktop.
Erlang IPv6 in IPv4 tunnel (RFC 4213)
-module(sut).
%%% Erlang 6in4 tunnel (RFC 4213)
%%%
%%% Not RFC compliant yet and probably buggy, but works!
%%%
%%% * Dependencies:
%%%
%%% https://github.com/msantos/procket
%%% https://github.com/msantos/pkt
%%% https://github.com/msantos/tunctl
%%%
%%% * Add to sudoers:
%%%
%%% <username> ALL = NOPASSWD: /sbin/ifconfig sut*
%%%
%%% * Sign up for an IPv6 with Hurricane Electric
%%%
%%% http://tunnelbroker.net/
%%%
%%% * Start the IPv6 tunnel:
%%%
%%% Serverv4 = HE IPv4 tunnel end
%%%
%%% Clientv4 = Your local IP address
%%% Clientv6 = The IPv6 address assigned by HE to your end of the tunnel
%%%
%%% sut:start([{serverv4, "216.66.22.2"}, {clientv4, "192.168.1.72"}, {clientv6, "2001:3:3:3::2"}]).
%%%
%%% * Set up MTU and routing (as root)
%%%
%%% ifconfig sut-ipv6 mtu 1480
%%% ip route add ::/0 dev sut-ipv6
%%%
%%% * Test the tunnel!
%%%
%%% ping6 ipv6.google.com
%%%
-export([
start/0, start/1
]).
-define(IPPROTO_IPV6, 41).
% https://gist.github.com/1213721 by gdamjan
-define(PROPLIST_TO_RECORD(Record),
fun(Proplist) ->
Fields = record_info(fields, Record),
[Tag| Values] = tuple_to_list(#Record{}),
Defaults = lists:zip(Fields, Values),
L = lists:map(fun ({K,V}) -> proplists:get_value(K, Proplist, V) end, Defaults),
list_to_tuple([Tag|L])
end).
-record(state, {
ifname = <<"sut-ipv6">>,
serverv4,
clientv4,
clientv6,
s,
dev
}).
start() ->
start(#state{}).
start(Opt) when is_list(Opt) ->
Fun = ?PROPLIST_TO_RECORD(state),
start(Fun(Opt));
start(#state{serverv4 = Server,
clientv4 = Client4,
clientv6 = Client6,
ifname = Ifname} = State) ->
{ok, FD} = procket:open(0, [
{protocol, ?IPPROTO_IPV6},
{type, raw},
{family, inet}
]),
{ok, Socket} = gen_udp:open(0, [
binary,
{fd, FD}
]),
{ok, Dev} = tuncer:create(Ifname, [
tun,
no_pi,
{active, true}
]),
ok = tuncer:up(Dev, Client6),
loop(State#state{
s = Socket,
dev = Dev,
serverv4 = aton(Server),
clientv4 = aton(Client4),
clientv6 = aton(Client6)
}).
loop(#state{
s = Socket,
clientv4 = {DA1,DA2,DA3,DA4},
serverv4 = {SA1,SA2,SA3,SA4},
dev = Dev
} = State) ->
receive
{udp, Socket, {SA1,SA2,SA3,SA4}, 0,
<<4:4, HL:4, _ToS:8, _Len:16, _Id:16, 0:1, _DF:1, _MF:1,
_Off:13, _TTL:8, ?IPPROTO_IPV6:8, _Sum:16,
SA1:8, SA2:8, SA3:8, SA4:8,
DA1:8, DA2:8, DA3:8, DA4:8,
Data/binary>>} ->
Opt = case (HL-5)*4 of
N when N > 0 -> N;
_ -> 0
end,
<<_:Opt/bits, Payload/bits>> = Data,
ok = case valid(Payload) of
true ->
tuncer:send(Dev, Payload);
Invalid ->
error_logger:info_report([
Invalid,
{source_address, inet_parse:ntoa({SA1,SA2,SA3,SA4})},
{packet, Data}
]),
ok
end,
loop(State);
% Invalid packet
{udp, Socket, Src, 0, Pkt} ->
error_logger:info_report([
{error, invalid_packet},
{source_address, Src},
{packet, Pkt}
]),
loop(State);
{tuntap, Dev, Data} ->
ok = gen_udp:send(Socket, {SA1,SA2,SA3,SA4}, 0, Data),
loop(State);
Error ->
error_logger:error_report([{loop, Error}])
end.
aton(Address) when is_list(Address) ->
{ok, N} = inet_parse:address(Address),
N;
aton(Address) when is_tuple(Address) ->
Address.
%%
%% Check for valid IPv6 packet
%%
% loopback
valid(<<6:4, _Class:8, _Flow:20,
_Len:16, _Next:8, _Hop:8,
_SA1:16, _SA2:16, _SA3:16, _SA4:16, _SA5:16, _SA6:16, _SA7:16, _SA8:16,
0:16, 0:16, 0:16, 0:16, 0:16, 0:16, 0:16, 1:16,
_Payload/binary>>) ->
{invalid, loopback};
% unspecified address
valid(<<6:4, _Class:8, _Flow:20,
_Len:16, _Next:8, _Hop:8,
_SA1:16, _SA2:16, _SA3:16, _SA4:16, _SA5:16, _SA6:16, _SA7:16, _SA8:16,
0:16, 0:16, 0:16, 0:16, 0:16, 0:16, 0:16, 0:16,
_Payload/binary>>) ->
{invalid, unspecified_address};
% Multicast
valid(<<6:4, _Class:8, _Flow:20,
_Len:16, _Next:8, _Hop:8,
_SA1:16, _SA2:16, _SA3:16, _SA4:16, _SA5:16, _SA6:16, _SA7:16, _SA8:16,
16#FF00:16, _:16, _:16, _:16, _:16, _:16, _:16, _:16,
_Payload/binary>>) ->
{invalid, multicast};
% IPv6 Addresses with Embedded IPv4 Addresses
valid(<<6:4, _Class:8, _Flow:20,
_Len:16, _Next:8, _Hop:8,
_SA1:16, _SA2:16, _SA3:16, _SA4:16, _SA5:16, _SA6:16, _SA7:16, _SA8:16,
0:16, 0:16, 0:16, 0:16, 0:16, 0:16, _:16, _:16,
_Payload/binary>>) ->
{invalid, ipv4_compatible_ipv6_address};
valid(<<6:4, _Class:8, _Flow:20,
_Len:16, _Next:8, _Hop:8,
_SA1:16, _SA2:16, _SA3:16, _SA4:16, _SA5:16, _SA6:16, _SA7:16, _SA8:16,
0:16, 0:16, 0:16, 0:16, 0:16, 16#FFFF:16, _:16, _:16,
_Payload/binary>>) ->
{invalid, ipv4_mapped_ipv6_address};
valid(<<6:4, _:4, _/binary>>) ->
true;
% Invalid protocol
valid(_Packet) ->
{invalid, invalid_protocol}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment