Skip to content

Instantly share code, notes, and snippets.

@ckhung
Last active February 9, 2017 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ckhung/b785a4214c0dffd2b318b62809555291 to your computer and use it in GitHub Desktop.
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 容器) 同時轉發好幾個通訊埠。
#!/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));
}
#!/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