Skip to content

Commit bb35277

Browse files
authored
Merge pull request #2132 from RaspAP/feat/bridged-toggle
Feature: dynamic IP bridged mode toggle
2 parents 644dfd6 + b02767a commit bb35277

5 files changed

Lines changed: 104 additions & 65 deletions

File tree

app/js/ui/hostapd.js

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ export function initHostapd() {
2727
load80211wSelect();
2828

2929
const bridgeCheckbox = document.getElementById('chxbridgedenable');
30+
const dynamicIpCheckbox = document.getElementById('chxbridgedynamic');
3031
const bridgeSection = document.getElementById('bridgeStaticIpSection');
32+
const staticIpFields = document.getElementById('bridgeStaticIpFields');
3133
const staticIpInput = document.getElementById('bridgeStaticIp');
3234
const netmaskInput = document.getElementById('bridgeNetmask');
3335
const gatewayInput = document.getElementById('bridgeGateway');
@@ -36,16 +38,35 @@ export function initHostapd() {
3638

3739
const bridgeInputs = [staticIpInput, netmaskInput, gatewayInput, dnsInput];
3840

39-
// toggle visibility and required fields
41+
function applyStaticIpVisibility(isDynamic) {
42+
if (staticIpFields) {
43+
staticIpFields.style.display = isDynamic ? 'none' : 'block';
44+
}
45+
bridgeInputs.forEach(input => {
46+
if (input) {
47+
isDynamic ? input.removeAttribute('required') : input.setAttribute('required', 'required');
48+
}
49+
});
50+
}
51+
52+
// initialize static IP field state on page load
53+
if (dynamicIpCheckbox) {
54+
applyStaticIpVisibility(dynamicIpCheckbox.checked);
55+
dynamicIpCheckbox.addEventListener('change', function() {
56+
applyStaticIpVisibility(this.checked);
57+
});
58+
}
59+
60+
// toggle visibility and required fields for the whole bridge section
4061
if (bridgeCheckbox) {
4162
bridgeCheckbox.addEventListener('change', function() {
42-
if (this.checked) {
43-
bridgeSection.style.display = 'block';
44-
bridgeInputs.forEach(input => input.setAttribute('required', 'required'));
45-
} else {
46-
bridgeSection.style.display = 'none';
47-
bridgeInputs.forEach(input => input.removeAttribute('required'));
48-
}
63+
if (this.checked) {
64+
bridgeSection.style.display = 'block';
65+
applyStaticIpVisibility(dynamicIpCheckbox && dynamicIpCheckbox.checked);
66+
} else {
67+
bridgeSection.style.display = 'none';
68+
bridgeInputs.forEach(input => { if (input) input.removeAttribute('required'); });
69+
}
4970
});
5071
}
5172

src/RaspAP/Networking/Hotspot/DhcpcdManager.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ public function buildConfig(
6666
$config[] = '# RaspAP br0 configuration';
6767
$config[] = 'denyinterfaces eth0 wlan0';
6868
$config[] = 'interface br0';
69-
$config[] = 'static ip_address='.$bridgeConfig['staticIp'] . '/'. $bridgeConfig['netmask'];
70-
$config[] = 'static routers='.$bridgeConfig['gateway'];
71-
$config[] = 'static domain_name_servers='.$bridgeConfig['dns'];
69+
if ($bridgeConfig !== null) {
70+
$config[] = 'static ip_address='.$bridgeConfig['staticIp'] . '/'. $bridgeConfig['netmask'];
71+
$config[] = 'static routers='.$bridgeConfig['gateway'];
72+
$config[] = 'static domain_name_servers='.$bridgeConfig['dns'];
73+
}
7274
$config[] = PHP_EOL;
7375
} elseif ($repeaterEnable) {
7476
$config = [

src/RaspAP/Networking/Hotspot/HostapdManager.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,20 +306,23 @@ public function deriveModeStates(array $post, array $currentIni): array
306306
{
307307
$prevWifiAPEnable = (int)($currentIni['WifiAPEnable'] ?? 0);
308308
$bridgedEnable = isset($post['bridgedEnable']) ? 1 : 0;
309+
$bridgedDynamic = isset($post['bridgedDynamic']) ? 1 : 0;
309310
$repeaterEnable = 0;
310311
$wifiAPEnable = 0;
311312

312313
if ($bridgedEnable === 0) {
313314
// only meaningful when not bridged
314315
$repeaterEnable = isset($post['repeaterEnable']) ? 1 : 0;
315316
$wifiAPEnable = isset($post['wifiAPEnable']) ? 1 : 0;
317+
$bridgedDynamic = 0;
316318
}
317319

318320
$logEnable = isset($post['logEnable']) ? 1 : 0;
319321
$effectiveWifiAPEnable = $bridgedEnable === 1 ? $prevWifiAPEnable : $wifiAPEnable;
320322

321323
return [
322324
'BridgedEnable' => $bridgedEnable,
325+
'BridgedDynamic' => $bridgedDynamic,
323326
'RepeaterEnable' => $repeaterEnable,
324327
'WifiAPEnable' => $effectiveWifiAPEnable,
325328
'LogEnable' => $logEnable
@@ -442,6 +445,7 @@ public function persistHostapdIni(array $states, string $apIface, string $cliIfa
442445
'LogEnable' => $states['LogEnable'] ?? false,
443446
'WifiAPEnable' => $states['WifiAPEnable'] ?? false,
444447
'BridgedEnable' => $states['BridgedEnable'] ?? false,
448+
'BridgedDynamic' => $states['BridgedDynamic'] ?? false,
445449
'RepeaterEnable' => $states['RepeaterEnable'] ?? false,
446450
'DualAPEnable' => $states['DualAPEnable'] ?? false,
447451
'WifiManaged' => $cliIface

src/RaspAP/Networking/Hotspot/Validators/HostapdValidator.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,14 @@ public function validate(
137137
$post['max_num_sta'] = $post['max_num_sta'] < 1 ? null : $post['max_num_sta'];
138138

139139
// validate bridged mode static IP configuration
140-
$bridgedEnable = !empty($post['bridgedEnable']);
140+
$bridgedEnable = !empty($post['bridgedEnable']);
141+
$bridgedDynamic = !empty($post['bridgedDynamic']);
141142
$bridgeStaticIp = trim($post['bridgeStaticIp'] ?? '');
142143
$bridgeNetmask = trim($post['bridgeNetmask'] ?? '');
143144
$bridgeGateway = trim($post['bridgeGateway'] ?? '');
144145
$bridgeDNS = trim($post['bridgeDNS'] ?? '');
145146

146-
if ($bridgedEnable) {
147+
if ($bridgedEnable && !$bridgedDynamic) {
147148
// validate static IP address
148149
if (!filter_var($bridgeStaticIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
149150
$status->addMessage('Bridge static IP address must be a valid IPv4 address', 'danger');
@@ -216,10 +217,10 @@ public function validate(
216217
'beacon_interval' => $post['beacon_interval'] ?? null,
217218
'disassoc_low_ack' => $post['disassoc_low_ackEnable'] ?? null,
218219
'bridge' => ($post['bridgedEnable'] ?? false) ? 'br0' : null,
219-
'bridgeStaticIp' => ($post['bridgeStaticIp']),
220-
'bridgeNetmask' => ($post['bridgeNetmask']),
221-
'bridgeGateway' => ($post['bridgeGateway']),
222-
'bridgeDNS' => ($post['bridgeDNS']),
220+
'bridgeStaticIp' => $bridgedDynamic ? '' : ($post['bridgeStaticIp'] ?? ''),
221+
'bridgeNetmask' => $bridgedDynamic ? '' : ($post['bridgeNetmask'] ?? ''),
222+
'bridgeGateway' => $bridgedDynamic ? '' : ($post['bridgeGateway'] ?? ''),
223+
'bridgeDNS' => $bridgedDynamic ? '' : ($post['bridgeDNS'] ?? ''),
223224
'he_oper_chwidth' => $post['he_oper_chwidth'] ?? null, // 802.11ax parameters
224225
'he_bss_color' => $post['he_bss_color'] ?? null, // 802.11be parameters
225226
'eht_oper_chwidth' => $post['eht_oper_chwidth'] ?? null

templates/hostapd/advanced.php

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,69 +11,80 @@
1111
</div>
1212
</div>
1313

14-
<!-- static IP settings -->
14+
<!-- bridge interface configuration -->
1515
<div class="row" id="bridgeStaticIpSection" style="display: <?php echo $arrHostapdConf['BridgedEnable'] == 1 ? 'block' : 'none' ?>;">
1616
<div class="col-md-12 mb-3">
1717
<div class="card">
1818
<div class="card-body">
1919
<h6 class="card-title"><?php echo _("Bridge interface configuration"); ?></h6>
20-
<p class="text-muted small mb-3">
21-
<?php echo _("Configure a static IP address for the <code>br0</code> interface to maintain connectivity during bridge mode activation."); ?>
22-
</p>
23-
24-
<div class="row g-3">
25-
<div class="col-md-6">
26-
<label for="bridgeStaticIp" class="form-label"><?php echo _("Static IP Address"); ?></label>
27-
<div class="input-group has-validation">
28-
<input type="text" class="form-control ip_address" id="bridgeStaticIp" name="bridgeStaticIp"
29-
value="<?php echo htmlspecialchars($arrConfig['bridgeStaticIP'] ?? '', ENT_QUOTES); ?>"
30-
placeholder="192.168.1.100" />
31-
<div class="invalid-feedback">
32-
<?php echo _("Please enter a valid IPv4 address"); ?>
20+
21+
<!-- dynamic IP toggle -->
22+
<div class="form-check form-switch mb-3">
23+
<?php $dynChecked = !empty($arrHostapdConf['BridgedDynamic']) && $arrHostapdConf['BridgedDynamic'] == 1 ? 'checked="checked"' : '' ?>
24+
<input class="form-check-input" id="chxbridgedynamic" name="bridgedDynamic" type="checkbox" value="1" <?php echo $dynChecked ?> />
25+
<label class="form-check-label" for="chxbridgedynamic"><?php echo _("Use dynamic IP address"); ?></label>
26+
<div class="form-text text-muted"><?php echo _("Requires mDNS or local DNS (e.g. <code>hostname.local</code>). If the DHCP server fails to assign an address, the device will be unreachable."); ?></div>
27+
</div>
28+
29+
<!-- static IP fields -->
30+
<div id="bridgeStaticIpFields" style="display: <?php echo (!empty($arrHostapdConf['BridgedDynamic']) && $arrHostapdConf['BridgedDynamic'] == 1) ? 'none' : 'block' ?>;">
31+
<p class="text-muted small mb-3">
32+
<?php echo _("Configure a static IP address for the <code>br0</code> interface to maintain connectivity during bridge mode activation."); ?>
33+
</p>
34+
<div class="row g-3">
35+
<div class="col-md-6">
36+
<label for="bridgeStaticIp" class="form-label"><?php echo _("Static IP Address"); ?></label>
37+
<div class="input-group has-validation">
38+
<input type="text" class="form-control ip_address" id="bridgeStaticIp" name="bridgeStaticIp"
39+
value="<?php echo htmlspecialchars($arrConfig['bridgeStaticIP'] ?? '', ENT_QUOTES); ?>"
40+
placeholder="192.168.1.100" />
41+
<div class="invalid-feedback">
42+
<?php echo _("Please enter a valid IPv4 address"); ?>
43+
</div>
3344
</div>
45+
<div class="form-text"><?php echo _("Example: 192.168.1.100"); ?></div>
3446
</div>
35-
<div class="form-text"><?php echo _("Example: 192.168.1.100"); ?></div>
36-
</div>
37-
38-
<div class="col-md-6">
39-
<label for="bridgeNetmask" class="form-label"><?php echo _("Netmask / CIDR"); ?></label>
40-
<div class="input-group has-validation">
41-
<input type="text" class="form-control" id="bridgeNetmask" name="bridgeNetmask"
42-
value="<?php echo htmlspecialchars($arrConfig['bridgeNetmask'] ?? '24', ENT_QUOTES); ?>"
43-
placeholder="24" />
44-
<div class="invalid-feedback">
45-
<?php echo _("Please enter a valid netmask"); ?>
47+
48+
<div class="col-md-6">
49+
<label for="bridgeNetmask" class="form-label"><?php echo _("Netmask / CIDR"); ?></label>
50+
<div class="input-group has-validation">
51+
<input type="text" class="form-control" id="bridgeNetmask" name="bridgeNetmask"
52+
value="<?php echo htmlspecialchars($arrConfig['bridgeNetmask'] ?? '24', ENT_QUOTES); ?>"
53+
placeholder="24" />
54+
<div class="invalid-feedback">
55+
<?php echo _("Please enter a valid netmask"); ?>
56+
</div>
4657
</div>
58+
<div class="form-text"><?php echo _("CIDR notation (e.g., 24 for 255.255.255.0)"); ?></div>
4759
</div>
48-
<div class="form-text"><?php echo _("CIDR notation (e.g., 24 for 255.255.255.0)"); ?></div>
49-
</div>
50-
51-
<div class="col-md-6">
52-
<label for="bridgeGateway" class="form-label"><?php echo _("Gateway"); ?></label>
53-
<div class="input-group has-validation">
54-
<input type="text" class="form-control ip_address" id="bridgeGateway" name="bridgeGateway"
55-
value="<?php echo htmlspecialchars($arrConfig['bridgeGateway'] ?? '', ENT_QUOTES); ?>"
56-
placeholder="192.168.1.1" />
57-
<div class="invalid-feedback">
58-
<?php echo _("Please enter a valid IPv4 address"); ?>
60+
61+
<div class="col-md-6">
62+
<label for="bridgeGateway" class="form-label"><?php echo _("Gateway"); ?></label>
63+
<div class="input-group has-validation">
64+
<input type="text" class="form-control ip_address" id="bridgeGateway" name="bridgeGateway"
65+
value="<?php echo htmlspecialchars($arrConfig['bridgeGateway'] ?? '', ENT_QUOTES); ?>"
66+
placeholder="192.168.1.1" />
67+
<div class="invalid-feedback">
68+
<?php echo _("Please enter a valid IPv4 address"); ?>
69+
</div>
5970
</div>
71+
<div class="form-text"><?php echo _("Your router's IP address"); ?></div>
6072
</div>
61-
<div class="form-text"><?php echo _("Your router's IP address"); ?></div>
62-
</div>
6373

64-
<div class="col-md-6">
65-
<label for="bridgeDNS" class="form-label"><?php echo _("DNS Server"); ?></label>
66-
<div class="input-group has-validation">
67-
<input type="text" class="form-control ip_address" id="bridgeDNS" name="bridgeDNS"
68-
value="<?php echo htmlspecialchars($arrConfig['bridgeDNS'] ?? '', ENT_QUOTES); ?>"
69-
placeholder="192.168.1.1" />
70-
<div class="invalid-feedback">
71-
<?php echo _("Please enter a valid IPv4 address"); ?>
74+
<div class="col-md-6">
75+
<label for="bridgeDNS" class="form-label"><?php echo _("DNS Server"); ?></label>
76+
<div class="input-group has-validation">
77+
<input type="text" class="form-control ip_address" id="bridgeDNS" name="bridgeDNS"
78+
value="<?php echo htmlspecialchars($arrConfig['bridgeDNS'] ?? '', ENT_QUOTES); ?>"
79+
placeholder="192.168.1.1" />
80+
<div class="invalid-feedback">
81+
<?php echo _("Please enter a valid IPv4 address"); ?>
82+
</div>
7283
</div>
84+
<div class="form-text"><?php echo _("Usually same as gateway"); ?></div>
7385
</div>
74-
<div class="form-text"><?php echo _("Usually same as gateway"); ?></div>
7586
</div>
76-
</div>
87+
</div><!-- /#bridgeStaticIpFields -->
7788

7889
</div>
7990
</div>

0 commit comments

Comments
 (0)