#!/usr/bin/perl -w -I/www/cgi-bin =for comment Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks Copyright (C) 2015 Conrad Lara See Contributors file for additional contributors Copyright (c) 2013 David Rivenburg et al. BroadBand-HamNet This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 3 of the License. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional Terms: Additional use restrictions exist on the AREDN(TM) trademark and logo. See AREDNLicense.txt for more info. Attributions to the AREDN Project must be retained in the source code. If importing this code into a new or existing project attribution to the AREDN project must be added to the source code. You must not misrepresent the origin of the material contained within. Modified versions must be modified to attribute to the original source and be marked in reasonable ways as differentiate it from the original version. =cut use perlfunc; $| = 1; $auto = 0; $do_basic = 1; sub usage { die " usage: node-setup [-a] [-p] -a: automatic mode - don't ask any questions -p: only process port forwarding and dhcp settings\n\n" } ########################## # main program starts here # validate args while(defined $ARGV[0] and $ARGV[0] =~ /^-/) { $arg = shift; if ($arg eq "-a") { $auto = 1 } elsif($arg eq "-p") { $do_basic = 0 } else { usage() } } $config = shift; usage() unless defined $config; die "'$config' is not a valid configuration\n" unless ($config eq "mesh" and -f "/etc/config.mesh/_setup"); chomp ($lanintf=`jsonfilter -e '@.network.lan.ifname' < /etc/board.json | cut -f1`); $node = nvram_get("node"); $tactical = nvram_get("tactical"); $mac2 = mac2ip(get_mac(get_interface("wifi")), 0); $dtdmac = mac2ip(get_mac($lanintf), 0); unless($auto) { print "\ncurrent node name is '$node'\n"; print "type a new name or just to keep the current name\n\n"; do { print "enter node name: "; $node2 = ; die "node-setup aborted\n" if not defined $node2; chomp $node2; } while($node2 =~ /[^\w\-]/ or $node2 =~ /_/); print "\ncurrent tactical name is "; if($tactical) { print "'$tactical'\n" } else { print "not set\n" } print "type a new name, to keep the current name,\n"; print "or @ to remove the tactical name\n\n"; do { print "enter tactical name: "; $tac2 = ; die "node-setup aborted\n" if not defined $tac2; chomp $tac2; } while($tac2 ne "@" and ($tac2 =~ /[^\w\-]/ or $tac2 =~ /_/)); $node = $node2 if $node2; $tactical = $tac2 if $tac2; $tactical = "" if $tac2 eq "@"; } # # load and verify the selected configuration # foreach $line (`cat /etc/config.mesh/_setup`) { next if $line =~ /^\s*#/; next if $line =~ /^\s*$/; $line =~ s//$node/; $line =~ s//$mac2/; $line =~ s//$dtdmac/; $line =~ /^(\w+)\s*=\s*(.*)$/; $cfg{$1} = $2; } chomp ($lanintf=`jsonfilter -e '@.network.lan.ifname' < /etc/board.json`); $cfg{lan_intf} = "$lanintf"; $cfg{wan_intf} = "dummy"; # wan_intf is set by wifi-setup directly to network config file $cfg{dtdlink_intf} = get_bridge_interfaces("dtdlink"); if ( $cfg{wifi_enable} == 1 ) { $cfg{wifi_intf} = `jsonfilter -e '@.network.wifi.ifname' < /etc/board.json | cut -f1`; $cfg{wifi_intf} =~ /wlan(\d+)/; chomp $cfg{wifi_intf}; } else { $wifi_sudo_intf = "$lanintf"; $wifi_sudo_intf =~ s/^([^\. \t]+).*$/$1/; $cfg{wifi_intf} = $wifi_sudo_intf . ".3975"; } die "configuration load failed\n" unless keys %cfg; # delete some config lines if necessary if($cfg{wan_proto} eq "dhcp") { $deleteme{wan_ip} = 1; $deleteme{wan_gw} = 1; $deleteme{wan_mask} = 1; } $deleteme{lan_gw} = 1 if $cfg{dmz_mode} or $cfg{wan_proto} ne "disabled"; # lan_dhcp sense is inverted in the dhcp config file # and it is a checkbox so it may not be defined - this fixes that if($cfg{lan_dhcp}) { $cfg{lan_dhcp} = 0 } else { $cfg{lan_dhcp} = 1 } # verify that we have all the variables we need chdir "/etc/config.mesh" or die; foreach(`grep "^[^#].*<" *`) { ($file, $parm) = /^(\S+):.*<(\w+)>/; if($parm eq uc $parm) # nvram variable { $lcparm = lc $parm; die "parameter '$parm' in file '$file' does not exist\n" unless nvram_get($lcparm) ne ""; } elsif(not $deleteme{$parm}) { die "parameter '$parm' in file '$file' does not exist\n" unless exists $cfg{$parm}; } } # switch to dmz values if needed if($cfg{dmz_mode}) { foreach(qw(lan_ip lan_mask dhcp_start dhcp_end dhcp_limit)) { $cfg{$_} = $cfg{"dmz_$_"}; } } # select ports and dhcp files based on mode $portfile = "/etc/config.mesh/_setup.ports"; $dhcpfile = "/etc/config.mesh/_setup.dhcp"; $portfile .= ($cfg{dmz_mode} ? ".dmz" : ".nat"); $dhcpfile .= ($cfg{dmz_mode} ? ".dmz" : ".nat"); # basic configuration if($do_basic) { # setup the staging area system "rm -rf /tmp/new_config; mkdir /tmp/new_config"; # copy and process the new configuration chdir "/etc/config.mesh" or die; foreach $file (glob "*") { chomp $file; next if $file =~ /^_setup/; next if $file =~ /^firewall.user/; next if $file =~ /^olsrd/; open(IN, $file) or die; open(OUT, "> /tmp/new_config/$file") or die; while(defined ($line = )) { if($line =~ /^include\s+(\S+)/) { ${incs} = $1; foreach $inc (`cat ${incs}`) { print OUT $inc; } next; } $line =~ s//$node/; $line =~ s//$mac2/; $line =~ s//$dtdmac/; $delparm = 0; while(($parm) = $line =~ /^[^\#].*<(\S+)>/) { if($deleteme{$parm}) { $delparm = 1; last; } $line =~ s/<$parm>/$cfg{$parm}/; } print OUT $line unless $delparm; } close(OUT); close(IN); } # make it official system "rm -f /etc/config/*"; system "mv /tmp/new_config/* /etc/config"; unlink "/tmp/new_config"; system "cp -f /etc/config.mesh/firewall.user /etc/"; nvram_set("config", "mesh"); nvram_set("node", $node); nvram_set("tactical", $tactical); } # # generate the system files # open(HOSTS, ">/etc/hosts") or die; print HOSTS "# automatically generated file - do not edit\n"; print HOSTS "# use /etc/hosts.user for custom entries\n"; print HOSTS "127.0.0.1\tlocalhost\n"; print HOSTS "$cfg{lan_ip}\tlocalnode "; print HOSTS "\n$cfg{wifi_ip}\t" if $cfg{wifi_ip}; print HOSTS "$node $tactical\n"; print HOSTS "$cfg{dtdlink_ip}\tdtdlink.$node.local.mesh dtdlink.$node\n" if $cfg{dtdlink_ip}; print HOSTS add_ip_address($cfg{lan_ip}, 1), "\tlocalap\n" unless $cfg{dmz_mode}; open(ETHER, ">/etc/ethers") or die; print ETHER "# automatically generated file - do not edit\n"; print ETHER "# use /etc/ethers.user for custom entries\n"; $netaddr = ip2decimal($cfg{lan_ip}) & ip2decimal($cfg{lan_mask}); foreach(`cat $dhcpfile`) { next if /^\s*#/; next if /^\s*$/; ($mac, $ip, $host) = split /\s+/, $_; $ip = decimal2ip($netaddr + $ip); # filter out addresses that are illegal for the lan subnet next unless validate_same_subnet($ip, $cfg{lan_ip}, $cfg{lan_mask}); next unless validate_ip_netmask($ip, $cfg{lan_mask}); printf ETHER "$mac\t$ip\n"; printf HOSTS "$ip\t$host\n"; } print HOSTS "\n"; close(HOSTS); close(ETHER); system "cat /etc/hosts.user >> /etc/hosts" if -e "/etc/hosts.user"; system "cat /etc/ethers.user >> /etc/ethers" if -e "/etc/ethers.user"; unless($do_basic) { system "cp -f /etc/config.mesh/firewall /etc/config"; system "cp -f /etc/config.mesh/firewall.user /etc/"; } open(FILE, ">>/etc/config/firewall") or die; if($cfg{dmz_mode}) { print FILE "\nconfig forwarding\n"; print FILE " option src wifi\n"; print FILE " option dest lan\n"; print FILE "\n"; print FILE "\nconfig forwarding\n"; print FILE " option src dtdlink\n"; print FILE " option dest lan\n"; system "uci set firewall.\@zone\[2\].masq=0"; } else { print FILE "\n"; print FILE "config 'include'\n"; print FILE " option 'path' '/etc/firewall.natmode'\n"; print FILE " option 'reload' '1'\n"; } if ($cfg{olsrd_gw}) { print FILE "\nconfig forwarding\n"; print FILE " option src wifi\n"; print FILE " option dest wan\n"; print FILE "\n"; print FILE "\nconfig forwarding\n"; print FILE " option src dtdlink\n"; print FILE " option dest wan\n"; } foreach(`cat $portfile`) { next if /^\s*#/; next if /^\s*$/; chomp; # set dmz server if(/dmz_ip = (\S+)/ and not $cfg{dmz_mode}) { print FILE "\nconfig redirect\n\toption src wifi\n\toption proto tcp\n\toption src_dip $cfg{wifi_ip}\n\toption dest_ip $1\n\n"; print FILE "config redirect\n\toption src wifi\n\toption proto udp\n\toption src_dip $cfg{wifi_ip}\n\toption dest_ip $1\n\n"; next; } # set port forwarding rule ($intf, $type, $oport, $host, $iport, $enable) = split /[:]/, $_; next unless $enable; if($cfg{dmz_mode}) { next if $intf eq "wifi"; $intf = "wan" if $intf eq "both"; } $match = "option src_dport $oport\n"; if ($type eq "tcp") { $match .= "option proto tcp\n" } elsif($type eq "udp") { $match .= "option proto udp\n" } # uci the host and than # set the inside port unless the rule uses an outside port range $host = "option dest_ip $host\n"; $host .="\toption dest_port $iport\n" unless $oport =~ /-/; if($intf eq "both") { print FILE "\nconfig redirect\n\toption src wifi\n\t$match\toption src_dip $cfg{wifi_ip}\n\t$host\n"; print FILE "\nconfig redirect\n\toption src dtdlink\n\t$match\toption src_dip $cfg{wifi_ip}\n\t$host\n"; print FILE "config redirect\n\toption src wan\n\t$match\t$host\n"; } elsif($intf eq "wifi") { print FILE "\nconfig redirect\n\toption src dtdlink\n\t$match\toption src_dip $cfg{wifi_ip}\n\t$host\n"; print FILE "config redirect\n\toption src wifi\n\t$match\toption src_dip $cfg{wifi_ip}\n\t$host\n"; } elsif($intf eq "wan") { print FILE "\nconfig redirect\n\toption src dtdlink\n\t$match\toption src_dip $cfg{wifi_ip}\n\t$host\n"; print FILE "config redirect\n\toption src wan\n\t$match\t$host\n"; } else { print STDERR "ERROR: unknown interface '$intf'\n"; close(FILE); exit 1; } } close(FILE); # generate the services file $servfile = "/etc/config.mesh/_setup.services." . ($cfg{dmz_mode} ? "dmz" : "nat"); open(SERV, ">/etc/config/services") or die; foreach(`cat $servfile 2>/dev/null`) { next if /^\s*#/; next if /^\s*$/; chomp; ($name, $link, $proto, $host, $port, $suffix) = split /\|/, $_; $proto = "http" unless $proto; $port = 0 unless $link; $suffix = "" unless $suffix; next unless defined $name and $name ne "" and defined $host and $host ne ""; printf SERV "%s://%s:%s/%s|%s|%s\n", $proto, $host, $port, $suffix, "tcp", $name; } close(SERV); # generate the local config script open(FILE, ">/etc/local/services") or die; print FILE "#!/bin/sh\n"; unless($cfg{wifi_proto} eq "disabled") { $cfg{wifi_txpower} = wifi_maxpower($cfg{wifi_channel}) if not defined $cfg{wifi_txpower} or $cfg{wifi_txpower} > wifi_maxpower($cfg{wifi_channel}); $cfg{wifi_txpower} = 1 if $cfg{wifi_txpower} < 1; if ( $cfg{wifi_enable} == 1 ) { print FILE "/usr/sbin/iw dev $cfg{wifi_intf} set txpower fixed $cfg{wifi_txpower}00\n"; } if(defined $cfg{aprs_lat} and defined $cfg{aprs_lon}) { printf FILE "echo %s,%s > /tmp/latlon.txt\n", $cfg{aprs_lat}, $cfg{aprs_lon}; } print FILE "/usr/local/bin/olsrd-watchdog &\n"; print FILE "/usr/local/bin/olsrd-namechange-loop &\n"; } close(FILE); system "chmod +x /etc/local/services"; # generate olsrd.conf if(-f "/etc/config.mesh/olsrd") { open(IN, "/etc/config.mesh/olsrd") or die; open(OUT, ">/etc/config/olsrd") or die; while(defined ($line = )) { if($line =~ //) { if($cfg{olsrd_bridge}) { $line =~ s//"wifi" "lan"/ } else { $line =~ s//"lan"/ } } elsif(($parm) = $line =~ /^[^\#].*<(\S+)>/) { $line =~ s/<$parm>/$cfg{$parm}/; } print OUT $line; } if($cfg{dmz_mode}) { print OUT "\n"; print OUT "config Hna4\n"; @parts = split /[.]/, $cfg{dmz_lan_ip}; --$parts[3]; # assume network = lan_ip - 1 print OUT "\toption netaddr ", join(".", @parts),"\n"; print OUT "\toption netmask 255.255.255.", ((0xff << $cfg{dmz_mode}) & 0xff), "\n"; print OUT "\n\n"; } if($cfg{olsrd_gw}) { print OUT "config LoadPlugin\n"; print OUT " option library 'olsrd_dyn_gw.so.0.5'\n"; print OUT " option Interval '60'\n"; print OUT " list Ping '8.8.8.8'\n"; # google dns\n"; print OUT " list Ping '8.8.4.4'\n"; # google dns\n"; print OUT "\n\n"; } close(OUT); close(IN); } # indicate whether lan is running in dmz mode system "echo $cfg{dmz_mode} > /etc/config/dmz-mode" if $cfg{dmz_mode}; # Setup node lan dhcp if ( $cfg{lan_dhcp_noroute} ) { $cmd .= "uci add_list dhcp.\@dhcp[0].dhcp_option='121,10.0.0.0/8,$cfg{lan_ip},172.16.0.0/12,$cfg{lan_ip}' >/dev/null 2>&1;"; $cmd .= "uci add_list dhcp.\@dhcp[0].dhcp_option='249,10.0.0.0/8,$cfg{lan_ip},172.16.0.0/12,$cfg{lan_ip}' >/dev/null 2>&1;"; $cmd .= "uci add_list dhcp.\@dhcp[0].dhcp_option=3 >/dev/null 2>&1;"; } else { $cmd .= "uci add_list dhcp.\@dhcp[0].dhcp_option='121,10.0.0.0/8,$cfg{lan_ip},172.16.0.0/12,$cfg{lan_ip},0.0.0.0/0,$cfg{lan_ip}' >/dev/null 2>&1;"; $cmd .= "uci add_list dhcp.\@dhcp[0].dhcp_option='249,10.0.0.0/8,$cfg{lan_ip},172.16.0.0/12,$cfg{lan_ip},0.0.0.0/0,$cfg{lan_ip}' >/dev/null 2>&1;"; } # finish up $cmd .= "uci -q commit;"; system $cmd; # # generate the wireless config file # system('/usr/local/bin/wifi-setup'); unless($auto) { print "configuration complete.\n"; print "you should now reboot the router.\n"; } exit 0;