diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..5f8b0b2 --- /dev/null +++ b/LICENCE @@ -0,0 +1,15 @@ +WrapSix +Copyright (C) 2008-2009 Michal Zima + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/README b/README index a517269..2e39e80 100644 --- a/README +++ b/README @@ -1,21 +1,47 @@ ##### ## WrapSix readme #### -# Author: Michal Zima, 2008 +# Author: Michal Zima, 2008-2009 # E-mail: xhire@tuxportal.cz ##### == About == -WrapSix is a revolutionary piece of software that make possible to reach IPv4-only servers from IPv6-only networks. It stands as a wrapper of faked IPv6 addresses to real IPv4 addresses. +WrapSix is a revolutionary piece of software that makes possible to reach IPv4-only servers from IPv6-only networks. It stands as a wrapper of faked IPv6 addresses to real IPv4 addresses. == Requirements == -WrapSix is very simple to use. You only need: -* GNU/Linux system -* Ruby (I've tested version 1.8.6) +* GNU/Linux system with IPv6 support (best with 2.6.12 or newer; tested with 2.6.11 (OK) and 2.6.17 (OK)) +* C compiler and such things (just to build binaries of course) +* libpcap (tested with version 0.8.3) +* Ruby (tested with version 1.8.6) + +== Building == +If you satisfied all requirements, let's run build script: './build.sh'. == Configuration == +Look at configuration file conf/wrapsix.conf. + +In section of the DNS resolver you have to set its IPv6 address (already assigned to the network interface of the server) and IPv4 address of already existing DNS resolver. + +In section of the wrapper you have to set some *unused* IPv6 prefix (/96) and *unused* IPv4 address that is not set anywhere. WrapSix manage them itself. If autodetection of network device fails, set it as well, e.g. to 'eth0'. + +The IPv6 prefix can be globaly routable - it is up to you to set the firewall appropriately. + +If you don't need debug mode, it is recommended to disable it. + +All configuration options can be passed on as command line arguments - run './wrapsix.rb --help' for more details. Command line arguments have higher priority and override those in configuration file. + +== Instalation == +WrapSix is not stable yet - it is just experimental version. So it is not supposed to be installed in system (=> there is no installation script), but you can install it by hand if you want. The best location would be in /opt/wrapsix/ I think... == Running == +Run WrapSix with wrapsix.rb. E.g. './wrapsix.rb'. Notice: you need root privileges. + +It is recommended to run WrapSix on its own dedicated machine. + +== Clients == +On client station set some routable IPv6 address (generally from similar prefix as WrapSix has, but not the same!) and DNS server to the address that you set in WrapSix. First test the DNS resolving (with dig, ping (as it does resolving too) or whatsoever) and then try pinging such destination that doesn't have native IPv6 connectivity (e.g. 'ping6 wrapsix.tuxportal.cz' (yes, this site doesn't have IPv6 connectivity yet). + +Note: you have to have IPv6 connectivity (native or even a tunnel is acceptable)! == Getting help == You can visit official WrapSix IRC channel #wrapsix on server irc.tuxportal.cz. Thank you for feedback. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..3dd7c95 --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ + #!/bin/sh + +cd wrapper +make clean && make diff --git a/conf/wrapsix.conf b/conf/wrapsix.conf index 6ccbc02..dc3a391 100644 --- a/conf/wrapsix.conf +++ b/conf/wrapsix.conf @@ -1,11 +1,13 @@ ### Configuration of Resolver -resolver: 1 -#resolver_ip: '::1' -resolver_ip: '2001:470:9985:1:200:e2ff:fe7f:414' -secondary_resolver: 10.0.0.139 # if not set => /etc/resolv.conf +resolver: true +resolver_ip: 'fc00:1::1' +secondary_resolver: 10.0.0.1 # if not set => /etc/resolv.conf ### Configuration of Wrapper -wrapper: 0 +wrapper: true +wrapper_device: # if not set => automatic detection +wrapper_ipv6_prefix: 'fc00:2::' +wrapper_ipv4_address: 10.0.0.111 ### Others -debug: 1 +debug: false diff --git a/lib/resolver.rb b/lib/resolver.rb index e7d0bf0..dd795c9 100644 --- a/lib/resolver.rb +++ b/lib/resolver.rb @@ -4,7 +4,7 @@ #> lib/resolver.rb #~ WrapSix Resolver #### -# Author: Michal Zima, 2008 +# Author: Michal Zima, 2008-2009 # E-mail: xhire@tuxportal.cz ##### @@ -20,7 +20,6 @@ class Resolver @resolver = UDPSocket.open Socket::AF_INET6 begin @resolver.bind $config['resolver_ip'], 53 - #rescue Errno::EPERM rescue Errno::EACCES $stderr.puts "You have to run #{$0} as root!" exit! @@ -28,7 +27,6 @@ class Resolver puts "Started DNS resolver on IPv6 address #{$config['resolver_ip']}" if @debug loop do - puts "---------- start ----------" if @debug # Receive and parse query data = @resolver.recvfrom 2048 print "Client: " if @debug @@ -44,16 +42,16 @@ class Resolver answer.opcode = query.opcode # Type of Query; copy from query answer.aa = 0 # Is this an authoritative response: 0 = No, 1 = Yes answer.rd = query.rd # Is Recursion Desired, copied from query - answer.ra = 0 # Does name server support recursion: 0 = No, 1 = Yes + answer.ra = 1 # Does name server support recursion: 0 = No, 1 = Yes answer.rcode = 0 # Response code: 0 = No errors query.each_question do |question, typeclass| # There may be multiple questions per query begin name = question.to_s # The domain name looked for in the query. + answer.add_question name, typeclass puts "Looking for: #{name}" if @debug #record_type = typeclass.name.split("::").last # For example "A", "MX" puts "RR: #{typeclass}" if @debug - #puts "RR: #{record_type}" if @debug # So let's look for it :c) (in secondary resolver) sr = Resolv::DNS::new :nameserver => $config['secondary_resolver'] @@ -75,18 +73,43 @@ class Resolver print "My answer: " if @debug p answer if @debug rescue Resolv::ResolvError - puts "Error: DNS result has no information for #{name}" + # creating 'faked' AAAA entry + begin + if typeclass == Resolv::DNS::Resource::IN::AAAA + sr = Resolv::DNS::new :nameserver => $config['secondary_resolver'] + sr_data = sr.getresource name, Resolv::DNS::Resource::IN::A + print "Raw answer: " if @debug + p sr_data if @debug + + # completing the answer + aaaa_answer = Resolv::DNS::Resource::IN::AAAA.new(ipaddr_4to6(sr_data.address)) + print "IPv4 address: " if @debug + p sr_data.address if @debug + p ipaddr_4to6(sr_data.address) if @debug + ttl = 86400 # I think ttl doesn't matter ;c) + answer.add_answer name + ".", ttl, aaaa_answer + + print "My answer: " if @debug + p answer if @debug + end + rescue Resolv::ResolvError + puts "Error: DNS result has no information for #{name}" + end end end # send the response @resolver.send answer.encode, 0, data[1][3], data[1][1] # msg, flags, client, port - - puts "---------- end ----------" if @debug end end def exit @resolver.close end + + private + def ipaddr_4to6 ip4addr + ip4parsed = ip4addr.to_s.match(Resolv::IPv4::Regex) + return $config['wrapper_ipv6_prefix'] + ("%02x%02x:%02x%02x" % [ ip4parsed[1], ip4parsed[2], ip4parsed[3], ip4parsed[4] ]) + end end diff --git a/lib/wrapper.rb b/lib/wrapper.rb index b7fb9d6..e5aa398 100644 --- a/lib/wrapper.rb +++ b/lib/wrapper.rb @@ -4,15 +4,22 @@ #> lib/wrapper.rb #~ WrapSix Wrapper #### -# Author: Michal Zima, 2008 +# Author: Michal Zima, 2008-2009 # E-mail: xhire@tuxportal.cz ##### class Wrapper def initializer + @debug = $config['debug'] end def start + params = "#{$config['wrapper_ipv4_address']} #{$config['wrapper_ipv6_prefix']}" + params += " #{$config['wrapper_device']}" if $config['wrapper_device'] + unless @debug + params += " > /dev/null" + end + system "wrapper/wrapper #{params}" end def exit diff --git a/wrapper/process.c b/wrapper/process.c index f849727..e3a9f15 100644 --- a/wrapper/process.c +++ b/wrapper/process.c @@ -52,24 +52,39 @@ void process_packet4(const struct s_ethernet *eth, const unsigned char *packet) printf(" From: %s\n", inet_ntoa(ip->ip_src)); printf(" To: %s\n", inet_ntoa(ip->ip_dest)); - /* check if this packet is ours */ - if (memcmp(&ip4addr_wrapsix, &ip->ip_dest, 4)) { - printf("==> This packet is not ours! <==\n"); - return; - } - /* determine protocol */ switch (ip->proto) { case IPPROTO_TCP: printf(" Protocol: TCP\n"); + + /* check if this packet is ours */ + if (memcmp(&ip4addr_wrapsix, &ip->ip_dest, 4)) { + printf("==> This packet is not ours! <==\n"); + return; + } + process_tcp4(eth, ip, payload, htons(ip->pckt_len) - header_length); break; case IPPROTO_UDP: printf(" Protocol: UDP\n"); + + /* check if this packet is ours */ + if (memcmp(dev_ip, &ip->ip_dest, 4)) { + printf("==> This packet is not ours! <==\n"); + return; + } + process_udp4(eth, ip, payload, htons(ip->pckt_len) - header_length); break; case IPPROTO_ICMP: printf(" Protocol: ICMP\n"); + + /* check if this packet is ours */ + if (memcmp(dev_ip, &ip->ip_dest, 4)) { + printf("==> This packet is not ours! <==\n"); + return; + } + process_icmp4(eth, ip, payload, htons(ip->pckt_len) - header_length); break; default: @@ -117,11 +132,11 @@ void process_tcp4(const struct s_ethernet *eth_hdr, struct s_ip4 *ip_hdr, const /* check if this packet is from wrapped connection */ if (ent == NULL) { - fprintf(stderr, "Error: data not found\n"); + printf("Error: data not found\n"); return; } else if (memcmp(&ent->addr_to, &ip_hdr->ip_src, sizeof(struct in_addr))) { - fprintf(stderr, "Error: data not appropriate\n"); + printf("Error: data not appropriate\n"); printf(" Ent-to: %s\n", inet_ntoa(ent->addr_to)); printf(" IP-from: %s\n", inet_ntoa(ip_hdr->ip_src)); return; @@ -280,11 +295,11 @@ void process_udp4(const struct s_ethernet *eth_hdr, struct s_ip4 *ip_hdr, const /* check if this packet is from wrapped connection */ if (ent == NULL) { - fprintf(stderr, "Error: data not found\n"); + printf("Error: data not found\n"); return; } else if (memcmp(&ent->addr_to, &ip_hdr->ip_src, sizeof(struct in_addr))) { - fprintf(stderr, "Error: data not appropriate\n"); + printf("Error: data not appropriate\n"); printf(" Ent-to: %s\n", inet_ntoa(ent->addr_to)); printf(" IP-from: %s\n", inet_ntoa(ip_hdr->ip_src)); return; @@ -387,11 +402,11 @@ void process_icmp4(const struct s_ethernet *eth_hdr, struct s_ip4 *ip_hdr, const /* check if this packet is from wrapped connection */ if (ent == NULL) { - fprintf(stderr, "Error: data not found\n"); + printf("Error: data not found\n"); return; } else if (memcmp(&ent->addr_to, &ip_hdr->ip_src, sizeof(struct in_addr))) { - fprintf(stderr, "Error: data not appropriate\n"); + printf("Error: data not appropriate\n"); printf(" Ent-to: %s\n", inet_ntoa(ent->addr_to)); printf(" IP-from: %s\n", inet_ntoa(ip_hdr->ip_src)); return; diff --git a/wrapper/wrapper.c b/wrapper/wrapper.c index c08cbf2..b8c53e3 100644 --- a/wrapper/wrapper.c +++ b/wrapper/wrapper.c @@ -13,9 +13,13 @@ jsw_rbtree_t *stg_conn_tcp; jsw_rbtree_t *stg_conn_udp; jsw_rbtree_t *stg_conn_icmp; +/* + * 1: IPv4 address + * 2: IPv6 prefix + * 3: ethernet device + */ int main(int argc, char **argv) { - char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */ pcap_t *handle; /* packet capture handle */ @@ -30,10 +34,20 @@ int main(int argc, char **argv) /* find a capture device */ dev = NULL; - dev = pcap_lookupdev(errbuf); - if (dev == NULL) { - fprintf(stderr, "Couldn't find default device: %s\n", errbuf); - exit(EXIT_FAILURE); + printf("Args: %d\n", argc); + if (argc == 4) { + if ((dev = malloc(strlen(argv[3]))) == NULL) { + fprintf(stderr, "Fatal Error! Lack of free memory!\n"); + exit(EXIT_FAILURE); + } + memcpy(dev, argv[3], sizeof(dev)); + } + else { + dev = pcap_lookupdev(errbuf); + if (dev == NULL) { + fprintf(stderr, "Couldn't find default device: %s\n", errbuf); + exit(EXIT_FAILURE); + } } /* print capture info */ @@ -72,8 +86,10 @@ int main(int argc, char **argv) dev_index = get_dev_index(dev); /* set the WrapSix addresses */ - inet_aton("10.0.0.111", &ip4addr_wrapsix); - inet_pton(AF_INET6, "fc00:1::", &ip6addr_wrapsix); + //inet_aton("10.0.0.111", &ip4addr_wrapsix); + //inet_pton(AF_INET6, "fc00:1::", &ip6addr_wrapsix); + inet_aton(argv[1], &ip4addr_wrapsix); + inet_pton(AF_INET6, argv[2], &ip6addr_wrapsix); /* compile the filter expression */ if (pcap_compile(handle, &fp, filter_exp, 0, 0) == -1) { diff --git a/wrapsix.rb b/wrapsix.rb index 8984326..108e9af 100755 --- a/wrapsix.rb +++ b/wrapsix.rb @@ -3,63 +3,102 @@ ## WrapSix ### #> wrapsix.rb -#~ Description... +#~ Main part of WrapSix that starts all other components #### -# Author: Michal Zima, 2008 -# E-mail: xhire@tuxportal.cz +# Author: Michal Zima, 2008-2009 +# E-mail: xhire@tuxportal.cz +# Homepage: http://wrapsix.tuxportal.cz/ ##### +$version = '0.1.0' -### Hardcoded configuration => configured by system administrator $config = {} $config['config_file'] = 'conf/wrapsix.conf' -#------------------------------------------------------------------------------# - ### Include all necessary libraries require 'yaml' require 'socket' +require 'optparse' # WrapSix libs require 'lib/resolver' require 'lib/wrapper' ### Parse command line arguments if any +OptionParser.new do |opts| + opts.banner = "Usage: wrapsix.rb [options]" -### Load configuration -configuration = YAML.load_file $config['config_file'] + opts.on("--[no-]resolver", "Run the DNS resolver") do |resolver| + $config['resolver'] = resolver + end -## Merge both configs -$config.merge! configuration # FIX: this overwrites those configs from command line! -#p $config + opts.on("--[no-]wrapper", "Run the wrapper") do |wrapper| + $config['wrapper'] = wrapper + end -### Start logging facility (system wide one) + opts.on("--resolver-ip=IPv6_address", "Set the IPv6 address for the DNS resolver") do |rip| + $config['resolver_ip'] = rip + end + + opts.on("--dns-resolver=IP_address", "Set the address of DNS resolver to be used") do |sr| + $config['secondary_resolver'] = sr + end + + opts.on("--device=dev", "Set the network interface to override automatic detection") do |nic| + $config['wrapper_device'] = nic + end + + opts.on("--ipv6=prefix", "Set the IPv6 preffix (max. /96), e.g. fc00::") do |prefix| + $config['wrapper_ipv6_prefix'] = prefix + end + + opts.on("--ipv4=address", "Set the IPv4 address") do |addr| + $config['wrapper_ipv4_address'] = addr + end + + opts.on("-d", "--[no-]debug", "Run in the debug mode") do |d| + $config['debug'] = d + end + + opts.on_tail("-h", "--help", "Show this message") do + puts opts + exit + end + + # Another typical switch to print the version. + opts.on_tail("-v", "--version", "Show version") do + puts "WrapSix #{$version}" + puts "Copyright (c) 2008-2009 Michal Zima" + exit + end +end.parse! + +### Load configuration from file and merge it with the original one +$config = YAML.load_file($config['config_file']).merge $config ### Handle some signals -# todo: replace this with right variables def exit $resolver.exit if $config['resolver'] $wrapper.exit if $config['wrapper'] Process.exit end -# TERM -KILL- QUIT INT +# TERM QUIT INT trap "INT" do; exit; end trap "TERM" do; exit; end trap "QUIT" do; exit; end services = [] ### Start DNS resolver function -if $config['resolver'] == 1 - $resolver = Resolver.new +$resolver = Resolver.new +if $config['resolver'] == true services << Thread.start do; $resolver.start; end end ### Start IPv6-to-IPv4 wrapper -if $config['wrapper'] == 1 - $wrapper = Wrapper.new +$wrapper = Wrapper.new +if $config['wrapper'] == true services << Thread.start do; $wrapper.start; end end ### Start WrapSix # in best conditions it would *never* stop services.each do |srvc| srvc.join end -