Skripte/nathelper

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Dieser Artikel ist größtenteils für alle Ubuntu-Versionen gültig.

Artikel für fortgeschrittene Anwender

Dieser Artikel erfordert mehr Erfahrung im Umgang mit Linux und ist daher nur für fortgeschrittene Benutzer gedacht.

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

  1. Ein Terminal öffnen

Dieses Perl-Skript dient dazu, mit einem einzigen Kommandozeilenbefehl einen Ubuntu-Router mit der nötigen Network Address Translation (NAT) Funktionalität auszustatten, ohne dass man sich mit der iptables-Syntax beschäftigen muss.

Installation und Benutzung

Einfach das Skript irgendwo hin kopieren, wo es passt, bspw. nach /usr/local/sbin/nathelper. Dann die geeigneten Kommandozeilenargumente (s.u.) zusammenstellen und wenn alles wie gewünscht funktioniert den Befehl in einem Startskript (bspw. in /etc/rc.local) konservieren.

Syntax

Das Skript wird komplett über seine Argumente gesteuert[1]:

nathelper -i IFACE OPTIONEN IP PORT(S) 

Beispiele:

nathelper -i ppp+ -m                                                   # nur Masquerading
nathelper -i ppp+ -m 192.168.6.5 80 21 192.168.6.6 6881-6889:7001-7009  # Masquerading und verschiedene Port-Weiterleitungen 

In diesem Beispiel wird auf dem externen ppp-Device (ppp+ ist ein Joker-Ausdruck, der auf alle ppp-Devices zutrifft) Masquerading eingerichtet, sowie ein paar Ports nach innen weitergeleitet. Die Ports 80 und 21 werden an den Rechner 192.168.6.5 weitergeleitet und die Ports 6881 bis 6889 werden an die Ports 7001 bis 7009 des Rechners 192.168.6.6 weitergeleitet.

Optionen

Die verschiedenen Operatoren können auch alle miteinander kombiniert werden, wobei aber auf eine gleiche Anzahl Ports rechts und links vom : geachtet werden muss. Beispiel:

nathelper -i ppp+ -m 192.168.1.1 21-25,80,3128:10001-10007 

Achtung!

Für jeden einzelnen Port wird eine iptables-Regel angelegt, was sich bei einer sehr hohen Anzahl an Regeln durchaus auf die Performance auswirken kann. Es ist deswegen nicht empfehlenswert, aus Bequemlichkeit bspw. mit 1-65535 alle Ports umzuleiten.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/perl -w
#
# nathelper - Masquerading und Port-Forwarding in einer Kommandozeile
#             benoetigt natuerlich iptables
#
# Autor: otzenpunk (UbuntuUsers.de)
# Lizenz: Public Domain

use strict;
use Getopt::Std;

our %opts;
usage() unless @ARGV;
getopts('qvdhmi:', \%opts);
usage() if $opts{'h'} or !$opts{'i'};

init();                             # ip_forward, iptables -F

masq($opts{'i'}) if ($opts{'m'});   # evtl. Masquerading

our @forwards;
our $fw_indx = 0;
while ($_ = shift @ARGV) {          # Array of Arrays: je 1xIP und n x Ports
  ++$fw_indx if /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && @forwards;
  push @{$forwards[$fw_indx]}, $_;
}

forw($_) for @forwards;             # jede IP verarbeiten

print "NAT erfolgreich aktiviert.\n" unless $opts{'q'} or $opts{'d'};
exit(0);

# masq(iface): Masquerading

sub masq {
  my @iptables = (qw(/sbin/iptables -t nat -A POSTROUTING -o), $opts{'i'}, qw(-j MASQUERADE));
  iptables(@iptables);
}

# forw(IP, Ports, ...): Ports duerfen Aufzaehlungen  (zahl,zahl)
#                                     Ranges         (zahl-zahl)
#                                 und Uebersetzungen (zahl:zahl) enthalten.

sub forw {
  my $argptr = shift;               # Pointer auf Array
  my $ip = shift @$argptr;          # Erstes Element = IP
  for (@$argptr) {
    s/[^0-9u:,-]//ig;               # saeubern von verbotenen Zeichen
    my $proto = s/^u//i ? 'udp' : 'tcp';   # u = UDP
    my ($von, $nach) = split ':';   # Uebersetzung trennen
    $nach ||= $von;                 # wenn kein ':' -> Ports bleiben gleich
    my @von = expand_range($von);   # ',' und '-' expandieren
    my @nach = expand_range($nach);
    unless (@von == @nach) {        # Falsche Parameter abfangen:
      print "Ungleiche Anzahl Ports: ", join(', ', @von), ' -> ', join(', ', @nach), "\n\n";
      usage();
    }
    my $i = 0;
    for (@von) {                    # je eine iptables-Zeile pro Port
      my @iptables = (qw(/sbin/iptables -t nat -A PREROUTING -i), $opts{'i'},
                      '-p', $proto, '--dport', $_,
                      qw(-j DNAT --to-destination), "$ip:$nach[$i++]");
      iptables(@iptables);
    }
  }
}

# iptables(Liste der Argumente, inkl. iptables-Pfad)

sub iptables {            # system() gibt >0 bei Fehler
  print join(' ', @_), "\n" if $opts{'v'} or $opts{'d'};
  system(@_) && die("Fehler bei iptables. Root?\n") unless ($opts{'d'});
}

# expand_range(Port-String inkl. [,-])
#             ',' und '-' expandieren und Liste zurueck

sub expand_range {
  my @werte = split ',', shift;
  @werte = map {
    my ($down, $up) = split '-', $_;
    $down < ($up ||= 0) ? ($down..$up) : $down;
  } @werte;
  return @werte;
}

sub init {
  print "sysctl -w net/ipv4/ip_forward=1\n" if $opts{'v'} or $opts{'d'};
  system(qw(sysctl -w net/ipv4/ip_forward=1)) if not $opts{'d'};
  iptables(qw(/sbin/iptables -t nat -F));
}

sub usage {
  print <<EOT;
Usage: $0 [-v] [-d] -i iface [-m] [ip ports ...] ...
       $0 [-h]

-h : Hilfe
-v : Verbose, iptables-Befehle ausgeben und durchfuehren
-d : Debug, nicht wirklich durchfuehren
-q : Keine Erfolgsmeldung ausgeben
-i : externes Interface
-m : Masquerading aktivieren

ip ports : Ports, die auf IP umgeleitet werden, mehrere moeglich
ports : 1234 -> Port wird 1:1 umgeleitet
        1234:5678 -> Port wird auf anderen Port umgeleitet
        1234-1238 -> Port-Range wird umgeleitet
        1234-1238:5674-5678 -> beides
        u1234 -> UDP-Port(s) verwenden
EOT
  exit(1);
}