Created
March 29, 2012 20:30
-
-
Save msantos/2243441 to your computer and use it in GitHub Desktop.
Erlang IPv6 in IPv4 tunnel (RFC 4213)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-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