1
0
mirror of https://github.com/billz/raspap-webgui.git synced 2023-10-10 13:37:24 +02:00

Merge pull request #704 from billz/feature/dhcp-eth0

Feature: dhcp for eth0
This commit is contained in:
Bill Zimmerman 2020-12-26 11:52:23 +01:00 committed by GitHub
commit 15e0b59054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 901 additions and 578 deletions

View File

@ -1,5 +1,5 @@
![](https://i.imgur.com/xeKD93p.png) ![](https://i.imgur.com/xeKD93p.png)
[![Release 2.5.2](https://img.shields.io/badge/Release-2.5.2-green.svg)](https://github.com/billz/raspap-webgui/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Financial Contributors on Open Collective](https://opencollective.com/raspap/all/badge.svg?label=financial+contributors)](https://opencollective.com/raspap) ![https://travis-ci.com/billz/raspap-webgui/](https://img.shields.io/travis/com/billz/raspap-webgui/master) [![Crowdin](https://badges.crowdin.net/raspap/localized.svg)](https://crowdin.com/project/raspap) [![Twitter URL](https://img.shields.io/twitter/url?label=%40RaspAP&logoColor=%23d8224c&url=https%3A%2F%2Ftwitter.com%2Frasp_ap)](https://twitter.com/rasp_ap) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/RaspAP?style=social)](https://www.reddit.com/r/RaspAP/) [![Release 2.6-beta](https://img.shields.io/badge/release-v2.6--beta-orange)](https://github.com/billz/raspap-webgui/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Financial Contributors on Open Collective](https://opencollective.com/raspap/all/badge.svg?label=financial+contributors)](https://opencollective.com/raspap) ![https://travis-ci.com/billz/raspap-webgui/](https://img.shields.io/travis/com/billz/raspap-webgui/master) [![Crowdin](https://badges.crowdin.net/raspap/localized.svg)](https://crowdin.com/project/raspap) [![Twitter URL](https://img.shields.io/twitter/url?label=%40RaspAP&logoColor=%23d8224c&url=https%3A%2F%2Ftwitter.com%2Frasp_ap)](https://twitter.com/rasp_ap) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/RaspAP?style=social)](https://www.reddit.com/r/RaspAP/)
RaspAP lets you quickly get a WiFi access point up and running to share the connectivity of many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our popular [Quick installer](#quick-installer) creates a known-good default configuration that "just works" on all current Raspberry Pis with onboard wireless. A responsive interface gives you control over the relevant services and networking options. Advanced DHCP settings, OpenVPN client support, SSL, security audits, themes and multilingual options are included. RaspAP lets you quickly get a WiFi access point up and running to share the connectivity of many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our popular [Quick installer](#quick-installer) creates a known-good default configuration that "just works" on all current Raspberry Pis with onboard wireless. A responsive interface gives you control over the relevant services and networking options. Advanced DHCP settings, OpenVPN client support, SSL, security audits, themes and multilingual options are included.

View File

@ -1,48 +0,0 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/config.php';
require_once '../../includes/functions.php';
if (isset($_POST['generate'])) {
$cnfNetworking = array_diff(scandir(RASPI_CONFIG_NETWORKING, 1), array('..','.','dhcpcd.conf','defaults'));
$cnfNetworking = array_combine($cnfNetworking, $cnfNetworking);
$strConfFile = file_get_contents(RASPI_CONFIG_NETWORKING.'/defaults')."\n";
foreach ($cnfNetworking as $index => $file) {
if ($index != "defaults") {
$cnfFile = parse_ini_file(RASPI_CONFIG_NETWORKING.'/'.$file, false, INI_SCANNER_RAW);
if ($cnfFile['static'] === 'true') {
$strConfFile .= "#Static IP configured for ".$cnfFile['interface']."\n";
$strConfFile .= "interface ".$cnfFile['interface']."\n";
if (isset($cnfFile['metric'])) {
$strConfFile .= "metric ".$cnfFile['metric']."\n";
}
$strConfFile .= "static ip_address=".$cnfFile['ip_address']."\n";
$strConfFile .= "static routers=".$cnfFile['routers']."\n";
$strConfFile .= "static domain_name_servers=".$cnfFile['domain_name_server']."\n\n";
} elseif ($cnfFile['static'] === 'false' && $cnfFile['failover'] === 'true') {
$strConfFile .= "#Failover static IP configured for ".$cnfFile['interface']."\n";
$strConfFile .= "profile static_".$cnfFile['interface']."\n";
$strConfFile .= "static ip_address=".$cnfFile['ip_address']."\n";
$strConfFile .= "static routers=".$cnfFile['routers']."\n";
$strConfFile .= "static domain_name_servers=".$cnfFile['domain_name_server']."\n\n";
$strConfFile .= "interface ".$cnfFile['interface']."\n";
if (isset($cnfFile['metric'])) {
$strConfFile .= "metric ".$cnfFile['metric']."\n";
}
$strConfFile .= "fallback static_".$cnfFile['interface']."\n\n";
} else {
$strConfFile .= "#DHCP configured for ".pathinfo($file, PATHINFO_FILENAME)."\n\n";
}
}
}
if (file_put_contents(RASPI_CONFIG_NETWORKING.'/dhcpcd.conf', $strConfFile)) {
exec('sudo /bin/cp '.RASPI_CONFIG_NETWORKING.'/dhcpcd.conf '.RASPI_DHCPCD_CONFIG);
$output = ['return'=>0,'output'=>'Settings successfully applied'];
} else {
$output = ['return'=>2,'output'=>'Unable to write to apply settings'];
}
echo json_encode($output);
}

View File

@ -1,23 +0,0 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/config.php';
require_once '../../includes/functions.php';
if (isset($_POST['interface'])) {
$int = preg_replace('/[^a-z0-9]/', '', $_POST['interface']);
if (!file_exists(RASPI_CONFIG_NETWORKING.'/'.$int.'.ini')) {
touch(RASPI_CONFIG_NETWORKING.'/'.$int.'.ini');
}
$intConfig = parse_ini_file(RASPI_CONFIG_NETWORKING.'/'.$int.'.ini', false, INI_SCANNER_RAW);
$jsonData = ['return'=>1,'output'=>['intConfig'=>$intConfig]];
echo json_encode($jsonData);
// Todo - get dhcp lease information from `dhcpcd -U eth0` ? maybe ?
} else {
$jsonData = ['return'=>2,'output'=>['Error getting data']];
echo json_encode($jsonData);
}

View File

@ -0,0 +1,56 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/config.php';
$interface = $_GET['iface'];
if (isset($interface)) {
// fetch dnsmasq.conf settings for interface
exec('cat '. RASPI_DNSMASQ_PREFIX.$interface.'.conf', $return);
$conf = ParseConfig($return);
$dhcpdata['DHCPEnabled'] = empty($conf) ? false : true;
$arrRange = explode(",", $conf['dhcp-range']);
$dhcpdata['RangeStart'] = $arrRange[0];
$dhcpdata['RangeEnd'] = $arrRange[1];
$dhcpdata['RangeMask'] = $arrRange[2];
$dhcpdata['leaseTime'] = $arrRange[3];
$dhcpHost = $conf["dhcp-host"];
$dhcpHost = empty($dhcpHost) ? [] : $dhcpHost;
$dhcpdata['dhcpHost'] = is_array($dhcpHost) ? $dhcpHost : [ $dhcpHost ];
$upstreamServers = is_array($conf['server']) ? $conf['server'] : [ $conf['server'] ];
$dhcpdata['upstreamServersEnabled'] = empty($conf['server']) ? false: true;
$dhcpdata['upstreamServers'] = array_filter($upstreamServers);
preg_match('/([0-9]*)([a-z])/i', $dhcpdata['leaseTime'], $arrRangeLeaseTime);
$dhcpdata['leaseTime'] = $arrRangeLeaseTime[1];
$dhcpdata['leaseTimeInterval'] = $arrRangeLeaseTime[2];
if (isset($conf['dhcp-option'])) {
$arrDns = explode(",", $conf['dhcp-option']);
if ($arrDns[0] == '6') {
if (count($arrDns) > 1) {
$dhcpdata['DNS1'] = $arrDns[1];
}
if (count($arrDns) > 2) {
$dhcpdata['DNS2'] = $arrDns[2];
}
}
}
// fetch dhcpcd.conf settings for interface
$conf = file_get_contents(RASPI_DHCPCD_CONFIG);
preg_match('/^#\sRaspAP\s'.$interface.'\s.*?(?=\s*+$)/ms', $conf, $matched);
preg_match('/metric\s(\d*)/', $matched[0], $metric);
preg_match('/static\sip_address=(.*)/', $matched[0], $static_ip);
preg_match('/static\srouters=(.*)/', $matched[0], $static_routers);
preg_match('/static\sdomain_name_server=(.*)/', $matched[0], $static_dns);
preg_match('/fallback\sstatic_'.$interface.'/', $matched[0], $fallback);
$dhcpdata['Metric'] = $metric[1];
$dhcpdata['StaticIP'] = strpos($static_ip[1],'/') ? substr($static_ip[1], 0, strpos($static_ip[1],'/')) : $static_ip[1];
$dhcpdata['SubnetMask'] = cidr2mask($static_ip[1]);
$dhcpdata['StaticRouters'] = $static_routers[1];
$dhcpdata['StaticDNS'] = $static_dns[1];
$dhcpdata['FallbackEnabled'] = empty($fallback) ? false: true;
echo json_encode($dhcpdata);
}

View File

@ -1,35 +0,0 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/config.php';
require_once '../../includes/functions.php';
if (isset($_POST['interface'])) {
$int = $_POST['interface'];
$cfg = [];
$file = $int.".ini";
$ip = $_POST[$int.'-ipaddress'];
$netmask = mask2cidr($_POST[$int.'-netmask']);
$dns1 = $_POST[$int.'-dnssvr'];
$dns2 = $_POST[$int.'-dnssvralt'];
$cfg['interface'] = $int;
$cfg['routers'] = $_POST[$int.'-gateway'];
$cfg['ip_address'] = $ip."/".$netmask;
$cfg['domain_name_server'] = $dns1." ".$dns2;
$cfg['static'] = $_POST[$int.'-static'];
$cfg['failover'] = $_POST[$int.'-failover'];
$cfg['metric'] = $_POST[$int.'-metric'];
if (write_php_ini($cfg, RASPI_CONFIG_NETWORKING.'/'.$file)) {
$jsonData = ['return'=>0,'output'=>['Successfully Updated Network Configuration']];
} else {
$jsonData = ['return'=>1,'output'=>['Error saving network configuration to file']];
}
} else {
$jsonData = ['return'=>2,'output'=>'Unable to detect interface'];
}
echo json_encode($jsonData);

View File

@ -52,19 +52,23 @@ body {
font-weight: 500; font-weight: 500;
} }
.card .card-header { .card .card-header, .modal-header {
border-color: <?php echo $color; ?>; border-color: <?php echo $color; ?>;
color: #fff; color: #fff;
background-color: <?php echo $color; ?>; background-color: <?php echo $color; ?>;
} }
.modal-header {
border-radius: 0px;
}
.btn-primary { .btn-primary {
color: <?php echo $color; ?>; color: <?php echo $color; ?>;
border-color: <?php echo $color; ?>; border-color: <?php echo $color; ?>;
background-color: #fff; background-color: #fff;
} }
.card-footer { .card-footer, .modal-footer {
background-color: #f2f1f0; background-color: #f2f1f0;
} }

View File

@ -34,12 +34,12 @@ h5.card-title {
color: #212529; color: #212529;
} }
.card { .card, .modal-dialog {
border-radius: 1px; border-radius: 1px;
border-color: #ff6600; border-color: #ff6600;
} }
.card>.card-header { .card>.card-header, .modal-header {
border-color: #ff6600; border-color: #ff6600;
background-color: #ff6600; background-color: #ff6600;
color: #000; color: #000;
@ -53,11 +53,20 @@ h5.card-title {
font-size: 1.0rem; font-size: 1.0rem;
} }
.card-header [class^="fa"] { .card-header [class^="fa"], .modal-header [class^="fa"] {
color: #fff; color: #fff;
font-size: 1.0rem; font-size: 1.0rem;
} }
.modal-title {
color: #000;
font-size: 1.0rem;
}
.modal-content {
border-radius: 0px;
}
.sidebar-brand-text { .sidebar-brand-text {
text-transform: none; text-transform: none;
color: #212529; color: #212529;

View File

@ -119,7 +119,7 @@ a:focus, a:hover {
color: #d2d2d2; color: #d2d2d2;
} }
.card>.card-header { .card>.card-header, .modal-content, .modal-header {
border-color: #404040; border-color: #404040;
background-color: #202020; background-color: #202020;
color: #afafaf; color: #afafaf;
@ -129,6 +129,10 @@ a:focus, a:hover {
font-weight: 400; font-weight: 400;
} }
.modal-body {
background-color: #141414;
}
.card>.card-header .fa { .card>.card-header .fa {
color: #202020; color: #202020;
} }
@ -197,11 +201,15 @@ hr {
width: 6.5rem; width: 6.5rem;
} }
.card-footer { .card-footer, .modal-footer {
background-color: #202020; background-color: #202020;
border-top: 0px; border-top: 0px;
} }
.modal-footer {
border-radius: 0.3rem;
}
.card>.card-header::before, .navbar-default::before { .card>.card-header::before, .navbar-default::before {
content: " "; content: " ";
display: block; display: block;

View File

@ -49,74 +49,6 @@ function setupTabs() {
}); });
} }
function loadCurrentSettings(strInterface) {
$.post('ajax/networking/get_int_config.php',{interface:strInterface},function(data){
jsonData = JSON.parse(data);
$.each(jsonData['output'],function(i,v) {
var int = v['interface'];
$.each(v,function(i2,v2) {
switch(i2) {
case "static":
if(v2 == 'true') {
$('#'+int+'-static').click();
$('#'+int+'-nofailover').click();
} else {
$('#'+int+'-dhcp').click();
}
break;
case "failover":
if(v2 === 'true') {
$('#'+int+'-failover').click();
} else {
$('#'+int+'-nofailover').click();
}
break;
case "ip_address":
var arrIPNetmask = v2.split('/');
$('#'+int+'-ipaddress').val(arrIPNetmask[0]);
$('#'+int+'-netmask').val(createNetmaskAddr(arrIPNetmask[1]));
break;
case "routers":
$('#'+int+'-gateway').val(v2);
break;
case "domain_name_server":
svrsDNS = v2.split(" ");
$('#'+int+'-dnssvr').val(svrsDNS[0]);
$('#'+int+'-dnssvralt').val(svrsDNS[1]);
break;
}
});
});
});
}
function saveNetworkSettings(int) {
var frmInt = $('#frm-'+int).find(':input');
var arrFormData = {};
$.each(frmInt,function(i3,v3){
if($(v3).attr('type') == 'radio') {
arrFormData[$(v3).attr('id')] = $(v3).prop('checked');
} else {
arrFormData[$(v3).attr('id')] = $(v3).val();
}
});
arrFormData['interface'] = int;
$.post('ajax/networking/save_int_config.php',arrFormData,function(data){
var jsonData = JSON.parse(data);
$('#msgNetworking').html(msgShow(jsonData['return'],jsonData['output']));
});
}
function applyNetworkSettings() {
var int = $(this).data('int');
arrFormData = {};
arrFormData['generate'] = '';
$.post('ajax/networking/gen_int_config.php',arrFormData,function(data){
var jsonData = JSON.parse(data);
$('#msgNetworking').html(msgShow(jsonData['return'],jsonData['output']));
});
}
$(document).on("click", ".js-add-dhcp-static-lease", function(e) { $(document).on("click", ".js-add-dhcp-static-lease", function(e) {
e.preventDefault(); e.preventDefault();
var container = $(".js-new-dhcp-static-lease"); var container = $(".js-new-dhcp-static-lease");
@ -222,6 +154,8 @@ function contentLoaded() {
setupBtns(); setupBtns();
case "hostapd_conf": case "hostapd_conf":
loadChannel(); loadChannel();
case "dhcpd_conf":
loadInterfaceDHCPSelect();
break; break;
} }
} }
@ -236,9 +170,61 @@ function loadWifiStations(refresh) {
.load('ajax/networking/wifi_stations.php'+qs, complete); .load('ajax/networking/wifi_stations.php'+qs, complete);
}; };
} }
$(".js-reload-wifi-stations").on("click", loadWifiStations(true)); $(".js-reload-wifi-stations").on("click", loadWifiStations(true));
/*
Populates the DHCP server form fields
Option toggles are set dynamically depending on the loaded configuration
*/
function loadInterfaceDHCPSelect() {
var iface = $('#cbxdhcpiface').val();
$.get('ajax/networking/get_netcfg.php?iface='+iface,function(data){
jsonData = JSON.parse(data);
$('#dhcp-iface')[0].checked = jsonData.DHCPEnabled;
$('#txtipaddress').val(jsonData.StaticIP);
$('#txtsubnetmask').val(jsonData.SubnetMask);
$('#txtgateway').val(jsonData.StaticRouters);
$('#chkfallback')[0].checked = jsonData.FallbackEnabled;
$('#txtrangestart').val(jsonData.RangeStart);
$('#txtrangeend').val(jsonData.RangeEnd);
$('#txtrangeleasetime').val(jsonData.leaseTime);
$('#txtdns1').val(jsonData.DNS1);
$('#txtdns2').val(jsonData.DNS2);
$('#cbxrangeleasetimeunits').val(jsonData.leaseTimeInterval);
$('#no-resolv')[0].checked = jsonData.upstreamServersEnabled;
$('#cbxdhcpupstreamserver').val(jsonData.upstreamServers[0]);
$('#txtmetric').val(jsonData.Metric);
if (jsonData.StaticIP !== null && jsonData.StaticIP !== '' && !jsonData.FallbackEnabled) {
$('#chkstatic').closest('.btn').button('toggle');
$('#chkstatic').closest('.btn').button('toggle').blur();
$('#chkstatic').blur();
$('#chkfallback').prop('disabled', true);
} else {
$('#chkdhcp').closest('.btn').button('toggle');
$('#chkdhcp').closest('.btn').button('toggle').blur();
$('#chkdhcp').blur();
$('#chkfallback').prop('disabled', false);
}
if (jsonData.FallbackEnabled || $('#chkdhcp').is(':checked')) {
$('#dhcp-iface').prop('disabled', true);
}
});
}
function setDHCPToggles(state) {
if ($('#chkfallback').is(':checked') && state) {
$('#chkfallback').prop('checked', state);
}
if ($('#dhcp-iface').is(':checked') && !state) {
$('#dhcp-iface').prop('checked', state);
}
$('#chkfallback').prop('disabled', state);
$('#dhcp-iface').prop('disabled', !state);
//$('#dhcp-iface').prop('checked', state);
}
function loadChannel() { function loadChannel() {
$.get('ajax/networking/get_channel.php',function(data){ $.get('ajax/networking/get_channel.php',function(data){
jsonData = JSON.parse(data); jsonData = JSON.parse(data);
@ -246,6 +232,18 @@ function loadChannel() {
}); });
} }
$('#hostapdModal').on('shown.bs.modal', function (e) {
var seconds = 9;
var countDown = setInterval(function(){
if(seconds <= 0){
clearInterval(countDown);
}
var pct = Math.floor(100-(seconds*100/9));
document.getElementsByClassName('progress-bar').item(0).setAttribute('style','width:'+Number(pct)+'%');
seconds --;
}, 1000);
});
/* /*
Sets the wirelss channel select options based on hw_mode and country_code. Sets the wirelss channel select options based on hw_mode and country_code.

4
config/090_raspap.conf Normal file
View File

@ -0,0 +1,4 @@
# RaspAP default config
log-facility=/tmp/dnsmasq.log
conf-dir=/etc/dnsmasq.d

6
config/090_wlan0.conf Normal file
View File

@ -0,0 +1,6 @@
# RaspAP wlan0 configuration for wired (ethernet) AP mode
interface=wlan0
domain-needed
dhcp-range=10.3.141.50,10.3.141.255,255.255.255.0,12h
dhcp-option=6,9.9.9.9,1.1.1.1

View File

@ -2,17 +2,17 @@
define('RASPI_BRAND_TEXT', 'RaspAP'); define('RASPI_BRAND_TEXT', 'RaspAP');
define('RASPI_CONFIG', '/etc/raspap'); define('RASPI_CONFIG', '/etc/raspap');
define('RASPI_CONFIG_NETWORKING', RASPI_CONFIG.'/networking'); define('RASPI_CONFIG_NETWORK', RASPI_CONFIG.'/networking/defaults.json');
define('RASPI_ADMIN_DETAILS', RASPI_CONFIG.'/raspap.auth'); define('RASPI_ADMIN_DETAILS', RASPI_CONFIG.'/raspap.auth');
define('RASPI_WIFI_AP_INTERFACE', 'wlan0'); define('RASPI_WIFI_AP_INTERFACE', 'wlan0');
define('RASPI_CACHE_PATH', sys_get_temp_dir() . '/raspap'); define('RASPI_CACHE_PATH', sys_get_temp_dir() . '/raspap');
// Constants for configuration file paths. // Constants for configuration file paths.
// These are typical for default RPi installs. Modify if needed. // These are typical for default RPi installs. Modify if needed.
define('RASPI_DNSMASQ_CONFIG', '/etc/dnsmasq.d/090_raspap.conf');
define('RASPI_DNSMASQ_LEASES', '/var/lib/misc/dnsmasq.leases'); define('RASPI_DNSMASQ_LEASES', '/var/lib/misc/dnsmasq.leases');
define('RASPI_DNSMASQ_PREFIX', '/etc/dnsmasq.d/090_');
define('RASPI_ADBLOCK_LISTPATH', '/etc/raspap/adblock/'); define('RASPI_ADBLOCK_LISTPATH', '/etc/raspap/adblock/');
define('RASPI_ADBLOCK_CONFIG', '/etc/dnsmasq.d/090_adblock.conf'); define('RASPI_ADBLOCK_CONFIG', RASPI_DNSMASQ_PREFIX.'adblock.conf');
define('RASPI_HOSTAPD_CONFIG', '/etc/hostapd/hostapd.conf'); define('RASPI_HOSTAPD_CONFIG', '/etc/hostapd/hostapd.conf');
define('RASPI_DHCPCD_CONFIG', '/etc/dhcpcd.conf'); define('RASPI_DHCPCD_CONFIG', '/etc/dhcpcd.conf');
define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf'); define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf');

View File

@ -1,12 +0,0 @@
# Location of hostapd configuration file
DAEMON_CONF="/etc/hostapd/hostapd.conf"
# Additional daemon options to be appended to hostapd command:-
# -d show more debug messages (-dd for even more)
# -K include key data in debug messages
# -t include timestamps in some debug messages
#
# Note that -B (daemon mode) and -P (pidfile) options are automatically
# configured by the init.d script and must not be added to DAEMON_OPTS.
#
#DAEMON_OPTS=""

38
config/defaults.json Normal file
View File

@ -0,0 +1,38 @@
{
"dhcp": {
"wlan0": {
"static ip_address": [ "10.3.141.1/24" ],
"static routers": [ "10.3.141.1" ],
"static domain_name_server": [ "1.1.1.1 8.8.8.8" ],
"subnetmask": [ "255.255.255.0" ]
},
"uap0": {
"static ip_address": [ "192.168.50.1/24" ],
"static routers": [ "192.168.50.1" ],
"static domain_name_server": [ "1.1.1.1 8.8.8.8" ],
"subnetmask": [ "255.255.255.0" ]
},
"options": {
"# RaspAP default configuration": null,
"hostname": null,
"clientid": null,
"persistent": null,
"option rapid_commit": null,
"option domain_name_servers, domain_name, domain_search, host_name": null,
"option classless_static_routes": null,
"option ntp_servers": null,
"require dhcp_server_identifier": null,
"slaac private": null,
"nohook lookup-hostname": null
}
},
"dnsmasq": {
"wlan0": {
"dhcp-range": [ "10.3.141.50,10.3.141.255,255.255.255.0,12h" ]
},
"uap0": {
"dhcp-range": [ "192.168.50.50,192.168.50.150,12h" ]
}
}
}

View File

@ -1,4 +1,4 @@
# Defaults from Raspberry Pi configuration # RaspAP default configuration
hostname hostname
clientid clientid
persistent persistent
@ -10,18 +10,9 @@ require dhcp_server_identifier
slaac private slaac private
nohook lookup-hostname nohook lookup-hostname
#denyinterfaces eth0 wlan0 #BRIDGED
# RaspAP br0 configuration
interface br0
# RaspAP wlan0 configuration # RaspAP wlan0 configuration
interface wlan0 interface wlan0
static ip_address=10.3.141.1/24 static ip_address=10.3.141.1/24
static routers=10.3.141.1 static routers=10.3.141.1
static domain_name_server=9.9.9.9 1.1.1.1 static domain_name_server=9.9.9.9 1.1.1.1
# RaspAP uap0 configuration
interface uap0
static ip_address=192.168.50.1/24
nohook wpa_supplicant

View File

@ -1,13 +0,0 @@
# RaspAP wlan0 configuration for wired (ethernet) AP mode
interface=wlan0
dhcp-range=10.3.141.50,10.3.141.255,255.255.255.0,12h
dhcp-option=6,1.1.1.1,8.8.8.8
# RaspAP uap0 configuration for wireless client AP mode
#interface=lo,uap0 # Use interfaces lo and uap0
#bind-interfaces # Bind to the interfaces
#server=8.8.8.8 # Forward DNS requests to Google DNS
#domain-needed # Don't forward short names
#bogus-priv # Never forward addresses in the non-routed address spaces
#dhcp-range=192.168.50.50,192.168.50.150,12h

View File

@ -6,18 +6,18 @@ if (!defined('RASPI_CONFIG')) {
$defaults = [ $defaults = [
'RASPI_BRAND_TEXT' => 'RaspAP', 'RASPI_BRAND_TEXT' => 'RaspAP',
'RASPI_VERSION' => '2.5.2', 'RASPI_VERSION' => '2.6-beta',
'RASPI_CONFIG_NETWORKING' => RASPI_CONFIG.'/networking', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json',
'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth',
'RASPI_WIFI_AP_INTERFACE' => 'wlan0', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0',
'RASPI_CACHE_PATH' => sys_get_temp_dir() . '/raspap', 'RASPI_CACHE_PATH' => sys_get_temp_dir() . '/raspap',
// Constants for configuration file paths. // Constants for configuration file paths.
// These are typical for default RPi installs. Modify if needed. // These are typical for default RPi installs. Modify if needed.
'RASPI_DNSMASQ_CONFIG' => '/etc/dnsmasq.d/090_raspap.conf',
'RASPI_DNSMASQ_LEASES' => '/var/lib/misc/dnsmasq.leases', 'RASPI_DNSMASQ_LEASES' => '/var/lib/misc/dnsmasq.leases',
'RASPI_DNSMASQ_PREFIX' => '/etc/dnsmasq.d/090_',
'RASPI_ADBLOCK_LISTPATH' => '/etc/raspap/adblock/', 'RASPI_ADBLOCK_LISTPATH' => '/etc/raspap/adblock/',
'RASPI_ADBLOCK_CONFIG' => '/etc/dnsmasq.d/090_adblock.conf', 'RASPI_ADBLOCK_CONFIG' => RASPI_DNSMASQ_PREFIX.'adblock.conf',
'RASPI_HOSTAPD_CONFIG' => '/etc/hostapd/hostapd.conf', 'RASPI_HOSTAPD_CONFIG' => '/etc/hostapd/hostapd.conf',
'RASPI_DHCPCD_CONFIG' => '/etc/dhcpcd.conf', 'RASPI_DHCPCD_CONFIG' => '/etc/dhcpcd.conf',
'RASPI_WPA_SUPPLICANT_CONFIG' => '/etc/wpa_supplicant/wpa_supplicant.conf', 'RASPI_WPA_SUPPLICANT_CONFIG' => '/etc/wpa_supplicant/wpa_supplicant.conf',

View File

@ -8,94 +8,12 @@ require_once 'config.php';
*/ */
function DisplayDHCPConfig() function DisplayDHCPConfig()
{ {
$status = new StatusMessages(); $status = new StatusMessages();
if (!RASPI_MONITOR_ENABLED) { if (!RASPI_MONITOR_ENABLED) {
if (isset($_POST['savedhcpdsettings'])) { if (isset($_POST['savedhcpdsettings'])) {
$errors = ''; saveDHCPConfig($status);
define('IFNAMSIZ', 16);
if (!preg_match('/^[a-zA-Z0-9]+$/', $_POST['interface'])
|| strlen($_POST['interface']) >= IFNAMSIZ
) {
$errors .= _('Invalid interface name.').'<br />'.PHP_EOL;
}
if (!preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/', $_POST['RangeStart'])
&& !empty($_POST['RangeStart'])
) { // allow ''/null ?
$errors .= _('Invalid DHCP range start.').'<br />'.PHP_EOL;
}
if (!preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/', $_POST['RangeEnd'])
&& !empty($_POST['RangeEnd'])
) { // allow ''/null ?
$errors .= _('Invalid DHCP range end.').'<br />'.PHP_EOL;
}
if (!ctype_digit($_POST['RangeLeaseTime']) && $_POST['RangeLeaseTimeUnits'] !== 'infinite') {
$errors .= _('Invalid DHCP lease time, not a number.').'<br />'.PHP_EOL;
}
if (!in_array($_POST['RangeLeaseTimeUnits'], array('m', 'h', 'd', 'infinite'))) {
$errors .= _('Unknown DHCP lease time unit.').'<br />'.PHP_EOL;
}
$return = 1;
if (empty($errors)) {
$config = 'interface='.$_POST['interface'].PHP_EOL.
'dhcp-range='.$_POST['RangeStart'].','.$_POST['RangeEnd'].
',255.255.255.0,';
if ($_POST['RangeLeaseTimeUnits'] !== 'infinite') {
$config .= $_POST['RangeLeaseTime'];
}
$config .= $_POST['RangeLeaseTimeUnits'].PHP_EOL;
for ($i=0; $i < count($_POST["static_leases"]["mac"]); $i++) {
$mac = trim($_POST["static_leases"]["mac"][$i]);
$ip = trim($_POST["static_leases"]["ip"][$i]);
if ($mac != "" && $ip != "") {
$config .= "dhcp-host=$mac,$ip".PHP_EOL;
} }
} }
if ($_POST['no-resolv'] == "1") {
$config .= "no-resolv".PHP_EOL;
}
foreach ($_POST['server'] as $server) {
$config .= "server=$server".PHP_EOL;
}
if ($_POST['log-dhcp'] == "1") {
$config .= "log-dhcp".PHP_EOL;
}
if ($_POST['log-queries'] == "1") {
$config .= "log-queries".PHP_EOL;
}
if ($_POST['DNS1']) {
$config .= "dhcp-option=6," . $_POST['DNS1'];
if ($_POST['DNS2']) {
$config .= ','.$_POST['DNS2'];
}
$config .= PHP_EOL;
}
$config .= "log-facility=/tmp/dnsmasq.log".PHP_EOL;
$config .= "conf-dir=/etc/dnsmasq.d".PHP_EOL;
file_put_contents("/tmp/dnsmasqdata", $config);
system('sudo cp /tmp/dnsmasqdata '.RASPI_DNSMASQ_CONFIG, $return);
} else {
$status->addMessage($errors, 'danger');
}
if ($return == 0) {
$status->addMessage('Dnsmasq configuration updated successfully', 'success');
} else {
$status->addMessage('Dnsmasq configuration failed to be updated.', 'danger');
}
}
}
exec('pidof dnsmasq | wc -l', $dnsmasq); exec('pidof dnsmasq | wc -l', $dnsmasq);
$dnsmasq_state = ($dnsmasq[0] > 0); $dnsmasq_state = ($dnsmasq[0] > 0);
@ -126,75 +44,20 @@ function DisplayDHCPConfig()
} }
} }
} }
getWifiInterface();
$serviceStatus = $dnsmasq_state ? "up" : "down"; $serviceStatus = $dnsmasq_state ? "up" : "down";
exec('cat '. RASPI_DNSMASQ_PREFIX.'raspap.conf', $return);
exec('cat '. RASPI_DNSMASQ_CONFIG, $return);
$conf = ParseConfig($return); $conf = ParseConfig($return);
$arrRange = explode(",", $conf['dhcp-range']);
$RangeStart = $arrRange[0];
$RangeEnd = $arrRange[1];
$RangeMask = $arrRange[2];
$leaseTime = $arrRange[3];
$dhcpHost = $conf["dhcp-host"];
$dhcpHost = empty($dhcpHost) ? [] : $dhcpHost;
$dhcpHost = is_array($dhcpHost) ? $dhcpHost : [ $dhcpHost ];
$upstreamServers = is_array($conf['server']) ? $conf['server'] : [ $conf['server'] ];
$upstreamServers = array_filter($upstreamServers);
$DNS1 = '';
$DNS2 = '';
if (isset($conf['dhcp-option'])) {
$arrDns = explode(",", $conf['dhcp-option']);
if ($arrDns[0] == '6') {
if (count($arrDns) > 1) {
$DNS1 = $arrDns[1];
}
if (count($arrDns) > 2) {
$DNS2 = $arrDns[2];
}
}
}
$hselected = '';
$mselected = '';
$dselected = '';
$infiniteselected = '';
preg_match('/([0-9]*)([a-z])/i', $leaseTime, $arrRangeLeaseTime);
if ($leaseTime === 'infinite') {
$infiniteselected = ' selected="selected"';
} else {
switch ($arrRangeLeaseTime[2]) {
case 'h':
$hselected = ' selected="selected"';
break;
case 'm':
$mselected = ' selected="selected"';
break;
case 'd':
$dselected = ' selected="selected"';
break;
}
}
exec("ip -o link show | awk -F': ' '{print $2}'", $interfaces); exec("ip -o link show | awk -F': ' '{print $2}'", $interfaces);
exec('cat ' . RASPI_DNSMASQ_LEASES, $leases); exec('cat ' . RASPI_DNSMASQ_LEASES, $leases);
$ap_iface = $_SESSION['ap_interface'];
echo renderTemplate( echo renderTemplate(
"dhcp", compact( "dhcp", compact(
"status", "status",
"serviceStatus", "serviceStatus",
"RangeStart",
"RangeEnd",
"DNS1",
"DNS2",
"upstreamServers",
"arrRangeLeaseTime",
"mselected",
"hselected",
"dselected",
"infiniteselected",
"dnsmasq_state", "dnsmasq_state",
"ap_iface",
"conf", "conf",
"dhcpHost", "dhcpHost",
"interfaces", "interfaces",
@ -202,3 +65,196 @@ function DisplayDHCPConfig()
) )
); );
} }
/**
* Saves a DHCP configuration
*
* @return object $status
*/
function saveDHCPConfig($status)
{
$iface = $_POST['interface'];
$return = 1;
// handle disable dhcp option
if (!isset($_POST['dhcp-iface']) && file_exists(RASPI_DNSMASQ_PREFIX.$iface.'.conf')) {
// remove dhcp + dnsmasq configs for selected interface
$return = removeDHCPConfig($iface,$status);
$return = removeDnsmasqConfig($iface,$status);
} else {
$errors = validateDHCPInput();
if (empty($errors)) {
$return = updateDHCPConfig($iface,$status);
} else {
$status->addMessage($errors, 'danger');
}
if ($return == 1) {
$status->addMessage('Dnsmasq configuration failed to be updated.', 'danger');
return false;
}
if (($_POST['dhcp-iface'] == "1")) {
$return = updateDnsmasqConfig($iface,$status);
}
if ($return == 0) {
$status->addMessage('Dnsmasq configuration updated successfully.', 'success');
} else {
$status->addMessage('Dnsmasq configuration failed to be updated.', 'danger');
return false;
}
return true;
}
}
/**
* Validates DHCP user input from the $_POST object
*
* @return string $errors
*/
function validateDHCPInput()
{
define('IFNAMSIZ', 16);
$iface = $_POST['interface'];
if (!preg_match('/^[a-zA-Z0-9]+$/', $iface)
|| strlen($iface) >= IFNAMSIZ
) {
$errors .= _('Invalid interface name.').'<br />'.PHP_EOL;
}
if (!filter_var($_POST['StaticIP'], FILTER_VALIDATE_IP) && !empty($_POST['StaticIP'])) {
$errors .= _('Invalid static IP address.').'<br />'.PHP_EOL;
}
if (!filter_var($_POST['SubnetMask'], FILTER_VALIDATE_IP) && !empty($_POST['SubnetMask'])) {
$errors .= _('Invalid subnet mask.').'<br />'.PHP_EOL;
}
if (!filter_var($_POST['DefaultGateway'], FILTER_VALIDATE_IP) && !empty($_POST['DefaultGateway'])) {
$errors .= _('Invalid default gateway.').'<br />'.PHP_EOL;
var_dump($_POST['DefaultGateway']);
die();
}
if (($_POST['dhcp-iface'] == "1")) {
if (!filter_var($_POST['RangeStart'], FILTER_VALIDATE_IP) && !empty($_POST['RangeStart'])) {
$errors .= _('Invalid DHCP range start.').'<br />'.PHP_EOL;
}
if (!filter_var($_POST['RangeEnd'], FILTER_VALIDATE_IP) && !empty($_POST['RangeEnd'])) {
$errors .= _('Invalid DHCP range end.').'<br />'.PHP_EOL;
}
if (!ctype_digit($_POST['RangeLeaseTime']) && $_POST['RangeLeaseTimeUnits'] !== 'infinite') {
$errors .= _('Invalid DHCP lease time, not a number.').'<br />'.PHP_EOL;
}
if (!in_array($_POST['RangeLeaseTimeUnits'], array('m', 'h', 'd', 'infinite'))) {
$errors .= _('Unknown DHCP lease time unit.').'<br />'.PHP_EOL;
}
if ($_POST['Metric'] !== '' && !ctype_digit($_POST['Metric'])) {
$errors .= _('Invalid metric value, not a number.').'<br />'.PHP_EOL;
}
}
return $errors;
}
/**
* Updates a dnsmasq configuration
*
* @param string $iface
* @param object $status
* @return boolean $result
*/
function updateDnsmasqConfig($iface,$status)
{
$config = '# RaspAP '.$iface.' configuration'.PHP_EOL;
$config .= 'interface='.$iface.PHP_EOL.
'dhcp-range='.$_POST['RangeStart'].','.$_POST['RangeEnd'].
',255.255.255.0,';
if ($_POST['RangeLeaseTimeUnits'] !== 'infinite') {
$config .= $_POST['RangeLeaseTime'];
}
$config .= $_POST['RangeLeaseTimeUnits'].PHP_EOL;
for ($i=0; $i < count($_POST["static_leases"]["mac"]); $i++) {
$mac = trim($_POST["static_leases"]["mac"][$i]);
$ip = trim($_POST["static_leases"]["ip"][$i]);
if ($mac != "" && $ip != "") {
$config .= "dhcp-host=$mac,$ip".PHP_EOL;
}
}
if ($_POST['no-resolv'] == "1") {
$config .= "no-resolv".PHP_EOL;
}
foreach ($_POST['server'] as $server) {
$config .= "server=$server".PHP_EOL;
}
if ($_POST['DNS1']) {
$config .= "dhcp-option=6," . $_POST['DNS1'];
if ($_POST['DNS2']) {
$config .= ','.$_POST['DNS2'];
}
$config .= PHP_EOL;
}
file_put_contents("/tmp/dnsmasqdata", $config);
$msg = file_exists(RASPI_DNSMASQ_PREFIX.$iface.'.conf') ? 'updated' : 'added';
system('sudo cp /tmp/dnsmasqdata '.RASPI_DNSMASQ_PREFIX.$iface.'.conf', $result);
if ($result == 0) {
$status->addMessage('Dnsmasq configuration for '.$iface.' '.$msg.'.', 'success');
}
// write default 090_raspap.conf
$config = '# RaspAP default config'.PHP_EOL;
$config .='log-facility=/tmp/dnsmasq.log'.PHP_EOL;
$config .='conf-dir=/etc/dnsmasq.d'.PHP_EOL;
// handle log option
if ($_POST['log-dhcp'] == "1") {
$config .= "log-dhcp".PHP_EOL;
}
if ($_POST['log-queries'] == "1") {
$config .= "log-queries".PHP_EOL;
}
$config .= PHP_EOL;
file_put_contents("/tmp/dnsmasqdata", $config);
system('sudo cp /tmp/dnsmasqdata '.RASPI_DNSMASQ_PREFIX.'raspap.conf', $result);
return $result;
}
/**
* Updates a dhcp configuration
*
* @param string $iface
* @param object $status
* @return boolean $result
*/
function updateDHCPConfig($iface,$status)
{
$cfg[] = '# RaspAP '.$iface.' configuration';
$cfg[] = 'interface '.$iface;
if (isset($_POST['StaticIP'])) {
$mask = ($_POST['SubnetMask'] !== '' && $_POST['SubnetMask'] !== '0.0.0.0') ? '/'.mask2cidr($_POST['SubnetMask']) : null;
$cfg[] = 'static ip_address='.$_POST['StaticIP'].$mask;
}
if (isset($_POST['DefaultGateway'])) {
$cfg[] = 'static routers='.$_POST['DefaultGateway'];
}
if ($_POST['DNS1'] !== '' || $_POST['DNS2'] !== '') {
$cfg[] = 'static domain_name_server='.$_POST['DNS1'].' '.$_POST['DNS2'];
}
if ($_POST['Metric'] !== '') {
$cfg[] = 'metric '.$_POST['Metric'];
}
if ($_POST['Fallback'] == 1) {
$cfg[] = 'profile static_'.$iface;
$cfg[] = 'fallback static_'.$iface;
}
$dhcp_cfg = file_get_contents(RASPI_DHCPCD_CONFIG);
if (!preg_match('/^interface\s'.$iface.'$/m', $dhcp_cfg)) {
$cfg[] = PHP_EOL;
$cfg = join(PHP_EOL, $cfg);
$dhcp_cfg .= $cfg;
$status->addMessage('DHCP configuration for '.$iface.' added.', 'success');
} else {
$cfg = join(PHP_EOL, $cfg);
$dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$iface.'\s.*?(?=\s*^\s*$)/ms', $cfg, $dhcp_cfg, 1);
$status->addMessage('DHCP configuration for '.$iface.' updated.', 'success');
}
file_put_contents("/tmp/dhcpddata", $dhcp_cfg);
system('sudo cp /tmp/dhcpddata '.RASPI_DHCPCD_CONFIG, $result);
return $result;
}

View File

@ -1,6 +1,12 @@
<?php <?php
/* Functions for Networking */ /* Functions for Networking */
/**
* Converts a netmask to CIDR notation string
*
* @param string $mask
* @return string
*/
function mask2cidr($mask) function mask2cidr($mask)
{ {
$long = ip2long($mask); $long = ip2long($mask);
@ -8,6 +14,126 @@ function mask2cidr($mask)
return 32-log(($long ^ $base)+1, 2); return 32-log(($long ^ $base)+1, 2);
} }
/**
* Converts a CIDR notation string to a netmask
*
* @param string $cidr
* @return string
*/
function cidr2mask($cidr)
{
$ta = substr ($cidr, strpos ($cidr, '/') + 1) * 1;
$netmask = str_split (str_pad (str_pad ('', $ta, '1'), 32, '0'), 8);
foreach ($netmask as &$element)
$element = bindec ($element);
return join ('.', $netmask);
}
/**
* Removes a dhcp configuration block for the specified interface
*
* @param string $iface
* @param object $status
* @return boolean $result
*/
function removeDHCPConfig($iface,$status)
{
$dhcp_cfg = file_get_contents(RASPI_DHCPCD_CONFIG);
$dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$iface.'\s.*?(?=\s*^\s*$)([\s]+)/ms', '', $dhcp_cfg, 1);
file_put_contents("/tmp/dhcpddata", $dhcp_cfg);
system('sudo cp /tmp/dhcpddata '.RASPI_DHCPCD_CONFIG, $result);
if ($result == 0) {
$status->addMessage('DHCP configuration for '.$iface.' removed.', 'success');
} else {
$status->addMessage('Failed to remove DHCP configuration for '.$iface.'.', 'danger');
return $result;
}
}
/**
* Removes a dhcp configuration block for the specified interface
*
* @param string $dhcp_cfg
* @param string $iface
* @return string $dhcp_cfg
*/
function removeDHCPIface($dhcp_cfg,$iface)
{
$dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$iface.'\s.*?(?=\s*^\s*$)([\s]+)/ms', '', $dhcp_cfg, 1);
return $dhcp_cfg;
}
/**
* Removes a dnsmasq configuration block for the specified interface
*
* @param string $iface
* @param object $status
* @return boolean $result
*/
function removeDnsmasqConfig($iface,$status)
{
system('sudo rm '.RASPI_DNSMASQ_PREFIX.$iface.'.conf', $result);
if ($result == 0) {
$status->addMessage('Dnsmasq configuration for '.$iface.' removed.', 'success');
} else {
$status->addMessage('Failed to remove dnsmasq configuration for '.$iface.'.', 'danger');
}
return $result;
}
/**
* Scans dnsmasq configuration dir for the specified interface
* Non-matching configs are removed, optional adblock.conf is protected
*
* @param string $dir_conf
* @param string $interface
* @param object $status
*/
function scanConfigDir($dir_conf,$interface,$status)
{
$syscnf = preg_grep('~\.(conf)$~', scandir($dir_conf));
foreach ($syscnf as $cnf) {
if ($cnf !== '090_adblock.conf' && !preg_match('/.*_'.$interface.'.conf/', $cnf)) {
system('sudo rm /etc/dnsmasq.d/'.$cnf, $result);
}
}
return $status;
}
/**
* Returns a default (fallback) value for the selected service, interface & setting
* from /etc/raspap/networking/defaults.json
*
* @param string $svc
* @param string $iface
* @return string $value
*/
function getDefaultNetValue($svc,$iface,$key)
{
$json = json_decode(file_get_contents(RASPI_CONFIG_NETWORK), true);
if ($json === null) {
return false;
} else {
return $json[$svc][$iface][$key][0];
}
}
/**
* Returns default options for the specified service
*
* @param string $svc
* @return object $json
*/
function getDefaultNetOpts($svc)
{
$json = json_decode(file_get_contents(RASPI_CONFIG_NETWORK), true);
if ($json === null) {
return false;
} else {
return $json[$svc]['options'];
}
}
/* Functions to write ini files */ /* Functions to write ini files */
function write_php_ini($array, $file) function write_php_ini($array, $file)
@ -213,6 +339,19 @@ function ParseConfig($arrConfig)
return $config; return $config;
} }
/**
* Fetches DHCP configuration for an interface, returned as JSON data
*
* @param string $interface
* @return json $jsonData
*/
function getNetConfig($interface)
{
$URI = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_NAME'] .'/ajax/networking/get_netcfg.php?iface='.$interface;
$jsonData = file_get_contents($URI);
return $jsonData;
}
/** /**
* *
* @param string $freq * @param string $freq

View File

@ -8,7 +8,7 @@ require_once 'includes/config.php';
getWifiInterface(); getWifiInterface();
/** /**
* * Initialize hostapd values, display interface
* *
*/ */
function DisplayHostAPDConfig() function DisplayHostAPDConfig()
@ -34,7 +34,6 @@ function DisplayHostAPDConfig()
SaveHostAPDConfig($arrSecurity, $arrEncType, $arr80211Standard, $interfaces, $status); SaveHostAPDConfig($arrSecurity, $arrEncType, $arr80211Standard, $interfaces, $status);
} }
} }
$arrHostapdConf = parse_ini_file('/etc/raspap/hostapd.ini'); $arrHostapdConf = parse_ini_file('/etc/raspap/hostapd.ini');
if (!RASPI_MONITOR_ENABLED) { if (!RASPI_MONITOR_ENABLED) {
@ -71,7 +70,6 @@ function DisplayHostAPDConfig()
if (strlen($hostapdconfigline) === 0) { if (strlen($hostapdconfigline) === 0) {
continue; continue;
} }
if ($hostapdconfigline[0] != "#") { if ($hostapdconfigline[0] != "#") {
$arrLine = explode("=", $hostapdconfigline); $arrLine = explode("=", $hostapdconfigline);
$arrConfig[$arrLine[0]]=$arrLine[1]; $arrConfig[$arrLine[0]]=$arrLine[1];
@ -107,6 +105,16 @@ function DisplayHostAPDConfig()
); );
} }
/**
* Validate user input, save configs for hostapd, dnsmasq & dhcp
*
* @param array $wpa_array
* @param array $enc_types
* @param array $modes
* @param string $interface
* @param object $status
* @return boolean
*/
function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status) function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
{ {
// It should not be possible to send bad data for these fields so clearly // It should not be possible to send bad data for these fields so clearly
@ -125,12 +133,10 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
$status->addMessage('Attempting to set channel to invalid number.', 'danger'); $status->addMessage('Attempting to set channel to invalid number.', 'danger');
$good_input = false; $good_input = false;
} }
if (intval($_POST['channel']) < 1 || intval($_POST['channel']) > RASPI_5GHZ_MAX_CHANNEL) { if (intval($_POST['channel']) < 1 || intval($_POST['channel']) > RASPI_5GHZ_MAX_CHANNEL) {
$status->addMessage('Attempting to set channel outside of permitted range', 'danger'); $status->addMessage('Attempting to set channel outside of permitted range', 'danger');
$good_input = false; $good_input = false;
} }
$arrHostapdConf = parse_ini_file('/etc/raspap/hostapd.ini'); $arrHostapdConf = parse_ini_file('/etc/raspap/hostapd.ini');
// Check for Bridged AP mode checkbox // Check for Bridged AP mode checkbox
@ -144,7 +150,6 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
$bridgedEnable = 1; $bridgedEnable = 1;
} }
} }
// Check for WiFi client AP mode checkbox // Check for WiFi client AP mode checkbox
$wifiAPEnable = 0; $wifiAPEnable = 0;
if ($bridgedEnable == 0) { // enable client mode actions when not bridged if ($bridgedEnable == 0) { // enable client mode actions when not bridged
@ -158,7 +163,6 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
} }
} }
} }
// Check for Logfile output checkbox // Check for Logfile output checkbox
$logEnable = 0; $logEnable = 0;
if ($arrHostapdConf['LogEnable'] == 0) { if ($arrHostapdConf['LogEnable'] == 0) {
@ -176,17 +180,21 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
exec('sudo '.RASPI_CONFIG.'/hostapd/disablelog.sh'); exec('sudo '.RASPI_CONFIG.'/hostapd/disablelog.sh');
} }
} }
// set AP interface default, override for ap-sta & bridged options
$ap_iface = $_POST['interface'];
if ($wifiAPEnable) { $ap_iface = 'uap0'; }
if ($bridgedEnable) { $ap_iface = 'br0'; }
// persist user options to /etc/raspap
$cfg = []; $cfg = [];
$cfg['WifiInterface'] = $_POST['interface']; $cfg['WifiInterface'] = $_POST['interface'];
$cfg['LogEnable'] = $logEnable; $cfg['LogEnable'] = $logEnable;
// Save previous Client mode status when Bridged // Save previous Client mode status when Bridged
$cfg['WifiAPEnable'] = ($bridgedEnable == 1 ? $cfg['WifiAPEnable'] = ($bridgedEnable == 1 ? $arrHostapdConf['WifiAPEnable'] : $wifiAPEnable);
$arrHostapdConf['WifiAPEnable'] : $wifiAPEnable);
$cfg['BridgedEnable'] = $bridgedEnable; $cfg['BridgedEnable'] = $bridgedEnable;
$cfg['WifiManaged'] = $_POST['interface']; $cfg['WifiManaged'] = $ap_iface;
write_php_ini($cfg, RASPI_CONFIG.'/hostapd.ini'); write_php_ini($cfg, RASPI_CONFIG.'/hostapd.ini');
$_SESSION['ap_interface'] = $_POST['interface']; $_SESSION['ap_interface'] = $ap_iface;
// Verify input // Verify input
if (empty($_POST['ssid']) || strlen($_POST['ssid']) > 32) { if (empty($_POST['ssid']) || strlen($_POST['ssid']) > 32) {
@ -240,6 +248,116 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
$_POST['max_num_sta'] = $_POST['max_num_sta'] < 1 ? null : $_POST['max_num_sta']; $_POST['max_num_sta'] = $_POST['max_num_sta'] < 1 ? null : $_POST['max_num_sta'];
if ($good_input) { if ($good_input) {
$return = updateHostapdConfig($ignore_broadcast_ssid,$wifiAPEnble,$bridgedEnable);
// Fetch dhcp-range, lease time from system config
$syscfg = parse_ini_file(RASPI_DNSMASQ_PREFIX.$ap_iface.'.conf', false, INI_SCANNER_RAW);
if ($wifiAPEnable == 1) {
// Enable uap0 configuration for ap-sta mode
// Set dhcp-range from system config, fallback to default if undefined
$dhcp_range = ($syscfg['dhcp-range'] == '') ? getDefaultNetValue('dnsmasq','uap0','dhcp-range') : $syscfg['dhcp-range'];
$config = [ '# RaspAP uap0 configuration' ];
$config[] = 'interface=lo,uap0 # Enable uap0 interface for wireless client AP mode';
$config[] = 'bind-dynamic # Hybrid between --bind-interfaces and default';
$config[] = 'server=8.8.8.8 # Forward DNS requests to Google DNS';
$config[] = 'domain-needed # Don\'t forward short names';
$config[] = 'bogus-priv # Never forward addresses in the non-routed address spaces';
$config[] = 'dhcp-range='.$dhcp_range;
if (!empty($syscfg['dhcp-option'])) {
$config[] = 'dhcp-option='.$syscfg['dhcp-option'];
}
$config[] = PHP_EOL;
scanConfigDir('/etc/dnsmasq.d/','uap0',$status);
$config = join(PHP_EOL, $config);
file_put_contents("/tmp/dnsmasqdata", $config);
system('sudo cp /tmp/dnsmasqdata '.RASPI_DNSMASQ_PREFIX.$ap_iface.'.conf', $return);
} elseif ($bridgedEnable !==1) {
$dhcp_range = ($syscfg['dhcp-range'] =='') ? getDefaultNetValue('dnsmasq','wlan0','dhcp-range') : $syscfg['dhcp-range'];
$config = [ '# RaspAP '.$_POST['interface'].' configuration' ];
$config[] = 'interface='.$_POST['interface'];
$config[] = 'domain-needed';
$config[] = 'dhcp-range='.$dhcp_range;
if (!empty($syscfg['dhcp-option'])) {
$config[] = 'dhcp-option='.$syscfg['dhcp-option'];
}
$config[] = PHP_EOL;
$config = join(PHP_EOL, $config);
file_put_contents("/tmp/dnsmasqdata", $config);
system('sudo cp /tmp/dnsmasqdata '.RASPI_DNSMASQ_PREFIX.$ap_iface.'.conf', $return);
}
// Set dhcp values from system config, fallback to default if undefined
$jsonData = json_decode(getNetConfig($ap_iface), true);
$ip_address = ($jsonData['StaticIP'] == '') ? getDefaultNetValue('dhcp',$ap_iface,'static ip_address') : $jsonData['StaticIP'];
$domain_name_server = ($jsonData['StaticDNS'] =='') ? getDefaultNetValue('dhcp',$ap_iface,'static domain_name_server') : $jsonData['StaticDNS'];
$routers = ($jsonData['StaticRouters'] == '') ? getDefaultNetValue('dhcp',$ap_iface,'static routers') : $jsonData['StaticRouters'];
$netmask = ($jsonData['SubnetMask'] == '' || $jsonData['SubnetMask'] == '0.0.0.0') ? getDefaultNetValue('dhcp',$ap_iface,'subnetmask') : $jsonData['SubnetMask'];
$ip_address.= (!preg_match('/.*\/\d+/', $ip_address)) ? '/'.mask2cidr($netmask) : null;
if ($bridgedEnable == 1) {
$config = array_keys(getDefaultNetOpts('dhcp'));
$config[] = PHP_EOL.'# RaspAP br0 configuration';
$config[] = 'interface br0';
$config[] = 'denyinterfaces eth0 wlan0';
$config[] = PHP_EOL;
} elseif ($wifiAPEnable == 1) {
$config = array_keys(getDefaultNetOpts('dhcp'));
$config[] = PHP_EOL.'# RaspAP uap0 configuration';
$config[] = 'interface uap0';
$config[] = 'static ip_address='.$ip_address;
$config[] = 'nohook wpa_supplicant';
$config[] = PHP_EOL;
} else {
// Default wlan0 config
$def_ip = array();
$config = [ '# RaspAP wlan0 configuration' ];
$config[] = 'interface wlan0';
$config[] = 'static ip_address='.$ip_address;
$config[] = 'static routers='.$routers;
$config[] = 'static domain_name_server='.$domain_name_server;
if (! is_null($jsonData['Metric'])) { $config[] = 'metric '.$jsonData['Metric']; }
}
$dhcp_cfg = file_get_contents(RASPI_DHCPCD_CONFIG);
if ($bridgedEnable == 1 || $wifiAPEnable == 1) {
$dhcp_cfg = join(PHP_EOL, $config);
$status->addMessage('DHCP configuration for '.$ap_iface.' enabled.', 'success');
} elseif (!preg_match('/^interface\s'.$ap_iface.'$/m', $dhcp_cfg)) {
$config[] = PHP_EOL;
$config= join(PHP_EOL, $config);
$dhcp_cfg = removeDHCPIface($dhcp_cfg,'br0');
$dhcp_cfg = removeDHCPIface($dhcp_cfg,'uap0');
$dhcp_cfg .= $config;
$status->addMessage('DHCP configuration for '.$ap_iface.' added.', 'success');
} else {
$config = join(PHP_EOL, $config);
$dhcp_cfg = removeDHCPIface($dhcp_cfg,'br0');
$dhcp_cfg = removeDHCPIface($dhcp_cfg,'uap0');
$dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$ap_iface.'\s.*?(?=\s*^\s*$)/ms', $config, $dhcp_cfg, 1);
$status->addMessage('DHCP configuration for '.$ap_iface.' updated.', 'success');
}
file_put_contents("/tmp/dhcpddata", $dhcp_cfg);
system('sudo cp /tmp/dhcpddata '.RASPI_DHCPCD_CONFIG, $return);
if ($return == 0) {
$status->addMessage('Wifi Hotspot settings saved', 'success');
} else {
$status->addMessage('Unable to save wifi hotspot settings', 'danger');
}
} else {
$status->addMessage('Unable to save wifi hotspot settings', 'danger');
return false;
}
return true;
}
/**
* Updates a hostapd configuration
*
* @return boolean $result
*/
function updateHostapdConfig($ignore_broadcast_ssid,$wifiAPEnble,$bridgedEnable)
{
// Fixed values // Fixed values
$country_code = $_POST['country_code']; $country_code = $_POST['country_code'];
$config = 'driver=nl80211'.PHP_EOL; $config = 'driver=nl80211'.PHP_EOL;
@ -290,7 +408,7 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
$config.='interface='.$_POST['interface'].PHP_EOL; $config.='interface='.$_POST['interface'].PHP_EOL;
$config.= 'bridge=br0'.PHP_EOL; $config.= 'bridge=br0'.PHP_EOL;
} else { } else {
$config.= 'interface='.$_POST['interface'].PHP_EOL; $config.= 'interface='.$_SESSION['ap_interface'].PHP_EOL;
} }
$config.= 'wpa='.$_POST['wpa'].PHP_EOL; $config.= 'wpa='.$_POST['wpa'].PHP_EOL;
$config.= 'wpa_pairwise='.$_POST['wpa_pairwise'].PHP_EOL; $config.= 'wpa_pairwise='.$_POST['wpa_pairwise'].PHP_EOL;
@ -299,105 +417,8 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status)
if (isset($_POST['max_num_sta'])) { if (isset($_POST['max_num_sta'])) {
$config.= 'max_num_sta='.$_POST['max_num_sta'].PHP_EOL; $config.= 'max_num_sta='.$_POST['max_num_sta'].PHP_EOL;
} }
file_put_contents("/tmp/hostapddata", $config); file_put_contents("/tmp/hostapddata", $config);
system("sudo cp /tmp/hostapddata " . RASPI_HOSTAPD_CONFIG, $return); system("sudo cp /tmp/hostapddata " . RASPI_HOSTAPD_CONFIG, $result);
return $result;
// Fetch dhcp-range, lease time from system config
$dhcpConfig = parse_ini_file(RASPI_DNSMASQ_CONFIG, false, INI_SCANNER_RAW);
if ($wifiAPEnable == 1) {
// Enable uap0 configuration in dnsmasq for Wifi client AP mode
// Set dhcp-range from system config. If undefined, fallback to default
$dhcp_range = ($dhcpConfig['dhcp-range'] =='10.3.141.50,10.3.141.255,255.255.255.0,12h' ||
$dhcpConfig['dhcp-range'] =='') ? '192.168.50.50,192.168.50.150,12h' : $dhcpConfig['dhcp-range'];
$config = 'interface=lo,uap0 # Enable uap0 interface for wireless client AP mode'.PHP_EOL;
$config.= 'bind-dynamic # Hybrid between --bind-interfaces and default'.PHP_EOL;
$config.= 'server=8.8.8.8 # Forward DNS requests to Google DNS'.PHP_EOL;
$config.= 'domain-needed # Don\'t forward short names'.PHP_EOL;
$config.= 'bogus-priv # Never forward addresses in the non-routed address spaces'.PHP_EOL;
$config.= 'dhcp-range='.$dhcp_range.PHP_EOL;
if (!empty($dhcpConfig['dhcp-option'])) {
$config.= 'dhcp-option='.$dhcpConfig['dhcp-option'].PHP_EOL;
}
} else {
// Set dhcp-range from system config. If undefined, fallback to default
$dhcp_range = ($dhcpConfig['dhcp-range'] =='192.168.50.50,192.168.50.150,12h' ||
$dhcpConfig['dhcp-range'] =='') ? '10.3.141.50,10.3.141.255,255.255.255.0,12h' : $dhcpConfig['dhcp-range'];
$config = 'domain-needed'.PHP_EOL;
$config.= 'interface='.$_POST['interface'].PHP_EOL;
$config.= 'dhcp-range='.$dhcp_range.PHP_EOL;
if (!empty($dhcpConfig['dhcp-option'])) {
$config.= 'dhcp-option='.$dhcpConfig['dhcp-option'].PHP_EOL;
}
}
file_put_contents("/tmp/dnsmasqdata", $config);
system('sudo cp /tmp/dnsmasqdata '.RASPI_DNSMASQ_CONFIG, $return);
// Set dnsmasq values from ini, fallback to default if undefined
$intConfig = parse_ini_file(RASPI_CONFIG_NETWORKING.'/'.$_POST['interface'].'.ini', false, INI_SCANNER_RAW);
$domain_name_server = ($intConfig['domain_name_server'] =='') ? '1.1.1.1 8.8.8.8' : $intConfig['domain_name_server'];
$routers = ($intConfig['routers'] == '') ? '10.3.141.1' : $intConfig['routers'];
// write options to dhcpcd.conf
$config = [ '# RaspAP '.$_POST['interface'].' configuration' ];
$config[] = 'hostname';
$config[] = 'clientid';
$config[] = 'persistent';
$config[] = 'option rapid_commit';
$config[] = 'option domain_name_servers, domain_name, domain_search, host_name';
$config[] = 'option classless_static_routes';
$config[] = 'option ntp_servers';
$config[] = 'require dhcp_server_identifier';
$config[] = 'slaac private';
$config[] = 'nohook lookup-hostname';
if ($bridgedEnable == 1) {
$config[] = 'denyinterfaces eth0 wlan0';
$config[] = 'interface br0';
} elseif ($wifiAPEnable == 1) {
// Enable uap0 configuration in dhcpcd for Wifi client AP mode
$intConfig = parse_ini_file(RASPI_CONFIG_NETWORKING.'/uap0.ini', false, INI_SCANNER_RAW);
$ip_address = ($intConfig['ip_address'] == '') ? '192.168.50.1/24' : $intConfig['ip_address'];
$config[] = 'interface uap0';
$config[] = 'static ip_address='.$ip_address;
$config[] = 'nohook wpa_supplicant';
} else {
// Default config
$ip_address = "10.3.141.1/24"; // fallback IP
// default IP of the AP xxx.xxx.xxx.1/24 of the selected dhcp range
$def_ip = array();
if (preg_match("/^([0-9]{1,3}\.){3}/",$dhcp_range,$def_ip) ) $ip_address = $def_ip[0]."1/24";
// use static IP assigned to interface only, if consistent with the selected dhcp range
if (preg_match("/^([0-9]{1,3}\.){3}/",$intConfig['ip_address'],$int_ip) && $def_ip[0] === $int_ip[0]) $ip_address = $intConfig['ip_address'];
$config[] = 'interface '.$_POST['interface'];
$config[] = 'static ip_address='.$ip_address;
$config[] = 'static domain_name_server='.$domain_name_server;
$config[] = PHP_EOL;
// write the static IP back to the $_POST['interface'].ini file
$intConfig['interface'] = $_POST['interface'];
$intConfig['ip_address'] = $ip_address;
$intConfig['domain_name_server'] = $domain_name_server;
$intConfig['routers'] = $routers;
$intConfig['static'] = "true";
$intConfig['failover'] = "false";
write_php_ini($intConfig, RASPI_CONFIG_NETWORKING.'/'.$_POST['interface'].".ini");
}
$config = join(PHP_EOL, $config);
file_put_contents("/tmp/dhcpddata", $config);
system('sudo cp /tmp/dhcpddata '.RASPI_DHCPCD_CONFIG, $return);
if ($return == 0) {
$status->addMessage('Wifi Hotspot settings saved', 'success');
} else {
$status->addMessage('Unable to save wifi hotspot settings', 'danger');
}
} else {
$status->addMessage('Unable to save wifi hotspot settings', 'danger');
return false;
}
return true;
} }

View File

@ -14,7 +14,7 @@
* @author Lawrence Yau <sirlagz@gmail.com> * @author Lawrence Yau <sirlagz@gmail.com>
* @author Bill Zimmerman <billzimmerman@gmail.com> * @author Bill Zimmerman <billzimmerman@gmail.com>
* @license GNU General Public License, version 3 (GPL-3.0) * @license GNU General Public License, version 3 (GPL-3.0)
* @version 2.5.2 * @version 2.6-beta
* @link https://github.com/billz/raspap-webgui/ * @link https://github.com/billz/raspap-webgui/
* @link https://raspap.com/ * @link https://raspap.com/
* @see http://sirlagz.net/2013/02/08/raspap-webgui/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/

View File

@ -20,9 +20,11 @@ set -o errtrace
readonly raspap_dir="/etc/raspap" readonly raspap_dir="/etc/raspap"
readonly raspap_user="www-data" readonly raspap_user="www-data"
readonly raspap_sudoers="/etc/sudoers.d/090_raspap" readonly raspap_sudoers="/etc/sudoers.d/090_raspap"
readonly raspap_dnsmasq="/etc/dnsmasq.d/090_raspap.conf" readonly raspap_default="/etc/dnsmasq.d/090_raspap.conf"
readonly raspap_wlan0="/etc/dnsmasq.d/090_wlan0.conf"
readonly raspap_adblock="/etc/dnsmasq.d/090_adblock.conf" readonly raspap_adblock="/etc/dnsmasq.d/090_adblock.conf"
readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf" readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf"
readonly raspap_network="$raspap_dir/networking/"
readonly rulesv4="/etc/iptables/rules.v4" readonly rulesv4="/etc/iptables/rules.v4"
readonly notracking_url="https://raw.githubusercontent.com/notracking/hosts-blocklists/master/" readonly notracking_url="https://raw.githubusercontent.com/notracking/hosts-blocklists/master/"
webroot_dir="/var/www/html" webroot_dir="/var/www/html"
@ -167,11 +169,7 @@ function _create_raspap_directories() {
# Create a directory to store networking configs # Create a directory to store networking configs
echo "Creating $raspap_dir/networking" echo "Creating $raspap_dir/networking"
sudo mkdir -p "$raspap_dir/networking" sudo mkdir -p "$raspap_dir/networking"
# Copy existing dhcpcd.conf to use as base config
echo "Adding /etc/dhcpcd.conf as base configuration"
cat /etc/dhcpcd.conf | sudo tee -a /etc/raspap/networking/defaults > /dev/null
echo "Changing file ownership of $raspap_dir"
sudo chown -R $raspap_user:$raspap_user "$raspap_dir" || _install_status 1 "Unable to change file ownership for '$raspap_dir'"
} }
# Generate hostapd logging and service control scripts # Generate hostapd logging and service control scripts
@ -258,9 +256,9 @@ function _install_adblock() {
echo "addn-hosts=$raspap_dir/adblock/hostnames.txt" | sudo tee -a "$raspap_adblock" > /dev/null || _install_status 1 "Unable to write to $raspap_adblock" echo "addn-hosts=$raspap_dir/adblock/hostnames.txt" | sudo tee -a "$raspap_adblock" > /dev/null || _install_status 1 "Unable to write to $raspap_adblock"
fi fi
# Remove dhcp-option=6 in dnsmasq.d/090_raspap.conf to force local DNS resolution for DHCP clients # Remove dhcp-option=6 in dnsmasq.d/090_wlan0.conf to force local DNS resolution for DHCP clients
echo "Enabling local DNS name resolution for DHCP clients" echo "Enabling local DNS name resolution for DHCP clients"
sudo sed -i '/dhcp-option=6/d' $raspap_dnsmasq || _install_status 1 "Unable to modify $raspap_dnsmasq" sudo sed -i '/dhcp-option=6/d' $raspap_wlan0 || _install_status 1 "Unable to modify $raspap_dnsmasq"
echo "Enabling ad blocking management option" echo "Enabling ad blocking management option"
sudo sed -i "s/\('RASPI_ADBLOCK_ENABLED', \)false/\1true/g" "$webroot_dir/includes/config.php" || _install_status 1 "Unable to modify config.php" sudo sed -i "s/\('RASPI_ADBLOCK_ENABLED', \)false/\1true/g" "$webroot_dir/includes/config.php" || _install_status 1 "Unable to modify config.php"
@ -358,9 +356,14 @@ function _check_for_old_configs() {
sudo ln -sf "$raspap_dir/backups/hostapd.conf.`date +%F-%R`" "$raspap_dir/backups/hostapd.conf" sudo ln -sf "$raspap_dir/backups/hostapd.conf.`date +%F-%R`" "$raspap_dir/backups/hostapd.conf"
fi fi
if [ -f $raspap_dnsmasq ]; then if [ -f $raspap_default ]; then
sudo cp $raspap_dnsmasq "$raspap_dir/backups/dnsmasq.conf.`date +%F-%R`" sudo cp $raspap_default "$raspap_dir/backups/090_raspap.conf.`date +%F-%R`"
sudo ln -sf "$raspap_dir/backups/dnsmasq.conf.`date +%F-%R`" "$raspap_dir/backups/dnsmasq.conf" sudo ln -sf "$raspap_dir/backups/090_raspap.conf.`date +%F-%R`" "$raspap_dir/backups/090_raspap.conf"
fi
if [ -f $raspap_wlan0 ]; then
sudo cp $raspap_wlan0 "$raspap_dir/backups/090_wlan0.conf.`date +%F-%R`"
sudo ln -sf "$raspap_dir/backups/090_wlan0.conf.`date +%F-%R`" "$raspap_dir/backups/090_wlan0.conf"
fi fi
if [ -f /etc/dhcpcd.conf ]; then if [ -f /etc/dhcpcd.conf ]; then
@ -394,13 +397,15 @@ function _move_config_file() {
function _default_configuration() { function _default_configuration() {
if [ "$upgrade" == 0 ]; then if [ "$upgrade" == 0 ]; then
_install_log "Applying default configuration to installed services" _install_log "Applying default configuration to installed services"
if [ -f /etc/default/hostapd ]; then
sudo mv /etc/default/hostapd /tmp/default_hostapd.old || _install_status 1 "Unable to remove old /etc/default/hostapd file"
fi
sudo cp $webroot_dir/config/default_hostapd /etc/default/hostapd || _install_status 1 "Unable to move hostapd defaults file"
sudo cp $webroot_dir/config/hostapd.conf /etc/hostapd/hostapd.conf || _install_status 1 "Unable to move hostapd configuration file" sudo cp $webroot_dir/config/hostapd.conf /etc/hostapd/hostapd.conf || _install_status 1 "Unable to move hostapd configuration file"
sudo cp $webroot_dir/config/dnsmasq.conf $raspap_dnsmasq || _install_status 1 "Unable to move dnsmasq configuration file" sudo cp $webroot_dir/config/090_raspap.conf $raspap_default || _install_status 1 "Unable to move dnsmasq default configuration file"
sudo cp $webroot_dir/config/090_wlan0.conf $raspap_wlan0 || _install_status 1 "Unable to move dnsmasq wlan0 configuration file"
sudo cp $webroot_dir/config/dhcpcd.conf /etc/dhcpcd.conf || _install_status 1 "Unable to move dhcpcd configuration file" sudo cp $webroot_dir/config/dhcpcd.conf /etc/dhcpcd.conf || _install_status 1 "Unable to move dhcpcd configuration file"
sudo cp $webroot_dir/config/defaults.json $raspap_network || _install_status 1 "Unable to move defaults.json settings"
echo "Changing file ownership of $raspap_dir"
sudo chown -R $raspap_user:$raspap_user "$raspap_dir" || _install_status 1 "Unable to change file ownership for '$raspap_dir'"
echo "Checking for existence of /etc/dnsmasq.d" echo "Checking for existence of /etc/dnsmasq.d"
[ -d /etc/dnsmasq.d ] || sudo mkdir /etc/dnsmasq.d [ -d /etc/dnsmasq.d ] || sudo mkdir /etc/dnsmasq.d

View File

@ -20,7 +20,8 @@ www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop openvpn-client@client
www-data ALL=(ALL) NOPASSWD:/bin/systemctl disable openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl disable openvpn-client@client
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/ovpnclient.ovpn /etc/openvpn/client/client.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/ovpnclient.ovpn /etc/openvpn/client/client.conf
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/authdata /etc/openvpn/client/login.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/authdata /etc/openvpn/client/login.conf
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_raspap.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_*.conf
www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/dnsmasq.d/090_*.conf
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dhcpddata /etc/dhcpcd.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dhcpddata /etc/dhcpcd.conf
www-data ALL=(ALL) NOPASSWD:/sbin/shutdown -h now www-data ALL=(ALL) NOPASSWD:/sbin/shutdown -h now
www-data ALL=(ALL) NOPASSWD:/sbin/reboot www-data ALL=(ALL) NOPASSWD:/sbin/reboot

View File

@ -18,8 +18,10 @@ set -o errtrace
readonly raspap_dir="/etc/raspap" readonly raspap_dir="/etc/raspap"
readonly raspap_user="www-data" readonly raspap_user="www-data"
readonly raspap_sudoers="/etc/sudoers.d/090_raspap" readonly raspap_sudoers="/etc/sudoers.d/090_raspap"
readonly raspap_dnsmasq="/etc/dnsmasq.d/090_raspap.conf" readonly raspap_default="/etc/dnsmasq.d/090_raspap.conf"
readonly raspap_wlan0="/etc/dnsmasq.d/090_wlan0.conf"
readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf" readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf"
readonly raspap_adblock="/etc/dnsmasq.d/090_adblock.conf"
readonly raspap_network="/etc/systemd/network/" readonly raspap_network="/etc/systemd/network/"
readonly rulesv4="/etc/iptables/rules.v4" readonly rulesv4="/etc/iptables/rules.v4"
webroot_dir="/var/www/html" webroot_dir="/var/www/html"
@ -139,7 +141,9 @@ function _remove_raspap_directories() {
# Removes raspapd.service # Removes raspapd.service
function _remove_raspap_service() { function _remove_raspap_service() {
_install_log "Removing raspapd.service" _install_log "Removing raspapd.service"
if [ -f /lib/systemd/system/raspapd.service ]; then
sudo rm /lib/systemd/system/raspapd.service || _install_error "Unable to remove raspap.service file" sudo rm /lib/systemd/system/raspapd.service || _install_error "Unable to remove raspap.service file"
fi
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl disable raspapd.service || _install_error "Failed to disable raspap.service" sudo systemctl disable raspapd.service || _install_error "Failed to disable raspap.service"
echo "Done." echo "Done."
@ -172,9 +176,19 @@ function _restore_networking() {
echo "Done." echo "Done."
# Remove dnsmasq and bridge configs # Remove dnsmasq and bridge configs
echo "Removing 090_raspap.conf from dnsmasq" echo "Removing 090_raspap.conf from dnsmasq"
sudo rm "$raspap_dnsmasq" || _install_error "Unable to remove $raspap_dnsmasq" if [ -f $raspap_default ]; then
sudo rm "$raspap_default" || _install_error "Unable to remove $raspap_default"
fi
echo "Removing 090_wlan0.conf from dnsmasq"
if [ -f $raspap_wlan0 ]; then
sudo rm "$raspap_wlan0" || _install_error "Unable to remove $raspap_wlan0"
fi
echo "Removing raspap bridge configurations" echo "Removing raspap bridge configurations"
sudo rm "$raspap_network"/raspap* || _install_error "Unable to remove bridge config" sudo rm "$raspap_network"/raspap* || _install_error "Unable to remove bridge config"
if [ -f $raspap_adblock ]; then
echo "Removing raspap adblock configuration"
sudo rm "$raspap_adblock" || _install_error "Unable to remove adblock config"
fi
} }
# Removes installed packages # Removes installed packages

Binary file not shown.

View File

@ -254,6 +254,12 @@ msgstr "Client list"
msgid "Interface" msgid "Interface"
msgstr "Interface" msgstr "Interface"
msgid "Enable DHCP for this interface"
msgstr "Enable DHCP for this interface"
msgid "Enable this option if you want RaspAP to assign IP addresses on the selected interface."
msgstr "Enable this option if you want RaspAP to assign IP addresses on the selected interface."
msgid "DNS Server" msgid "DNS Server"
msgstr "DNS Server" msgstr "DNS Server"
@ -347,6 +353,9 @@ msgstr "Format"
msgid "Choose a hosted server" msgid "Choose a hosted server"
msgstr "Choose a hosted server" msgstr "Choose a hosted server"
msgid "Enable these options to log DHCP server activity."
msgstr "Enable these options to log DHCP server activity."
msgid "Log DHCP requests" msgid "Log DHCP requests"
msgstr "Log DHCP requests" msgstr "Log DHCP requests"
@ -456,6 +465,15 @@ msgstr "Disable <code>disassoc_low_ack</code>"
msgid "Do not disassociate stations based on excessive transmission failures." msgid "Do not disassociate stations based on excessive transmission failures."
msgstr "Do not disassociate stations based on excessive transmission failures." msgstr "Do not disassociate stations based on excessive transmission failures."
msgid "Executing RaspAP service start"
msgstr "Executing RaspAP service start"
msgid "Close"
msgstr "Close"
msgid "Enable this option to log <code>hostapd</code> activity."
msgstr "Enable this option to log <code>hostapd</code> activity."
#: includes/networking.php #: includes/networking.php
msgid "Summary" msgid "Summary"
msgstr "Summary" msgstr "Summary"

Binary file not shown.

View File

@ -4,7 +4,6 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h5><?php echo _("Upstream DNS servers") ?></h5> <h5><?php echo _("Upstream DNS servers") ?></h5>
<div class="input-group"> <div class="input-group">
<input type="hidden" name="no-resolv" value="0"> <input type="hidden" name="no-resolv" value="0">
<div class="custom-control custom-switch"> <div class="custom-control custom-switch">
@ -42,7 +41,7 @@
<code class="text-muted"><?php echo htmlspecialchars("[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]"); ?></code> <code class="text-muted"><?php echo htmlspecialchars("[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]"); ?></code>
</small> </small>
</p> </p>
<select class="custom-select custom-select-sm js-field-preset" data-field-preset-target="#add-dhcp-upstream-server-field"> <select class="custom-select custom-select-sm js-field-preset" id="cbxdhcpupstreamserver" data-field-preset-target="#add-dhcp-upstream-server-field">
<option value=""><?php echo _("Choose a hosted server") ?></option> <option value=""><?php echo _("Choose a hosted server") ?></option>
<option disabled="disabled"></option> <option disabled="disabled"></option>
<?php echo optionsForSelect(dnsServers()) ?> <?php echo optionsForSelect(dnsServers()) ?>

View File

@ -3,41 +3,99 @@
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code">Interface</label> <label for="code">Interface</label>
<select class="form-control" name="interface"> <?php SelectorOptions('interface', $interfaces, $ap_iface, 'cbxdhcpiface', 'loadInterfaceDHCPSelect', $DHCPDisabled); ?>
<?php foreach ($interfaces as $if) : ?>
<?php $if_quoted = htmlspecialchars($if, ENT_QUOTES) ?>
<?php $selected = $if === $conf['interface'] ? ' selected="selected"' : '' ?>
<option value="<?php echo $if_quoted ?>"<?php echo $selected ?>><?php echo $if_quoted ?></option>
<?php endforeach ?>
</select>
</div> </div>
</div> </div>
<h5 class="mt-1"><?php echo _("Adapter IP Address Settings"); ?></h5>
<div class="row">
<div class="form-group col-md-6">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-light active" checked onclick="setDHCPToggles(false)">
<input type="radio" name="adapter-ip" id="chkdhcp" autocomplete="off"> DHCP
</label>
<label class="btn btn-light" onclick="setDHCPToggles(true)">
<input type="radio" name="adapter-ip" id="chkstatic" autocomplete="off"> Static IP
</label>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<div class="custom-control custom-switch">
<input class="custom-control-input" id="chkfallback" type="checkbox" name="Fallback" value="1" aria-describedby="fallback-description">
<label class="custom-control-label" for="chkfallback"><?php echo _("Enable fallback to static option") ?></label>
</div>
<p class="mb-0" id="fallback-description">
<small><?php echo _("Enable this option to configure a static profile and fall back to it when DHCP lease fails.") ?></small>
</p>
</div>
</div>
<h5 class="mt-1">Static IP options</h5>
<div class="row">
<div class="form-group col-md-6">
<label for="code"><?php echo _("IP Address"); ?></label>
<input type="text" class="form-control" id="txtipaddress" name="StaticIP" />
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label for="code"><?php echo _("Subnet Mask"); ?></label>
<input type="text" class="form-control" id="txtsubnetmask" name="SubnetMask" />
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label for="code"><?php echo _("Default gateway"); ?></label>
<input type="text" class="form-control" id="txtgateway" name="DefaultGateway" />
</div>
</div>
<h5 class="mt-1">DHCP options</h5>
<div class="row">
<div class="form-group col-md-6">
<div class="input-group">
<div class="custom-control custom-switch">
<input class="custom-control-input" id="dhcp-iface" type="checkbox" name="dhcp-iface" value="1" aria-describedby="dhcp-iface-description">
<label class="custom-control-label" for="dhcp-iface"><?php echo _("Enable DHCP for this interface") ?></label>
</div>
<p class="mb-0" id="dhcp-iface-description">
<small><?php echo _("Enable this option if you want RaspAP to assign IP addresses to clients on the selected interface. A static IP address is required for this option.") ?></small>
</p>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("Starting IP Address"); ?></label> <label for="code"><?php echo _("Starting IP Address"); ?></label>
<input type="text" class="form-control"name="RangeStart" value="<?php echo htmlspecialchars($RangeStart, ENT_QUOTES); ?>" /> <input type="text" class="form-control" id="txtrangestart" name="RangeStart" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("Ending IP Address"); ?></label> <label for="code"><?php echo _("Ending IP Address"); ?></label>
<input type="text" class="form-control" name="RangeEnd" value="<?php echo htmlspecialchars($RangeEnd, ENT_QUOTES); ?>" /> <input type="text" class="form-control" id="txtrangeend" name="RangeEnd" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-xs-3 col-sm-3"> <div class="form-group col-xs-3 col-sm-3">
<label for="code"><?php echo _("Lease Time"); ?></label> <label for="code"><?php echo _("Lease Time"); ?></label>
<input type="text" class="form-control" name="RangeLeaseTime" value="<?php echo htmlspecialchars($arrRangeLeaseTime[1], ENT_QUOTES); ?>" /> <input type="text" class="form-control" id="txtrangeleasetime" name="RangeLeaseTime" />
</div> </div>
<div class="col-xs-3 col-sm-3"> <div class="col-xs-3 col-sm-3">
<label for="code"><?php echo _("Interval"); ?></label> <label for="code"><?php echo _("Interval"); ?></label>
<select name="RangeLeaseTimeUnits" class="form-control" > <select id="cbxrangeleasetimeunits" name="RangeLeaseTimeUnits" class="form-control" >
<option value="m"<?php echo $mselected; ?>><?php echo _("Minute(s)"); ?></option> <option value="m"><?php echo _("Minute(s)"); ?></option>
<option value="h"<?php echo $hselected; ?>><?php echo _("Hour(s)"); ?></option> <option value="h"><?php echo _("Hour(s)"); ?></option>
<option value="d"<?php echo $dselected; ?>><?php echo _("Day(s)"); ?></option> <option value="d"><?php echo _("Day(s)"); ?></option>
<option value="infinite"<?php echo $infiniteselected; ?>><?php echo _("Infinite"); ?></option> <option value="infinite"><?php echo _("Infinite"); ?></option>
</select> </select>
</div> </div>
</div> </div>
@ -45,14 +103,21 @@
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("DNS Server"); ?> 1</label> <label for="code"><?php echo _("DNS Server"); ?> 1</label>
<input type="text" class="form-control"name="DNS1" value="<?php echo htmlspecialchars($DNS1, ENT_QUOTES); ?>" /> <input type="text" class="form-control" id="txtdns1" name="DNS1" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("DNS Server"); ?> 2</label> <label for="code"><?php echo _("DNS Server"); ?> 2</label>
<input type="text" class="form-control" name="DNS2" value="<?php echo htmlspecialchars($DNS2, ENT_QUOTES); ?>" /> <input type="text" class="form-control" id="txtdns2" name="DNS2" />
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label for="<metric"><?php echo _("Metric") ?></label>
<input type="text" class="form-control" id="txtmetric" name="Metric">
</div> </div>
</div> </div>

View File

@ -12,9 +12,17 @@
<label class="custom-control-label" for="log-queries"><?php echo _("Log DNS queries") ?></label> <label class="custom-control-label" for="log-queries"><?php echo _("Log DNS queries") ?></label>
</div> </div>
<div class="row">
<div class="form-group col-md-8 mt-2">
<?php <?php
if ($conf['log-dhcp'] == 1 || $conf['log-queries'] == 1) {
exec('sudo chmod o+r /tmp/dnsmasq.log'); exec('sudo chmod o+r /tmp/dnsmasq.log');
$log = file_get_contents('/tmp/dnsmasq.log'); $log = file_get_contents('/tmp/dnsmasq.log');
echo '<textarea class="logoutput my-3">'.htmlspecialchars($log, ENT_QUOTES).'</textarea>'; echo '<textarea class="logoutput">'.htmlspecialchars($log, ENT_QUOTES).'</textarea>';
} else {
echo '<textarea class="logoutput my-3"></textarea>';
}
?> ?>
</div>
</div>
</div><!-- /.tab-pane --> </div><!-- /.tab-pane -->

View File

@ -2,11 +2,30 @@
<?php if (!RASPI_MONITOR_ENABLED) : ?> <?php if (!RASPI_MONITOR_ENABLED) : ?>
<input type="submit" class="btn btn-outline btn-primary" name="SaveHostAPDSettings" value="<?php echo _("Save settings"); ?>" /> <input type="submit" class="btn btn-outline btn-primary" name="SaveHostAPDSettings" value="<?php echo _("Save settings"); ?>" />
<?php if ($hostapdstatus[0] == 0) : ?> <?php if ($hostapdstatus[0] == 0) : ?>
<input type="submit" class="btn btn-success" name="StartHotspot" value="<?php echo _("Start hotspot") ?>"/> <input type="submit" class="btn btn-success" name="StartHotspot" value="<?php echo _("Start hotspot"); $msg=_("Starting hotspot"); ?>" data-toggle="modal" data-target="#hostapdModal"/>
<?php else : ?> <?php else : ?>
<input type="submit" class="btn btn-warning" name="StopHotspot" value="<?php echo _("Stop hotspot") ?>"/> <input type="submit" class="btn btn-warning" name="StopHotspot" value="<?php echo _("Stop hotspot") ?>"/>
<input type ="submit" class="btn btn-warning" name="RestartHotspot" value="<?php echo _("Restart hotspot") ?>"/> <input type ="submit" class="btn btn-warning" name="RestartHotspot" value="<?php echo _("Restart hotspot"); $msg=_("Restarting hotspot"); ?>" data-toggle="modal" data-target="#hostapdModal"/>
<?php endif ?> <?php endif ?>
<!-- Modal -->
<div class="modal fade" id="hostapdModal" tabindex="-1" role="dialog" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title" id="ModalLabel"><i class="fas fa-sync-alt mr-2"></i><?php echo $msg ?></div>
</div>
<div class="modal-body">
<div class="col-md-12 mb-3 mt-1"><?php echo _("Executing RaspAP service start") ?>...</div>
<div class="progress" style="height: 20px;">
<div class="progress-bar bg-info" role="progressbar" id="progressBar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="9"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline btn-primary" data-dismiss="modal"><?php echo _("Close"); ?></button>
</div>
</div>
</div>
</div>
<?php endif ?> <?php endif ?>
<?php $buttons = ob_get_clean(); ob_end_clean() ?> <?php $buttons = ob_get_clean(); ob_end_clean() ?>

View File

@ -19,15 +19,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-6 mb-2">
<div class="custom-control custom-switch">
<?php $checked = $arrHostapdConf['LogEnable'] == 1 ? 'checked="checked"' : '' ?>
<input class="custom-control-input" id="chxlogenable" name="logEnable" type="checkbox" value="1" <?php echo $checked ?> />
<label class="custom-control-label" for="chxlogenable"><?php echo _("Logfile output"); ?></label>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-2"> <div class="col-md-6 mb-2">
<div class="custom-control custom-switch"> <div class="custom-control custom-switch">

View File

@ -1,15 +1,23 @@
<!-- logfile output tab --> <!-- logfile output tab -->
<div class="tab-pane fade" id="logoutput"> <div class="tab-pane fade" id="logoutput">
<h4 class="mt-3"><?php echo _("Logging"); ?></h4> <h4 class="mt-3"><?php echo _("Logging"); ?></h4>
<p><?php echo _("Enable this option to log <code>hostapd</code> activity.") ?></p>
<div class="custom-control custom-switch">
<?php $checked = $arrHostapdConf['LogEnable'] == 1 ? 'checked="checked"' : '' ?>
<input class="custom-control-input" id="chxlogenable" name="logEnable" type="checkbox" value="1" <?php echo $checked ?> />
<label class="custom-control-label" for="chxlogenable"><?php echo _("Logfile output"); ?></label>
</div>
<div class="row"> <div class="row">
<div class="form-group col-md-8"> <div class="form-group col-md-8 mt-2">
<?php <?php
if ($arrHostapdConf['LogEnable'] == 1) { if ($arrHostapdConf['LogEnable'] == 1) {
exec('sudo /bin/chmod o+r /tmp/hostapd.log'); exec('sudo /bin/chmod o+r /tmp/hostapd.log');
$log = file_get_contents('/tmp/hostapd.log'); $log = file_get_contents('/tmp/hostapd.log');
echo '<br /><textarea class="logoutput">'.htmlspecialchars($log, ENT_QUOTES).'</textarea>'; echo '<textarea class="logoutput">'.htmlspecialchars($log, ENT_QUOTES).'</textarea>';
} else { } else {
echo "<br />Logfile output not enabled"; echo '<textarea class="logoutput my-3"></textarea>';
} }
?> ?>
</div> </div>

View File

@ -18,12 +18,6 @@
// defaults to false // defaults to false
$bridgedEnabled = $arrHostapdConf['BridgedEnable']; $bridgedEnabled = $arrHostapdConf['BridgedEnable'];
?> ?>
<?php if (!$bridgedEnabled) : // no interface details when bridged ?>
<?php foreach ($interfaces as $if): ?>
<?php $if_quoted = htmlspecialchars($if, ENT_QUOTES) ?>
<li role="presentation" class="nav-item"><a class="nav-link" href="#<?php echo $if_quoted ?>" aria-controls="<?php echo $if_quoted ?>" role="tab" data-toggle="tab"><?php echo $if_quoted ?></a></li>
<?php endforeach ?>
<?php endif ?>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="summary"> <div role="tabpanel" class="tab-pane active" id="summary">

View File

@ -1,6 +1,8 @@
<?php if (empty($networks)) { ?> <?php if (empty($networks)) { ?>
<div class="col-md-6 ml-6">
<p class="lead text-center"><?php echo _('No Wifi stations found') ?></p> <p class="lead text-center"><?php echo _('No Wifi stations found') ?></p>
<p class="text-center"><?php echo _('Click "Rescan" to search for nearby Wifi stations.') ?></p> <p class="text-center"><?php echo _('Click "Rescan" to search for nearby Wifi stations.') ?></p>
</div>
<?php } elseif (count($networks) == 1) { <?php } elseif (count($networks) == 1) {
$prop_col = "col-sm-12"; $prop_col = "col-sm-12";
$prop_w = "w-50"; $prop_w = "w-50";