1<%- 2 local ubus = require "ubus" 3 local sys = require "luci.sys" 4 local utl = require "luci.util" 5 6 function connect_ubus(methods) 7 local result 8 local conn = ubus.connect() 9 10 if not conn then 11 error("Failed to connect to ubusd") 12 end 13 14 result = conn:call("otbr", methods, {}) 15 16 return result 17 end 18 19 function threadget(action) 20 local result = connect_ubus(action) 21 22 return result 23 end 24 25 local state = threadget("state").State 26 27 28 29 function addrlist() 30 local k, v 31 local l = { } 32 33 local addrlist = connect_ubus("macfilteraddr").addrlist 34 35 for k, v in pairs(addrlist) do 36 l[#l+1] = v 37 end 38 39 return l 40 end 41 42 43-%> 44<%+header%> 45 46<h2><%:Thread Network: %><%=threadget("networkname").NetworkName%><%: (wpan0)%></h2> 47<div> The Network Configuration section covers physical settings of the Thread Network such as channel, PAN ID. Per interface related settings like networkkey or MAC-filter are grouped in the Interface Configuration.</div> 48<br /> 49 50<form class="inline" action="<%=url('admin/network/thread_handler_setting')%>" method="post" id="settingForm" name="settingForm" onsubmit="return validateForm()"> 51 <h3><%:Network Configuration%></h3> 52 <br /> 53 <ul class="cbi-tabmenu"> 54 <li class="cbi-tab" id="generaltab"><a href="javascript:generalSetting();"><%:General Setup%></a></li> 55 <li class="cbi-tab-disabled" id="advancedtab"><a href="javascript:advancedSetting();"><%:Advanced Settings%></a></li> 56 </ul> 57 58 <input type="hidden" name="submitcontent" id="submitcontent" /> 59 <input type="hidden" name="removeAddrIndex" id="removeAddrIndex" /> 60 <input type="hidden" name="token" value="<%=token%>" /> 61 <!-- General Setup --> 62 <div style="width:80%;margin-left:10%;" id="generaldiv"> 63 <div class="cbi-value"> 64 <label class="cbi-value-title" style="margin-right:5%;">Thread Name</label> 65 <div class="cbi-value-title"> 66 <input type="text" name="threadname" value="<%=threadget("networkname").NetworkName%>" style="width:30%;"/> 67 </div> 68 </div> 69 <div class="cbi-value"> 70 <label class="cbi-value-title" style="margin-right:5%;">Status</label> 71 <div class="cbi-value-title"> 72 <span class="ifacebadge large" style="padding:2%;"> 73 <span> 74 <strong>PAN ID: </strong> 75 <%=threadget("panid").PanId%> 76 <br> 77 <strong>Extended PAN ID: </strong> 78 <%=threadget("extpanid").ExtPanId%> 79 <br> 80 <strong>State: </strong> 81 <%=state%> 82 <br> 83 <strong>Channel: </strong> 84 <%=threadget("channel").Channel%> 85 <span> 86 </span> 87 </div> 88 </div> 89 <div class="cbi-value"> 90 <% if state == "disabled" then %> 91 <label class="cbi-value-title" style="margin-right:5%;">Thread network is disabled</label> 92 <div class="cbi-value-field"> 93 <input class="cbi-button cbi-button-add" type="submit" id="enable" name="enable" value="Enable" /> 94 </div> 95 <% else %> 96 <label class="cbi-value-title" style="margin-right:5%;">Thread network is enabled</label> 97 <div class="cbi-value-field"> 98 <input class="cbi-button cbi-button-reset" type="submit" id="disable" name="disable" value="Disable" /> 99 </div> 100 <% end %> 101 </div> 102 <div class="cbi-value"> 103 <label class="cbi-value-title" style="margin-right:5%;">Protocol</label> 104 <div class="cbi-value-field"> 105 <select style="width:30%;" id="protocol" name="protocol"> 106 <option value>unmanaged</option> 107 </select> 108 </div> 109 </div> 110 </div> 111 112 <!-- Advanced Settings --> 113 <div style="width:70%;margin-left:10%;display:none;" id="advanceddiv"> 114 <div class="cbi-value"> 115 <label class="cbi-value-title" style="margin-right:5%;">Channel</label> 116 <div class="cbi-value-title"> 117 <input type="text" name="channel" value="<%=threadget("channel").Channel%>" style="width:50%;"/> 118 </div> 119 </div> 120 <div class="cbi-value"> 121 <label class="cbi-value-title" style="margin-right:5%;">PAN ID</label> 122 <div class="cbi-value-title"> 123 <input type="text" name="panid" value="<%=threadget("panid").PanId%>" style="width:50%;"/> 124 </div> 125 </div> 126 <div class="cbi-value"> 127 <label class="cbi-value-title" style="margin-right:5%;">Extended PAN ID</label> 128 <div class="cbi-value-title"> 129 <input type="text" name="extpanid" value="<%=threadget("extpanid").ExtPanId%>" style="width:50%;"/> 130 </div> 131 </div> 132 <div class="cbi-value"> 133 <% if state == 'disabled' then %> 134 <label class="cbi-value-title" style="margin-right:5%;">Mode</label> 135 <div class="cbi-value-title"> 136 <input type="text" name="mode" value="<%=threadget("mode").Mode%>" style="width:50%;"/> 137 </div> 138 <div style="margin-left:30%;margin-top:1%;"> 139 <span><img src="<%=resource .. "/cbi/help.gif"%>"/><%:set the thread device mode value, must be consist of 'r', 's', 'd', 'n'.%></span> 140 </div> 141 <% else %> 142 <label class="cbi-value-title" style="margin-right:5%;">Mode</label> 143 <div class="cbi-value-title"> 144 <input type="text" name="mode" value="<%=threadget("mode").Mode%>" readonly="readonly" style="width:50%;"/> 145 </div> 146 <div style="margin-left:30%;margin-top:1%;"> 147 <span><img src="<%=resource .. "/cbi/help.gif"%>"/><%:can not change mode when thread network is started.%></span> 148 </div> 149 <% end %> 150 </div> 151 <div class="cbi-value" style="display:none"> 152 <label class="cbi-value-title" style="margin-right:5%;">State</label> 153 <div class="cbi-value-field"> 154 <select style="width:30%;" id="state" name="state" value="<%=state%>"> 155 <option value="disabled">disabled</option> 156 <option value="leader">leader</option> 157 <option value="router">router</option> 158 <option value="child">child</option> 159 </select> 160 </div> 161 </div> 162 <div class="cbi-value"> 163 <label class="cbi-value-title" style="margin-right:5%;">PartitionId</label> 164 <div class="cbi-value-title"> 165 <%=threadget("partitionid").Partitionid%> 166 </div> 167 <div style="margin-left:30%;margin-top:1%;"> 168 <span><img src="<%=resource .. "/cbi/help.gif"%>"/><%:can not change partitionid when thread network is started.%></span> 169 </div> 170 </div> 171 <div class="cbi-value"> 172 <label class="cbi-value-title" style="margin-right:5%;">Leave</label> 173 <div class="cbi-value-field"> 174 <input class="cbi-button cbi-button-reset" type="submit" id="leave" name="leave" value="LEAVE" /> 175 </div> 176 <div style="margin-left:30%;margin-top:1%;"> 177 <span><img src="<%=resource .. "/cbi/help.gif"%>"/><%:This will delete all the existed Configuration of your thread network!%></span> 178 </div> 179 </div> 180 </div> 181 182 <h3><%:Interface Configuration%></h3> 183 <br /> 184 <ul class="cbi-tabmenu"> 185 <li class="cbi-tab" id="securitytab"><a href="javascript:securitySetting();"><%:Thread Security%></a></li> 186 <li class="cbi-tab-disabled" id="macfiltertab"><a href="javascript:macfilterSetting();"><%:MAC-Filter%></a></li> 187 </ul> 188 <br/> 189 190 <!-- Thread Security --> 191 <div style="width:60%;margin-left:10%;" id="securitydiv"> 192 <div class="cbi-value"> 193 <label class="cbi-value-title" style="margin-right:5%;">Network Key</label> 194 <div class="cbi-value-title"> 195 <input type="text" name="networkkey" value="<%=threadget("networkkey").Networkkey%>" style="width:60%;"/> 196 </div> 197 </div> 198 <div class="cbi-value"> 199 <label class="cbi-value-title" style="margin-right:5%;">Network Password</label> 200 <div class="cbi-value-title"> 201 <input type="text" name="pskc" value="<%=threadget("pskc").pskc%>" style="width:60%;"/> 202 </div> 203 </div> 204 </div> 205 206 <!-- MAC Filter --> 207 <div style="width:60%;margin-left:10%;display:none;" id="macfilterdiv"> 208 <div class="cbi-value"> 209 <label class="cbi-value-title" style="margin-right:1%;">Protocol</label> 210 <div class="cbi-value-field"> 211 <select style="width:30%;" id="macfilterselect" name="macfilterselect" onchange='macselectchange()' autocomplete="off"> 212 <option value="disable">Disable</option> 213 <option value="allowlist">Allowlist</option> 214 <option value="denylist">Denylist</option> 215 </select> 216 </div> 217 </div> 218 <div class="cbi-value" id="macfilterlistdiv" style="display:none;"> 219 <div class="table"> 220 <label class="cbi-value-title" style="margin-right:1%;">Existed Address</label> 221 <div class="cbi-value-field"> 222 <!-- addr list --> 223 <% for i, addr in ipairs(addrlist()) do %> 224 <input type="hidden" id="macfilterremove" name="macfilterremove" value="<%=addr%>" /> 225 <% if i == 1 then %> 226 <div class="tr"> 227 <div class="center" style="margin-top:15px;"><%=addr%></div> 228 <div class="th cbi-section-actions"> 229 <input class="cbi-button cbi-button-reset" type="submit" id="removeAddr<%=i%>" name="removeAddr<%=i%>" value="<%:Remove%>" /> 230 </div> 231 </div> 232 <% else %> 233 <div class="tr cbi-rowstyle-<%=1 + ((i-1) % 2)%>"> 234 <div class="td col-2 center"><%=addr%></div> 235 <div class="td cbi-section-actions"> 236 <input class="cbi-button cbi-button-reset" type="submit" id="removeAddr<%=i%>" name="removeAddr<%=i%>" value="<%:Remove%>" /> 237 </div> 238 </div> 239 <% end %> 240 <% end %> 241 <!-- /addr list --> 242 </div> 243 </div> 244 245 <div class="cbi-value"> 246 <div class="cbi-value-field"> 247 <input class="cbi-button cbi-button-reset" type="submit" id="clearAddr" name="clearAddr" value="<%:Clear%>" /> 248 <span style="margin-left:3%"><img src="<%=resource .. "/cbi/help.gif"%>" style="margin-right:2%"/><%:This will clear all existed macfilter address!%></span> 249 </div> 250 </div> 251 252 <label class="cbi-value-title" style="margin-right:1%;">Add Address</label> 253 <div class="cbi-value-field"> 254 <div class="table"> 255 <div class="tr table-titles"> 256 <label><input type="text" id="macfilteradd" name="macfilteradd"/></label> 257 <input class="cbi-button cbi-button-add" type="submit" id="addAddr" name="addAddr" value="<%:Add%>" /> 258 </div> 259 </div> 260 </div> 261 </div> 262 </div> 263 264 <div class="cbi-page-actions right"> 265 <input class="cbi-button cbi-button-neutral" style="float:left;" type="button" onclick="window.location.href='thread'" value="<%:Back to Overview%>" /> 266 <input class="cbi-button cbi-button-apply" type="submit" id="save" value="<%:Save & Apply%>" /> 267 <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" /> 268 </div> 269 270</form> 271 272<%+footer%> 273 274<script type="text/javascript" src="/luci-static/resources/handle_error.js"></script> 275<script type="text/javascript">//<![CDATA[ 276 handle_error(GetURLParameter('error')); 277 278 var macfilterstate = "<%=threadget("macfilterstate").state%>"; 279 var macselect = document.querySelector('#macfilterselect'); 280 281 if(macselect) { 282 macselect.value = macfilterstate; 283 var opt = macselect.options[macselect.selectedIndex]; 284 285 if(opt.value == "disable") 286 { 287 document.getElementById('macfilterlistdiv').style.display = "none"; 288 } 289 else 290 { 291 document.getElementById('macfilterlistdiv').style.display = "block"; 292 } 293 } 294 295 function macselectchange() 296 { 297 var macselect = document.querySelector('#macfilterselect'); 298 299 if(macselect) { 300 var opt = macselect.options[macselect.selectedIndex]; 301 if(opt.value == "disable") 302 { 303 document.getElementById('macfilterlistdiv').style.display = "none"; 304 } 305 else 306 { 307 document.getElementById('macfilterlistdiv').style.display = "block"; 308 } 309 } 310 } 311 312 function generalSetting() { 313 document.getElementById('generaldiv').style.display = "block"; 314 document.getElementById('advanceddiv').style.display = "none"; 315 document.getElementById('generaltab').className = "cbi-tab"; 316 document.getElementById('advancedtab').className = "cbi-tab-disabled"; 317 } 318 319 function advancedSetting() { 320 document.getElementById('generaldiv').style.display = "none"; 321 document.getElementById('advanceddiv').style.display = "block"; 322 document.getElementById('generaltab').className = "cbi-tab-disabled"; 323 document.getElementById('advancedtab').className = "cbi-tab"; 324 } 325 326 function securitySetting() { 327 document.getElementById('securitydiv').style.display = "block"; 328 document.getElementById('macfilterdiv').style.display = "none"; 329 document.getElementById('securitytab').className = "cbi-tab"; 330 document.getElementById('macfiltertab').className = "cbi-tab-disabled"; 331 } 332 333 function macfilterSetting() { 334 document.getElementById('securitydiv').style.display = "none"; 335 document.getElementById('macfilterdiv').style.display = "block"; 336 document.getElementById('securitytab').className = "cbi-tab-disabled"; 337 document.getElementById('macfiltertab').className = "cbi-tab"; 338 } 339 340 document.getElementById('settingForm').addEventListener('submit', function() { 341 var length = document.activeElement.id.length; 342 document.getElementById('submitcontent').value = document.activeElement.id.substring(0, 10); 343 document.getElementById('removeAddrIndex').value = document.activeElement.id.substring(10, length); 344 }); 345 346 function validateForm() 347 { 348 var activeElement = document.activeElement.id; 349 if(activeElement == "addAddr") 350 { 351 var addr = document.forms["settingForm"]["macfilteradd"].value; 352 if(addr.length != 16) 353 { 354 alert("Address length must be 16 bytes"); 355 return false; 356 } 357 } 358 else if(activeElement.substring(0, 10) != "removeAddr") 359 { 360 var panid = document.forms["settingForm"]["panid"].value; 361 if (isNaN(panid)) { 362 alert("panid must be a number"); 363 return false; 364 } 365 366 var extpanid = document.forms["settingForm"]["extpanid"].value; 367 var re = /^[0-9a-f]+$/; 368 var OK = re.exec(extpanid); 369 if (!OK) { 370 alert("extpanid must be a number"); 371 return false; 372 } 373 if (extpanid.length != 16) { 374 alert("extpanid length must be 16 bytes"); 375 return false; 376 } 377 378 var mode = document.forms["settingForm"]["mode"].value; 379 var re = /^[rsdn]+$/; 380 var OK = re.exec(mode); 381 if (!OK) { 382 alert("mode must be consist of 'r', 's', 'd', 'n'"); 383 return false; 384 } 385 386 var networkkey = document.forms["settingForm"]["networkkey"].value; 387 if (networkkey.length != 32) { 388 alert("Networkkey length must be 32 bytes"); 389 return false; 390 } 391 392 var pskc = document.forms["settingForm"]["pskc"].value; 393 if (pskc.length != 32) { 394 alert("Network password length must be 32 bytes"); 395 return false; 396 } 397 } 398 } 399//]]></script> 400