Last active
February 9, 2017 14:35
-
-
Save ckhung/b785a4214c0dffd2b318b62809555291 to your computer and use it in GitHub Desktop.
forward multiple port in one command ; 詳見 [fwmp 懶人的通訊埠轉發指令](https://newtoypia.blogspot.tw/2017/02/fwmp.html): 用一個指令幫區網內的一部機器 (可以是實體機、 kvm 虛擬機或 lxc 容器) 同時轉發好幾個通訊埠。
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
#!/usr/bin/perl -w | |
# LXC calls us with the following arguments: | |
# fwmp.lxc set $IP $hp1:$cp1 $hp2:$cp2 ... $cname net up veth $iface | |
# fwmp.lxc clear $IP $cname net down veth $iface | |
use strict; | |
my ($iface, undef, $when, undef, $cid, @ARGV) = reverse @ARGV; | |
@ARGV = reverse @ARGV; | |
my @mapping = (); | |
while ($#ARGV >= 0 && $ARGV[$#ARGV] =~ /^\d+:\d+$/) { | |
push(@mapping, pop(@ARGV)); | |
} | |
@mapping = reverse @mapping; | |
my ($IP) = pop(@ARGV); | |
my ($action) = pop(@ARGV); | |
if ($when eq 'up') { | |
system(qq(fwmp @ARGV $action $IP @mapping)); | |
exit unless -r "/etc/pve/lxc/$cid.conf"; | |
# somehow lxc in proxmox fails to add the interface into the bridge | |
# when there is "lxc.network.script.up = ..." in /etc/pve/lxc/*.conf | |
# So we hack. | |
my ($index) = $iface =~ /(\d)$/; | |
my $bridge = qx(grep -P '^net$index:.*?bridge=' /etc/pve/lxc/$cid.conf); | |
($bridge) = $bridge =~ /bridge=(\w+)/; | |
system(qq(brctl addif $bridge $iface)); | |
} else { | |
system(qq(fwmp @ARGV $action $IP)); | |
} | |
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
#!/usr/bin/perl -w | |
# usage: | |
# fwmp list 192.168.99.88 | |
# => list forward rules about 192.168.99.88 | |
# fwmp add 192.168.99.88 12012:22 12018:80 | |
# => add iptables rules to forward this host's | |
# => port 12012 to 192.168.99.88's port 22 and | |
# => port 12018 to 192.168.99.88's port 80 | |
# fwmp clear 192.168.99.88 | |
# => remove all port forwarding rules for 192.168.99.88 generated by this script | |
# fwmp set 192.168.99.88 12012:22 12018:80 | |
# ==> same as 'clear' then 'set' | |
# | |
# For lxc guests, cid can be used in place of ip address. | |
# If the ip address looks like an lxc container id, | |
# this program then looks up its address in /etc/lxc/guests . | |
# syntax inspired by https://github.com/patrakov/lxc-expose-port | |
# other references: | |
# https://stackoverflow.com/a/19734874 | |
# https://raymii.org/s/tutorials/Proxmox_VE_One_Public_IP.html | |
use strict; | |
use Getopt::Std; | |
my (%opts) = ( | |
s => 'vmbr0', # source (outside-facing) network interface | |
l => '/etc/lxc/guests', # lxc ip lookup table | |
); | |
getopts('s:l:', \%opts); | |
die "usage: $0 [list|clear|add] IP/CID host_port_1:guest_port_1 hp2:gp2 ...\n" | |
unless $#ARGV >= 1; | |
my ($cmd) = shift @ARGV; | |
my ($cid) = shift @ARGV; | |
my ($ip, $rule); | |
if ($cid) { | |
if ($cid =~ /^(\d+\.){3}\d+$/) { | |
$ip = $cid; | |
print "[$ip]\n"; | |
} elsif ($cid =~ /^\w+$/) { | |
if ($opts{l} eq 'lxc-info') { | |
# lxc ip lookup: using "lxc-info" | |
$ip = qx(lxc-info -n $cid -iH); | |
if ($ip and $ip =~ /^\d+(\.\d+){3}$/) { | |
chomp $ip; | |
} else { | |
die "lxc container '$cid' is not running or does not have an IP\n"; | |
} | |
} elsif (-r $opts{l}) { | |
$ip = qx(grep -P '(\\d+\\.){3}(\\d+)\\s+$cid\\b' $opts{l}); | |
$ip =~ /((\d+\.){3}\d+)/; | |
$ip = $1; | |
die "cannot find lxc container $cid in $opts{l}" unless $ip; | |
} else { | |
die "unknown or unreadable lxc ip lookup table '$opts{l}'"; | |
} | |
print "[$cid => $ip]\n"; | |
} else { | |
die "expecting a Container ID or an IP but got unknown string '$cid'\n"; | |
} | |
} | |
if ($cmd eq 'list') { | |
my ($ip_re) = ip_re($ip); | |
print qx(iptables -L -t nat | grep -P "\\[fwmp $ip_re \\d+:\\d+\\]"); | |
} elsif ($cmd eq 'clear') { | |
nat_clear($ip); | |
} elsif ($cmd eq 'add' or $cmd eq 'set') { | |
my ($err) = qx(ip link show $opts{s} 2>&1 1>/dev/null); | |
die "ip link show $opts{s} failed:\n$err" if $err; | |
nat_clear($ip) if $cmd eq 'set'; | |
foreach $rule (@ARGV) { | |
nat_add($ip, $rule); | |
} | |
} else { | |
die "unknown command '$cmd'\n"; | |
} | |
sub nat_clear { | |
my ($ip) = @_; | |
my ($ip_re) = ip_re($ip); | |
my $cmd = qq(iptables-save | grep -Pv "\\[fwmp $ip_re \\d+:\\d+\\]" | iptables-restore); | |
system($cmd); | |
} | |
sub nat_add { | |
my ($ip, $rule) = @_; | |
my ($ip_re) = ip_re($ip); | |
my ($from, $to) = $rule =~ /^(\d+):(\d+)$/; | |
if ($from and $to) { | |
system(qq(iptables -t nat -A PREROUTING -i $opts{s} -p tcp --dport $from -j DNAT --to $ip:$to -m comment --comment "[fwmp $ip $rule]")); | |
} else { | |
print STDERR "warning: ignoring unknown rule '$rule'\n"; | |
} | |
} | |
sub ip_re { | |
my ($ip) = @_; | |
$ip =~ s/\./\\./g; | |
return $ip; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment