#!/usr/bin/perl $debug = 0; BEGIN {push @INC, '/www/cgi-bin'}; use perlfunc; $config = nvram_get("config"); $node = nvram_get("node"); $node = "NOCALL" if $node eq ""; read_postdata(); if($config eq "" or -e "/tmp/reboot-required") { http_header(); html_header("$node setup", 1); print "
\n"; print "
\n"; alert_banner(); navbar("ports"); print "

"; if($config eq "") { print "This page is not available until the configuration has been set."; } else { print "The configuration has been changed.
This page will not be available until the node is rebooted.\n"; } print "
\n"; exit; } # check for dmz mode if(-e "/etc/config/dmz-mode") { chomp ($dmz_mode = `cat /etc/config/dmz-mode`) } else { $dmz_mode = 0 } # get the network details of the lan interface for dhcp calculations if(system "ifconfig br-lan >/dev/null 2>&1") { ($lanip, $lanmask, $junk, $lannet, $lancidr) = get_ip4_network("eth0"); } else { ($lanip, $lanmask, $junk, $lannet, $lancidr) = get_ip4_network("br-lan"); } $lannet_d = ip2decimal($lannet); $tmpdir = "/tmp/web/ports"; system "rm -rf $tmpdir" unless $parms{reload}; system "mkdir -p $tmpdir"; $portfile = "/etc/config.$config/_setup.ports"; $dhcpfile = "/etc/config.$config/_setup.dhcp"; $servfile = "/etc/config.$config/_setup.services"; if($config eq "mesh") { my $suffix = $dmz_mode ? ".dmz" : ".nat"; $portfile .= $suffix; $dhcpfile .= $suffix; $servfile .= $suffix; } # if a reset or a first time page load # read the data from the config files if($parms{button_reset} or not $parms{reload}) { $i = 0; foreach(`cat $portfile 2>/dev/null`) { next if /^\s*#/; next if /^\s*$/; chomp; # set parameters if(/(\S+)\s+=\s+(\S+)/) { $parms{$1} = $2; next; } # set port forwarding rules @parts = split /[:]/, $_; next unless scalar(@parts) == 6; ++$i; #foreach $var (qw(intf type out ip in enable adv link proto suffix name)) foreach $var (qw(intf type out ip in enable)) { $parms{"port${i}_$var"} = shift @parts; } } $parms{port_num} = $i; # set dhcp reservations # ip addresses are stored as offsets from the lan network address $i = 0; foreach(`cat $dhcpfile 2>/dev/null`) { next if /^\s*#/; next if /^\s*$/; chomp; @parts = split /\s+/, $_; next unless scalar(@parts) == 3; ++$i; $parms{"dhcp${i}_host"} = $parts[2]; $parms{"dhcp${i}_ip"} = add_ip_address($lannet, $parts[1]); $parms{"dhcp${i}_mac"} = $parts[0]; } $parms{dhcp_num} = $i; # services $i = 0; foreach(`cat $servfile 2>/dev/null`) { next if /^\s*#/; next if /^\s*$/; chomp; @parts = split /\|/, $_; $parts[5] = "" unless $parts[5]; next unless scalar(@parts) == 6; ++$i; foreach $var (qw(name link proto host port suffix)) { $parms{"serv${i}_$var"} = shift @parts; } } $parms{serv_num} = $i; # sanitize the "add" values $parms{port_add_intf} = $dmz_mode ? "wan" : "wifi"; $parms{port_add_type} = "tcp"; $parms{dmz_ip} = "" unless defined $parms{dmz_ip}; foreach $var (qw(port_add_out port_add_ip port_add_in dhcp_add_host dhcp_add_ip dhcp_add_mac serv_add_name serv_add_proto serv_add_host serv_add_port serv_add_suffix)) { $parms{$var} = ""; } } # get the dhcp range # assume that the lan setup is the only one that exists foreach(`cat /etc/config/dhcp`) { $dhcp_start = $1 if /option start\s+(\d+)/; $dhcp_limit = $1 if /option limit\s+(\d+)/; } $dhcp_end = $dhcp_start + $dhcp_limit - 1; # # load and validate the ports # for($i = 1, @list = (); $i <= $parms{port_num}; ++$i) { push @list, $i } push @list, "_add"; $port_num = 0; open(FILE, ">$tmpdir/ports"); foreach $val (@list) { # load strings foreach $var (qw(intf type out ip in)) { $varname = "port${val}_$var"; $parms{$varname} = "" unless $parms{$varname}; $parms{$varname} =~ s/^\s+//; $parms{$varname} =~ s/\s+$//; eval sprintf("\$%s = \$parms{%s}", $var, $varname); } # load bools foreach $var (qw(enable)) { $varname = "port${val}_$var"; $parms{$varname} = 0 unless $parms{$varname}; eval sprintf("\$%s = \$parms{%s}", $var, $varname); } $enable = 1 if $val eq "_add"; if($val eq "_add") { next unless ($out or $ip or $in) and ($parms{port_add} or $parms{button_save}) } else { next if $parms{"port${val}_del"} } if($val eq "_add" and $parms{button_save}) { push(@port_err, "$val this rule must be added or cleared out before saving changes"); next; } if($out =~ /-/) # this is a port range { if(validate_port_range($out)) { $out =~ s/\s+//; ($in) = $out =~ /^(\d+)/; # force inside to match outside } else { push(@port_err, "$val '$out' is not a valid port range"); } } else { if($out eq "") { push @port_err, "$val an outside port is required" } else { push(@port_err, "$val '$out' is not a valid port") unless validate_port($out) } } if($ip eq "") { push @port_err, "$val an address must be selected" } elsif(not validate_ip($ip)) { push @port_err, "$val '$ip' is not a valid address" } if($in eq "") { push @port_err, "$val a LAN port is required" } else { push(@port_err, "$val '$in' is not a valid port") unless validate_port($in) } next if $val eq "_add" and @port_err and $port_err[-1] =~ /^_add /; # commit the data for this rule ++$port_num; $usedports{$out} = 1; $type = "both" unless $type eq "tcp" or $type eq "udp"; print FILE "$intf:$type:$out:$ip:$in:$enable\n"; foreach $var (qw(intf type out ip in enable)) { eval sprintf("\$parms{port%d_%s} = \$%s", $port_num, $var, $var); } if($val eq "_add") { $parms{port_add_intf} = "wifi"; $parms{port_add_out} = ""; $parms{port_add_ip} = ""; $parms{port_add_in} = ""; } } if($parms{dmz_ip}) { print FILE "dmz_ip = $parms{dmz_ip}\n"; push(@dmz_err, "'$parms{dmz_ip}' is not a valid address") unless validate_ip($parms{dmz_ip}); } close(FILE); $parms{port_num} = $port_num; # # load and validate the dhcp reservations # for($i = 1, @list = (); $i <= $parms{dhcp_num}; ++$i) { push @list, $i } push @list, "_add"; $dhcp_num = 0; foreach $val (@list) { $host = $parms{"dhcp${val}_host"}; $ip = $parms{"dhcp${val}_ip"}; $mac = $parms{"dhcp${val}_mac"}; if($val eq "_add") { next unless ($host or $ip or $mac) and ($parms{dhcp_add} or $parms{button_save}); } else { next if $parms{"dhcp${val}_del"}; } if($val eq "_add" and $parms{button_save}) { push @dhcp_err, "$val this reservation must be added or cleared out before saving changes"; next; } if(validate_hostname($host)) { push(@dhcp_err, "$val hostname '$host' is already in use") if $hosts{$host}; } else { if($host) { push @dhcp_err, "$val '$host' is not a valid hostname" } else { push @dhcp_err, "$val a hostname is required" } } if(validate_ip($ip)) { if($addrs{$ip}) { push(@dhcp_err, "$val $ip is already in use"); } elsif($ip eq $lanip or not validate_same_subnet($ip, $lanip, $lanmask) or not validate_ip_netmask($ip, $lanmask)) { push @dhcp_err, "$val '$ip' is not a valid LAN address"; } } else { if($ip) { push @dhcp_err, "$val '$ip' is not a valid address" } else { push @dhcp_err, "$val an IP Address must be selected" } } if(validate_mac($mac)) { push(@dhcp_err, "$val MAC $mac is already in use") if $macs{$mac}; } else { if($mac) { push @dhcp_err, "$val '$mac' is not a valid mac address" } else { push @dhcp_err, "$val a MAC Address is required" } } next if $val eq "_add" and @dhcp_err and $dhcp_err[-1] =~ /^$val /; # commit the data for this reservation ++$dhcp_num; #print FILE "$mac $ip $host\n"; $parms{"dhcp${dhcp_num}_host"} = $host; $parms{"dhcp${dhcp_num}_ip"} = $ip; $parms{"dhcp${dhcp_num}_mac"} = $mac; $hosts{$host} = 1; $addrs{$ip} = 1; $macs{$mac} = 1; if($val eq "_add") { $parms{dhcp_add_host} = ""; $parms{dhcp_add_ip} = ""; $parms{dhcp_add_mac} = ""; } } # add existing leases foreach $lease (keys %parms) { #$hosts{$parms{$lease}} = 1 if $lease =~ /^lease\d+_host$/; next unless ($n) = $lease =~ /^lease(\d+)_add$/; next unless $parms{$lease}; # eliminate duplicate mac addresses $found = 0; foreach(keys %parms) { next unless /dhcp\d+_mac/; $found = 1 if $parms{$_} eq $parms{"lease${n}_mac"}; } next if $found; ++$dhcp_num; $host = $parms{"lease${n}_host"}; $ip = $parms{"lease${n}_ip"}; $mac = $parms{"lease${n}_mac"}; $parms{"dhcp${dhcp_num}_host"} = $host; $parms{"dhcp${dhcp_num}_ip"} = $ip; $parms{"dhcp${dhcp_num}_mac"} = $mac; push(@dhcp_err, "$dhcp_num hostname '$host' is already in use") if $hosts{$host}; push(@dhcp_err, "$dhcp_num $ip is already in use") if $addrs{$ip}; push(@dhcp_err, "$dhcp_num MAC $mac is already in use") if $macs{$mac}; last; } $parms{dhcp_num} = $dhcp_num; $dhcphosts{$lanip} = "localnode"; # replace "blank" dhcp hostnames and save the dhcp info into the tmpdir open(FILE, ">$tmpdir/dhcp"); for($i = $nn = 1; $i <= $parms{dhcp_num}; $i++) { if($parms{"dhcp${i}_host"} eq "*") { while(exists $hosts{"noname$nn"}) { $nn++ } $parms{"dhcp${i}_host"} = "noname$nn"; $hosts{"noname$nn"} = 1; } printf FILE "%s %d %s\n", $parms{"dhcp${i}_mac"}, ip2decimal($parms{"dhcp${i}_ip"}) - $lannet_d, $parms{"dhcp${i}_host"}; # save it in a lookup table $dhcphosts{$parms{"dhcp${i}_ip"}} = $parms{"dhcp${i}_host"} unless $dhcphosts{$parms{"dhcp${i}_ip"}}; } close(FILE); # # load and validate the services # for($i = 1, @list = (); $i <= $parms{serv_num}; ++$i) { push @list, $i } push @list, "_add"; $serv_num = 0; $hosts{""} = 1; $hosts{$node} = 1; $usedports{""} = 1; open(FILE, ">$tmpdir/services"); foreach $val (@list) { foreach $var (qw(name proto host port suffix)) { $varname = "serv${val}_$var"; $parms{$varname} = "" unless $parms{$varname}; $parms{$varname} =~ s/^\s+//; $parms{$varname} =~ s/\s+$//; eval sprintf("\$%s = \$parms{%s}", $var, $varname); } $host = $node unless $dmz_mode; # remove services that have had their host or port deleted #next if $val ne "_add" and not ($dmz_mode ? $hosts{$host} : $usedports{$port}); next if $val ne "_add" and not ($dmz_mode ? $hosts{$host} : 1 ); $link = $parms{"serv${val}_link"}; $link = 0 unless $link; if($val eq "_add") { next unless ($name or $proto or $port or $suffix) and ($parms{serv_add} or $parms{button_save}) } else { next if $parms{"serv${val}_del"} or not ($name or $proto or $port or $suffix); } if($val eq "_add" and $parms{button_save}) { push @serv_err, "$val this service must be added or cleared out before saving changes"; next; } if($name eq "") { push @serv_err, "$val a name is required"; } else { push(@serv_err, "$val '$name' is not a valid service name") unless validate_service_name($name); push(@serv_err, "$val the name '$name' is already in use") if $servicenames{$name}; } if($link) { $parms{"serv${val}_proto"} = $proto = "http" unless $proto; #$port = 80 if $proto eq "http" and not $port; push(@serv_err, "$val '$proto' is not a valid service protocol") unless validate_service_protocol($proto); if($port eq "") { push @serv_err, "$val a port number is required" } else { push(@serv_err, "$val '$port' is not a valid port") unless validate_port($port) } push(@serv_err, "$val '$suffix' is not a valid service suffix") unless validate_service_suffix($suffix); } elsif($val eq "_add") { $proto = $port = $suffix = ""; } next if $val eq "_add" and @serv_err and $serv_err[-1] =~ /^_add /; # commit the data for this service ++$serv_num; $servicenames{$name} = 1; print FILE "$name|$link|$proto|$host|$port|$suffix\n"; foreach $var (qw(name link proto host port suffix)) { eval sprintf("\$parms{serv%d_%s} = \$%s", $serv_num, $var, $var); } if($val eq "_add") { foreach(qw(name link proto host port suffix)) { $parms{"serv_add_$_"} = "" } } } close(FILE); $parms{serv_num} = $serv_num; # # save configuration # if($parms{button_save} and not (@port_err or @dhcp_err or @dmz_err or @serv_err)) { system "cp -f $tmpdir/ports $portfile"; system "cp -f $tmpdir/dhcp $dhcpfile"; system "cp -f $tmpdir/services $servfile"; push(@errors, "problem with configuration") if system "/usr/local/bin/node-setup -a -p $config"; unless($debug == 3) { push(@errors, "problem with dnsmasq") if system "/etc/init.d/dnsmasq reload >/dev/null 2>&1"; push(@errors, "problem with port setup") if system "/etc/init.d/firewall reload >/dev/null 2>&1"; if($config eq "mesh") { push(@errors, "problem with olsr setup") if system "/etc/init.d/olsrd restart >/dev/null 2>&1"; } } } # # generate the page # http_header() unless $debug == 2; html_header("$node setup", 1); print "
\n"; alert_banner(); print "
\n" unless $debug == 2; print "\n" if $debug == 2; print "\n"; print "\n"; # # control buttons # print "\n"; push @hidden, ""; # # messages # if($parms{button_save}) { if(@port_err or @dhcp_err or @dmz_err or @serv_err) { print "\n"; } elsif(@errors) { print "\n"; } else { print "\n"; } print "\n"; } # # everything else # if($dmz_mode) { print "\n"; print "\n"; print "\n"; print "\n"; } else { print "\n"; print "\n"; print "\n"; print "\n"; } print "
\n"; navbar("ports"); print "
Help          \n"; print "
 
Configuration NOT saved!
Configuration saved, however:
"; foreach(@errors) { print "$_
" } print "
Configuration saved and is now active.
 
\n"; print "\n"; if($config eq "mesh") { print "\n"; } print "
\n"; &print_reservations(); print "     \n"; &print_services(); print "
 

\n"; &print_forwarding(); print "
\n"; print "\n"; if($config eq "mesh") { print "\n"; } print "
\n"; &print_forwarding(); print "     \n"; &print_services(); print "
 

\n"; &print_reservations(); print "
\n"; push @hidden, ""; push @hidden, ""; push @hidden, ""; foreach(@hidden) { print "$_\n" } print "
\n"; show_debug_info(); print "\n"; exit; # # page subsections # sub print_forwarding { print "\n"; print ""; print ""; print "\n"; for($i = 1, @list = (); $i <= $parms{port_num}; ++$i) { push @list, $i } push @list, "_add"; foreach $val (@list) { foreach $var (qw(intf type out ip in enable adv link proto suffix name)) { eval sprintf("\$%s = \$parms{port%s_%s}", $var, $val, $var); } print "\n" if $val eq "_add" and scalar(@list) > 1; # enable checkbox if(0)#$val ne "_add") { print "" : " rowspan=2>"; print "enable  "; } else { print ""; push @hidden, ""; } # port forwarding settings print ""; print ""; print "\n"; print "\n"; print "\n"; print "\n"; # display any errors while(@port_err and $port_err[0] =~ /^$val /) { $err = shift @port_err; $err =~ s/^\S+ //; print "\n"; } print "\n"; } # dmz server for nat mode unless($dmz_mode) { print "\n"; print ""; print "\n"; foreach(@dmz_err) { print "\n" } } print "
Port Forwarding
 InterfaceTypeOutside
Port
LAN IPLAN
Port
 
  
$err
DMZ Server  
$_
\n"; } sub print_reservations { print "\n"; print "\n"; print "\n"; print "\n"; for($i = 1, @list = (); $i <= $parms{dhcp_num}; ++$i) { push @list, $i } push @list, "_add"; foreach $val (@list) { $host = $parms{"dhcp${val}_host"}; $ip = $parms{"dhcp${val}_ip"}; $mac = lc $parms{"dhcp${val}_mac"}; print "\n" if $val eq "_add" and scalar(@list) > 1; print "\n"; print "\n"; print "\n"; print "\n"; # display any errors while(@dhcp_err and $dhcp_err[0] =~ /^$val /) { $err = shift @dhcp_err; $err =~ s/^\S+ //; print "\n"; } print "\n"; } print "\n"; print "\n"; $i = 0; foreach(`cat /tmp/dhcp.leases 2>/dev/null`) { ++$i; ($junk, $mac, $ip, $host) = split /\s+/, $_; print "\n"; print ""; print "\n"; push @hidden, ""; push @hidden, ""; push @hidden, ""; } print "\n" unless $i; print "
DHCP Address Reservations
HostnameIP AddressMAC Address
 
$err
 
Current DHCP Leases
$host$ip$mac "; print "
there are no active leases
\n"; } sub print_services { print "\n"; unless($dmz_mode or $parms{port_num} or $parms{dmz_ip}) { if($dmz_mode) { print "\n" } else { print "\n" } print "\n"; print "
Advertised Services
 
 

", "
none
\n"; return; } print "\n" if $dmz_mode; print "NameLinkURL", $dmz_mode ? "" : "

", "\n"; print "\n" if $dmz_mode; for($i = 1, @list = (); $i <= $parms{serv_num}; ++$i) { push @list, $i } push @list, "_add"; foreach $val (@list) { foreach $var (qw(name link proto host port suffix)) { eval sprintf("\$%s = \$parms{serv%s_%s}", $var, $val, $var); } unless($dmz_mode) { $parms{"serv${val}_host"} = $host = $node } #unless($link) { $proto = $port = $suffix = "" } print "\n" if $val eq "_add" and scalar(@list) > 1; print ""; print ""; print "
"; print ""; if($dmz_mode) { print "://\n"; } else { print "://$host"; } print ": / "; print " \n"; # display any errors while(@serv_err and $serv_err[0] =~ /^$val /) { $err = shift @serv_err; $err =~ s/^\S+ //; print "$err\n"; } unless($link or $val eq "_add") { push @hidden, ""; push @hidden, ""; push @hidden, ""; push @hidden, ""; } print "\n"; } print "\n"; }