2020-03-27 09:27:18 +00:00
'use strict' ;
2020-04-09 12:39:09 +00:00
'require view' ;
'require dom' ;
'require poll' ;
2020-03-27 09:27:18 +00:00
'require fs' ;
'require ui' ;
'require uci' ;
'require form' ;
'require network' ;
'require firewall' ;
'require tools.widgets as widgets' ;
2021-06-25 17:45:23 +00:00
'require tools.network as nettools' ;
2020-03-27 09:27:18 +00:00
2020-05-20 08:46:25 +00:00
var isReadonlyView = ! L . hasViewPermission ( ) || null ;
2020-03-27 09:27:18 +00:00
function count _changes ( section _id ) {
var changes = ui . changes . changes , n = 0 ;
if ( ! L . isObject ( changes ) )
return n ;
if ( Array . isArray ( changes . network ) )
for ( var i = 0 ; i < changes . network . length ; i ++ )
n += ( changes . network [ i ] [ 1 ] == section _id ) ;
if ( Array . isArray ( changes . dhcp ) )
for ( var i = 0 ; i < changes . dhcp . length ; i ++ )
n += ( changes . dhcp [ i ] [ 1 ] == section _id ) ;
return n ;
}
function render _iface ( dev , alias ) {
var type = dev ? dev . getType ( ) : 'ethernet' ,
up = dev ? dev . isUp ( ) : false ;
return E ( 'span' , { class : 'cbi-tooltip-container' } , [
E ( 'img' , { 'class' : 'middle' , 'src' : L . resource ( 'icons/%s%s.png' ) . format (
alias ? 'alias' : type ,
up ? '' : '_disabled' ) } ) ,
E ( 'span' , { 'class' : 'cbi-tooltip ifacebadge large' } , [
E ( 'img' , { 'src' : L . resource ( 'icons/%s%s.png' ) . format (
type , up ? '' : '_disabled' ) } ) ,
L . itemlist ( E ( 'span' , { 'class' : 'left' } ) , [
_ ( 'Type' ) , dev ? dev . getTypeI18n ( ) : null ,
_ ( 'Device' ) , dev ? dev . getName ( ) : _ ( 'Not present' ) ,
_ ( 'Connected' ) , up ? _ ( 'yes' ) : _ ( 'no' ) ,
_ ( 'MAC' ) , dev ? dev . getMAC ( ) : null ,
_ ( 'RX' ) , dev ? '%.2mB (%d %s)' . format ( dev . getRXBytes ( ) , dev . getRXPackets ( ) , _ ( 'Pkts.' ) ) : null ,
_ ( 'TX' ) , dev ? '%.2mB (%d %s)' . format ( dev . getTXBytes ( ) , dev . getTXPackets ( ) , _ ( 'Pkts.' ) ) : null
] )
] )
] ) ;
}
function render _status ( node , ifc , with _device ) {
var desc = null , c = [ ] ;
if ( ifc . isDynamic ( ) )
desc = _ ( 'Virtual dynamic interface' ) ;
else if ( ifc . isAlias ( ) )
desc = _ ( 'Alias Interface' ) ;
else if ( ! uci . get ( 'network' , ifc . getName ( ) ) )
return L . itemlist ( node , [
null , E ( 'em' , _ ( 'Interface is marked for deletion' ) )
] ) ;
var i18n = ifc . getI18n ( ) ;
if ( i18n )
desc = desc ? '%s (%s)' . format ( desc , i18n ) : i18n ;
var changecount = with _device ? 0 : count _changes ( ifc . getName ( ) ) ,
ipaddrs = changecount ? [ ] : ifc . getIPAddrs ( ) ,
ip6addrs = changecount ? [ ] : ifc . getIP6Addrs ( ) ,
errors = ifc . getErrors ( ) ,
maindev = ifc . getL3Device ( ) || ifc . getDevice ( ) ,
macaddr = maindev ? maindev . getMAC ( ) : null ;
return L . itemlist ( node , [
_ ( 'Protocol' ) , with _device ? null : ( desc || '?' ) ,
_ ( 'Device' ) , with _device ? ( maindev ? maindev . getShortName ( ) : E ( 'em' , _ ( 'Not present' ) ) ) : null ,
_ ( 'Uptime' ) , ( ! changecount && ifc . isUp ( ) ) ? '%t' . format ( ifc . getUptime ( ) ) : null ,
_ ( 'MAC' ) , ( ! changecount && ! ifc . isDynamic ( ) && ! ifc . isAlias ( ) && macaddr ) ? macaddr : null ,
_ ( 'RX' ) , ( ! changecount && ! ifc . isDynamic ( ) && ! ifc . isAlias ( ) && maindev ) ? '%.2mB (%d %s)' . format ( maindev . getRXBytes ( ) , maindev . getRXPackets ( ) , _ ( 'Pkts.' ) ) : null ,
_ ( 'TX' ) , ( ! changecount && ! ifc . isDynamic ( ) && ! ifc . isAlias ( ) && maindev ) ? '%.2mB (%d %s)' . format ( maindev . getTXBytes ( ) , maindev . getTXPackets ( ) , _ ( 'Pkts.' ) ) : null ,
_ ( 'IPv4' ) , ipaddrs [ 0 ] ,
_ ( 'IPv4' ) , ipaddrs [ 1 ] ,
_ ( 'IPv4' ) , ipaddrs [ 2 ] ,
_ ( 'IPv4' ) , ipaddrs [ 3 ] ,
_ ( 'IPv4' ) , ipaddrs [ 4 ] ,
_ ( 'IPv6' ) , ip6addrs [ 0 ] ,
_ ( 'IPv6' ) , ip6addrs [ 1 ] ,
_ ( 'IPv6' ) , ip6addrs [ 2 ] ,
_ ( 'IPv6' ) , ip6addrs [ 3 ] ,
_ ( 'IPv6' ) , ip6addrs [ 4 ] ,
_ ( 'IPv6' ) , ip6addrs [ 5 ] ,
_ ( 'IPv6' ) , ip6addrs [ 6 ] ,
_ ( 'IPv6' ) , ip6addrs [ 7 ] ,
_ ( 'IPv6' ) , ip6addrs [ 8 ] ,
_ ( 'IPv6' ) , ip6addrs [ 9 ] ,
_ ( 'IPv6-PD' ) , changecount ? null : ifc . getIP6Prefix ( ) ,
_ ( 'Information' ) , with _device ? null : ( ifc . get ( 'auto' ) != '0' ? null : _ ( 'Not started on boot' ) ) ,
_ ( 'Error' ) , errors ? errors [ 0 ] : null ,
_ ( 'Error' ) , errors ? errors [ 1 ] : null ,
_ ( 'Error' ) , errors ? errors [ 2 ] : null ,
_ ( 'Error' ) , errors ? errors [ 3 ] : null ,
_ ( 'Error' ) , errors ? errors [ 4 ] : null ,
null , changecount ? E ( 'a' , {
href : '#' ,
click : L . bind ( ui . changes . displayChanges , ui . changes )
} , _ ( 'Interface has %d pending changes' ) . format ( changecount ) ) : null
] ) ;
}
function render _modal _status ( node , ifc ) {
var dev = ifc ? ( ifc . getDevice ( ) || ifc . getL3Device ( ) || ifc . getL3Device ( ) ) : null ;
2020-04-09 12:39:09 +00:00
dom . content ( node , [
2020-03-27 09:27:18 +00:00
E ( 'img' , {
'src' : L . resource ( 'icons/%s%s.png' ) . format ( dev ? dev . getType ( ) : 'ethernet' , ( dev && dev . isUp ( ) ) ? '' : '_disabled' ) ,
'title' : dev ? dev . getTypeI18n ( ) : _ ( 'Not present' )
} ) ,
ifc ? render _status ( E ( 'span' ) , ifc , true ) : E ( 'em' , _ ( 'Interface not present or not connected yet.' ) )
] ) ;
return node ;
}
function render _ifacebox _status ( node , ifc ) {
var dev = ifc . getL3Device ( ) || ifc . getDevice ( ) ,
2021-06-25 17:45:23 +00:00
subdevs = dev ? dev . getPorts ( ) : null ,
2020-03-27 09:27:18 +00:00
c = [ render _iface ( dev , ifc . isAlias ( ) ) ] ;
if ( subdevs && subdevs . length ) {
var sifs = [ ' (' ] ;
for ( var j = 0 ; j < subdevs . length ; j ++ )
sifs . push ( render _iface ( subdevs [ j ] ) ) ;
sifs . push ( ')' ) ;
c . push ( E ( 'span' , { } , sifs ) ) ;
}
c . push ( E ( 'br' ) ) ;
c . push ( E ( 'small' , { } , ifc . isAlias ( ) ? _ ( 'Alias of "%s"' ) . format ( ifc . isAlias ( ) )
: ( dev ? dev . getName ( ) : E ( 'em' , _ ( 'Not present' ) ) ) ) ) ;
2020-04-09 12:39:09 +00:00
dom . content ( node , c ) ;
2020-03-27 09:27:18 +00:00
return firewall . getZoneByNetwork ( ifc . getName ( ) ) . then ( L . bind ( function ( zone ) {
this . style . backgroundColor = zone ? zone . getColor ( ) : '#EEEEEE' ;
this . title = zone ? _ ( 'Part of zone %q' ) . format ( zone . getName ( ) ) : _ ( 'No zone assigned' ) ;
} , node . previousElementSibling ) ) ;
}
function iface _updown ( up , id , ev , force ) {
var row = document . querySelector ( '.cbi-section-table-row[data-sid="%s"]' . format ( id ) ) ,
dsc = row . querySelector ( '[data-name="_ifacestat"] > div' ) ,
btns = row . querySelectorAll ( '.cbi-section-actions .reconnect, .cbi-section-actions .down' ) ;
btns [ + ! up ] . blur ( ) ;
btns [ + ! up ] . classList . add ( 'spinning' ) ;
btns [ 0 ] . disabled = true ;
btns [ 1 ] . disabled = true ;
if ( ! up ) {
L . resolveDefault ( fs . exec _direct ( '/usr/libexec/luci-peeraddr' ) ) . then ( function ( res ) {
var info = null ; try { info = JSON . parse ( res ) ; } catch ( e ) { }
if ( L . isObject ( info ) &&
Array . isArray ( info . inbound _interfaces ) &&
info . inbound _interfaces . filter ( function ( i ) { return i == id } ) [ 0 ] ) {
ui . showModal ( _ ( 'Confirm disconnect' ) , [
E ( 'p' , _ ( 'You appear to be currently connected to the device via the "%h" interface. Do you really want to shut down the interface?' ) . format ( id ) ) ,
E ( 'div' , { 'class' : 'right' } , [
E ( 'button' , {
'class' : 'cbi-button cbi-button-neutral' ,
'click' : function ( ev ) {
btns [ 1 ] . classList . remove ( 'spinning' ) ;
btns [ 1 ] . disabled = false ;
btns [ 0 ] . disabled = false ;
ui . hideModal ( ) ;
}
} , _ ( 'Cancel' ) ) ,
' ' ,
E ( 'button' , {
'class' : 'cbi-button cbi-button-negative important' ,
'click' : function ( ev ) {
dsc . setAttribute ( 'disconnect' , '' ) ;
2020-04-09 12:39:09 +00:00
dom . content ( dsc , E ( 'em' , _ ( 'Interface is shutting down...' ) ) ) ;
2020-03-27 09:27:18 +00:00
ui . hideModal ( ) ;
}
} , _ ( 'Disconnect' ) )
] )
] ) ;
}
else {
dsc . setAttribute ( 'disconnect' , '' ) ;
2020-04-09 12:39:09 +00:00
dom . content ( dsc , E ( 'em' , _ ( 'Interface is shutting down...' ) ) ) ;
2020-03-27 09:27:18 +00:00
}
} ) ;
}
else {
dsc . setAttribute ( up ? 'reconnect' : 'disconnect' , force ? 'force' : '' ) ;
2020-04-09 12:39:09 +00:00
dom . content ( dsc , E ( 'em' , up ? _ ( 'Interface is reconnecting...' ) : _ ( 'Interface is shutting down...' ) ) ) ;
2020-03-27 09:27:18 +00:00
}
}
function get _netmask ( s , use _cfgvalue ) {
var readfn = use _cfgvalue ? 'cfgvalue' : 'formvalue' ,
2021-06-15 15:14:10 +00:00
addrs = L . toArray ( s [ readfn ] ( s . section , 'ipaddr' ) ) ,
mask = s [ readfn ] ( s . section , 'netmask' ) ,
firstsubnet = mask ? addrs [ 0 ] + '/' + mask : addrs . filter ( function ( a ) { return a . indexOf ( '/' ) > 0 } ) [ 0 ] ;
2020-03-27 09:27:18 +00:00
if ( firstsubnet == null )
return null ;
2021-06-15 15:14:10 +00:00
var subnetmask = firstsubnet . split ( '/' ) [ 1 ] ;
2020-03-27 09:27:18 +00:00
2021-06-15 15:14:10 +00:00
if ( ! isNaN ( subnetmask ) )
subnetmask = network . prefixToMask ( + subnetmask ) ;
2020-03-27 09:27:18 +00:00
2021-06-15 15:14:10 +00:00
return subnetmask ;
2020-03-27 09:27:18 +00:00
}
2023-11-27 15:37:15 +00:00
function has _peerdns ( proto ) {
switch ( proto ) {
case 'dhcp' :
case 'dhcpv6' :
case 'qmi' :
case 'ppp' :
case 'pppoe' :
case 'pppoa' :
case 'pptp' :
case 'openvpn' :
case 'sstp' :
return true ;
}
return false ;
}
2024-06-19 13:31:41 +00:00
function has _sourcefilter ( proto ) {
switch ( proto ) {
case '3g' :
case 'dhcpv6' :
case 'directip' :
case 'mbim' :
case 'modemmanager' :
case 'ncm' :
case 'ppp' :
case 'pppoa' :
case 'pppoe' :
case 'pptp' :
case 'qmi' :
return true ;
}
return false ;
}
2021-06-25 17:45:23 +00:00
var cbiRichListValue = form . ListValue . extend ( {
renderWidget : function ( section _id , option _index , cfgvalue ) {
var choices = this . transformChoices ( ) ;
var widget = new ui . Dropdown ( ( cfgvalue != null ) ? cfgvalue : this . default , choices , {
id : this . cbid ( section _id ) ,
sort : this . keylist ,
optional : true ,
select _placeholder : this . select _placeholder || this . placeholder ,
custom _placeholder : this . custom _placeholder || this . placeholder ,
validate : L . bind ( this . validate , this , section _id ) ,
disabled : ( this . readonly != null ) ? this . readonly : this . map . readonly
} ) ;
return widget . render ( ) ;
} ,
value : function ( value , title , description ) {
if ( description ) {
form . ListValue . prototype . value . call ( this , value , E ( [ ] , [
E ( 'span' , { 'class' : 'hide-open' } , [ title ] ) ,
E ( 'div' , { 'class' : 'hide-close' , 'style' : 'min-width:25vw' } , [
E ( 'strong' , [ title ] ) ,
E ( 'br' ) ,
E ( 'span' , { 'style' : 'white-space:normal' } , description )
] )
] ) ) ;
}
else {
form . ListValue . prototype . value . call ( this , value , title ) ;
}
}
} ) ;
2020-04-09 12:39:09 +00:00
return view . extend ( {
2020-03-27 09:27:18 +00:00
poll _status : function ( map , networks ) {
var resolveZone = null ;
for ( var i = 0 ; i < networks . length ; i ++ ) {
var ifc = networks [ i ] ,
row = map . querySelector ( '.cbi-section-table-row[data-sid="%s"]' . format ( ifc . getName ( ) ) ) ;
if ( row == null )
continue ;
var dsc = row . querySelector ( '[data-name="_ifacestat"] > div' ) ,
box = row . querySelector ( '[data-name="_ifacebox"] .ifacebox-body' ) ,
btn1 = row . querySelector ( '.cbi-section-actions .reconnect' ) ,
btn2 = row . querySelector ( '.cbi-section-actions .down' ) ,
stat = document . querySelector ( '[id="%s-ifc-status"]' . format ( ifc . getName ( ) ) ) ,
resolveZone = render _ifacebox _status ( box , ifc ) ,
disabled = ifc ? ! ifc . isUp ( ) : true ,
dynamic = ifc ? ifc . isDynamic ( ) : false ;
if ( dsc . hasAttribute ( 'reconnect' ) ) {
2020-04-09 12:39:09 +00:00
dom . content ( dsc , E ( 'em' , _ ( 'Interface is starting...' ) ) ) ;
2020-03-27 09:27:18 +00:00
}
else if ( dsc . hasAttribute ( 'disconnect' ) ) {
2020-04-09 12:39:09 +00:00
dom . content ( dsc , E ( 'em' , _ ( 'Interface is stopping...' ) ) ) ;
2020-03-27 09:27:18 +00:00
}
else if ( ifc . getProtocol ( ) || uci . get ( 'network' , ifc . getName ( ) ) == null ) {
render _status ( dsc , ifc , false ) ;
}
else if ( ! ifc . getProtocol ( ) ) {
var e = map . querySelector ( '[id="cbi-network-%s"] .cbi-button-edit' . format ( ifc . getName ( ) ) ) ;
if ( e ) e . disabled = true ;
var link = L . url ( 'admin/system/opkg' ) + '?query=luci-proto' ;
2020-04-09 12:39:09 +00:00
dom . content ( dsc , [
2020-03-27 09:27:18 +00:00
E ( 'em' , _ ( 'Unsupported protocol type.' ) ) , E ( 'br' ) ,
E ( 'a' , { href : link } , _ ( 'Install protocol extensions...' ) )
] ) ;
}
else {
2020-04-09 12:39:09 +00:00
dom . content ( dsc , E ( 'em' , _ ( 'Interface not present or not connected yet.' ) ) ) ;
2020-03-27 09:27:18 +00:00
}
if ( stat ) {
var dev = ifc . getDevice ( ) ;
2020-04-09 12:39:09 +00:00
dom . content ( stat , [
2020-03-27 09:27:18 +00:00
E ( 'img' , {
'src' : L . resource ( 'icons/%s%s.png' ) . format ( dev ? dev . getType ( ) : 'ethernet' , ( dev && dev . isUp ( ) ) ? '' : '_disabled' ) ,
'title' : dev ? dev . getTypeI18n ( ) : _ ( 'Not present' )
} ) ,
render _status ( E ( 'span' ) , ifc , true )
] ) ;
}
2020-05-20 08:46:25 +00:00
btn1 . disabled = isReadonlyView || btn1 . classList . contains ( 'spinning' ) || btn2 . classList . contains ( 'spinning' ) || dynamic ;
btn2 . disabled = isReadonlyView || btn1 . classList . contains ( 'spinning' ) || btn2 . classList . contains ( 'spinning' ) || dynamic || disabled ;
2020-03-27 09:27:18 +00:00
}
2021-06-25 17:45:23 +00:00
document . querySelectorAll ( '.port-status-device[data-device]' ) . forEach ( function ( node ) {
nettools . updateDevBadge ( node , network . instantiateDevice ( node . getAttribute ( 'data-device' ) ) ) ;
} ) ;
document . querySelectorAll ( '.port-status-link[data-device]' ) . forEach ( function ( node ) {
nettools . updatePortStatus ( node , network . instantiateDevice ( node . getAttribute ( 'data-device' ) ) ) ;
} ) ;
2020-03-27 09:27:18 +00:00
return Promise . all ( [ resolveZone , network . flushCache ( ) ] ) ;
} ,
load : function ( ) {
return Promise . all ( [
network . getDSLModemType ( ) ,
2021-06-25 17:45:23 +00:00
network . getDevices ( ) ,
fs . lines ( '/etc/iproute2/rt_tables' ) ,
L . resolveDefault ( fs . read ( '/usr/lib/opkg/info/netifd.control' ) ) ,
2020-03-27 09:27:18 +00:00
uci . changes ( )
] ) ;
} ,
2021-06-25 17:45:23 +00:00
interfaceBridgeWithIfnameSections : function ( ) {
return uci . sections ( 'network' , 'interface' ) . filter ( function ( ns ) {
return ns . type == 'bridge' && ! ns . ports && ns . ifname ;
} ) ;
} ,
deviceWithIfnameSections : function ( ) {
return uci . sections ( 'network' , 'device' ) . filter ( function ( ns ) {
return ns . type == 'bridge' && ! ns . ports && ns . ifname ;
} ) ;
} ,
interfaceWithIfnameSections : function ( ) {
return uci . sections ( 'network' , 'interface' ) . filter ( function ( ns ) {
return ! ns . device && ns . ifname ;
} ) ;
} ,
handleBridgeMigration : function ( ev ) {
var tasks = [ ] ;
this . interfaceBridgeWithIfnameSections ( ) . forEach ( function ( ns ) {
var device _name = 'br-' + ns [ '.name' ] ;
tasks . push ( uci . callAdd ( 'network' , 'device' , null , {
'name' : device _name ,
'type' : 'bridge' ,
'ports' : L . toArray ( ns . ifname ) ,
'mtu' : ns . mtu ,
'macaddr' : ns . macaddr ,
'igmp_snooping' : ns . igmp _snooping
} ) ) ;
tasks . push ( uci . callSet ( 'network' , ns [ '.name' ] , {
'type' : '' ,
'ifname' : '' ,
'mtu' : '' ,
'macaddr' : '' ,
'igmp_snooping' : '' ,
'device' : device _name
} ) ) ;
} ) ;
return Promise . all ( tasks )
. then ( L . bind ( ui . changes . init , ui . changes ) )
. then ( L . bind ( ui . changes . apply , ui . changes ) ) ;
} ,
renderBridgeMigration : function ( ) {
ui . showModal ( _ ( 'Network bridge configuration migration' ) , [
E ( 'p' , _ ( 'The existing network configuration needs to be changed for LuCI to function properly.' ) ) ,
E ( 'p' , _ ( 'Upon pressing "Continue", bridges configuration will be updated and the network will be restarted to apply the updated configuration.' ) ) ,
E ( 'div' , { 'class' : 'right' } ,
E ( 'button' , {
'class' : 'btn cbi-button-action important' ,
'click' : ui . createHandlerFn ( this , 'handleBridgeMigration' )
} , _ ( 'Continue' ) ) )
] ) ;
} ,
handleIfnameMigration : function ( ev ) {
var tasks = [ ] ;
this . deviceWithIfnameSections ( ) . forEach ( function ( ds ) {
tasks . push ( uci . callSet ( 'network' , ds [ '.name' ] , {
'ifname' : '' ,
'ports' : L . toArray ( ds . ifname )
} ) ) ;
} ) ;
this . interfaceWithIfnameSections ( ) . forEach ( function ( ns ) {
tasks . push ( uci . callSet ( 'network' , ns [ '.name' ] , {
'ifname' : '' ,
'device' : ns . ifname
} ) ) ;
} ) ;
return Promise . all ( tasks )
. then ( L . bind ( ui . changes . init , ui . changes ) )
. then ( L . bind ( ui . changes . apply , ui . changes ) ) ;
} ,
renderIfnameMigration : function ( ) {
ui . showModal ( _ ( 'Network ifname configuration migration' ) , [
E ( 'p' , _ ( 'The existing network configuration needs to be changed for LuCI to function properly.' ) ) ,
E ( 'p' , _ ( 'Upon pressing "Continue", ifname options will get renamed and the network will be restarted to apply the updated configuration.' ) ) ,
E ( 'div' , { 'class' : 'right' } ,
E ( 'button' , {
'class' : 'btn cbi-button-action important' ,
'click' : ui . createHandlerFn ( this , 'handleIfnameMigration' )
} , _ ( 'Continue' ) ) )
] ) ;
} ,
2020-03-27 09:27:18 +00:00
render : function ( data ) {
2021-06-25 17:45:23 +00:00
var netifdVersion = ( data [ 3 ] || '' ) . match ( /Version: ([^\n]+)/ ) ;
if ( netifdVersion && netifdVersion [ 1 ] >= "2021-05-26" ) {
if ( this . interfaceBridgeWithIfnameSections ( ) . length )
return this . renderBridgeMigration ( ) ;
else if ( this . deviceWithIfnameSections ( ) . length || this . interfaceWithIfnameSections ( ) . length )
return this . renderIfnameMigration ( ) ;
}
2020-03-27 09:27:18 +00:00
var dslModemType = data [ 0 ] ,
2021-06-25 17:45:23 +00:00
netDevs = data [ 1 ] ,
2020-03-27 09:27:18 +00:00
m , s , o ;
2021-06-25 17:45:23 +00:00
var rtTables = data [ 2 ] . map ( function ( l ) {
var m = l . trim ( ) . match ( /^(\d+)\s+(\S+)$/ ) ;
return m ? [ + m [ 1 ] , m [ 2 ] ] : null ;
} ) . filter ( function ( e ) {
return e && e [ 0 ] > 0 ;
} ) ;
2020-03-27 09:27:18 +00:00
m = new form . Map ( 'network' ) ;
m . tabbed = true ;
m . chain ( 'dhcp' ) ;
s = m . section ( form . GridSection , 'interface' , _ ( 'Interfaces' ) ) ;
s . anonymous = true ;
s . addremove = true ;
s . addbtntitle = _ ( 'Add new interface...' ) ;
s . load = function ( ) {
return Promise . all ( [
network . getNetworks ( ) ,
firewall . getZones ( )
] ) . then ( L . bind ( function ( data ) {
this . networks = data [ 0 ] ;
this . zones = data [ 1 ] ;
} , this ) ) ;
} ;
s . tab ( 'general' , _ ( 'General Settings' ) ) ;
s . tab ( 'advanced' , _ ( 'Advanced Settings' ) ) ;
s . tab ( 'physical' , _ ( 'Physical Settings' ) ) ;
2021-06-25 17:45:23 +00:00
s . tab ( 'brport' , _ ( 'Bridge port specific options' ) ) ;
s . tab ( 'bridgevlan' , _ ( 'Bridge VLAN filtering' ) ) ;
2020-03-27 09:27:18 +00:00
s . tab ( 'firewall' , _ ( 'Firewall Settings' ) ) ;
s . tab ( 'dhcp' , _ ( 'DHCP Server' ) ) ;
s . cfgsections = function ( ) {
return this . networks . map ( function ( n ) { return n . getName ( ) } )
. filter ( function ( n ) { return n != 'loopback' } ) ;
} ;
s . modaltitle = function ( section _id ) {
2023-11-27 15:37:15 +00:00
return _ ( 'Interfaces' ) + ' » ' + section _id ;
2020-03-27 09:27:18 +00:00
} ;
s . renderRowActions = function ( section _id ) {
var tdEl = this . super ( 'renderRowActions' , [ section _id , _ ( 'Edit' ) ] ) ,
net = this . networks . filter ( function ( n ) { return n . getName ( ) == section _id } ) [ 0 ] ,
disabled = net ? ! net . isUp ( ) : true ,
dynamic = net ? net . isDynamic ( ) : false ;
2020-04-09 12:39:09 +00:00
dom . content ( tdEl . lastChild , [
2020-03-27 09:27:18 +00:00
E ( 'button' , {
'class' : 'cbi-button cbi-button-neutral reconnect' ,
'click' : iface _updown . bind ( this , true , section _id ) ,
'title' : _ ( 'Reconnect this interface' ) ,
'disabled' : dynamic ? 'disabled' : null
} , _ ( 'Restart' ) ) ,
E ( 'button' , {
'class' : 'cbi-button cbi-button-neutral down' ,
'click' : iface _updown . bind ( this , false , section _id ) ,
'title' : _ ( 'Shutdown this interface' ) ,
'disabled' : ( dynamic || disabled ) ? 'disabled' : null
} , _ ( 'Stop' ) ) ,
tdEl . lastChild . firstChild ,
tdEl . lastChild . lastChild
] ) ;
if ( ! dynamic && net && ! uci . get ( 'network' , net . getName ( ) ) ) {
tdEl . lastChild . childNodes [ 0 ] . disabled = true ;
tdEl . lastChild . childNodes [ 2 ] . disabled = true ;
tdEl . lastChild . childNodes [ 3 ] . disabled = true ;
}
2024-06-19 13:31:41 +00:00
if ( dynamic ) {
//disable the 'Edit' button on dynamic interfaces
tdEl . lastChild . childNodes [ 2 ] . disabled = true ;
}
2020-03-27 09:27:18 +00:00
return tdEl ;
} ;
s . addModalOptions = function ( s ) {
var protoval = uci . get ( 'network' , s . section , 'proto' ) ,
protoclass = protoval ? network . getProtocol ( protoval ) : null ,
2021-06-25 17:45:23 +00:00
o , proto _select , proto _switch , type , stp , igmp , ss , so ;
2020-03-27 09:27:18 +00:00
if ( ! protoval )
return ;
return network . getNetwork ( s . section ) . then ( L . bind ( function ( ifc ) {
var protocols = network . getProtocols ( ) ;
protocols . sort ( function ( a , b ) {
2023-11-27 15:37:15 +00:00
return L . naturalCompare ( a . getProtocol ( ) , b . getProtocol ( ) ) ;
2020-03-27 09:27:18 +00:00
} ) ;
o = s . taboption ( 'general' , form . DummyValue , '_ifacestat_modal' , _ ( 'Status' ) ) ;
o . modalonly = true ;
o . cfgvalue = L . bind ( function ( section _id ) {
var net = this . networks . filter ( function ( n ) { return n . getName ( ) == section _id } ) [ 0 ] ;
return render _modal _status ( E ( 'div' , {
'id' : '%s-ifc-status' . format ( section _id ) ,
'class' : 'ifacebadge large'
} ) , net ) ;
} , this ) ;
o . write = function ( ) { } ;
2020-06-26 12:59:47 +00:00
2020-03-27 09:27:18 +00:00
proto _select = s . taboption ( 'general' , form . ListValue , 'proto' , _ ( 'Protocol' ) ) ;
proto _select . modalonly = true ;
proto _switch = s . taboption ( 'general' , form . Button , '_switch_proto' ) ;
proto _switch . modalonly = true ;
proto _switch . title = _ ( 'Really switch protocol?' ) ;
proto _switch . inputtitle = _ ( 'Switch protocol' ) ;
proto _switch . inputstyle = 'apply' ;
proto _switch . onclick = L . bind ( function ( ev ) {
s . map . save ( )
. then ( L . bind ( m . load , m ) )
. then ( L . bind ( m . render , m ) )
. then ( L . bind ( this . renderMoreOptionsModal , this , s . section ) ) ;
} , this ) ;
2021-06-25 17:45:23 +00:00
o = s . taboption ( 'general' , widgets . DeviceSelect , '_net_device' , _ ( 'Device' ) ) ;
o . ucioption = 'device' ;
o . nobridges = false ;
o . optional = false ;
o . network = ifc . getName ( ) ;
2024-06-19 13:31:41 +00:00
o . exclude = '@' + ifc . getName ( ) ;
o = s . taboption ( 'general' , form . Flag , 'disabled' , _ ( 'Disable this interface' ) ) ;
o . modalonly = true ;
2021-06-25 17:45:23 +00:00
2020-03-27 09:27:18 +00:00
o = s . taboption ( 'general' , form . Flag , 'auto' , _ ( 'Bring up on boot' ) ) ;
o . modalonly = true ;
o . default = o . enabled ;
if ( L . hasSystemFeature ( 'firewall' ) ) {
2020-04-09 12:39:09 +00:00
o = s . taboption ( 'firewall' , widgets . ZoneSelect , '_zone' , _ ( 'Create / Assign firewall-zone' ) , _ ( 'Choose the firewall zone you want to assign to this interface. Select <em>unspecified</em> to remove the interface from the associated zone or fill out the <em>custom</em> field to define a new zone and attach the interface to it.' ) ) ;
2020-03-27 09:27:18 +00:00
o . network = ifc . getName ( ) ;
o . optional = true ;
o . cfgvalue = function ( section _id ) {
return firewall . getZoneByNetwork ( ifc . getName ( ) ) . then ( function ( zone ) {
return ( zone != null ? zone . getName ( ) : null ) ;
} ) ;
} ;
o . write = o . remove = function ( section _id , value ) {
return Promise . all ( [
firewall . getZoneByNetwork ( ifc . getName ( ) ) ,
( value != null ) ? firewall . getZone ( value ) : null
] ) . then ( function ( data ) {
var old _zone = data [ 0 ] ,
new _zone = data [ 1 ] ;
if ( old _zone == null && new _zone == null && ( value == null || value == '' ) )
return ;
if ( old _zone != null && new _zone != null && old _zone . getName ( ) == new _zone . getName ( ) )
return ;
if ( old _zone != null )
old _zone . deleteNetwork ( ifc . getName ( ) ) ;
if ( new _zone != null )
new _zone . addNetwork ( ifc . getName ( ) ) ;
else if ( value != null )
return firewall . addZone ( value ) . then ( function ( new _zone ) {
new _zone . addNetwork ( ifc . getName ( ) ) ;
} ) ;
} ) ;
} ;
}
for ( var i = 0 ; i < protocols . length ; i ++ ) {
proto _select . value ( protocols [ i ] . getProtocol ( ) , protocols [ i ] . getI18n ( ) ) ;
if ( protocols [ i ] . getProtocol ( ) != uci . get ( 'network' , s . section , 'proto' ) )
proto _switch . depends ( 'proto' , protocols [ i ] . getProtocol ( ) ) ;
}
if ( L . hasSystemFeature ( 'dnsmasq' ) || L . hasSystemFeature ( 'odhcpd' ) ) {
o = s . taboption ( 'dhcp' , form . SectionValue , '_dhcp' , form . TypedSection , 'dhcp' ) ;
ss = o . subsection ;
ss . uciconfig = 'dhcp' ;
ss . addremove = false ;
ss . anonymous = true ;
ss . tab ( 'general' , _ ( 'General Setup' ) ) ;
ss . tab ( 'advanced' , _ ( 'Advanced Settings' ) ) ;
ss . tab ( 'ipv6' , _ ( 'IPv6 Settings' ) ) ;
2021-06-25 17:45:23 +00:00
ss . tab ( 'ipv6-ra' , _ ( 'IPv6 RA Settings' ) ) ;
2020-03-27 09:27:18 +00:00
ss . filter = function ( section _id ) {
return ( uci . get ( 'dhcp' , section _id , 'interface' ) == ifc . getName ( ) ) ;
} ;
ss . renderSectionPlaceholder = function ( ) {
return E ( 'div' , { 'class' : 'cbi-section-create' } , [
E ( 'p' , _ ( 'No DHCP Server configured for this interface' ) + '   ' ) ,
E ( 'button' , {
'class' : 'cbi-button cbi-button-add' ,
2023-11-27 15:37:15 +00:00
'title' : _ ( 'Set up DHCP Server' ) ,
2020-03-27 09:27:18 +00:00
'click' : ui . createHandlerFn ( this , function ( section _id , ev ) {
this . map . save ( function ( ) {
uci . add ( 'dhcp' , 'dhcp' , section _id ) ;
uci . set ( 'dhcp' , section _id , 'interface' , section _id ) ;
2021-06-25 17:45:23 +00:00
if ( protoval == 'static' ) {
uci . set ( 'dhcp' , section _id , 'start' , 100 ) ;
uci . set ( 'dhcp' , section _id , 'limit' , 150 ) ;
uci . set ( 'dhcp' , section _id , 'leasetime' , '12h' ) ;
}
else {
uci . set ( 'dhcp' , section _id , 'ignore' , 1 ) ;
}
2020-03-27 09:27:18 +00:00
} ) ;
} , ifc . getName ( ) )
2023-11-27 15:37:15 +00:00
} , _ ( 'Set up DHCP Server' ) )
2020-03-27 09:27:18 +00:00
] ) ;
} ;
ss . taboption ( 'general' , form . Flag , 'ignore' , _ ( 'Ignore interface' ) , _ ( 'Disable <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> for this interface.' ) ) ;
2021-06-25 17:45:23 +00:00
if ( protoval == 'static' ) {
so = ss . taboption ( 'general' , form . Value , 'start' , _ ( 'Start' ) , _ ( 'Lowest leased address as offset from the network address.' ) ) ;
so . optional = true ;
so . datatype = 'or(uinteger,ip4addr("nomask"))' ;
so . default = '100' ;
so = ss . taboption ( 'general' , form . Value , 'limit' , _ ( 'Limit' ) , _ ( 'Maximum number of leased addresses.' ) ) ;
so . optional = true ;
so . datatype = 'uinteger' ;
so . default = '150' ;
so = ss . taboption ( 'general' , form . Value , 'leasetime' , _ ( 'Lease time' ) , _ ( 'Expiry time of leased addresses, minimum is 2 minutes (<code>2m</code>).' ) ) ;
so . optional = true ;
so . default = '12h' ;
so = ss . taboption ( 'advanced' , form . Flag , 'dynamicdhcp' , _ ( 'Dynamic <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>' ) , _ ( 'Dynamically allocate DHCP addresses for clients. If disabled, only clients having static leases will be served.' ) ) ;
so . default = so . enabled ;
ss . taboption ( 'advanced' , form . Flag , 'force' , _ ( 'Force' ) , _ ( 'Force DHCP on this network even if another server is detected.' ) ) ;
// XXX: is this actually useful?
//ss.taboption('advanced', form.Value, 'name', _('Name'), _('Define a name for this network.'));
so = ss . taboption ( 'advanced' , form . Value , 'netmask' , _ ( '<abbr title="Internet Protocol Version 4">IPv4</abbr>-Netmask' ) , _ ( 'Override the netmask sent to clients. Normally it is calculated from the subnet that is served.' ) ) ;
so . optional = true ;
so . datatype = 'ip4addr' ;
so . render = function ( option _index , section _id , in _table ) {
this . placeholder = get _netmask ( s , true ) ;
return form . Value . prototype . render . apply ( this , [ option _index , section _id , in _table ] ) ;
} ;
so . validate = function ( section _id , value ) {
var uielem = this . getUIElement ( section _id ) ;
if ( uielem )
uielem . setPlaceholder ( get _netmask ( s , false ) ) ;
return form . Value . prototype . validate . apply ( this , [ section _id , value ] ) ;
} ;
ss . taboption ( 'advanced' , form . DynamicList , 'dhcp_option' , _ ( 'DHCP-Options' ) , _ ( 'Define additional DHCP options, for example "<code>6,192.168.2.1,192.168.2.2</code>" which advertises different DNS servers to clients.' ) ) ;
}
var has _other _master = uci . sections ( 'dhcp' , 'dhcp' ) . filter ( function ( s ) {
return ( s . interface != ifc . getName ( ) && s . master == '1' ) ;
} ) [ 0 ] ;
so = ss . taboption ( 'ipv6' , form . Flag , 'master' , _ ( 'Designated master' ) ) ;
so . readonly = has _other _master ? true : false ;
so . description = has _other _master
? _ ( 'Interface "%h" is already marked as designated master.' ) . format ( has _other _master . interface || has _other _master [ '.name' ] )
: _ ( 'Set this interface as master for RA and DHCPv6 relaying as well as NDP proxying.' )
;
so . validate = function ( section _id , value ) {
var hybrid _downstream _desc = _ ( 'Operate in <em>relay mode</em> if a designated master interface is configured and active, otherwise fall back to <em>server mode</em>.' ) ,
ndp _downstream _desc = _ ( 'Operate in <em>relay mode</em> if a designated master interface is configured and active, otherwise disable <abbr title="Neighbour Discovery Protocol">NDP</abbr> proxying.' ) ,
hybrid _master _desc = _ ( 'Operate in <em>relay mode</em> if an upstream IPv6 prefix is present, otherwise disable service.' ) ,
2023-11-27 15:37:15 +00:00
ra _server _allowed = true ,
2021-06-25 17:45:23 +00:00
checked = this . formvalue ( section _id ) ,
dhcpv6 = this . section . getOption ( 'dhcpv6' ) . getUIElement ( section _id ) ,
ndp = this . section . getOption ( 'ndp' ) . getUIElement ( section _id ) ,
ra = this . section . getOption ( 'ra' ) . getUIElement ( section _id ) ;
2023-11-27 15:37:15 +00:00
/ * A s s u m e t h a t s e r v i n g R A s b y d e f a u l t i s f i n e , b u t d i s a l l o w i t f o r c e r t a i n
interface protocols such as DHCP , DHCPv6 or the various PPP flavors .
The intent is to only allow RA serving for interface protocols doing
some kind of static IP config over something resembling a layer 2
ethernet device . * /
switch ( protoval ) {
case 'dhcp' :
case 'dhcpv6' :
case '3g' :
case 'l2tp' :
case 'ppp' :
case 'pppoa' :
case 'pppoe' :
case 'pptp' :
case 'pppossh' :
case 'ipip' :
case 'gre' :
case 'grev6' :
ra _server _allowed = false ;
break ;
}
if ( checked == '1' || ! ra _server _allowed ) {
2021-06-25 17:45:23 +00:00
dhcpv6 . node . querySelector ( 'li[data-value="server"]' ) . setAttribute ( 'unselectable' , '' ) ;
if ( dhcpv6 . getValue ( ) == 'server' )
dhcpv6 . setValue ( 'hybrid' ) ;
ra . node . querySelector ( 'li[data-value="server"]' ) . setAttribute ( 'unselectable' , '' ) ;
if ( ra . getValue ( ) == 'server' )
ra . setValue ( 'hybrid' ) ;
}
if ( checked == '1' ) {
dhcpv6 . node . querySelector ( 'li[data-value="hybrid"] > div > span' ) . innerHTML = hybrid _master _desc ;
ra . node . querySelector ( 'li[data-value="hybrid"] > div > span' ) . innerHTML = hybrid _master _desc ;
ndp . node . querySelector ( 'li[data-value="hybrid"] > div > span' ) . innerHTML = hybrid _master _desc ;
}
else {
2023-11-27 15:37:15 +00:00
if ( ra _server _allowed ) {
2021-06-25 17:45:23 +00:00
dhcpv6 . node . querySelector ( 'li[data-value="server"]' ) . removeAttribute ( 'unselectable' ) ;
ra . node . querySelector ( 'li[data-value="server"]' ) . removeAttribute ( 'unselectable' ) ;
}
dhcpv6 . node . querySelector ( 'li[data-value="hybrid"] > div > span' ) . innerHTML = hybrid _downstream _desc ;
ra . node . querySelector ( 'li[data-value="hybrid"] > div > span' ) . innerHTML = hybrid _downstream _desc ;
ndp . node . querySelector ( 'li[data-value="hybrid"] > div > span' ) . innerHTML = ndp _downstream _desc ;
}
return true ;
} ;
so = ss . taboption ( 'ipv6' , cbiRichListValue , 'ra' , _ ( '<abbr title="Router Advertisement">RA</abbr>-Service' ) ,
_ ( 'Configures the operation mode of the <abbr title="Router Advertisement">RA</abbr> service on this interface.' ) ) ;
so . value ( '' , _ ( 'disabled' ) ,
_ ( 'Do not send any <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr> messages on this interface.' ) ) ;
so . value ( 'server' , _ ( 'server mode' ) ,
_ ( 'Send <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr> messages advertising this device as IPv6 router.' ) ) ;
so . value ( 'relay' , _ ( 'relay mode' ) ,
_ ( 'Forward <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr> messages received on the designated master interface to downstream interfaces.' ) ) ;
so . value ( 'hybrid' , _ ( 'hybrid mode' ) , ' ' ) ;
so = ss . taboption ( 'ipv6-ra' , cbiRichListValue , 'ra_default' , _ ( 'Default router' ) ,
_ ( 'Configures the default router advertisement in <abbr title="Router Advertisement">RA</abbr> messages.' ) ) ;
so . value ( '' , _ ( 'automatic' ) ,
_ ( 'Announce this device as default router if a local IPv6 default route is present.' ) ) ;
so . value ( '1' , _ ( 'on available prefix' ) ,
_ ( 'Announce this device as default router if a public IPv6 prefix is available, regardless of local default route availability.' ) ) ;
so . value ( '2' , _ ( 'forced' ) ,
_ ( 'Announce this device as default router regardless of whether a prefix or default route is present.' ) ) ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
so = ss . taboption ( 'ipv6-ra' , form . Flag , 'ra_slaac' , _ ( 'Enable <abbr title="Stateless Address Auto Config">SLAAC</abbr>' ) ,
_ ( 'Set the autonomous address-configuration flag in the prefix information options of sent <abbr title="Router Advertisement">RA</abbr> messages. When enabled, clients will perform stateless IPv6 address autoconfiguration.' ) ) ;
so . default = so . enabled ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
so = ss . taboption ( 'ipv6-ra' , cbiRichListValue , 'ra_flags' , _ ( '<abbr title="Router Advertisement">RA</abbr> Flags' ) ,
_ ( 'Specifies the flags sent in <abbr title="Router Advertisement">RA</abbr> messages, for example to instruct clients to request further information via stateful DHCPv6.' ) ) ;
so . value ( 'managed-config' , _ ( 'managed config (M)' ) ,
_ ( 'The <em>Managed address configuration</em> (M) flag indicates that IPv6 addresses are available via DHCPv6.' ) ) ;
so . value ( 'other-config' , _ ( 'other config (O)' ) ,
_ ( 'The <em>Other configuration</em> (O) flag indicates that other information, such as DNS servers, is available via DHCPv6.' ) ) ;
so . value ( 'home-agent' , _ ( 'mobile home agent (H)' ) ,
_ ( 'The <em>Mobile IPv6 Home Agent</em> (H) flag indicates that the device is also acting as Mobile IPv6 home agent on this link.' ) ) ;
so . multiple = true ;
so . select _placeholder = _ ( 'none' ) ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
so . cfgvalue = function ( section _id ) {
var flags = L . toArray ( uci . get ( 'dhcp' , section _id , 'ra_flags' ) ) ;
return flags . length ? flags : [ 'other-config' ] ;
} ;
so . remove = function ( section _id ) {
2023-11-27 15:37:15 +00:00
var existing = L . toArray ( uci . get ( 'dhcp' , section _id , 'ra_flags' ) ) ;
if ( this . isActive ( section _id ) ) {
if ( existing . length != 1 || existing [ 0 ] != 'none' )
uci . set ( 'dhcp' , section _id , 'ra_flags' , [ 'none' ] ) ;
}
else if ( existing . length ) {
uci . unset ( 'dhcp' , section _id , 'ra_flags' ) ;
}
2021-06-25 17:45:23 +00:00
} ;
2023-11-27 15:37:15 +00:00
so = ss . taboption ( 'ipv6-ra' , form . Value , 'ra_pref64' , _ ( 'NAT64 prefix' ) , _ ( 'Announce NAT64 prefix in <abbr title="Router Advertisement">RA</abbr> messages.' ) ) ;
so . optional = true ;
so . datatype = 'cidr6' ;
so . placeholder = '64:ff9b::/96' ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6-ra' , form . Value , 'ra_maxinterval' , _ ( 'Max <abbr title="Router Advertisement">RA</abbr> interval' ) , _ ( 'Maximum time allowed between sending unsolicited <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr>. Default is 600 seconds.' ) ) ;
2020-03-27 09:27:18 +00:00
so . optional = true ;
2021-06-25 17:45:23 +00:00
so . datatype = 'uinteger' ;
so . placeholder = '600' ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6-ra' , form . Value , 'ra_mininterval' , _ ( 'Min <abbr title="Router Advertisement">RA</abbr> interval' ) , _ ( 'Minimum time allowed between sending unsolicited <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr>. Default is 200 seconds.' ) ) ;
2020-03-27 09:27:18 +00:00
so . optional = true ;
so . datatype = 'uinteger' ;
2021-06-25 17:45:23 +00:00
so . placeholder = '200' ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6-ra' , form . Value , 'ra_lifetime' , _ ( '<abbr title="Router Advertisement">RA</abbr> Lifetime' ) , _ ( 'Router Lifetime published in <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr> messages. Maximum is 9000 seconds.' ) ) ;
2020-03-27 09:27:18 +00:00
so . optional = true ;
2021-06-25 17:45:23 +00:00
so . datatype = 'range(0, 9000)' ;
so . placeholder = '1800' ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6-ra' , form . Value , 'ra_mtu' , _ ( '<abbr title="Router Advertisement">RA</abbr> MTU' ) , _ ( 'The <abbr title="Maximum Transmission Unit">MTU</abbr> to be published in <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr> messages. Minimum is 1280 bytes.' ) ) ;
so . optional = true ;
so . datatype = 'range(1280, 65535)' ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
so . load = function ( section _id ) {
2023-11-27 15:37:15 +00:00
var dev = ifc . getL3Device ( ) ,
path = dev ? "/proc/sys/net/ipv6/conf/%s/mtu" . format ( dev . getName ( ) ) : null ;
2020-03-27 09:27:18 +00:00
2023-11-27 15:37:15 +00:00
return Promise . all ( [
dev ? L . resolveDefault ( fs . read ( path ) , dev . getMTU ( ) ) : null ,
this . super ( 'load' , [ section _id ] )
] ) . then ( L . bind ( function ( res ) {
this . placeholder = + res [ 0 ] ;
2020-03-27 09:27:18 +00:00
2023-11-27 15:37:15 +00:00
return res [ 1 ] ;
} , this ) ) ;
2021-06-25 17:45:23 +00:00
} ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6-ra' , form . Value , 'ra_hoplimit' , _ ( '<abbr title="Router Advertisement">RA</abbr> Hop Limit' ) , _ ( 'The maximum hops to be published in <abbr title="Router Advertisement">RA</abbr> messages. Maximum is 255 hops.' ) ) ;
2020-03-27 09:27:18 +00:00
so . optional = true ;
2021-06-25 17:45:23 +00:00
so . datatype = 'range(0, 255)' ;
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
so . load = function ( section _id ) {
2023-11-27 15:37:15 +00:00
var dev = ifc . getL3Device ( ) ,
path = dev ? "/proc/sys/net/ipv6/conf/%s/hop_limit" . format ( dev . getName ( ) ) : null ;
2020-03-27 09:27:18 +00:00
2023-11-27 15:37:15 +00:00
return Promise . all ( [
dev ? L . resolveDefault ( fs . read ( path ) , 64 ) : null ,
this . super ( 'load' , [ section _id ] )
] ) . then ( L . bind ( function ( res ) {
this . placeholder = + res [ 0 ] ;
2020-03-27 09:27:18 +00:00
2023-11-27 15:37:15 +00:00
return res [ 1 ] ;
} , this ) ) ;
2020-03-27 09:27:18 +00:00
} ;
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6' , cbiRichListValue , 'dhcpv6' , _ ( 'DHCPv6-Service' ) ,
_ ( 'Configures the operation mode of the DHCPv6 service on this interface.' ) ) ;
so . value ( '' , _ ( 'disabled' ) ,
_ ( 'Do not offer DHCPv6 service on this interface.' ) ) ;
so . value ( 'server' , _ ( 'server mode' ) ,
_ ( 'Provide a DHCPv6 server on this interface and reply to DHCPv6 solicitations and requests.' ) ) ;
so . value ( 'relay' , _ ( 'relay mode' ) ,
_ ( 'Forward DHCPv6 messages between the designated master interface and downstream interfaces.' ) ) ;
so . value ( 'hybrid' , _ ( 'hybrid mode' ) , ' ' ) ;
2023-11-27 15:37:15 +00:00
so = ss . taboption ( 'ipv6' , form . Value , 'dhcpv6_pd_min_len' , _ ( '<abbr title="Prefix Delegation">PD</abbr> minimum length' ) ,
_ ( 'Configures the minimum delegated prefix length assigned to a requesting downstream router, potentially overriding a requested prefix length. If left unspecified, the device will assign the smallest available prefix greater than or equal to the requested prefix.' ) ) ;
so . datatype = 'range(1,62)' ;
so . depends ( 'dhcpv6' , 'server' ) ;
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6' , form . DynamicList , 'dns' , _ ( 'Announced IPv6 DNS servers' ) ,
_ ( 'Specifies a fixed list of IPv6 DNS server addresses to announce via DHCPv6. If left unspecified, the device will announce itself as IPv6 DNS server unless the <em>Local IPv6 DNS server</em> option is disabled.' ) ) ;
so . datatype = 'ip6addr("nomask")' ; /* restrict to IPv6 only for now since dnsmasq (DHCPv4) does not honour this option */
2023-11-27 15:37:15 +00:00
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
2020-03-27 09:27:18 +00:00
so . depends ( 'dhcpv6' , 'server' ) ;
2021-06-25 17:45:23 +00:00
so . depends ( { dhcpv6 : 'hybrid' , master : '0' } ) ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6' , form . Flag , 'dns_service' , _ ( 'Local IPv6 DNS server' ) ,
_ ( 'Announce this device as IPv6 DNS server.' ) ) ;
so . default = so . enabled ;
2023-11-27 15:37:15 +00:00
so . depends ( { ra : 'server' , dns : /^$/ } ) ;
so . depends ( { ra : 'hybrid' , dns : /^$/ , master : '0' } ) ;
2021-06-25 17:45:23 +00:00
so . depends ( { dhcpv6 : 'server' , dns : /^$/ } ) ;
so . depends ( { dhcpv6 : 'hybrid' , dns : /^$/ , master : '0' } ) ;
so = ss . taboption ( 'ipv6' , form . DynamicList , 'domain' , _ ( 'Announced DNS domains' ) ,
_ ( 'Specifies a fixed list of DNS search domains to announce via DHCPv6. If left unspecified, the local device DNS search domain will be announced.' ) ) ;
so . datatype = 'hostname' ;
2023-11-27 15:37:15 +00:00
so . depends ( 'ra' , 'server' ) ;
so . depends ( { ra : 'hybrid' , master : '0' } ) ;
2021-06-25 17:45:23 +00:00
so . depends ( 'dhcpv6' , 'server' ) ;
so . depends ( { dhcpv6 : 'hybrid' , master : '0' } ) ;
so = ss . taboption ( 'ipv6' , cbiRichListValue , 'ndp' , _ ( '<abbr title="Neighbour Discovery Protocol">NDP</abbr>-Proxy' ) ,
_ ( 'Configures the operation mode of the NDP proxy service on this interface.' ) ) ;
so . value ( '' , _ ( 'disabled' ) ,
_ ( 'Do not proxy any <abbr title="Neighbour Discovery Protocol">NDP</abbr> packets.' ) ) ;
so . value ( 'relay' , _ ( 'relay mode' ) ,
_ ( 'Forward <abbr title="Neighbour Discovery Protocol">NDP</abbr> <abbr title="Neighbour Solicitation, Type 135">NS</abbr> and <abbr title="Neighbour Advertisement, Type 136">NA</abbr> messages between the designated master interface and downstream interfaces.' ) ) ;
so . value ( 'hybrid' , _ ( 'hybrid mode' ) , ' ' ) ;
2023-11-27 15:37:15 +00:00
so = ss . taboption ( 'ipv6' , form . Flag , 'ndproxy_routing' , _ ( 'Learn routes' ) , _ ( 'Set up routes for proxied IPv6 neighbours.' ) ) ;
2021-06-25 17:45:23 +00:00
so . default = so . enabled ;
so . depends ( 'ndp' , 'relay' ) ;
so . depends ( 'ndp' , 'hybrid' ) ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
so = ss . taboption ( 'ipv6' , form . Flag , 'ndproxy_slave' , _ ( 'NDP-Proxy slave' ) , _ ( 'Set interface as NDP-Proxy external slave. Default is off.' ) ) ;
so . depends ( { ndp : 'relay' , master : '0' } ) ;
so . depends ( { ndp : 'hybrid' , master : '0' } ) ;
2023-11-27 15:37:15 +00:00
so = ss . taboption ( 'ipv6' , form . Value , 'preferred_lifetime' , _ ( 'IPv6 Prefix Lifetime' ) , _ ( 'Preferred lifetime for a prefix.' ) ) ;
so . optional = true ;
so . placeholder = '12h' ;
so . value ( '5m' , _ ( '5m (5 minutes)' ) ) ;
so . value ( '3h' , _ ( '3h (3 hours)' ) ) ;
so . value ( '12h' , _ ( '12h (12 hours - default)' ) ) ;
so . value ( '7d' , _ ( '7d (7 days)' ) ) ;
//This is a ra_* setting, but its placement is more logical/findable under IPv6 settings.
so = ss . taboption ( 'ipv6' , form . Flag , 'ra_useleasetime' , _ ( 'Follow IPv4 Lifetime' ) , _ ( 'DHCPv4 <code>leasetime</code> is used as limit and preferred lifetime of the IPv6 prefix.' ) ) ;
so . optional = true ;
2020-03-27 09:27:18 +00:00
}
ifc . renderFormOptions ( s ) ;
2021-06-25 17:45:23 +00:00
// Common interface options
o = nettools . replaceOption ( s , 'advanced' , form . Flag , 'defaultroute' , _ ( 'Use default gateway' ) , _ ( 'If unchecked, no default route is configured' ) ) ;
o . default = o . enabled ;
2023-11-27 15:37:15 +00:00
if ( has _peerdns ( protoval ) ) {
2021-06-25 17:45:23 +00:00
o = nettools . replaceOption ( s , 'advanced' , form . Flag , 'peerdns' , _ ( 'Use DNS servers advertised by peer' ) , _ ( 'If unchecked, the advertised DNS server addresses are ignored' ) ) ;
o . default = o . enabled ;
}
o = nettools . replaceOption ( s , 'advanced' , form . DynamicList , 'dns' , _ ( 'Use custom DNS servers' ) ) ;
2023-11-27 15:37:15 +00:00
if ( has _peerdns ( protoval ) )
2021-06-25 17:45:23 +00:00
o . depends ( 'peerdns' , '0' ) ;
o . datatype = 'ipaddr' ;
o = nettools . replaceOption ( s , 'advanced' , form . DynamicList , 'dns_search' , _ ( 'DNS search domains' ) ) ;
if ( protoval != 'static' )
o . depends ( 'peerdns' , '0' ) ;
o . datatype = 'hostname' ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'dns_metric' , _ ( 'DNS weight' ) , _ ( 'The DNS server entries in the local resolv.conf are primarily sorted by the weight specified here' ) ) ;
o . datatype = 'uinteger' ;
o . placeholder = '0' ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'metric' , _ ( 'Use gateway metric' ) ) ;
o . datatype = 'uinteger' ;
o . placeholder = '0' ;
2021-06-26 06:26:01 +00:00
o = nettools . replaceOption ( s , 'advanced' , form . ListValue , 'multipath' , _ ( 'Multipath setting' ) , _ ( 'Only one interface must be set as Master.' ) ) ;
2021-06-25 17:45:23 +00:00
o . value ( 'on' , _ ( 'Enabled' ) ) ;
o . value ( 'off' , _ ( 'Disabled' ) ) ;
o . value ( 'master' , _ ( 'Master' ) ) ;
o . value ( 'backup' , _ ( 'Backup' ) ) ;
o . default = 'off' ;
2021-06-26 06:26:01 +00:00
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'addlatency' , _ ( 'Additional latency' ) ) ;
2021-06-25 17:45:23 +00:00
o . datatype = 'uinteger' ;
o . default = '0' ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'ip4table' , _ ( 'Override IPv4 routing table' ) ) ;
o . datatype = 'or(uinteger, string)' ;
for ( var i = 0 ; i < rtTables . length ; i ++ )
o . value ( rtTables [ i ] [ 1 ] , '%s (%d)' . format ( rtTables [ i ] [ 1 ] , rtTables [ i ] [ 0 ] ) ) ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'ip6table' , _ ( 'Override IPv6 routing table' ) ) ;
o . datatype = 'or(uinteger, string)' ;
for ( var i = 0 ; i < rtTables . length ; i ++ )
2023-11-27 15:37:15 +00:00
o . value ( rtTables [ i ] [ 1 ] , '%s (%d)' . format ( rtTables [ i ] [ 1 ] , rtTables [ i ] [ 0 ] ) ) ;
2024-06-19 13:31:41 +00:00
if ( has _sourcefilter ( protoval ) ) {
2023-11-27 15:37:15 +00:00
o = nettools . replaceOption ( s , 'advanced' , form . Flag , 'sourcefilter' , _ ( 'IPv6 source routing' ) , _ ( 'Automatically handle multiple uplink interfaces using source-based policy routing.' ) ) ;
o . default = o . enabled ;
}
2021-06-25 17:45:23 +00:00
o = nettools . replaceOption ( s , 'advanced' , form . Flag , 'delegate' , _ ( 'Delegate IPv6 prefixes' ) , _ ( 'Enable downstream delegation of IPv6 prefixes available on this interface' ) ) ;
o . default = o . enabled ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'ip6assign' , _ ( 'IPv6 assignment length' ) , _ ( 'Assign a part of given length of every public IPv6-prefix to this interface' ) ) ;
o . value ( '' , _ ( 'disabled' ) ) ;
o . value ( '64' ) ;
o . datatype = 'max(128)' ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'ip6hint' , _ ( 'IPv6 assignment hint' ) , _ ( 'Assign prefix parts using this hexadecimal subprefix ID for this interface.' ) ) ;
o . placeholder = '0' ;
o . validate = function ( section _id , value ) {
if ( value == null || value == '' )
return true ;
var n = parseInt ( value , 16 ) ;
if ( ! /^(0x)?[0-9a-fA-F]+$/ . test ( value ) || isNaN ( n ) || n >= 0xffffffff )
return _ ( 'Expecting a hexadecimal assignment hint' ) ;
return true ;
} ;
for ( var i = 33 ; i <= 64 ; i ++ )
o . depends ( 'ip6assign' , String ( i ) ) ;
o = nettools . replaceOption ( s , 'advanced' , form . DynamicList , 'ip6class' , _ ( 'IPv6 prefix filter' ) , _ ( 'If set, downstream subnets are only allocated from the given IPv6 prefix classes.' ) ) ;
o . value ( 'local' , 'local (%s)' . format ( _ ( 'Local ULA' ) ) ) ;
var prefixClasses = { } ;
this . networks . forEach ( function ( net ) {
var prefixes = net . _ubus ( 'ipv6-prefix' ) ;
if ( Array . isArray ( prefixes ) ) {
prefixes . forEach ( function ( pfx ) {
if ( L . isObject ( pfx ) && typeof ( pfx [ 'class' ] ) == 'string' ) {
prefixClasses [ pfx [ 'class' ] ] = prefixClasses [ pfx [ 'class' ] ] || { } ;
prefixClasses [ pfx [ 'class' ] ] [ net . getName ( ) ] = true ;
}
} ) ;
}
} ) ;
Object . keys ( prefixClasses ) . sort ( ) . forEach ( function ( c ) {
var networks = Object . keys ( prefixClasses [ c ] ) . sort ( ) . join ( ', ' ) ;
o . value ( c , ( c != networks ) ? '%s (%s)' . format ( c , networks ) : c ) ;
} ) ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'ip6ifaceid' , _ ( 'IPv6 suffix' ) , _ ( "Optional. Allowed values: 'eui64', 'random', fixed value like '::1' or '::1:2'. When IPv6 prefix (like 'a:b:c:d::') is received from a delegating server, use the suffix (like '::1') to form the IPv6 address ('a:b:c:d::1') for the interface." ) ) ;
o . datatype = 'ip6hostid' ;
o . placeholder = '::1' ;
o = nettools . replaceOption ( s , 'advanced' , form . Value , 'ip6weight' , _ ( 'IPv6 preference' ) , _ ( 'When delegating prefixes to multiple downstreams, interfaces with a higher preference value are considered first when allocating subnets.' ) ) ;
o . datatype = 'uinteger' ;
o . placeholder = '0' ;
2020-03-27 09:27:18 +00:00
for ( var i = 0 ; i < s . children . length ; i ++ ) {
o = s . children [ i ] ;
switch ( o . option ) {
case 'proto' :
case 'auto' :
case '_dhcp' :
case '_zone' :
case '_switch_proto' :
case '_ifacestat_modal' :
continue ;
2021-06-25 17:45:23 +00:00
case 'igmp_snooping' :
case 'stp' :
case 'type' :
case '_net_device' :
var deps = [ ] ;
for ( var j = 0 ; j < protocols . length ; j ++ ) {
if ( ! protocols [ j ] . isVirtual ( ) ) {
if ( o . deps . length )
for ( var k = 0 ; k < o . deps . length ; k ++ )
deps . push ( Object . assign ( { proto : protocols [ j ] . getProtocol ( ) } , o . deps [ k ] ) ) ;
else
deps . push ( { proto : protocols [ j ] . getProtocol ( ) } ) ;
}
}
o . deps = deps ;
break ;
2020-03-27 09:27:18 +00:00
default :
if ( o . deps . length )
for ( var j = 0 ; j < o . deps . length ; j ++ )
o . deps [ j ] . proto = protoval ;
else
o . depends ( 'proto' , protoval ) ;
}
}
2021-06-25 17:45:23 +00:00
this . activeSection = s . section ;
2020-03-27 09:27:18 +00:00
} , this ) ) ;
} ;
2021-06-25 17:45:23 +00:00
s . handleModalCancel = function ( /* ... */ ) {
var type = uci . get ( 'network' , this . activeSection || this . addedSection , 'type' ) ,
device = ( type == 'bridge' ) ? 'br-%s' . format ( this . activeSection || this . addedSection ) : null ;
uci . sections ( 'network' , 'bridge-vlan' , function ( bvs ) {
if ( device != null && bvs . device == device )
uci . remove ( 'network' , bvs [ '.name' ] ) ;
} ) ;
return form . GridSection . prototype . handleModalCancel . apply ( this , arguments ) ;
} ;
2020-03-27 09:27:18 +00:00
s . handleAdd = function ( ev ) {
var m2 = new form . Map ( 'network' ) ,
s2 = m2 . section ( form . NamedSection , '_new_' ) ,
protocols = network . getProtocols ( ) ,
2021-06-25 17:45:23 +00:00
proto , name , device ;
2020-03-27 09:27:18 +00:00
protocols . sort ( function ( a , b ) {
2023-11-27 15:37:15 +00:00
return L . naturalCompare ( a . getProtocol ( ) , b . getProtocol ( ) ) ;
2020-03-27 09:27:18 +00:00
} ) ;
s2 . render = function ( ) {
return Promise . all ( [
{ } ,
this . renderUCISection ( '_new_' )
] ) . then ( this . renderContents . bind ( this ) ) ;
} ;
name = s2 . option ( form . Value , 'name' , _ ( 'Name' ) ) ;
name . rmempty = false ;
name . datatype = 'uciname' ;
name . placeholder = _ ( 'New interface name…' ) ;
name . validate = function ( section _id , value ) {
if ( uci . get ( 'network' , value ) != null )
return _ ( 'The interface name is already used' ) ;
var pr = network . getProtocol ( proto . formvalue ( section _id ) , value ) ,
ifname = pr . isVirtual ( ) ? '%s-%s' . format ( pr . getProtocol ( ) , value ) : 'br-%s' . format ( value ) ;
if ( value . length > 15 )
return _ ( 'The interface name is too long' ) ;
return true ;
} ;
proto = s2 . option ( form . ListValue , 'proto' , _ ( 'Protocol' ) ) ;
proto . validate = name . validate ;
2021-06-25 17:45:23 +00:00
device = s2 . option ( widgets . DeviceSelect , 'device' , _ ( 'Device' ) ) ;
device . noaliases = false ;
device . optional = false ;
2020-03-27 09:27:18 +00:00
for ( var i = 0 ; i < protocols . length ; i ++ ) {
proto . value ( protocols [ i ] . getProtocol ( ) , protocols [ i ] . getI18n ( ) ) ;
2021-06-25 17:45:23 +00:00
if ( ! protocols [ i ] . isVirtual ( ) )
device . depends ( 'proto' , protocols [ i ] . getProtocol ( ) ) ;
2020-03-27 09:27:18 +00:00
}
m2 . render ( ) . then ( L . bind ( function ( nodes ) {
ui . showModal ( _ ( 'Add new interface...' ) , [
nodes ,
E ( 'div' , { 'class' : 'right' } , [
E ( 'button' , {
'class' : 'btn' ,
'click' : ui . hideModal
} , _ ( 'Cancel' ) ) , ' ' ,
E ( 'button' , {
'class' : 'cbi-button cbi-button-positive important' ,
'click' : ui . createHandlerFn ( this , function ( ev ) {
var nameval = name . isValid ( '_new_' ) ? name . formvalue ( '_new_' ) : null ,
protoval = proto . isValid ( '_new_' ) ? proto . formvalue ( '_new_' ) : null ,
2021-06-25 17:45:23 +00:00
protoclass = protoval ? network . getProtocol ( protoval , nameval ) : null ;
2020-03-27 09:27:18 +00:00
if ( nameval == null || protoval == null || nameval == '' || protoval == '' )
return ;
return protoclass . isCreateable ( nameval ) . then ( function ( checkval ) {
if ( checkval != null ) {
ui . addNotification ( null ,
E ( 'p' , _ ( 'New interface for "%s" can not be created: %s' ) . format ( protoclass . getI18n ( ) , checkval ) ) ) ;
ui . hideModal ( ) ;
return ;
}
return m . save ( function ( ) {
var section _id = uci . add ( 'network' , 'interface' , nameval ) ;
2021-06-25 17:45:23 +00:00
protoclass . set ( 'proto' , protoval ) ;
protoclass . addDevice ( device . formvalue ( '_new_' ) ) ;
2020-03-27 09:27:18 +00:00
2021-06-25 17:45:23 +00:00
m . children [ 0 ] . addedSection = section _id ;
2023-11-27 15:37:15 +00:00
ui . hideModal ( ) ;
ui . showModal ( null , E ( 'p' , { 'class' : 'spinning' } , [ _ ( 'Loading data…' ) ] ) ) ;
2020-03-27 09:27:18 +00:00
} ) . then ( L . bind ( m . children [ 0 ] . renderMoreOptionsModal , m . children [ 0 ] , nameval ) ) ;
} ) ;
} )
} , _ ( 'Create interface' ) )
] )
] , 'cbi-modal' ) ;
nodes . querySelector ( '[id="%s"] input[type="text"]' . format ( name . cbid ( '_new_' ) ) ) . focus ( ) ;
} , this ) ) ;
} ;
s . handleRemove = function ( section _id , ev ) {
return network . deleteNetwork ( section _id ) . then ( L . bind ( function ( section _id , ev ) {
return form . GridSection . prototype . handleRemove . apply ( this , [ section _id , ev ] ) ;
} , this , section _id , ev ) ) ;
} ;
o = s . option ( form . DummyValue , '_ifacebox' ) ;
o . modalonly = false ;
o . textvalue = function ( section _id ) {
var net = this . section . networks . filter ( function ( n ) { return n . getName ( ) == section _id } ) [ 0 ] ,
zone = net ? this . section . zones . filter ( function ( z ) { return ! ! z . getNetworks ( ) . filter ( function ( n ) { return n == section _id } ) [ 0 ] } ) [ 0 ] : null ;
if ( ! net )
return ;
var node = E ( 'div' , { 'class' : 'ifacebox' } , [
E ( 'div' , {
'class' : 'ifacebox-head' ,
2023-11-27 15:37:15 +00:00
'style' : firewall . getZoneColorStyle ( zone ) ,
2020-03-27 09:27:18 +00:00
'title' : zone ? _ ( 'Part of zone %q' ) . format ( zone . getName ( ) ) : _ ( 'No zone assigned' )
2023-11-27 15:37:15 +00:00
} , E ( 'strong' , net . getName ( ) ) ) ,
2020-03-27 09:27:18 +00:00
E ( 'div' , {
'class' : 'ifacebox-body' ,
'id' : '%s-ifc-devices' . format ( section _id ) ,
'data-network' : section _id
} , [
E ( 'img' , {
'src' : L . resource ( 'icons/ethernet_disabled.png' ) ,
'style' : 'width:16px; height:16px'
} ) ,
E ( 'br' ) , E ( 'small' , '?' )
] )
] ) ;
render _ifacebox _status ( node . childNodes [ 1 ] , net ) ;
return node ;
} ;
o = s . option ( form . DummyValue , '_ifacestat' ) ;
o . modalonly = false ;
o . textvalue = function ( section _id ) {
var net = this . section . networks . filter ( function ( n ) { return n . getName ( ) == section _id } ) [ 0 ] ;
if ( ! net )
return ;
var node = E ( 'div' , { 'id' : '%s-ifc-description' . format ( section _id ) } ) ;
render _status ( node , net , false ) ;
return node ;
} ;
o = s . taboption ( 'advanced' , form . Flag , 'delegate' , _ ( 'Use builtin IPv6-management' ) ) ;
o . modalonly = true ;
o . default = o . enabled ;
o = s . taboption ( 'advanced' , form . Flag , 'force_link' , _ ( 'Force link' ) , _ ( 'Set interface properties regardless of the link carrier (If set, carrier sense events do not invoke hotplug handlers).' ) ) ;
o . modalonly = true ;
2021-06-15 15:14:10 +00:00
o . defaults = {
'1' : [ { proto : 'static' } ] ,
'0' : [ ]
2020-03-27 09:27:18 +00:00
} ;
2021-06-25 17:45:23 +00:00
// Device configuration
s = m . section ( form . GridSection , 'device' , _ ( 'Devices' ) ) ;
s . addremove = true ;
s . anonymous = true ;
s . addbtntitle = _ ( 'Add device configuration…' ) ;
s . cfgsections = function ( ) {
var sections = uci . sections ( 'network' , 'device' ) ,
2023-11-27 15:37:15 +00:00
section _ids = sections . sort ( function ( a , b ) { return L . naturalCompare ( a . name , b . name ) } ) . map ( function ( s ) { return s [ '.name' ] } ) ;
2021-06-25 17:45:23 +00:00
for ( var i = 0 ; i < netDevs . length ; i ++ ) {
if ( sections . filter ( function ( s ) { return s . name == netDevs [ i ] . getName ( ) } ) . length )
continue ;
if ( netDevs [ i ] . getType ( ) == 'wifi' && ! netDevs [ i ] . isUp ( ) )
continue ;
/* Unless http:/ / lists . openwrt . org / pipermail / openwrt - devel / 2020 - July / 030397. html is implemented ,
we cannot properly redefine bridges as devices , so filter them away for now ... * /
var m = netDevs [ i ] . isBridge ( ) ? netDevs [ i ] . getName ( ) . match ( /^br-([A-Za-z0-9_]+)$/ ) : null ,
s = m ? uci . get ( 'network' , m [ 1 ] ) : null ;
if ( s && s [ '.type' ] == 'interface' && s . type == 'bridge' )
continue ;
section _ids . push ( 'dev:%s' . format ( netDevs [ i ] . getName ( ) ) ) ;
}
return section _ids ;
} ;
s . renderMoreOptionsModal = function ( section _id , ev ) {
var m = section _id . match ( /^dev:(.+)$/ ) ;
if ( m ) {
var devtype = getDevType ( section _id ) ;
section _id = uci . add ( 'network' , 'device' ) ;
uci . set ( 'network' , section _id , 'name' , m [ 1 ] ) ;
uci . set ( 'network' , section _id , 'type' , ( devtype != 'ethernet' ) ? devtype : null ) ;
this . addedSection = section _id ;
}
return this . super ( 'renderMoreOptionsModal' , [ section _id , ev ] ) ;
} ;
s . renderRowActions = function ( section _id ) {
var trEl = this . super ( 'renderRowActions' , [ section _id , _ ( 'Configure…' ) ] ) ,
deleteBtn = trEl . querySelector ( 'button:last-child' ) ;
2023-11-27 15:37:15 +00:00
deleteBtn . firstChild . data = _ ( 'Unconfigure' ) ;
2021-06-25 17:45:23 +00:00
deleteBtn . setAttribute ( 'title' , _ ( 'Remove related device settings from the configuration' ) ) ;
deleteBtn . disabled = section _id . match ( /^dev:/ ) ? true : null ;
return trEl ;
} ;
s . modaltitle = function ( section _id ) {
var m = section _id . match ( /^dev:(.+)$/ ) ,
name = m ? m [ 1 ] : uci . get ( 'network' , section _id , 'name' ) ;
return name ? '%s: %q' . format ( getDevTypeDesc ( section _id ) , name ) : _ ( 'Add device configuration' ) ;
} ;
s . addModalOptions = function ( s ) {
var isNew = ( uci . get ( 'network' , s . section , 'name' ) == null ) ,
dev = getDevice ( s . section ) ;
nettools . addDeviceOptions ( s , dev , isNew ) ;
} ;
s . handleModalCancel = function ( map /*, ... */ ) {
var name = uci . get ( 'network' , this . addedSection , 'name' )
uci . sections ( 'network' , 'bridge-vlan' , function ( bvs ) {
if ( name != null && bvs . device == name )
uci . remove ( 'network' , bvs [ '.name' ] ) ;
} ) ;
if ( map . addedVLANs )
for ( var i = 0 ; i < map . addedVLANs . length ; i ++ )
uci . remove ( 'network' , map . addedVLANs [ i ] ) ;
2023-11-27 15:37:15 +00:00
if ( this . addedSection )
uci . remove ( 'network' , this . addedSection ) ;
2021-06-25 17:45:23 +00:00
return form . GridSection . prototype . handleModalCancel . apply ( this , arguments ) ;
} ;
2023-11-27 15:37:15 +00:00
s . handleRemove = function ( section _id /*, ... */ ) {
var name = uci . get ( 'network' , section _id , 'name' ) ,
type = uci . get ( 'network' , section _id , 'type' ) ;
if ( name != null && type == 'bridge' ) {
uci . sections ( 'network' , 'bridge-vlan' , function ( bvs ) {
if ( bvs . device == name )
uci . remove ( 'network' , bvs [ '.name' ] ) ;
} ) ;
}
return form . GridSection . prototype . handleRemove . apply ( this , arguments ) ;
} ;
2021-06-25 17:45:23 +00:00
function getDevice ( section _id ) {
var m = section _id . match ( /^dev:(.+)$/ ) ,
name = m ? m [ 1 ] : uci . get ( 'network' , section _id , 'name' ) ;
return netDevs . filter ( function ( d ) { return d . getName ( ) == name } ) [ 0 ] ;
}
function getDevType ( section _id ) {
var dev = getDevice ( section _id ) ,
cfg = uci . get ( 'network' , section _id ) ,
type = cfg ? ( uci . get ( 'network' , section _id , 'type' ) || 'ethernet' ) : ( dev ? dev . getType ( ) : '' ) ;
switch ( type ) {
case '' :
return null ;
case 'vlan' :
case '8021q' :
return '8021q' ;
case '8021ad' :
return '8021ad' ;
case 'bridge' :
return 'bridge' ;
case 'tunnel' :
return 'tunnel' ;
case 'macvlan' :
return 'macvlan' ;
case 'veth' :
return 'veth' ;
case 'wifi' :
case 'alias' :
case 'switch' :
case 'ethernet' :
default :
return 'ethernet' ;
}
}
function getDevTypeDesc ( section _id ) {
switch ( getDevType ( section _id ) || '' ) {
case '' :
return E ( 'em' , [ _ ( 'Device not present' ) ] ) ;
case '8021q' :
return _ ( 'VLAN (802.1q)' ) ;
case '8021ad' :
return _ ( 'VLAN (802.1ad)' ) ;
case 'bridge' :
return _ ( 'Bridge device' ) ;
case 'tunnel' :
return _ ( 'Tunnel device' ) ;
case 'macvlan' :
return _ ( 'MAC VLAN' ) ;
case 'veth' :
return _ ( 'Virtual Ethernet' ) ;
default :
return _ ( 'Network device' ) ;
}
}
o = s . option ( form . DummyValue , 'name' , _ ( 'Device' ) ) ;
o . modalonly = false ;
o . textvalue = function ( section _id ) {
var dev = getDevice ( section _id ) ,
ext = section _id . match ( /^dev:/ ) ,
icon = render _iface ( dev ) ;
if ( ext )
icon . querySelector ( 'img' ) . style . opacity = '.5' ;
return E ( 'span' , { 'class' : 'ifacebadge' } , [
icon ,
E ( 'span' , { 'style' : ext ? 'opacity:.5' : null } , [
dev ? dev . getName ( ) : ( uci . get ( 'network' , section _id , 'name' ) || '?' )
] )
] ) ;
} ;
o = s . option ( form . DummyValue , 'type' , _ ( 'Type' ) ) ;
o . textvalue = getDevTypeDesc ;
o . modalonly = false ;
o = s . option ( form . DummyValue , 'macaddr' , _ ( 'MAC Address' ) ) ;
o . modalonly = false ;
o . textvalue = function ( section _id ) {
var dev = getDevice ( section _id ) ,
val = uci . get ( 'network' , section _id , 'macaddr' ) ,
mac = dev ? dev . getMAC ( ) : null ;
return val ? E ( 'strong' , {
2023-11-27 15:37:15 +00:00
'data-tooltip' : _ ( 'The value is overridden by configuration.' )
2021-06-25 17:45:23 +00:00
} , [ val . toUpperCase ( ) ] ) : ( mac || '-' ) ;
} ;
o = s . option ( form . DummyValue , 'mtu' , _ ( 'MTU' ) ) ;
o . modalonly = false ;
o . textvalue = function ( section _id ) {
var dev = getDevice ( section _id ) ,
val = uci . get ( 'network' , section _id , 'mtu' ) ,
mtu = dev ? dev . getMTU ( ) : null ;
return val ? E ( 'strong' , {
2023-11-27 15:37:15 +00:00
'data-tooltip' : _ ( 'The value is overridden by configuration.' )
2021-06-25 17:45:23 +00:00
} , [ val ] ) : ( mtu || '-' ) . toString ( ) ;
} ;
2020-03-27 09:27:18 +00:00
s = m . section ( form . TypedSection , 'globals' , _ ( 'Global network options' ) ) ;
s . addremove = false ;
s . anonymous = true ;
2021-06-25 17:45:23 +00:00
o = s . option ( form . Value , 'ula_prefix' , _ ( 'IPv6 ULA-Prefix' ) , _ ( 'Unique Local Address - in the range <code>fc00::/7</code>. Typically only within the ‘local’ half <code>fd00::/8</code>. ULA for IPv6 is analogous to IPv4 private network addressing. This prefix is randomly generated at first install.' ) ) ;
2020-03-27 09:27:18 +00:00
o . datatype = 'cidr6' ;
2024-06-19 13:31:41 +00:00
o = s . option ( form . ListValue , 'packet_steering' , _ ( 'Packet Steering' ) , _ ( 'Enable packet steering across CPUs. May help or hinder network speed.' ) ) ;
o . value ( '' , _ ( 'Disabled' ) ) ;
o . value ( '1' , _ ( 'Enabled' ) ) ;
o . value ( '2' , _ ( 'Enabled (all CPUs)' ) ) ;
2020-03-27 09:27:18 +00:00
o . optional = true ;
2024-06-19 13:31:41 +00:00
var steer _flow = uci . get ( 'network' , 'globals' , 'steering_flows' ) ;
o = s . option ( form . Value , 'steering_flows' , _ ( 'Steering flows (<abbr title="Receive Packet Steering">RPS</abbr>)' ) ,
_ ( 'Directs packet flows to specific CPUs where the local socket owner listens (the local service).' ) + ' ' +
_ ( 'Note: this setting is for local services on the device only (not for forwarding).' ) ) ;
o . value ( '' , _ ( 'Standard: none' ) ) ;
o . value ( '128' , _ ( 'Suggested: 128' ) ) ;
o . value ( '256' , _ ( '256' ) ) ;
o . depends ( 'packet_steering' , '1' ) ;
o . depends ( 'packet_steering' , '2' ) ;
o . datatype = 'uinteger' ;
o . default = steer _flow ;
2020-03-27 09:27:18 +00:00
if ( dslModemType != null ) {
s = m . section ( form . TypedSection , 'dsl' , _ ( 'DSL' ) ) ;
s . anonymous = true ;
o = s . option ( form . ListValue , 'annex' , _ ( 'Annex' ) ) ;
2023-11-27 15:37:15 +00:00
if ( dslModemType == 'vdsl' ) {
o . value ( 'a' , _ ( 'ADSL (all variants) Annex A/L/M + VDSL2 Annex A/B/C' ) ) ;
o . value ( 'b' , _ ( 'ADSL (all variants) Annex B + VDSL2 Annex A/B/C' ) ) ;
o . value ( 'j' , _ ( 'ADSL (all variants) Annex B/J + VDSL2 Annex A/B/C' ) ) ;
} else {
o . value ( 'a' , _ ( 'ADSL (all variants) Annex A/L/M' ) ) ;
o . value ( 'b' , _ ( 'ADSL (all variants) Annex B' ) ) ;
o . value ( 'j' , _ ( 'ADSL (all variants) Annex B/J' ) ) ;
}
o . value ( 'm' , _ ( 'ADSL (all variants) Annex M' ) ) ;
2020-03-27 09:27:18 +00:00
o . value ( 'at1' , _ ( 'ANSI T1.413' ) ) ;
2023-11-27 15:37:15 +00:00
o . value ( 'admt' , _ ( 'ADSL (G.992.1) Annex A' ) ) ;
o . value ( 'bdmt' , _ ( 'ADSL (G.992.1) Annex B' ) ) ;
o . value ( 'alite' , _ ( 'Splitterless ADSL (G.992.2) Annex A' ) ) ;
o . value ( 'a2' , _ ( 'ADSL2 (G.992.3) Annex A' ) ) ;
o . value ( 'b2' , _ ( 'ADSL2 (G.992.3) Annex B' ) ) ;
o . value ( 'l' , _ ( 'ADSL2 (G.992.3) Annex L' ) ) ;
o . value ( 'm2' , _ ( 'ADSL2 (G.992.3) Annex M' ) ) ;
o . value ( 'a2p' , _ ( 'ADSL2+ (G.992.5) Annex A' ) ) ;
o . value ( 'b2p' , _ ( 'ADSL2+ (G.992.5) Annex B' ) ) ;
o . value ( 'm2p' , _ ( 'ADSL2+ (G.992.5) Annex M' ) ) ;
2020-03-27 09:27:18 +00:00
o = s . option ( form . ListValue , 'tone' , _ ( 'Tone' ) ) ;
o . value ( '' , _ ( 'auto' ) ) ;
o . value ( 'a' , _ ( 'A43C + J43 + A43' ) ) ;
o . value ( 'av' , _ ( 'A43C + J43 + A43 + V43' ) ) ;
o . value ( 'b' , _ ( 'B43 + B43C' ) ) ;
o . value ( 'bv' , _ ( 'B43 + B43C + V43' ) ) ;
if ( dslModemType == 'vdsl' ) {
o = s . option ( form . ListValue , 'xfer_mode' , _ ( 'Encapsulation mode' ) ) ;
o . value ( '' , _ ( 'auto' ) ) ;
o . value ( 'atm' , _ ( 'ATM (Asynchronous Transfer Mode)' ) ) ;
o . value ( 'ptm' , _ ( 'PTM/EFM (Packet Transfer Mode)' ) ) ;
o = s . option ( form . ListValue , 'line_mode' , _ ( 'DSL line mode' ) ) ;
o . value ( '' , _ ( 'auto' ) ) ;
o . value ( 'adsl' , _ ( 'ADSL' ) ) ;
o . value ( 'vdsl' , _ ( 'VDSL' ) ) ;
o = s . option ( form . ListValue , 'ds_snr_offset' , _ ( 'Downstream SNR offset' ) ) ;
o . default = '0' ;
for ( var i = - 100 ; i <= 100 ; i += 5 )
o . value ( i , _ ( '%.1f dB' ) . format ( i / 10 ) ) ;
}
s . option ( form . Value , 'firmware' , _ ( 'Firmware File' ) ) ;
}
// Show ATM bridge section if we have the capabilities
if ( L . hasSystemFeature ( 'br2684ctl' ) ) {
s = m . section ( form . TypedSection , 'atm-bridge' , _ ( 'ATM Bridges' ) , _ ( 'ATM bridges expose encapsulated ethernet in AAL5 connections as virtual Linux network interfaces which can be used in conjunction with DHCP or PPP to dial into the provider network.' ) ) ;
s . addremove = true ;
s . anonymous = true ;
s . addbtntitle = _ ( 'Add ATM Bridge' ) ;
s . handleAdd = function ( ev ) {
var sections = uci . sections ( 'network' , 'atm-bridge' ) ,
max _unit = - 1 ;
for ( var i = 0 ; i < sections . length ; i ++ ) {
var unit = + sections [ i ] . unit ;
if ( ! isNaN ( unit ) && unit > max _unit )
max _unit = unit ;
}
return this . map . save ( function ( ) {
var sid = uci . add ( 'network' , 'atm-bridge' ) ;
uci . set ( 'network' , sid , 'unit' , max _unit + 1 ) ;
uci . set ( 'network' , sid , 'atmdev' , 0 ) ;
uci . set ( 'network' , sid , 'encaps' , 'llc' ) ;
uci . set ( 'network' , sid , 'payload' , 'bridged' ) ;
uci . set ( 'network' , sid , 'vci' , 35 ) ;
uci . set ( 'network' , sid , 'vpi' , 8 ) ;
} ) ;
} ;
s . tab ( 'general' , _ ( 'General Setup' ) ) ;
s . tab ( 'advanced' , _ ( 'Advanced Settings' ) ) ;
o = s . taboption ( 'general' , form . Value , 'vci' , _ ( 'ATM Virtual Channel Identifier (VCI)' ) ) ;
s . taboption ( 'general' , form . Value , 'vpi' , _ ( 'ATM Virtual Path Identifier (VPI)' ) ) ;
o = s . taboption ( 'general' , form . ListValue , 'encaps' , _ ( 'Encapsulation mode' ) ) ;
o . value ( 'llc' , _ ( 'LLC' ) ) ;
o . value ( 'vc' , _ ( 'VC-Mux' ) ) ;
s . taboption ( 'advanced' , form . Value , 'atmdev' , _ ( 'ATM device number' ) ) ;
s . taboption ( 'advanced' , form . Value , 'unit' , _ ( 'Bridge unit number' ) ) ;
o = s . taboption ( 'advanced' , form . ListValue , 'payload' , _ ( 'Forwarding mode' ) ) ;
o . value ( 'bridged' , _ ( 'bridged' ) ) ;
o . value ( 'routed' , _ ( 'routed' ) ) ;
}
return m . render ( ) . then ( L . bind ( function ( m , nodes ) {
2020-04-09 12:39:09 +00:00
poll . add ( L . bind ( function ( ) {
2020-03-27 09:27:18 +00:00
var section _ids = m . children [ 0 ] . cfgsections ( ) ,
tasks = [ ] ;
for ( var i = 0 ; i < section _ids . length ; i ++ ) {
var row = nodes . querySelector ( '.cbi-section-table-row[data-sid="%s"]' . format ( section _ids [ i ] ) ) ,
dsc = row . querySelector ( '[data-name="_ifacestat"] > div' ) ,
btn1 = row . querySelector ( '.cbi-section-actions .reconnect' ) ,
btn2 = row . querySelector ( '.cbi-section-actions .down' ) ;
if ( dsc . getAttribute ( 'reconnect' ) == '' ) {
dsc . setAttribute ( 'reconnect' , '1' ) ;
tasks . push ( fs . exec ( '/sbin/ifup' , [ section _ids [ i ] ] ) . catch ( function ( e ) {
ui . addNotification ( null , E ( 'p' , e . message ) ) ;
} ) ) ;
}
else if ( dsc . getAttribute ( 'disconnect' ) == '' ) {
dsc . setAttribute ( 'disconnect' , '1' ) ;
tasks . push ( fs . exec ( '/sbin/ifdown' , [ section _ids [ i ] ] ) . catch ( function ( e ) {
ui . addNotification ( null , E ( 'p' , e . message ) ) ;
} ) ) ;
}
else if ( dsc . getAttribute ( 'reconnect' ) == '1' ) {
dsc . removeAttribute ( 'reconnect' ) ;
btn1 . classList . remove ( 'spinning' ) ;
btn1 . disabled = false ;
}
else if ( dsc . getAttribute ( 'disconnect' ) == '1' ) {
dsc . removeAttribute ( 'disconnect' ) ;
btn2 . classList . remove ( 'spinning' ) ;
btn2 . disabled = false ;
}
}
return Promise . all ( tasks )
. then ( L . bind ( network . getNetworks , network ) )
. then ( L . bind ( this . poll _status , this , nodes ) ) ;
} , this ) , 5 ) ;
return nodes ;
} , this , m ) ) ;
}
} ) ;