Wireless support

This commit is contained in:
bergware
2025-01-29 14:42:53 +01:00
parent 3691a4f50b
commit 5662fece45
20 changed files with 763 additions and 24 deletions

View File

@@ -7,7 +7,7 @@ Code="e924"
if (!isset($display['favorites'])) {
$favorites = true;
} else {
$favorites = $display['favorites'] == "yes" ? true : false;
$favorites = $display['favorites'] == "yes" ? 'true' : 'false';
}
?>
<script>

View File

@@ -0,0 +1,141 @@
Menu="NetworkSettings:1000"
Title="_(Wireless)_ wlan0"
Tag="fa-wifi"
Cond="file_exists('/sys/class/net/wlan0')"
---
<?PHP
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$title = _('Connect to WiFi network');
$cfg = '/boot/config/wireless.cfg';
if (file_exists($cfg)) $wlan0 = parse_ini_file($cfg,true)['wlan0'];
?>
<div class="title nocontrol shift">&nbsp;</div>
<form markdown="1" name="wifi_settings" method="POST" action="/update.php" target="progressFrame" onsubmit="enable_wifi(this.WIFI.value)">
<input type="hidden" name="#file" value="<?=$cfg?>"/>
<input type="hidden" name="#section" value="wlan0"/>
<input type="hidden" name="#command" value="/webGui/scripts/wireless">
<input type="hidden" name="#arg[1]" value=""/>
_(Wi-Fi)_:
: <select name="WIFI">
<?=mk_option(_var($wlan0,'WIFI'), "no", _("Disabled"))?>
<?=mk_option(_var($wlan0,'WIFI'), "yes", _("Enabled"))?>
</select>
&nbsp;
: <input type="submit" value="_(Apply)_" disabled><input type="button" value="_(Done)_" onclick="done()">
</form>
<div id="wifi" class="hide">
<div markdown="1" id="connected" class="shade-<?=$display['theme']?>">
_(Connected)_:
: <i class="fa fa-spin fa-circle-o-notch"></i>
</div>
<div markdown="1" id="my_networks" class="shade-<?=$display['theme']?>">
_(My networks)_:
: <i class="fa fa-spin fa-circle-o-notch"></i>
</div>
<div markdown="1" id="other_networks" class="shade-<?=$display['theme']?>">
_(Other networks)_:
: <i class="fa fa-spin fa-circle-o-notch"></i>
</div>
<div markdown="1" class="shade-<?=$display['theme']?>">
_(Add network)_:
: <span class="wifi"><input name="CUSTOM" type="text" class="narrow" maxlength="20" autocomplete="off" spellcheck="false" value=""></span><i class="fa fa-wifi hand grey-text" onclick="info(encodeURIComponent($('input[name=CUSTOM]').val(),0))" title="<?=$title?>"></i>
</div>
</div>
<script>
function enable_wifi(state) {
$('input[name="#arg[1]"]').val(state);
}
function update_wifi() {
$.post('/webGui/include/Wireless.php',{cmd:'list'},function(text) {
if (text.length > 0) {
var wifi = JSON.parse(text);
$('#connected').html(wifi.active);
$('#my_networks').html(wifi.saved);
$('#other_networks').html(wifi.other);
}
});
timers.wifi = setTimeout(update_wifi,5000);
}
function manage_wifi(ssid,drop) {
if (drop==2) {
$.post('/webGui/include/Wireless.php',{cmd:'forget',ssid:ssid},function(){
clearTimeout(timers.wifi);
swal.close();
setTimeout(update_wifi);
});
return;
}
if (ssid) {
$.post('/webGui/include/Wireless.php',{cmd:'join',ssid:ssid,drop:drop},function(text) {
swal({title:decodeURIComponent(ssid),text:text,animation:'none',html:true,confirmButtonText:"_(Join this network)_",showCancelButton:true,cancelButtonText:"_(Cancel)_"},function(join){
if (join) $('form[name=wifi]').submit();
});
});
} else {
swal({title:"_(Missing Network Name)_",animation:'none',html:true,type:'error',confirmButtonText:"_(Ok)_"});
}
}
function showDHCP(val,v) {
if (val=='no') {
$('tr.static'+v).show();
$('select[name=DNS'+v+']').val('yes');
$('select[name=DNS'+v+'] option:eq(0)').prop('disabled',true);
if (v=='6') $('tr.dns6').show();
$('tr.server'+v).show();
} else if (val=='yes') {
$('tr.static'+v).hide();
$('select[name=DNS'+v+']').val('no');
$('select[name=DNS'+v+'] option:eq(0)').prop('disabled',false);
if (v=='6') $('tr.dns6').show();
$('tr.server'+v).hide();
} else {
$('tr.static'+v).hide();
$('tr.dns'+v).hide();
$('tr.server'+v).hide();
}
}
function showDNS(val,v) {
if (val=='no') $('tr.server'+v).hide();
if (val=='yes') $('tr.server'+v).show();
}
function showPassword() {
if ($('#showPass').hasClass('checked')) {
$('#showPass').removeClass('checked fa-eye-slash').addClass('fa-eye');
var view = 'password';
} else {
$('#showPass').addClass('checked fa-eye-slash').removeClass('fa-eye');
var view = 'text';
}
$('input[name="PASSWORD"]').attr('type',view);
}
<?if (_var($wlan0,'WIFI')=='yes'):?>
$(function() {
$('#wifi').show();
update_wifi();
});
<?endif;?>
</script>

View File

@@ -721,7 +721,7 @@ if (isset($myPage['Load']) && $myPage['Load']>0) echo "\n<script>timers.reload =
echo "<div class='tabs'>";
$tab = 1;
$pages = [];
if (!empty($myPage['text']) && page_enabled($myPage)) $pages[$myPage['name']] = $myPage;
if (!empty($myPage['text'])) $pages[$myPage['name']] = $myPage;
if (_var($myPage,'Type')=='xmenu') $pages = array_merge($pages, find_pages($myPage['name']));
if (isset($myPage['Tabs'])) $display['tabs'] = strtolower($myPage['Tabs'])=='true' ? 0 : 1;
$tabbed = $display['tabs']==0 && count($pages)>1;
@@ -729,7 +729,7 @@ $tabbed = $display['tabs']==0 && count($pages)>1;
foreach ($pages as $page) {
$close = false;
if (isset($page['Title'])) {
eval("\$title=\"{$page['Title']}\";");
eval("\$title=\"".htmlspecialchars($page['Title'])."\";");
if ($tabbed) {
echo "<div class='tab'><input type='radio' id='tab{$tab}' name='tabs' onclick='settab(this.id)'><label for='tab{$tab}'>";
echo tab_title($title,$page['root'],_var($page,'Tag',false));
@@ -746,7 +746,7 @@ foreach ($pages as $page) {
if (isset($page['Type']) && $page['Type']=='menu') {
$pgs = find_pages($page['name']);
foreach ($pgs as $pg) {
@eval("\$title=\"{$pg['Title']}\";");
@eval("\$title=\"".htmlspecialchars($pg['Title'])."\";");
$icon = _var($pg,'Icon',"<i class='icon-app PanelIcon'></i>");
if (substr($icon,-4)=='.png') {
$root = $pg['root'];

View File

@@ -18,27 +18,29 @@ require_once "$docroot/webGui/include/Translations.php";
function port($eth) {
$sys = "/sys/class/net";
if (substr($eth,0,4)=='wlan') return $eth;
$x = preg_replace('/[^0-9]/','',$eth);
return file_exists("$sys/br{$x}") ? "br${x}" : (file_exists("$sys/bond{$x}") ? "bond{$x}" : "eth{$x}");
}
exec("grep -Po 'nameserver \K.*' /etc/resolv.conf",$ns);
exec("grep -Po 'nameserver \K\S+' /etc/resolv.conf 2>/dev/null",$ns);
$eth = $_POST['port'];
$vlan = $_POST['vlan'];
$port = port($eth).($vlan ? ".$vlan" : "");
$v6on = @file_get_contents("/proc/sys/net/ipv6/conf/$port/disable_ipv6")!=1;
$v6on = trim(file_get_contents("/proc/sys/net/ipv6/conf/$port/disable_ipv6"))==='0';
$none = _('None');
$error = "<span class='red-text'>"._('Missing')."</span>";
$note = $eth=='eth0' && !$vlan ? $error : $none;
$link = _(ucfirst(exec("ethtool $eth | awk '$1==\"Link\" {print $3;exit}'")))." ("._(exec("ethtool $eth | grep -Pom1 '^\s+Port: \K.*'")).")";
$speed = _(preg_replace(['/^(\d+)/','/!/'],['$1 ',''],exec("ethtool $eth | awk '$1==\"Speed:\" {print $2;exit}'")));
$ipv4 = array_filter(explode(' ',exec("ip -4 -br addr show $port scope global | awk '{\$1=\$2=\"\";print;exit}' | sed -r 's/ metric [0-9]+//g; s/\/[0-9]+//g'")));
$gw4 = exec("ip -4 route show default dev $port | awk '{print \$3;exit}'") ?: $note;
$note = in_array($eth,['eth0','wlan0']) && !$vlan ? $error : $none;
$link = _(ucfirst(exec("ethtool $eth 2>/dev/null | awk '$1==\"Link\" {print $3;exit}'")) ?: 'Unknown')." ("._(exec("ethtool $eth 2>/dev/null | grep -Pom1 '^\s+Port: \K.*'") ?: ($eth=='wlan0' ? 'wifi' :'not present')).")";
$speed = _(preg_replace(['/^(\d+)/','/!/'],['$1 ',''],exec("ethtool $eth 2>/dev/null | awk '$1==\"Speed:\" {print $2;exit}'")) ?: 'Unknown');
$ipv4 = array_filter(explode(' ',exec("ip -4 -br addr show $port scope global 2>/dev/null | awk '{\$1=\$2=\"\";print;exit}' | sed -r 's/ metric [0-9]+//g; s/\/[0-9]+//g'")));
$gw4 = exec("ip -4 route show default dev $port 2>/dev/null | awk '{print \$3;exit}'") ?: $note;
$dns4 = array_filter($ns,function($ns){return strpos($ns,':')===false;});
$domain = exec("grep -Pom1 'domain \K.*' /etc/resolv.conf") ?: '---';
$domain = exec("grep -Pom1 'domain \K.*' /etc/resolv.conf 2>/dev/null") ?: '---';
if ($v6on) {
$ipv6 = array_filter(explode(' ',exec("ip -6 -br addr show $port scope global -temporary | awk '{\$1=\$2=\"\";print;exit}' | sed -r 's/ metric [0-9]+//g; s/\/[0-9]+//g'")));
$gw6 = exec("ip -6 route show default dev $port | awk '{print \$3;exit}'") ?: $note;
$ipv6 = array_filter(explode(' ',exec("ip -6 -br addr show $port scope global -temporary 2>/dev/null | awk '{\$1=\$2=\"\";print;exit}' | sed -r 's/ metric [0-9]+//g; s/\/[0-9]+//g'")));
$gw6 = exec("ip -6 route show default dev $port 2>/dev/null | awk '{print \$3;exit}'") ?: $note;
$dns6 = array_filter($ns,function($ns){return strpos($ns,':')!==false;});
}
@@ -46,6 +48,22 @@ echo "<table style='text-align:left;font-size:1.2rem'>";
echo "<tr><td>&nbsp;</td><td>&nbsp;</td></tr>";
echo "<tr><td>"._('Interface link').":</td><td>$link</td></tr>";
echo "<tr><td>"._('Interface speed').":</td><td>$speed</td></tr>";
if ($eth=='wlan0') {
$ini = '/boot/config/wireless-networks.cfg';
$wifi = (array)@parse_ini_file($ini,true);
$att1 = $att2 = $att3 = '';
foreach ($wifi as $network => $option) {
if (isset($option['GROUP']) && $option['GROUP']=='active') {
$att1 = $network;
$att2 = $option['ATTR2'];
$att3 = $option['ATTR3'];
break;
}
}
if ($att1) echo "<tr><td>"._('Network').":</td><td>$att1</td></tr>";
if ($att2) echo "<tr><td>"._('Health').":</td><td>$att2</td></tr>";
if ($att3) echo "<tr><td>"._('Security').":</td><td>$att3</td></tr>";
}
if (count($ipv4)) foreach ($ipv4 as $ip) {
echo "<tr><td>"._('IPv4 address').":</td><td>$ip</td></tr>";
} else {

View File

@@ -0,0 +1,17 @@
<?PHP
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$cipher = 'aes-256-cbc';
$key = 'UnraidEncryptionKey';
$iv = '12345678910111213';
?>

View File

@@ -0,0 +1,199 @@
<?PHP
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
$var = '/var/local/emhttp/var.ini';
$cfg = '/boot/config/wireless.cfg';
$ini = '/var/local/emhttp/wireless.ini';
$tmp = '/var/tmp/attr';
$wifi = (array)@parse_ini_file($cfg,true);
$attr = (array)@parse_ini_file($tmp,true);
$md5 = md5(json_encode($attr),true);
$cmd = $_POST['cmd'];
$masks = [
'255.0.0.0' => '8', '255.255.0.0' => '16', '255.255.128.0' => '17', '255.255.192.0' => '18',
'255.255.224.0' => '19', '255.255.240.0' => '20', '255.255.248.0' => '21', '255.255.252.0' => '22',
'255.255.254.0' => '23', '255.255.255.0' => '24', '255.255.255.128' => '25', '255.255.255.192' => '26',
'255.255.255.224' => '27', '255.255.255.240' => '28', '255.255.255.248' => '29', '255.255.255.252' => '30'
];
// add translations
$_SERVER['REQUEST_URI'] = 'settings';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/webGui/include/Helpers.php";
function scanWifi($port) {
$wlan = [];
exec("iw $port scan | grep -P '^BSS|signal:|SSID:|Authentication'",$scan);
$n = -1;
for ($i=0; $i<count($scan); $i++) {
if (substr($scan[$i],0,3)=='BSS') {
$wlan[++$n]['bss'] = substr($scan[$i],4,17);
} elseif (strpos($scan[$i],'signal:')!==false) {
$wlan[$n]['signal'] = trim(explode(': ',$scan[$i])[1]);
} elseif (strpos($scan[$i],'SSID:')!==false) {
$wlan[$n]['ssid'] = trim(explode(': ',$scan[$i])[1]);
} elseif (strpos($scan[$i],'suites:')!==false) {
$wlan[$n]['security'] = trim(explode(': ',$scan[$i])[1]);
}
}
return array_values(array_intersect_key($wlan,array_unique(array_column($wlan,'ssid'))));
}
function saveWifi() {
global $cfg, $wifi;
$text = [];
foreach ($wifi as $network => $block) {
$text[] = "[$network]";
foreach ($block as $key => $value) $text[] = "$key=\"$value\"";
}
file_put_contents_atomic($cfg,implode("\n",$text)."\n");
}
function saveAttr() {
global $tmp, $attr, $md5;
$text = [];
if (md5(json_encode($attr),true) === $md5) return;
foreach ($attr as $network => $block) {
$text[] = "[$network]";
foreach ($block as $key => $value) $text[] = "$key=\"$value\"";
}
file_put_contents_atomic($tmp,implode("\n",$text)."\n");
}
switch ($cmd) {
case 'list':
$title = _('Connect to WiFi network');
$port = array_key_first($wifi);
$wlan = scanWifi($port);
$echo = [];
$index = 0;
if (count(array_column($wlan,'ssid'))) {
$up = file_get_contents("/sys/class/net/$port/carrier") == 1;
$alive = $up ? exec("iw $port link | grep -Pom1 'SSID: \K.+'") : '';
$state = $up ? _('Connected') : _('Disconnected');
$color = $up ? 'blue' : 'red';
foreach (array_column($wlan,'ssid') as $network) {
$attr[$network]['ATTR1'] = $wlan[$index]['bss'];
$attr[$network]['ATTR2'] = $wlan[$index]['signal'];
$attr[$network]['ATTR3'] = $wlan[$index]['security'];
$index++;
if (isset($wifi[$network]['GROUP'])) {
if ($network == $alive) {
$echo['active'][] = "<dl><dt>$state:</dt>";
$echo['active'][] = "<dd><span class=\"wifi\">$network</span><i class=\"fa fa-fw fa-wifi hand $color-text\" onclick=\"manage_wifi(encodeURIComponent('$network'),1)\" title=\"$title\"></i><input type=\"button\" class=\"form\" value=\""._('Info')."\" onclick=\"networkInfo('$port')\"></dd>";
} else {
$echo['saved'][] = empty($echo['saved']) ? "<dl><dt>"._('My networks').":</dt>" : "<dt>&nbsp;</dt>";
$echo['saved'][] = "<dd><span class=\"wifi\">$network</span><i class=\"fa fa-wifi hand blue-text\" onclick=\"manage_wifi(encodeURIComponent('$network'),1)\" title=\"$title\"></i></dd>";
}
} else {
$echo['other'][] = empty($echo['other']) ? "<dl><dt>"._('Other networks').":</dt>" : "<dt>&nbsp;</dt>";
$echo['other'][] = "<dd><span class=\"wifi\">$network</span><i class=\"fa fa-wifi hand grey-text\" onclick=\"manage_wifi(encodeURIComponent('$network'),0)\" title=\"$title\"></i></dd>";
}
}
if (empty($echo['active'])) $echo['active'][] = "<dl><dt>"._('Connected').":</dt><dd>"._('None')."</dd>";
if (empty($echo['saved'])) $echo['saved'][] = "<dl><dt>"._('My networks').":</dt><dd>"._('None')."</dd>";
if (empty($echo['other'])) $echo['other'][] = "<dl><dt>"._('Other networks').":</dt><dd>"._('None')."</dd>";
$echo['active'] = implode($echo['active']);
$echo['saved'] = implode($echo['saved']);
$echo['other'] = implode($echo['other']);
saveAttr();
}
echo json_encode($echo);
break;
case 'join':
require_once "$docroot/webGui/include/OpenSSL.php";
$token = parse_ini_file($var)['csrf_token'];
$ssid = rawurldecode($_POST['ssid']);
$drop = $_POST['drop']==1;
$user = _var($wifi[$ssid],'USERNAME') ? openssl_decrypt($wifi[$ssid]['USERNAME'],$cipher,$key,0,$iv) : '';
$passwd = _var($wifi[$ssid],'PASSWORD') ? openssl_decrypt($wifi[$ssid]['PASSWORD'],$cipher,$key,0,$iv) : '';
$join = _var($wifi[$ssid],'AUTOJOIN','no');
$dhcp4 = _var($wifi[$ssid],'DHCP4','yes');
$dns4 = _var($wifi[$ssid],'DNS4','no');
$ip4 = _var($wifi[$ssid],'IP4');
$mask4 = _var($wifi[$ssid],'MASK4','255.255.255.0');
$gw4 = _var($wifi[$ssid],'GATEWAY4');
$server4 = _var($wifi[$ssid],'SERVER4');
$dhcp6 = _var($wifi[$ssid],'DHCP6');
$dns6 = _var($wifi[$ssid],'DNS6','no');
$ip6 = _var($wifi[$ssid],'IP6');
$mask6 = _var($wifi[$ssid],'MASK6','64');
$gwv6 = _var($wifi[$ssid],'GATEWAY6');
$server6 = _var($wifi[$ssid],'SERVER6');
$hide1 = $dhcp4=='no' ? '': 'hide';
$hide2 = $dns4=='no' ? 'hide' : '';
$hide3 = $dhcp6=='no' ? '' : 'hide';
$hide4 = $dhcp6=='' ? 'hide' : '';
$hide5 = $dns6=='no' ? 'hide' : '';
$attr1 = $attr[$ssid]['ATTR1'];
$attr2 = $attr[$ssid]['ATTR2'];
$attr3 = $attr[$ssid]['ATTR3'];
echo "<form name=\"wifi\" method=\"POST\" action=\"/update.php\" target=\"progressFrame\">";
echo "<input type=\"hidden\" name=\"#file\" value=\"$cfg\">";
echo "<input type=\"hidden\" name=\"#include\" value=\"/webGui/include/update.wireless.php\">";
echo "<input type=\"hidden\" name=\"#command\" value=\"/webGui/scripts/wireless\">";
echo "<input type=\"hidden\" name=\"#section\" value=\"$ssid\">";
echo "<input type=\"hidden\" name=\"ATTR1\" value=\"$attr1\">";
echo "<input type=\"hidden\" name=\"ATTR2\" value=\"$attr2\">";
echo "<input type=\"hidden\" name=\"ATTR3\" value=\"$attr3\">";
echo "<input type=\"hidden\" name=\"csrf_token\" value=\"$token\">";
echo "<table class=\"swal\">";
echo "<tr><td colspan=\"2\">&nbsp;</td></tr>";
if ($drop && isset($wifi[$ssid])) {
echo "<tr><td colspan=\"2\"><center><input type=\"button\" class=\"form\" value=\""._('Forget this network')."\" onclick=\"manage_wifi(encodeURIComponent('$ssid'),2)\"></center></td></tr>";
echo "<tr><td colspan=\"2\">&nbsp;</td></tr>";
}
if (strpos($attr3,'IEEE')!==false) echo "<tr><td>"._('Username').":</td><td><input type=\"text\" name=\"USERNAME\" class=\"narrow\" autocomplete=\"off\" spellcheck=\"false\" value=\"$user\"></td></tr>";
if ($attr3) echo "<tr><td>"._('Password').":</td><td><input type=\"password\" name=\"PASSWORD\" class=\"narrow\" autocomplete=\"off\" spellcheck=\"false\" value=\"$passwd\"><i id=\"showPass\" class=\"fa fa-eye\" onclick=\"showPassword()\"></i></td></tr>";
echo "<tr><td colspan=\"2\">&nbsp;</td></tr>";
echo "<tr><td>"._('IPv4 address assignment').":</td><td><select name=\"DHCP4\" onclick=\"showDHCP(this.value,4)\">";
echo mk_option($dhcp4, 'yes', _('Automatic'));
echo mk_option($dhcp4, 'no', _('Static'));
echo "</select></td></tr>";
echo "<tr class=\"static4 $hide1\"><td>"._('IPv4 address').":</td><td><input type=\"text\" name=\"IP4\" class=\"narrow\" maxlength=\"15\" autocomplete=\"off\" spellcheck=\"false\" value=\"$ip4\">/<select name=\"MASK4\" class=\"slim\">";
foreach ($masks as $mask => $prefix) echo mk_option($mask4, $mask, $prefix);
echo "</select></td></tr>";
echo "<tr class=\"static4 $hide1\"><td>"._('IPv4 default gateway').":</td><td><input type=\"text\" name=\"GATEWAY4\" class=\"narrow\" maxlength=\"15\" autocomplete=\"off\" spellcheck=\"false\" value=\"$gw4\"></td></tr>";
echo "<tr class=\"dns4\"><td>"._('IPv4 DNS assignment').":</td><td><select name=\"DNS4\" onclick=\"showDNS(this.value,4)\">";
echo mk_option($dns4, "no", _("Automatic"));
echo mk_option($dns4, "yes", _("Static"));
echo "</select></td></tr>";
echo "<tr class=\"server4 $hide2\"><td>"._('DNSv4 server').":</td><td><input type=\"text\" name=\"SERVER4\" class=\"narrow\" autocomplete=\"off\" spellcheck=\"false\" value=\"$server4\"></td></tr>";
echo "<tr><td colspan=\"2\">&nbsp;</td></tr>";
echo "<tr><td>"._('IPv6 address assignment').":</td><td><select name=\"DHCP6\" onclick=\"showDHCP(this.value,6)\">";
echo mk_option($dhcp6, '', _('None'));
echo mk_option($dhcp6, 'yes', _('Automatic'));
echo mk_option($dhcp6, 'no', _('Static'));
echo "</select></td></tr>";
echo "<tr class=\"static6 $hide3\"><td>"._('IPv6 address').":</td><td><input type=\"text\" name=\"IP6\" class=\"narrow\" maxlength=\"39\" autocomplete=\"off\" spellcheck=\"false\" value=\"$ip6\">/<input type=\"number\" min=\"1\" max=\"128\" maxlength=\"3\" name=\"MASK6\" class=\"slim\" value=\"$mask6\"></td></tr>";
echo "<tr class=\"static6 $hide3\"><td>"._('IPv6 default gateway').":</td><td><input type=\"text\" name=\"GATEWAY6\" class=\"narrow\" maxlength=\"39\" autocomplete=\"off\" spellcheck=\"false\" value=\"$gw6\"></td></tr>";
echo "<tr class=\"dns6 $hide4\"><td>"._('IPv6 DNS assignment').":</td><td><select name=\"DNS6\" onclick=\"showDNS(this.value,6)\">";
echo mk_option($dns4, "no", _("Automatic"));
echo mk_option($dns4, "yes", _("Static"));
echo "</select></td></tr>";
echo "<tr class=\"server6 $hide5\"><td>"._('DNSv6 server').":</td><td><input type=\"text\" name=\"SERVER6\" class=\"narrow\" autocomplete=\"off\" spellcheck=\"false\" value=\"$server6\"></td></tr>";
echo "<tr><td colspan=\"2\">&nbsp;</td></tr>";
echo "</table>";
echo "</form>";
break;
case 'forget':
$ssid = rawurldecode($_POST['ssid']);
if ($wifi[$ssid]['GROUP'] == 'active') exec("/etc/rc.d/rc.wireless stop &>/dev/null &");
unset($wifi[$ssid]);
saveWifi();
break;
}
?>

View File

@@ -0,0 +1,24 @@
<?PHP
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/OpenSSL.php";
// encrypt username and password before saving (if existing)
if (!empty($_POST['USERNAME'])) $_POST['USERNAME'] = openssl_encrypt($_POST['USERNAME'],$cipher,$key,0,$iv);
if (!empty($_POST['PASSWORD'])) $_POST['PASSWORD'] = openssl_encrypt($_POST['PASSWORD'],$cipher,$key,0,$iv);
// update active wifi selection
foreach ($keys as $key => $val) if (isset($val['GROUP'])) $keys[$key]['GROUP'] = 'saved';
$keys[$section]['GROUP'] = 'active';
?>

View File

@@ -0,0 +1,50 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2025, Lime Technology
* Copyright 2012-2025, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $_SERVER['DOCUMENT_ROOT'] ?: "/usr/local/emhttp";
require_once "$docroot/plugins/dynamix/include/Wrappers.php";
$arg = $argv[1] ?? '';
$ini = '/var/local/emhttp/wireless.ini';
$cfg = '/boot/config/wireless.cfg';
$wifi = (array)@parse_ini_file($cfg,true);
$port = array_key_first($wifi);
$text = ["PORT=\"$port\""];
if (!$arg) {
foreach ($wifi as $network => $block) {
if ($network == $port) continue;
if ($block['GROUP'] == 'active') {
$text[] = "SSID=\"$network\"";
unset($block['GROUP']);
foreach ($block as $key => $value) $text[] = "$key=\"$value\"";
}
}
} elseif (!in_array($arg,['no','yes'])) {
foreach ($wifi as $network => $block) {
if ($network == $port) continue;
if ($block['GROUP'] == 'saved' && $network == $arg) {
$text[] = "SSID=\"$network\"";
unset($block['GROUP']);
foreach ($block as $key => $value) $text[] = "$key=\"$value\"";
}
}
}
exec("/etc/rc.d/rc.wireless stop");
if ($arg == 'yes') {
exec("/etc/rc.d/rc.wireless start &>/dev/null &");
} elseif (count($text)>2) {
file_put_contents_atomic($ini,implode("\n",$text)."\n");
exec("/etc/rc.d/rc.wireless join &>/dev/null &");
}
?>

View File

@@ -0,0 +1,13 @@
.mybar{height:30px;line-height:30px;margin-bottom:24px;border:none;border-radius:3px;box-shadow:inset 0 1px 0 rgba(255,255,255,.5)}
.mybar.left{float:left}
.whitebar{background:-webkit-radial-gradient(#c7c7c7,#eeeeee);background:linear-gradient(#c7c7c7,#eeeeee)}
img.left{float:left;margin-right:5px}
img.top{margin-top:-3px}
div.leftbar{float:left;line-height:16px;width:16%}
div.rightbar{float:right;line-height:16px;width:12%}
.inside{float:left;width:30px;margin-right:6px;clear:both}
.graph1{float:left;margin:0}
.graph2{float:left;margin:0 0 40px 0}
.graph2.last{margin-bottom:0}
.graph3{float:left;margin:20px 6px 20px 0}
.graph3.last{margin-right:0}

View File

@@ -0,0 +1 @@
input.narrow:focus,input.slim:focus{background-color:#edeaef;box-shadow:none;outline:none;border:none;border:1px solid #0072c6}

View File

@@ -0,0 +1 @@
input.narrow:focus,input.slim:focus{background-color:#262626;box-shadow:none;outline:none;border:none;border-bottom:1px solid #e5e5e5}

View File

@@ -0,0 +1 @@
input.narrow:focus,input.slim:focus{background-color:#121510;box-shadow:none;outline:none;border:none;border:1px solid #0072c6}

View File

@@ -0,0 +1 @@
input.narrow:focus,input.slim:focus{background-color:#e8e8e8;box-shadow:none;outline:none;border:none;border-bottom:1px solid #1c1b1b}

View File

@@ -0,0 +1,8 @@
.wifi{display:inline-block;width:300px}
.hide{display:none}
i.hand{cursor:pointer;text-decoration:none}
input.form{font-size:1rem;padding:5px 10px;margin:0}
input[type=button].form{margin-left:20px}
table.swal{text-align:left;font-size:1.2rem}
table.swal td:nth-child(1){width:42%;text-align:right;padding-right:30px}
#showPass{cursor:pointer;margin-left:10px}

View File

@@ -0,0 +1 @@
.uplift{margin-top:-30px!important}

View File

@@ -0,0 +1 @@
.uplift{margin-top:-30px!important}

View File

@@ -26,7 +26,7 @@ pre#swaltext{text-align:left;margin:0;padding:0;height:650px;white-space:normal;
.sweet-alert .sa-input-error::before{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}
.sweet-alert .sa-input-error::after{-webkit-transform:rotate(45deg);transform:rotate(45deg)}
.sweet-alert .sa-input-error.show{opacity:1;-webkit-transform:scale(1);transform:scale(1)}
.sweet-alert input[type=text]{width:100%;box-sizing:border-box;border-radius:3px;border:1px solid #d7d7d7;height:43px;margin-top:10px;margin-bottom:17px;font-size:1.8rem;box-shadow:inset 0px 1px 1px rgba(0, 0, 0, 0.06);padding:0 12px;display:none;-webkit-transition:all 0.3s;transition:all 0.3s}
.sweet-alert fieldset input[type=text]{width:100%;box-sizing:border-box;border-radius:3px;border:1px solid #d7d7d7;height:43px;margin-top:10px;margin-bottom:17px;font-size:1.8rem;box-shadow:inset 0px 1px 1px rgba(0, 0, 0, 0.06);padding:0 12px;display:none;-webkit-transition:all 0.3s;transition:all 0.3s}
.sweet-alert input:focus{outline:none;box-shadow:0px 0px 3px #c4e6f5;border:1px solid #b4dbed}
.sweet-alert input:focus::-moz-placeholder{transition:opacity 0.3s 0.03s ease;opacity:0.5}
.sweet-alert input:focus:-ms-input-placeholder{transition:opacity 0.3s 0.03s ease;opacity:0.5}

View File

@@ -78,18 +78,18 @@ echo -n >$RESOLV.tail
if [[ $DHCP_KEEPRESOLV == yes ]]; then
echo "# Generated by rc.inet1" >>$RESOLV
[[ -n $DNS_SERVER1 ]] && echo "nameserver $(unzero $DNS_SERVER1)" >>$RESOLV
[[ -n $DNS_SERVER2 ]] && echo "nameserver $(unzero $DNS_SERVER2)" >>$RESOLV
[[ -n $DNS_SERVER3 ]] && echo "nameserver $(unzero $DNS_SERVER3)" >>$RESOLV
[[ -n $DNS_SERVER4 ]] && echo "nameserver $(unzero $DNS_SERVER4)" >>$RESOLV
[[ -n $DNS_SERVER1 ]] && echo "nameserver $(unzero $DNS_SERVER1) # eth0:v4" >>$RESOLV
[[ -n $DNS_SERVER2 ]] && echo "nameserver $(unzero $DNS_SERVER2) # eth0:v4" >>$RESOLV
[[ -n $DNS_SERVER3 ]] && echo "nameserver $(unzero $DNS_SERVER3) # eth0:v4" >>$RESOLV
[[ -n $DNS_SERVER4 ]] && echo "nameserver $(unzero $DNS_SERVER4) # eth0:v4" >>$RESOLV
[[ $DHCP6_KEEPRESOLV == no ]] && cp -f $RESOLV $RESOLV.head
fi
if [[ $DHCP6_KEEPRESOLV == yes ]]; then
[[ $DHCP_KEEPRESOLV == no ]] && echo "# Generated by rc.inet1" >>$RESOLV
[[ -n $DNS6_SERVER1 ]] && echo "nameserver $(unzero6 $DNS6_SERVER1)" >>$RESOLV
[[ -n $DNS6_SERVER2 ]] && echo "nameserver $(unzero6 $DNS6_SERVER2)" >>$RESOLV
[[ -n $DNS6_SERVER3 ]] && echo "nameserver $(unzero6 $DNS6_SERVER3)" >>$RESOLV
[[ -n $DNS6_SERVER4 ]] && echo "nameserver $(unzero6 $DNS6_SERVER4)" >>$RESOLV
[[ -n $DNS6_SERVER1 ]] && echo "nameserver $(unzero6 $DNS6_SERVER1) # eth0:v6" >>$RESOLV
[[ -n $DNS6_SERVER2 ]] && echo "nameserver $(unzero6 $DNS6_SERVER2) # eth0:v6" >>$RESOLV
[[ -n $DNS6_SERVER3 ]] && echo "nameserver $(unzero6 $DNS6_SERVER3) # eth0:v6" >>$RESOLV
[[ -n $DNS6_SERVER4 ]] && echo "nameserver $(unzero6 $DNS6_SERVER4) # eth0:v6" >>$RESOLV
[[ $DHCP_KEEPRESOLV == no ]] && cp -f $RESOLV $RESOLV.tail
fi

View File

@@ -159,7 +159,7 @@ check(){
fi
[[ $(ipv $ADDR) == 4 ]] && IPV4=yes || IPV6=yes
done
done <<< $(ip -br addr show scope global -temporary -deprecated | awk '$1~"^(br|bond|eth|wg)[0-9]+(.[0-9]+)?" && $3!="" {gsub("@.+","",$1);$2="";print}' | sed -r 's/ metric [0-9]+//g' | sort)
done <<< $(ip -br addr show scope global -temporary -deprecated | awk '$1~"^(br|bond|eth|wlan|wg)[0-9]+(.[0-9]+)?" && $3!="" {gsub("@.+","",$1);$2="";print}' | sed -r 's/ metric [0-9]+//g' | sort)
# add loopback interface
if [[ "smb nfs" =~ "$CALLER" ]]; then
[[ $IPV4 == yes ]] && BIND+=(127.0.0.1)

263
etc/rc.d/rc.wireless Normal file
View File

@@ -0,0 +1,263 @@
#!/bin/bash
#
# script: rc.wireless
#
# This script is used to bring up the wireless network interface.
#
# Bergware - created for Unraid OS, January 2025
DAEMON="WiFi network"
CALLER="wifi"
INI="/var/local/emhttp/wireless.ini"
CFG="/boot/config/wireless.cfg"
SSL="/usr/local/emhttp/webGui/include/OpenSSL.php"
STARTWIFI="/usr/local/emhttp/webGui/scripts/wireless"
WPA="/etc/wpa_supplicant.conf"
# system network references
SYSTEM=/sys/class/net
CONF6=/proc/sys/net/ipv6/conf
# run & log functions
. /etc/rc.d/rc.runlog
# library functions
. /etc/rc.d/rc.library.source
# get settings
[[ -r $INI ]] && . $INI
PORT=${PORT:-wlan0}
# function to get openSSL settings
get(){
local KEY=$1
[[ -r $SSL ]] && awk "\$1==\"\$$KEY\" {print \$3}" $SSL | sed -r "s/[';]//g"
}
# function to convert text to hex
hex(){
echo -n $(get $1) | od -An -tx1 | tr -d ' \n'
}
# function to wait for carrier of interface
carrier_up(){
local n
for n in {1..10}; do
[[ $(cat $SYSTEM/$1/carrier 2>/dev/null) == 1 ]] && return 0 || sleep 1
done
return 1
}
# function to enable/disable ipv6 protocol per interface
ipv6_up(){
[[ -d $CONF6/${PORT/$1/$2} ]] && echo $4 >$CONF6/${PORT/$1/$2}/disable_ipv6
[[ -d $CONF6/${PORT/$1/$3} ]] && echo $4 >$CONF6/${PORT/$1/$3}/disable_ipv6
}
# function to enable/disable ipv6 assignment per interface
ipv6_addr(){
if [[ -d $CONF6/$1 ]]; then
echo $2 >$CONF6/$1/accept_ra
echo $2 >$CONF6/$1/accept_ra_defrtr
echo $3 >$CONF6/$1/autoconf
fi
}
# function to assign IP address
ipaddr_up(){
if [[ $DHCP == yes ]]; then
# bring up interface using DHCP/SLAAC
ipv6_addr 1 1
OPTIONS="-q -n -p -t 10"
[[ -n $HOSTNAME ]] && OPTIONS="$OPTIONS -h $HOSTNAME"
[[ $DNS == yes ]] && OPTIONS="$OPTIONS -C resolv.conf"
[[ $IP == ipv4 ]] && OPTIONS="$OPTIONS -4"
[[ $IP == ipv6 ]] && OPTIONS="$OPTIONS -6"
if carrier_up $PORT; then
# interface is UP
log "interface $PORT is UP, polling up to 60 sec for DHCP $IP server"
if ! run timeout 60 dhcpcd -w $OPTIONS $PORT; then
log "can't obtain IP address, continue polling in background on interface $PORT"
run dhcpcd -b $OPTIONS $PORT
fi
else
# interface is DOWN
log "interface $PORT is DOWN, polling DHCP $IP server in background"
run dhcpcd -b $OPTIONS $PORT
fi
elif [[ $DHCP == no ]]; then
# bring up interface using static IP address
if carrier_up $PORT; then STATE="UP"; else STATE="DOWN"; fi
log "interface $PORT is $STATE, setting static $IP address"
ipv6_addr $PORT 0 1
if [[ $IP != ipv6 ]]; then
if [[ -n $IP4 ]]; then
[[ -n $MASK4 ]] && run ip -4 addr add $(unzero $IP4)/$MASK4 dev $PORT metric 1
fi
fi
if [[ $IP != ipv4 ]]; then
if [[ -n $IP6 ]]; then
[[ -n $MASK6 ]] && run ip -6 addr add $(unzero6 $IP6)/$MASK6 dev $PORT metric 1
fi
fi
fi
if [[ $DNS == yes ]]; then
[[ $IP == ipv4 ]] && echo "nameserver $SERVER4 # $PORT:v4" >>/etc/resolv.conf
[[ $IP == ipv6 ]] && echo "nameserver $SERVER6 # $PORT:v6" >>/etc/resolv.conf
else
[[ $IP == ipv4 ]] && sed -ri '/^nameserver .+# $PORT:v4/d' /etc/resolv.conf
[[ $IP == ipv6 ]] && sed -ri '/^nameserver .+# $PORT:v6/d' /etc/resolv.conf
fi
}
# function to release IP address
ipaddr_down(){
if [[ $DHCP == yes ]]; then
OPTIONS="-q -k"
[[ $DNS == yes ]] && OPTIONS="$OPTIONS -C resolv.conf"
[[ $IP == ipv4 ]] && OPTIONS="$OPTIONS -4"
[[ $IP == ipv6 ]] && OPTIONS="$OPTIONS -6"
# release DHCP assigned address
run dhcpcd $OPTIONS $PORT
fi
}
wifi_running(){
sleep 0.1
[[ $(cat $SYSTEM/$PORT/carrier 2>/dev/null) == 1 ]]
}
wifi_start(){
log "Starting $DAEMON..."
local REPLY
if [[ -e $SYSTEM/$PORT ]]; then
ip link set $PORT up
# start active SSID
$STARTWIFI
if ! carrier_up $PORT; then
# try the saved SSIDs
for SSID in $(grep -P '^\[.+\]$' $CFG 2>/dev/null | sed 1d | sed -r 's/\[|\]/"/g'); do
[[ -n $SSID ]] && $STARTWIFI "$SSID" || break
if carrier_up $PORT; then break; fi
done
fi
if wifi_running; then REPLY="Started"; else REPLY="Failed"; fi
else
REPLY="No Wifi present"
fi
log "$DAEMON... $REPLY."
}
wifi_stop(){
log "Stopping $DAEMON..."
local REPLY
if [[ -e $SYSTEM/$PORT ]]; then
IP=ipv4
DHCP=$DHCP4
DNS=$DNS4
ipaddr_down
if [[ -n $DHCP6 ]]; then
IP=ipv6
DHCP=$DHCP6
DNS=$DNS6
ipaddr_down
fi
pkill wpa_supplicant
iw dev $PORT disconnect
rm -f $INI
if ! wifi_running; then REPLY="Stopped"; else REPLY="Failed"; fi
else
REPLY="No Wifi present"
fi
log "$DAEMON... $REPLY."
}
wifi_join(){
log "Joining $DAEMON..."
local REPLY
if [[ ! -r $CFG ]]; then
log "$DAEMON... No configuration."
return
fi
[[ -n $USERNAME ]] && USERNAME=$(echo $USERNAME | openssl $(get cipher) -a -d -K $(hex key) -iv $(hex iv) 2>/dev/null)
[[ -n $PASSWORD ]] && PASSWORD=$(echo $PASSWORD | openssl $(get cipher) -a -d -K $(hex key) -iv $(hex iv) 2>/dev/null)
if [[ -z $ATTR3 || $ATTR3 =~ "Open" ]]; then
# open network
iw dev $PORT connect "$SSID" auth open
elif [[ $ATTR3 =~ "WEP" || $ATTR3 =~ "TKIP" ]]; then
# WEP encryption
iw dev $PORT connect "$SSID" auth shared key 0:$(hex "$PASSWORD")
elif [[ $ATTR3 =~ "IEEE" ]]; then
# WPA2/WPA3 enterprise
echo "network={" >$WPA
echo " ssid=\"$SSID\"" >>$WPA
echo " key_mgmt=WPA-EAP" >>$WPA
echo " eap=PEAP" >>$WPA
echo " identity=\"$USERNAME\"" >>$WPA
echo " password=\"$PASSWORD\"" >>$WPA
echo " phase1=\"peaplabel=0\"" >>$WPA
echo " phase2=\"autheap=MSCHAPV2\"" >>$WPA
echo "}" >>$WPA
wpa_supplicant -B -i $PORT -c $WPA
else
# assume WPA2/WPA3 personal is used
wpa_passphrase "$SSID" "$PASSWORD" >$WPA
wpa_supplicant -B -i $PORT -c $WPA
sed -i '3d' $WPA
fi
# IPv4 address assignment
IP=ipv4
DHCP=$DHCP4
DNS=$DNS4
ipaddr_up
# IPv6 address assignment (if enabled)
if [[ -n $DHCP6 ]]; then
echo 0 >$CONF6/$PORT/disable_ipv6
IP=ipv6
DHCP=$DHCP6
DNS=$DNS6
ipaddr_up
else
echo 1 >$CONF6/$PORT/disable_ipv6
fi
if wifi_running; then REPLY="Joined"; else REPLY="Failed"; fi
log "$DAEMON... $REPLY."
}
wifi_restart(){
log "Restarting $DAEMON..."
wifi_stop
sleep 1
wifi_start
}
wifi_status(){
if wifi_running; then
echo "$DAEMON is currently connected."
else
echo "$DAEMON is not connected."
exit 1
fi
}
case "$1" in
'start')
wifi_start
;;
'stop')
wifi_stop
;;
'join')
wifi_join
;;
'restart')
wifi_restart
;;
'status')
wifi_status
;;
*)
echo "Usage: $BASENAME start|stop|join|restart|status"
exit 1
esac
exit 0