2021-06-25 17:45:23 +00:00
'use strict' ;
2023-11-27 15:37:15 +00:00
'require fs' ;
2021-06-25 17:45:23 +00:00
'require ui' ;
'require dom' ;
'require uci' ;
'require form' ;
'require network' ;
'require baseclass' ;
'require validation' ;
'require tools.widgets as widgets' ;
function validateAddr ( section _id , value ) {
if ( value == '' )
return true ;
var ipv6 = /6$/ . test ( this . section . formvalue ( section _id , 'mode' ) ) ,
addr = ipv6 ? validation . parseIPv6 ( value ) : validation . parseIPv4 ( value ) ;
return addr ? true : ( ipv6 ? _ ( 'Expecting a valid IPv6 address' ) : _ ( 'Expecting a valid IPv4 address' ) ) ;
}
function validateQoSMap ( section _id , value ) {
if ( value == '' )
return true ;
var m = value . match ( /^(\d+):(\d+)$/ ) ;
if ( ! m || + m [ 1 ] > 0xFFFFFFFF || + m [ 2 ] > 0xFFFFFFFF )
return _ ( 'Expecting two priority values separated by a colon' ) ;
return true ;
}
function deviceSectionExists ( section _id , devname ) {
var exists = false ;
uci . sections ( 'network' , 'device' , function ( ss ) {
exists = exists || (
ss [ '.name' ] != section _id &&
ss . name == devname
) ;
} ) ;
return exists ;
}
function isBridgePort ( dev ) {
if ( ! dev )
return false ;
if ( dev . isBridgePort ( ) )
return true ;
var isPort = false ;
uci . sections ( 'network' , null , function ( s ) {
if ( s [ '.type' ] != 'interface' && s [ '.type' ] != 'device' )
return ;
if ( s . type == 'bridge' && L . toArray ( s . ifname ) . indexOf ( dev . getName ( ) ) > - 1 )
isPort = true ;
} ) ;
return isPort ;
}
function updateDevBadge ( node , dev ) {
var type = dev . getType ( ) ,
up = dev . getCarrier ( ) ;
dom . content ( node , [
E ( 'img' , {
'class' : 'middle' ,
'src' : L . resource ( 'icons/%s%s.png' ) . format ( type , up ? '' : '_disabled' )
} ) ,
'\x0a' , dev . getName ( )
] ) ;
return node ;
}
function renderDevBadge ( dev ) {
return updateDevBadge ( E ( 'span' , {
'class' : 'ifacebadge port-status-device' ,
'style' : 'font-weight:normal' ,
'data-device' : dev . getName ( )
} ) , dev ) ;
}
function updatePortStatus ( node , dev ) {
var carrier = dev . getCarrier ( ) ,
duplex = dev . getDuplex ( ) ,
speed = dev . getSpeed ( ) ,
desc , title ;
if ( carrier && speed > 0 && duplex != null ) {
desc = '%d%s' . format ( speed , duplex == 'full' ? 'FD' : 'HD' ) ;
title = '%s, %d MBit/s, %s' . format ( _ ( 'Connected' ) , speed , duplex == 'full' ? _ ( 'full-duplex' ) : _ ( 'half-duplex' ) ) ;
}
else if ( carrier ) {
desc = _ ( 'Connected' ) ;
}
else {
desc = _ ( 'no link' ) ;
}
dom . content ( node , [
E ( 'img' , {
'class' : 'middle' ,
'src' : L . resource ( 'icons/port_%s.png' ) . format ( carrier ? 'up' : 'down' )
} ) ,
'\x0a' , desc
] ) ;
if ( title )
node . setAttribute ( 'data-tooltip' , title ) ;
else
node . removeAttribute ( 'data-tooltip' ) ;
return node ;
}
function renderPortStatus ( dev ) {
return updatePortStatus ( E ( 'span' , {
'class' : 'ifacebadge port-status-link' ,
'data-device' : dev . getName ( )
} ) , dev ) ;
}
function updatePlaceholders ( opt , section _id ) {
var dev = network . instantiateDevice ( opt . getUIElement ( section _id ) . getValue ( ) ) ;
for ( var i = 0 , co ; ( co = opt . section . children [ i ] ) != null ; i ++ ) {
if ( co !== opt ) {
switch ( co . option ) {
case 'mtu' :
case 'mtu6' :
co . getUIElement ( section _id ) . setPlaceholder ( dev . getMTU ( ) ) ;
break ;
case 'macaddr' :
co . getUIElement ( section _id ) . setPlaceholder ( dev . getMAC ( ) ) ;
break ;
case 'txqueuelen' :
co . getUIElement ( section _id ) . setPlaceholder ( dev . _devstate ( 'qlen' ) ) ;
break ;
}
}
}
}
2023-11-27 15:37:15 +00:00
var cbiFlagTristate = form . ListValue . extend ( {
_ _init _ _ : function ( /* ... */ ) {
this . super ( '__init__' , arguments ) ;
this . keylist = [ '' , '0!' , '1!' ] ;
this . vallist = [ _ ( 'automatic' ) , _ ( 'disabled' ) , _ ( 'enabled' ) ] ;
} ,
load : function ( section _id ) {
var invert = false , sysfs = this . sysfs ;
if ( sysfs ) {
if ( sysfs . charAt ( 0 ) == '!' ) {
invert = true ;
sysfs = sysfs . substring ( 1 ) ;
}
return L . resolveDefault ( fs . read ( sysfs ) , '' ) . then ( L . bind ( function ( res ) {
res = ( res || '' ) . trim ( ) ;
if ( res == '0' )
this . sysfs _default = invert ;
else if ( res == '1' )
this . sysfs _default = ! invert ;
return this . super ( 'load' , [ section _id ] ) ;
} , this ) ) ;
}
return this . super ( 'load' , [ section _id ] ) ;
} ,
write : function ( section _id , formvalue ) {
if ( formvalue == '1!' )
return this . super ( 'write' , [ section _id , '1' ] ) ;
else if ( formvalue == '0!' )
return this . super ( 'write' , [ section _id , '0' ] ) ;
else
return this . super ( 'remove' , [ section _id ] ) ;
} ,
renderWidget : function ( section _id , option _index , cfgvalue ) {
var sysdef = this . sysfs _default ;
if ( this . sysfs _default !== null ) {
this . keylist [ 0 ] = sysdef ? '1' : '0' ;
this . vallist [ 0 ] = sysdef ? _ ( 'automatic (enabled)' ) : _ ( 'automatic (disabled)' ) ;
}
return this . super ( 'renderWidget' , [ section _id , option _index , cfgvalue ] ) ;
}
} ) ;
2021-06-25 17:45:23 +00:00
var cbiTagValue = form . Value . extend ( {
renderWidget : function ( section _id , option _index , cfgvalue ) {
var widget = new ui . Dropdown ( cfgvalue || [ '-' ] , {
'-' : E ( [ ] , [
E ( 'span' , { 'class' : 'hide-open' , 'style' : 'font-family:monospace' } , [ '—' ] ) ,
2023-11-27 15:37:15 +00:00
E ( 'span' , { 'class' : 'hide-close' } , [ _ ( 'Not Member' , 'VLAN port state' ) ] )
2021-06-25 17:45:23 +00:00
] ) ,
'u' : E ( [ ] , [
2023-11-27 15:37:15 +00:00
E ( 'span' , { 'class' : 'hide-open' , 'style' : 'font-family:monospace' } , [ 'U' ] ) ,
E ( 'span' , { 'class' : 'hide-close' } , [ _ ( 'Untagged' , 'VLAN port state' ) ] )
2021-06-25 17:45:23 +00:00
] ) ,
't' : E ( [ ] , [
2023-11-27 15:37:15 +00:00
E ( 'span' , { 'class' : 'hide-open' , 'style' : 'font-family:monospace' } , [ 'T' ] ) ,
E ( 'span' , { 'class' : 'hide-close' } , [ _ ( 'Tagged' , 'VLAN port state' ) ] )
2021-06-25 17:45:23 +00:00
] ) ,
'*' : E ( [ ] , [
E ( 'span' , { 'class' : 'hide-open' , 'style' : 'font-family:monospace' } , [ '*' ] ) ,
2023-11-27 15:37:15 +00:00
E ( 'span' , { 'class' : 'hide-close' } , [ _ ( 'Is Primary VLAN' , 'VLAN port state' ) ] )
2021-06-25 17:45:23 +00:00
] )
} , {
id : this . cbid ( section _id ) ,
sort : [ '-' , 'u' , 't' , '*' ] ,
optional : false ,
multiple : true
} ) ;
var field = this ;
widget . toggleItem = function ( sb , li , force _state ) {
var lis = li . parentNode . querySelectorAll ( 'li' ) ,
toggle = ui . Dropdown . prototype . toggleItem ;
toggle . apply ( this , [ sb , li , force _state ] ) ;
if ( force _state != null )
return ;
switch ( li . getAttribute ( 'data-value' ) )
{
case '-' :
if ( li . hasAttribute ( 'selected' ) ) {
for ( var i = 0 ; i < lis . length ; i ++ ) {
switch ( lis [ i ] . getAttribute ( 'data-value' ) ) {
case '-' :
break ;
case '*' :
toggle . apply ( this , [ sb , lis [ i ] , false ] ) ;
lis [ i ] . setAttribute ( 'unselectable' , '' ) ;
break ;
default :
toggle . apply ( this , [ sb , lis [ i ] , false ] ) ;
}
}
}
break ;
case 't' :
case 'u' :
if ( li . hasAttribute ( 'selected' ) ) {
for ( var i = 0 ; i < lis . length ; i ++ ) {
switch ( lis [ i ] . getAttribute ( 'data-value' ) ) {
case li . getAttribute ( 'data-value' ) :
break ;
case '*' :
lis [ i ] . removeAttribute ( 'unselectable' ) ;
break ;
default :
toggle . apply ( this , [ sb , lis [ i ] , false ] ) ;
}
}
}
else {
toggle . apply ( this , [ sb , li , true ] ) ;
}
break ;
case '*' :
if ( li . hasAttribute ( 'selected' ) ) {
var section _ids = field . section . cfgsections ( ) ;
for ( var i = 0 ; i < section _ids . length ; i ++ ) {
var other _widget = field . getUIElement ( section _ids [ i ] ) ,
other _value = L . toArray ( other _widget . getValue ( ) ) ;
if ( other _widget === this )
continue ;
var new _value = other _value . filter ( function ( v ) { return v != '*' } ) ;
if ( new _value . length == other _value . length )
continue ;
other _widget . setValue ( new _value ) ;
break ;
}
}
}
} ;
var node = widget . render ( ) ;
node . style . minWidth = '4em' ;
if ( cfgvalue == '-' )
node . querySelector ( 'li[data-value="*"]' ) . setAttribute ( 'unselectable' , '' ) ;
return E ( 'div' , { 'style' : 'display:inline-block' } , node ) ;
} ,
cfgvalue : function ( section _id ) {
var ports = L . toArray ( uci . get ( 'network' , section _id , 'ports' ) ) ;
for ( var i = 0 ; i < ports . length ; i ++ ) {
var s = ports [ i ] . split ( /:/ ) ;
if ( s [ 0 ] != this . port )
continue ;
var t = /t/ . test ( s [ 1 ] || '' ) ? 't' : 'u' ;
2023-11-27 15:37:15 +00:00
return /\x2a/ . test ( s [ 1 ] || '' ) ? [ t , '*' ] : [ t ] ;
2021-06-25 17:45:23 +00:00
}
return [ '-' ] ;
} ,
write : function ( section _id , value ) {
var ports = [ ] ;
for ( var i = 0 ; i < this . section . children . length ; i ++ ) {
var opt = this . section . children [ i ] ;
if ( opt . port ) {
var val = L . toArray ( opt . formvalue ( section _id ) ) . join ( '' ) ;
switch ( val ) {
case '-' :
break ;
case 'u' :
ports . push ( opt . port ) ;
break ;
default :
ports . push ( '%s:%s' . format ( opt . port , val ) ) ;
break ;
}
}
}
2023-11-27 15:37:15 +00:00
uci . set ( 'network' , section _id , 'ports' , ports . length ? ports : null ) ;
2021-06-25 17:45:23 +00:00
} ,
remove : function ( ) { }
} ) ;
return baseclass . extend ( {
replaceOption : function ( s , tabName , optionClass , optionName , optionTitle , optionDescription ) {
var o = s . getOption ( optionName ) ;
if ( o ) {
if ( o . tab ) {
s . tabs [ o . tab ] . children = s . tabs [ o . tab ] . children . filter ( function ( opt ) {
return opt . option != optionName ;
} ) ;
}
s . children = s . children . filter ( function ( opt ) {
return opt . option != optionName ;
} ) ;
}
return s . taboption ( tabName , optionClass , optionName , optionTitle , optionDescription ) ;
} ,
addDeviceOptions : function ( s , dev , isNew ) {
var parent _dev = dev ? dev . getParent ( ) : null ,
2023-11-27 15:37:15 +00:00
devname = dev ? dev . getName ( ) : null ,
2021-06-25 17:45:23 +00:00
o , ss ;
s . tab ( 'devgeneral' , _ ( 'General device options' ) ) ;
s . tab ( 'devadvanced' , _ ( 'Advanced device options' ) ) ;
s . tab ( 'brport' , _ ( 'Bridge port specific options' ) ) ;
s . tab ( 'bridgevlan' , _ ( 'Bridge VLAN filtering' ) ) ;
o = this . replaceOption ( s , 'devgeneral' , form . ListValue , 'type' , _ ( 'Device type' ) ) ;
o . readonly = ! isNew ;
o . value ( '' , _ ( 'Network device' ) ) ;
o . value ( 'bridge' , _ ( 'Bridge device' ) ) ;
o . value ( '8021q' , _ ( 'VLAN (802.1q)' ) ) ;
o . value ( '8021ad' , _ ( 'VLAN (802.1ad)' ) ) ;
o . value ( 'macvlan' , _ ( 'MAC VLAN' ) ) ;
o . value ( 'veth' , _ ( 'Virtual Ethernet' ) ) ;
o . validate = function ( section _id , value ) {
if ( value == 'bridge' || value == 'veth' )
updatePlaceholders ( this . section . getOption ( 'name_complex' ) , section _id ) ;
return true ;
} ;
o = this . replaceOption ( s , 'devgeneral' , widgets . DeviceSelect , 'name_simple' , _ ( 'Existing device' ) ) ;
o . readonly = ! isNew ;
o . rmempty = false ;
o . noaliases = true ;
o . default = ( dev ? dev . getName ( ) : '' ) ;
o . ucioption = 'name' ;
o . filter = function ( section _id , value ) {
var dev = network . instantiateDevice ( value ) ;
return ! deviceSectionExists ( section _id , value ) && ( dev . getType ( ) != 'wifi' || dev . isUp ( ) ) ;
} ;
o . validate = function ( section _id , value ) {
updatePlaceholders ( this , section _id ) ;
return deviceSectionExists ( section _id , value )
? _ ( 'A configuration for the device "%s" already exists' ) . format ( value ) : true ;
} ;
o . onchange = function ( ev , section _id , values ) {
updatePlaceholders ( this , section _id ) ;
} ;
o . depends ( 'type' , '' ) ;
o = this . replaceOption ( s , 'devgeneral' , widgets . DeviceSelect , 'ifname_single' , _ ( 'Base device' ) ) ;
o . readonly = ! isNew ;
o . rmempty = false ;
o . noaliases = true ;
o . default = ( dev ? dev . getName ( ) : '' ) . match ( /^.+\.\d+$/ ) ? dev . getName ( ) . replace ( /\.\d+$/ , '' ) : '' ;
o . ucioption = 'ifname' ;
o . filter = function ( section _id , value ) {
var dev = network . instantiateDevice ( value ) ;
return ( dev . getType ( ) != 'wifi' || dev . isUp ( ) ) ;
} ;
o . validate = function ( section _id , value ) {
updatePlaceholders ( this , section _id ) ;
if ( isNew ) {
var type = this . section . formvalue ( section _id , 'type' ) ,
name = this . section . getUIElement ( section _id , 'name_complex' ) ;
if ( type == 'macvlan' && value && name && ! name . isChanged ( ) ) {
var i = 0 ;
while ( deviceSectionExists ( section _id , '%smac%d' . format ( value , i ) ) )
i ++ ;
name . setValue ( '%smac%d' . format ( value , i ) ) ;
name . triggerValidation ( ) ;
}
}
return true ;
} ;
o . onchange = function ( ev , section _id , values ) {
updatePlaceholders ( this , section _id ) ;
} ;
o . depends ( 'type' , '8021q' ) ;
o . depends ( 'type' , '8021ad' ) ;
o . depends ( 'type' , 'macvlan' ) ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'vid' , _ ( 'VLAN ID' ) ) ;
o . readonly = ! isNew ;
o . datatype = 'range(1, 4094)' ;
o . rmempty = false ;
o . default = ( dev ? dev . getName ( ) : '' ) . match ( /^.+\.\d+$/ ) ? dev . getName ( ) . replace ( /^.+\./ , '' ) : '' ;
o . validate = function ( section _id , value ) {
var base = this . section . formvalue ( section _id , 'ifname_single' ) ,
vid = this . section . formvalue ( section _id , 'vid' ) ,
name = this . section . getUIElement ( section _id , 'name_complex' ) ;
2023-11-27 15:37:15 +00:00
if ( base && vid && name && ! name . isChanged ( ) && isNew ) {
2021-06-25 17:45:23 +00:00
name . setValue ( '%s.%d' . format ( base , vid ) ) ;
name . triggerValidation ( ) ;
}
return true ;
} ;
o . depends ( 'type' , '8021q' ) ;
o . depends ( 'type' , '8021ad' ) ;
o = this . replaceOption ( s , 'devgeneral' , form . ListValue , 'mode' , _ ( 'Mode' ) ) ;
o . value ( 'vepa' , _ ( 'VEPA (Virtual Ethernet Port Aggregator)' , 'MACVLAN mode' ) ) ;
o . value ( 'private' , _ ( 'Private (Prevent communication between MAC VLANs)' , 'MACVLAN mode' ) ) ;
o . value ( 'bridge' , _ ( 'Bridge (Support direct communication between MAC VLANs)' , 'MACVLAN mode' ) ) ;
o . value ( 'passthru' , _ ( 'Pass-through (Mirror physical device to single MAC VLAN)' , 'MACVLAN mode' ) ) ;
o . depends ( 'type' , 'macvlan' ) ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'name_complex' , _ ( 'Device name' ) ) ;
o . rmempty = false ;
o . datatype = 'maxlength(15)' ;
o . readonly = ! isNew ;
o . ucioption = 'name' ;
o . validate = function ( section _id , value ) {
var dev = network . instantiateDevice ( value ) ;
if ( deviceSectionExists ( section _id , value ) || ( isNew && ( dev . dev || { } ) . idx ) )
return _ ( 'The device name "%s" is already taken' ) . format ( value ) ;
return true ;
} ;
o . depends ( { type : '' , '!reverse' : true } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . DynamicList , 'ingress_qos_mapping' , _ ( 'Ingress QoS mapping' ) , _ ( 'Defines a mapping of VLAN header priority to the Linux internal packet priority on incoming frames' ) ) ;
o . rmempty = true ;
o . validate = validateQoSMap ;
o . depends ( 'type' , '8021q' ) ;
o . depends ( 'type' , '8021ad' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . DynamicList , 'egress_qos_mapping' , _ ( 'Egress QoS mapping' ) , _ ( 'Defines a mapping of Linux internal packet priority to VLAN header priority but for outgoing frames' ) ) ;
o . rmempty = true ;
o . validate = validateQoSMap ;
o . depends ( 'type' , '8021q' ) ;
o . depends ( 'type' , '8021ad' ) ;
o = this . replaceOption ( s , 'devgeneral' , widgets . DeviceSelect , 'ifname_multi' , _ ( 'Bridge ports' ) ) ;
o . size = 10 ;
o . rmempty = true ;
o . multiple = true ;
o . noaliases = true ;
o . nobridges = true ;
o . ucioption = 'ports' ;
o . default = L . toArray ( dev ? dev . getPorts ( ) : null ) . filter ( function ( p ) { return p . getType ( ) != 'wifi' } ) . map ( function ( p ) { return p . getName ( ) } ) ;
o . filter = function ( section _id , device _name ) {
var bridge _name = uci . get ( 'network' , section _id , 'name' ) ,
choice _dev = network . instantiateDevice ( device _name ) ,
parent _dev = choice _dev . getParent ( ) ;
/* only show wifi networks which are already present in "option ifname" */
if ( choice _dev . getType ( ) == 'wifi' ) {
var ifnames = L . toArray ( uci . get ( 'network' , section _id , 'ports' ) ) ;
for ( var i = 0 ; i < ifnames . length ; i ++ )
if ( ifnames [ i ] == device _name )
return true ;
return false ;
}
return ( ! parent _dev || parent _dev . getName ( ) != bridge _name ) ;
} ;
o . description = _ ( 'Specifies the wired ports to attach to this bridge. In order to attach wireless networks, choose the associated interface as network in the wireless settings.' )
o . onchange = function ( ev , section _id , values ) {
ss . updatePorts ( values ) ;
return ss . parse ( ) . then ( function ( ) {
ss . redraw ( ) ;
} ) ;
} ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devgeneral' , form . Flag , 'bridge_empty' , _ ( 'Bring up empty bridge' ) , _ ( 'Bring up the bridge interface even if no ports are attached' ) ) ;
o . default = o . disabled ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'priority' , _ ( 'Priority' ) ) ;
o . placeholder = '32767' ;
o . datatype = 'range(0, 65535)' ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'ageing_time' , _ ( 'Ageing time' ) , _ ( 'Timeout in seconds for learned MAC addresses in the forwarding database' ) ) ;
o . placeholder = '30' ;
o . datatype = 'uinteger' ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'stp' , _ ( 'Enable <abbr title="Spanning Tree Protocol">STP</abbr>' ) , _ ( 'Enables the Spanning Tree Protocol on this bridge' ) ) ;
o . default = o . disabled ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'hello_time' , _ ( 'Hello interval' ) , _ ( 'Interval in seconds for STP hello packets' ) ) ;
o . placeholder = '2' ;
o . datatype = 'range(1, 10)' ;
o . depends ( { type : 'bridge' , stp : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'forward_delay' , _ ( 'Forward delay' ) , _ ( 'Time in seconds to spend in listening and learning states' ) ) ;
o . placeholder = '15' ;
o . datatype = 'range(2, 30)' ;
o . depends ( { type : 'bridge' , stp : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'max_age' , _ ( 'Maximum age' ) , _ ( 'Timeout in seconds until topology updates on link loss' ) ) ;
o . placeholder = '20' ;
o . datatype = 'range(6, 40)' ;
o . depends ( { type : 'bridge' , stp : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'igmp_snooping' , _ ( 'Enable <abbr title="Internet Group Management Protocol">IGMP</abbr> snooping' ) , _ ( 'Enables IGMP snooping on this bridge' ) ) ;
o . default = o . disabled ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'hash_max' , _ ( 'Maximum snooping table size' ) ) ;
o . placeholder = '512' ;
o . datatype = 'uinteger' ;
o . depends ( { type : 'bridge' , igmp _snooping : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'multicast_querier' , _ ( 'Enable multicast querier' ) ) ;
o . defaults = { '1' : [ { 'igmp_snooping' : '1' } ] , '0' : [ { 'igmp_snooping' : '0' } ] } ;
o . depends ( 'type' , 'bridge' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'robustness' , _ ( 'Robustness' ) , _ ( 'The robustness value allows tuning for the expected packet loss on the network. If a network is expected to be lossy, the robustness value may be increased. IGMP is robust to (Robustness-1) packet losses' ) ) ;
o . placeholder = '2' ;
o . datatype = 'min(1)' ;
o . depends ( { type : 'bridge' , multicast _querier : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'query_interval' , _ ( 'Query interval' ) , _ ( 'Interval in centiseconds between multicast general queries. By varying the value, an administrator may tune the number of IGMP messages on the subnet; larger values cause IGMP Queries to be sent less often' ) ) ;
o . placeholder = '12500' ;
o . datatype = 'uinteger' ;
o . depends ( { type : 'bridge' , multicast _querier : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'query_response_interval' , _ ( 'Query response interval' ) , _ ( 'The max response time in centiseconds inserted into the periodic general queries. By varying the value, an administrator may tune the burstiness of IGMP messages on the subnet; larger values make the traffic less bursty, as host responses are spread out over a larger interval' ) ) ;
o . placeholder = '1000' ;
o . datatype = 'uinteger' ;
o . validate = function ( section _id , value ) {
var qiopt = L . toArray ( this . map . lookupOption ( 'query_interval' , section _id ) ) [ 0 ] ,
qival = qiopt ? ( qiopt . formvalue ( section _id ) || qiopt . placeholder ) : '' ;
if ( value != '' && qival != '' && + value >= + qival )
return _ ( 'The query response interval must be lower than the query interval value' ) ;
return true ;
} ;
o . depends ( { type : 'bridge' , multicast _querier : '1' } ) ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'last_member_interval' , _ ( 'Last member interval' ) , _ ( 'The max response time in centiseconds inserted into group-specific queries sent in response to leave group messages. It is also the amount of time between group-specific query messages. This value may be tuned to modify the "leave latency" of the network. A reduced value results in reduced time to detect the loss of the last member of a group' ) ) ;
o . placeholder = '100' ;
o . datatype = 'uinteger' ;
o . depends ( { type : 'bridge' , multicast _querier : '1' } ) ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'mtu' , _ ( 'MTU' ) ) ;
o . datatype = 'range(576, 9200)' ;
o . validate = function ( section _id , value ) {
var parent _mtu = ( dev && dev . getType ( ) == 'vlan' ) ? ( parent _dev ? parent _dev . getMTU ( ) : null ) : null ;
if ( parent _mtu !== null && + value > parent _mtu )
return _ ( 'The MTU must not exceed the parent device MTU of %d bytes' ) . format ( parent _mtu ) ;
return true ;
} ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'macaddr' , _ ( 'MAC address' ) ) ;
o . datatype = 'macaddr' ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'peer_name' , _ ( 'Peer device name' ) ) ;
o . rmempty = true ;
o . datatype = 'maxlength(15)' ;
o . depends ( 'type' , 'veth' ) ;
o . load = function ( section _id ) {
var sections = uci . sections ( 'network' , 'device' ) ,
idx = 0 ;
for ( var i = 0 ; i < sections . length ; i ++ )
if ( sections [ i ] [ '.name' ] == section _id )
break ;
else if ( sections [ i ] . type == 'veth' )
idx ++ ;
this . placeholder = 'veth%d' . format ( idx ) ;
return form . Value . prototype . load . apply ( this , arguments ) ;
} ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'peer_macaddr' , _ ( 'Peer MAC address' ) ) ;
o . rmempty = true ;
o . datatype = 'macaddr' ;
o . depends ( 'type' , 'veth' ) ;
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'txqueuelen' , _ ( 'TX queue length' ) ) ;
o . placeholder = dev ? dev . _devstate ( 'qlen' ) : '' ;
o . datatype = 'uinteger' ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'promisc' , _ ( 'Enable promiscuous mode' ) ) ;
o . default = o . disabled ;
2022-07-04 19:46:22 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'autoneg' , _ ( 'Autonegociation' ) ) ;
o . default = o . enabled ;
2023-04-01 06:32:30 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'gro' , _ ( 'Generic Receive Offload (GRO)' ) ) ;
o . default = o . enabled ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'gso' , _ ( 'Generic Segmentation Offload (GSO)' ) ) ;
o . default = o . enabled ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'tso' , _ ( 'TCP Segmentation Offload (TSO)' ) ) ;
o . default = o . enabled ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'lro' , _ ( 'Large Receive Offload (LRO)' ) ) ;
o . default = o . enabled ;
o = this . replaceOption ( s , 'devadvanced' , form . Flag , 'ufo' , _ ( 'UDP Fragmentation Offload (UFO)' ) ) ;
o . default = o . enabled ;
2022-07-04 19:46:22 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'speed' , _ ( 'Speed' ) ) ;
o . placeholder = dev ? dev . getSpeed ( ) : '' ;
o . default = '' ;
o . rmempty = true ;
o . datatype = 'uinteger' ;
o . depends ( 'autoneg' , '0' ) ;
o = this . replaceOption ( s , 'devadvanced' , form . ListValue , 'duplex' , _ ( 'Duplex' ) ) ;
o . default = '' ;
o . value ( '' , _ ( 'unknown' ) ) ;
o . value ( 'half' , _ ( 'half' ) ) ;
o . value ( 'full' , _ ( 'full' ) ) ;
o . depends ( 'autoneg' , '0' ) ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . ListValue , 'rpfilter' , _ ( 'Reverse path filter' ) ) ;
o . default = '' ;
o . value ( '' , _ ( 'disabled' ) ) ;
o . value ( 'loose' , _ ( 'Loose filtering' ) ) ;
o . value ( 'strict' , _ ( 'Strict filtering' ) ) ;
2023-11-27 15:37:15 +00:00
o . cfgvalue = function ( /* ... */ ) {
var val = form . ListValue . prototype . cfgvalue . apply ( this , arguments ) ;
2021-06-25 17:45:23 +00:00
switch ( val || '' ) {
case 'loose' :
case '1' :
return 'loose' ;
case 'strict' :
case '2' :
return 'strict' ;
default :
return '' ;
}
} ;
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'acceptlocal' , _ ( 'Accept local' ) , _ ( 'Accept packets with local source addresses' ) ) ;
o . sysfs = '/proc/sys/net/ipv4/conf/%s/accept_local' . format ( devname || 'default' ) ;
2021-06-25 17:45:23 +00:00
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'sendredirects' , _ ( 'Send ICMP redirects' ) ) ;
o . sysfs = '/proc/sys/net/ipv4/conf/%s/send_redirects' . format ( devname || 'default' ) ;
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'arp_accept ' , _ ( 'Honor gratuitous ARP' ) , _ ( 'When enabled, new ARP table entries are added from received gratuitous APR requests or replies, otherwise only preexisting table entries are updated, but no new hosts are learned.' ) ) ;
o . sysfs = '/proc/sys/net/ipv4/conf/%s/arp_accept' . format ( devname || 'default' ) ;
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'drop_gratuitous_arp' , _ ( 'Drop gratuitous ARP' ) , _ ( 'Drop all gratuitous ARP frames, for example if there’ s a known good ARP proxy on the network and such frames need not be used or in the case of 802.11, must not be used to prevent attacks.' ) ) ;
o . sysfs = '/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp' . format ( devname || 'default' ) ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'neighreachabletime' , _ ( 'Neighbour cache validity' ) , _ ( 'Time in milliseconds' ) ) ;
o . placeholder = '30000' ;
o . datatype = 'uinteger' ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'neighgcstaletime' , _ ( 'Stale neighbour cache timeout' ) , _ ( 'Timeout in seconds' ) ) ;
o . placeholder = '60' ;
o . datatype = 'uinteger' ;
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'neighlocktime' , _ ( 'Minimum ARP validity time' ) , _ ( 'Minimum required time in seconds before an ARP entry may be replaced. Prevents ARP cache thrashing.' ) ) ;
o . placeholder = '0' ;
o . datatype = 'uinteger' ;
2021-08-30 19:47:24 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . Value , 'ttl' , _ ( 'Force TTL' ) , _ ( 'Some LTE providers detect tethering by inspecting packet TTL values' ) ) ;
o . placeholder = '65' ;
o . datatype = 'uinteger' ;
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'devgeneral' , cbiFlagTristate , 'ipv6' , _ ( 'Enable IPv6' ) ) ;
o . sysfs = '!/proc/sys/net/ipv6/conf/%s/disable_ipv6' . format ( devname || 'default' ) ;
2021-06-25 17:45:23 +00:00
o . migrate = false ;
2023-11-27 15:37:15 +00:00
//o.default = o.enabled;
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'ip6segmentrouting' , _ ( 'Enable IPv6 segment routing' ) ) ;
o . sysfs = '/proc/sys/net/ipv6/conf/%s/seg6_enabled' . format ( devname || 'default' ) ;
o . depends ( 'ipv6' , /1/ ) ;
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'drop_unsolicited_na' , _ ( 'Drop unsolicited NA' ) , _ ( 'Drop all unsolicited neighbor advertisements, for example if there’ s a known good NA proxy on the network and such frames need not be used or in the case of 802.11, must not be used to prevent attacks.' ) ) ;
o . sysfs = '/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na' . format ( devname || 'default' ) ;
o . depends ( 'ipv6' , /1/ ) ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'mtu6' , _ ( 'IPv6 MTU' ) ) ;
o . datatype = 'max(9200)' ;
2023-11-27 15:37:15 +00:00
o . depends ( 'ipv6' , /1/ ) ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'devgeneral' , form . Value , 'dadtransmits' , _ ( 'DAD transmits' ) , _ ( 'Amount of Duplicate Address Detection probes to send' ) ) ;
o . placeholder = '1' ;
o . datatype = 'uinteger' ;
2023-11-27 15:37:15 +00:00
o . depends ( 'ipv6' , /1/ ) ;
2021-06-25 17:45:23 +00:00
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'devadvanced' , cbiFlagTristate , 'multicast' , _ ( 'Enable multicast support' ) ) ;
o . sysfs _default = ( dev && dev . dev && dev . dev . flags ) ? dev . dev . flags . multicast : null ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . ListValue , 'igmpversion' , _ ( 'Force IGMP version' ) ) ;
o . value ( '' , _ ( 'No enforcement' ) ) ;
o . value ( '1' , _ ( 'Enforce IGMPv1' ) ) ;
o . value ( '2' , _ ( 'Enforce IGMPv2' ) ) ;
o . value ( '3' , _ ( 'Enforce IGMPv3' ) ) ;
2023-11-27 15:37:15 +00:00
o . depends ( 'multicast' , /1/ ) ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'devadvanced' , form . ListValue , 'mldversion' , _ ( 'Force MLD version' ) ) ;
o . value ( '' , _ ( 'No enforcement' ) ) ;
o . value ( '1' , _ ( 'Enforce MLD version 1' ) ) ;
o . value ( '2' , _ ( 'Enforce MLD version 2' ) ) ;
2023-11-27 15:37:15 +00:00
o . depends ( 'multicast' , /1/ ) ;
2021-06-25 17:45:23 +00:00
if ( isBridgePort ( dev ) ) {
o = this . replaceOption ( s , 'brport' , form . Flag , 'learning' , _ ( 'Enable MAC address learning' ) ) ;
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'learning' , _ ( 'Enable MAC address learning' ) ) ;
o . sysfs = '/sys/class/net/%s/brport/learning' . format ( devname || 'default' ) ;
2021-06-25 17:45:23 +00:00
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'unicast_flood' , _ ( 'Enable unicast flooding' ) ) ;
o . sysfs = '/sys/class/net/%s/brport/unicast_flood' . format ( devname || 'default' ) ;
2021-06-25 17:45:23 +00:00
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'isolate' , _ ( 'Port isolation' ) , _ ( 'Only allow communication with non-isolated bridge ports when enabled' ) ) ;
o . sysfs = '/sys/class/net/%s/brport/isolated' . format ( devname || 'default' ) ;
2021-06-25 17:45:23 +00:00
o = this . replaceOption ( s , 'brport' , form . ListValue , 'multicast_router' , _ ( 'Multicast routing' ) ) ;
o . value ( '' , _ ( 'Never' ) ) ;
o . value ( '1' , _ ( 'Learn' ) ) ;
o . value ( '2' , _ ( 'Always' ) ) ;
2023-11-27 15:37:15 +00:00
o . depends ( 'multicast' , /1/ ) ;
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'multicast_to_unicast' , _ ( 'Multicast to unicast' ) , _ ( 'Forward multicast packets as unicast packets on this device.' ) ) ;
o . sysfs = '/sys/class/net/%s/brport/multicast_to_unicast' . format ( devname || 'default' ) ;
o . depends ( 'multicast' , /1/ ) ;
2021-06-25 17:45:23 +00:00
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'multicast_fast_leave' , _ ( 'Enable multicast fast leave' ) ) ;
o . sysfs = '/sys/class/net/%s/brport/multicast_fast_leave' . format ( devname || 'default' ) ;
o . depends ( 'multicast' , /1/ ) ;
2021-06-25 17:45:23 +00:00
2023-11-27 15:37:15 +00:00
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'drop_v4_unicast_in_l2_multicast' , _ ( 'Drop nested IPv4 unicast' ) , _ ( 'Drop layer 2 multicast frames containing IPv4 unicast packets.' ) ) ;
o . sysfs = '/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast' . format ( devname || 'default' ) ;
o . depends ( 'multicast' , /1/ ) ;
o = this . replaceOption ( s , 'brport' , cbiFlagTristate , 'drop_v6_unicast_in_l2_multicast' , _ ( 'Drop nested IPv6 unicast' ) , _ ( 'Drop layer 2 multicast frames containing IPv6 unicast packets.' ) ) ;
o . sysfs = '/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast' . format ( devname || 'default' ) ;
o . depends ( 'multicast' , /1/ ) ;
2021-06-25 17:45:23 +00:00
}
o = this . replaceOption ( s , 'bridgevlan' , form . Flag , 'vlan_filtering' , _ ( 'Enable VLAN filtering' ) ) ;
o . depends ( 'type' , 'bridge' ) ;
o . updateDefaultValue = function ( section _id ) {
var device = uci . get ( 'network' , s . section , 'name' ) ,
uielem = this . getUIElement ( section _id ) ,
has _vlans = false ;
uci . sections ( 'network' , 'bridge-vlan' , function ( bvs ) {
has _vlans = has _vlans || ( bvs . device == device ) ;
} ) ;
this . default = has _vlans ? this . enabled : this . disabled ;
if ( uielem && ! uielem . isChanged ( ) )
uielem . setValue ( this . default ) ;
} ;
o = this . replaceOption ( s , 'bridgevlan' , form . SectionValue , 'bridge-vlan' , form . TableSection , 'bridge-vlan' ) ;
o . depends ( 'type' , 'bridge' ) ;
ss = o . subsection ;
ss . addremove = true ;
ss . anonymous = true ;
ss . renderHeaderRows = function ( /* ... */ ) {
var node = form . TableSection . prototype . renderHeaderRows . apply ( this , arguments ) ;
node . querySelectorAll ( '.th' ) . forEach ( function ( th ) {
th . classList . add ( 'left' ) ;
th . classList . add ( 'middle' ) ;
} ) ;
return node ;
} ;
ss . filter = function ( section _id ) {
var devname = uci . get ( 'network' , s . section , 'name' ) ;
return ( uci . get ( 'network' , section _id , 'device' ) == devname ) ;
} ;
ss . render = function ( /* ... */ ) {
return form . TableSection . prototype . render . apply ( this , arguments ) . then ( L . bind ( function ( node ) {
node . style . overflow = 'auto hidden' ;
node . style . paddingTop = '1em' ;
if ( this . node )
this . node . parentNode . replaceChild ( node , this . node ) ;
this . node = node ;
return node ;
} , this ) ) ;
} ;
ss . redraw = function ( ) {
return this . load ( ) . then ( L . bind ( this . render , this ) ) ;
} ;
ss . updatePorts = function ( ports ) {
var devices = ports . map ( function ( port ) {
return network . instantiateDevice ( port )
} ) . filter ( function ( dev ) {
return dev . getType ( ) != 'wifi' || dev . isUp ( ) ;
2023-11-27 15:37:15 +00:00
} ) . sort ( function ( a , b ) {
return L . naturalCompare ( a . getName ( ) , b . getName ( ) ) ;
2021-06-25 17:45:23 +00:00
} ) ;
this . children = this . children . filter ( function ( opt ) { return ! opt . option . match ( /^port_/ ) } ) ;
for ( var i = 0 ; i < devices . length ; i ++ ) {
o = ss . option ( cbiTagValue , 'port_%s' . format ( sfh ( devices [ i ] . getName ( ) ) ) , renderDevBadge ( devices [ i ] ) , renderPortStatus ( devices [ i ] ) ) ;
o . port = devices [ i ] . getName ( ) ;
}
var section _ids = this . cfgsections ( ) ,
device _names = devices . reduce ( function ( names , dev ) { names [ dev . getName ( ) ] = true ; return names } , { } ) ;
for ( var i = 0 ; i < section _ids . length ; i ++ ) {
var old _spec = L . toArray ( uci . get ( 'network' , section _ids [ i ] , 'ports' ) ) ,
new _spec = old _spec . filter ( function ( spec ) { return device _names [ spec . replace ( /:[ut*]+$/ , '' ) ] } ) ;
if ( old _spec . length != new _spec . length )
uci . set ( 'network' , section _ids [ i ] , 'ports' , new _spec . length ? new _spec : null ) ;
}
} ;
ss . handleAdd = function ( ev ) {
return s . parse ( ) . then ( L . bind ( function ( ) {
var device = uci . get ( 'network' , s . section , 'name' ) ,
section _ids = this . cfgsections ( ) ,
section _id = null ,
max _vlan _id = 0 ;
if ( ! device )
return ;
for ( var i = 0 ; i < section _ids . length ; i ++ ) {
var vid = + uci . get ( 'network' , section _ids [ i ] , 'vlan' ) ;
if ( vid > max _vlan _id )
max _vlan _id = vid ;
}
section _id = uci . add ( 'network' , 'bridge-vlan' ) ;
uci . set ( 'network' , section _id , 'device' , device ) ;
uci . set ( 'network' , section _id , 'vlan' , max _vlan _id + 1 ) ;
s . children . forEach ( function ( opt ) {
switch ( opt . option ) {
case 'type' :
case 'name_complex' :
var input = opt . map . findElement ( 'id' , 'widget.%s' . format ( opt . cbid ( s . section ) ) ) ;
if ( input )
input . disabled = true ;
break ;
}
} ) ;
s . getOption ( 'vlan_filtering' ) . updateDefaultValue ( s . section ) ;
s . map . addedVLANs = s . map . addedVLANs || [ ] ;
s . map . addedVLANs . push ( section _id ) ;
return this . redraw ( ) ;
} , this ) ) ;
} ;
o = ss . option ( form . Value , 'vlan' , _ ( 'VLAN ID' ) ) ;
o . datatype = 'range(1, 4094)' ;
o . renderWidget = function ( /* ... */ ) {
var node = form . Value . prototype . renderWidget . apply ( this , arguments ) ;
node . style . width = '5em' ;
return node ;
} ;
o . validate = function ( section _id , value ) {
var section _ids = this . section . cfgsections ( ) ;
for ( var i = 0 ; i < section _ids . length ; i ++ ) {
if ( section _ids [ i ] == section _id )
continue ;
if ( uci . get ( 'network' , section _ids [ i ] , 'vlan' ) == value )
return _ ( 'The VLAN ID must be unique' ) ;
}
return true ;
} ;
o = ss . option ( form . Flag , 'local' , _ ( 'Local' ) ) ;
o . default = o . enabled ;
var ports = [ ] ;
var seen _ports = { } ;
L . toArray ( uci . get ( 'network' , s . section , 'ports' ) ) . forEach ( function ( port ) {
seen _ports [ port ] = true ;
} ) ;
uci . sections ( 'network' , 'bridge-vlan' , function ( bvs ) {
if ( uci . get ( 'network' , s . section , 'name' ) != bvs . device )
return ;
L . toArray ( bvs . ports ) . forEach ( function ( portspec ) {
var m = portspec . match ( /^([^:]+)(?::[ut*]+)?$/ ) ;
if ( m )
seen _ports [ m [ 1 ] ] = true ;
} ) ;
} ) ;
for ( var port _name in seen _ports )
ports . push ( port _name ) ;
ss . updatePorts ( ports ) ;
} ,
updateDevBadge : updateDevBadge ,
updatePortStatus : updatePortStatus
} ) ;