2024-12-11 15:53:25 +00:00
'use strict' ;
'require view' ;
'require ui' ;
'require rpc' ;
'require uci' ;
'require form' ;
'require firewall as fwmodel' ;
'require tools.firewall as fwtool' ;
'require tools.widgets as widgets' ;
function rule _proto _txt ( s , ctHelpers ) {
var family = ( uci . get ( 'firewall' , s , 'family' ) || '' ) . toLowerCase ( ) . replace ( /^(?:all|\*)$/ , 'any' ) ;
var dip = uci . get ( 'firewall' , s , 'dest_ip' ) || '' ;
var proto = L . toArray ( uci . get ( 'firewall' , s , 'proto' ) ) . filter ( function ( p ) {
return ( p != '*' && p != 'any' && p != 'all' ) ;
} ) . map ( function ( p ) {
var pr = fwtool . lookupProto ( p ) ;
return {
num : pr [ 0 ] ,
name : pr [ 1 ] ,
types : ( pr [ 0 ] == 1 || pr [ 0 ] == 58 ) ? L . toArray ( uci . get ( 'firewall' , s , 'icmp_type' ) ) : null
} ;
} ) ;
var m = String ( uci . get ( 'firewall' , s , 'helper' ) || '' ) . match ( /^(!\s*)?(\S+)$/ ) ;
var h = m ? {
val : m [ 0 ] . toUpperCase ( ) ,
inv : m [ 1 ] ,
name : ( ctHelpers . filter ( function ( ctH ) { return ctH . name . toLowerCase ( ) == m [ 2 ] . toLowerCase ( ) } ) [ 0 ] || { } ) . description
} : null ;
m = String ( uci . get ( 'firewall' , s , 'mark' ) ) . match ( /^(!\s*)?(0x[0-9a-f]{1,8}|[0-9]{1,10})(?:\/(0x[0-9a-f]{1,8}|[0-9]{1,10}))?$/i ) ;
var f = m ? {
val : m [ 0 ] . toUpperCase ( ) . replace ( /X/g , 'x' ) ,
inv : m [ 1 ] ,
num : '0x%02X' . format ( + m [ 2 ] ) ,
mask : m [ 3 ] ? '0x%02X' . format ( + m [ 3 ] ) : null
} : null ;
return fwtool . fmt ( _ ( 'Incoming %{ipv6?%{ipv4?<var>IPv4</var> and <var>IPv6</var>:<var>IPv6</var>}:<var>IPv4</var>}%{proto?, protocol %{proto#%{next?, }%{item.types?<var class="cbi-tooltip-container">%{item.name}<span class="cbi-tooltip">ICMP with types %{item.types#%{next?, }<var>%{item}</var>}</span></var>:<var>%{item.name}</var>}}}%{mark?, mark <var%{mark.inv? data-tooltip="Match fwmarks except %{mark.num}%{mark.mask? with mask %{mark.mask}}.":%{mark.mask? data-tooltip="Mask fwmark value with %{mark.mask} before compare."}}>%{mark.val}</var>}%{helper?, helper %{helper.inv?<var data-tooltip="Match any helper except "%{helper.name}"">%{helper.val}</var>:<var data-tooltip="%{helper.name}">%{helper.val}</var>}}' ) , {
ipv4 : ( ( ! family && dip . indexOf ( ':' ) == - 1 ) || family == 'any' || ( ! family && ! dip ) || family == 'ipv4' ) ,
ipv6 : ( ( ! family && dip . indexOf ( ':' ) != - 1 ) || family == 'any' || family == 'ipv6' ) ,
proto : proto ,
helper : h ,
mark : f
} ) ;
}
function rule _src _txt ( s , hosts ) {
var z = uci . get ( 'firewall' , s , 'src' ) ;
return fwtool . fmt ( _ ( 'From %{src}%{src_ip?, IP %{src_ip#%{next?, }<var%{item.inv? data-tooltip="Match IP addresses except %{item.val}."}>%{item.ival}</var>}}%{src_port?, port %{src_port#%{next?, }<var%{item.inv? data-tooltip="Match ports except %{item.val}."}>%{item.ival}</var>}}%{src_mac?, MAC %{src_mac#%{next?, }<var%{item.inv? data-tooltip="Match MACs except %{item.val}%{item.hint.name? a.k.a. %{item.hint.name}}.":%{item.hint.name? data-tooltip="%{item.hint.name}"}}>%{item.ival}</var>}}' ) , {
src : E ( 'span' , { 'class' : 'zonebadge' , 'style' : fwmodel . getZoneColorStyle ( z ) } , [ ( z == '*' ) ? E ( 'em' , _ ( 'any zone' ) ) : ( z ? E ( 'strong' , z ) : E ( 'em' , _ ( 'this device' ) ) ) ] ) ,
src _ip : fwtool . map _invert ( uci . get ( 'firewall' , s , 'src_ip' ) , 'toLowerCase' ) ,
src _mac : fwtool . map _invert ( uci . get ( 'firewall' , s , 'src_mac' ) , 'toUpperCase' ) . map ( function ( v ) { return Object . assign ( v , { hint : hosts [ v . val ] } ) } ) ,
src _port : fwtool . map _invert ( uci . get ( 'firewall' , s , 'src_port' ) )
} ) ;
}
function rule _dest _txt ( s ) {
return fwtool . fmt ( _ ( 'To %{dest}%{dest_ip?, IP %{dest_ip#%{next?, }<var%{item.inv? data-tooltip="Match IP addresses except %{item.val}."}>%{item.ival}</var>}}%{dest_port?, port %{dest_port#%{next?, }<var%{item.inv? data-tooltip="Match ports except %{item.val}."}>%{item.ival}</var>}}' ) , {
dest : E ( 'span' , { 'class' : 'zonebadge' , 'style' : fwmodel . getZoneColorStyle ( null ) } , [ E ( 'em' , _ ( 'this device' ) ) ] ) ,
dest _ip : fwtool . map _invert ( uci . get ( 'firewall' , s , 'src_dip' ) , 'toLowerCase' ) ,
dest _port : fwtool . map _invert ( uci . get ( 'firewall' , s , 'src_dport' ) )
} ) ;
}
function rule _limit _txt ( s ) {
var m = String ( uci . get ( 'firewall' , s , 'limit' ) ) . match ( /^(\d+)\/([smhd])\w*$/i ) ,
l = m ? {
num : + m [ 1 ] ,
unit : ( { s : _ ( 'second' ) , m : _ ( 'minute' ) , h : _ ( 'hour' ) , d : _ ( 'day' ) } ) [ m [ 2 ] ] ,
burst : uci . get ( 'firewall' , s , 'limit_burst' )
} : null ;
if ( ! l )
return '' ;
return fwtool . fmt ( _ ( 'Limit matching to <var>%{limit.num}</var> packets per <var>%{limit.unit}</var>%{limit.burst? burst <var>%{limit.burst}</var>}' ) , { limit : l } ) ;
}
function rule _target _txt ( s ) {
var z = uci . get ( 'firewall' , s , 'dest' ) ;
return fwtool . fmt ( _ ( '<var data-tooltip="DNAT">Forward</var> to %{dest}%{dest_ip? IP <var>%{dest_ip}</var>}%{dest_port? port <var>%{dest_port}</var>}' ) , {
dest : E ( 'span' , { 'class' : 'zonebadge' , 'style' : 'background-color:' + fwmodel . getColorForName ( ( z && z != '*' ) ? z : null ) } , [ ( z == '*' ) ? E ( 'em' , _ ( 'any zone' ) ) : ( z ? E ( 'strong' , z ) : E ( 'em' , _ ( 'this device' ) ) ) ] ) ,
dest _ip : ( uci . get ( 'firewall' , s , 'dest_ip' ) || '' ) . toLowerCase ( ) ,
dest _port : uci . get ( 'firewall' , s , 'dest_port' )
} ) ;
}
function validate _opt _family ( m , section _id , opt ) {
var dopt = m . section . getOption ( 'dest_ip' ) ,
fmopt = m . section . getOption ( 'family' ) ;
if ( ! dopt . isValid ( section _id ) && opt != 'dest_ip' )
return true ;
if ( ! fmopt . isValid ( section _id ) && opt != 'family' )
return true ;
var dip = dopt . formvalue ( section _id ) || '' ,
fm = fmopt . formvalue ( section _id ) || '' ;
if ( fm == '' || ( fm == 'any' && dip == '' ) || ( fm == 'ipv6' && ( dip . indexOf ( ':' ) != - 1 || dip == '' ) ) || ( fm == 'ipv4' && dip . indexOf ( ':' ) == - 1 ) )
return true ;
return _ ( 'Address family, Internal IP address must match' ) ;
}
return view . extend ( {
callHostHints : rpc . declare ( {
object : 'luci-rpc' ,
method : 'getHostHints' ,
expect : { '' : { } }
} ) ,
callConntrackHelpers : rpc . declare ( {
object : 'luci' ,
method : 'getConntrackHelpers' ,
expect : { result : [ ] }
} ) ,
callNetworkDevices : rpc . declare ( {
object : 'luci-rpc' ,
method : 'getNetworkDevices' ,
expect : { '' : { } }
} ) ,
load : function ( ) {
return Promise . all ( [
this . callHostHints ( ) ,
this . callConntrackHelpers ( ) ,
this . callNetworkDevices ( ) ,
uci . load ( 'firewall' )
] ) ;
} ,
render : function ( data ) {
if ( fwtool . checkLegacySNAT ( ) )
return fwtool . renderMigration ( ) ;
else
return this . renderForwards ( data ) ;
} ,
renderForwards : function ( data ) {
var hosts = data [ 0 ] ,
ctHelpers = data [ 1 ] ,
devs = data [ 2 ] ,
m , s , o ;
var fw4 = L . hasSystemFeature ( 'firewall4' ) ;
m = new form . Map ( 'firewall' , _ ( 'Firewall - Port Forwards' ) ,
_ ( 'Port forwarding allows remote computers on the Internet to connect to a specific computer or service within the private LAN.' ) ) ;
s = m . section ( form . GridSection , 'redirect' , _ ( 'Port Forwards' ) ) ;
s . addremove = true ;
s . anonymous = true ;
s . sortable = true ;
s . cloneable = true ;
s . tab ( 'general' , _ ( 'General Settings' ) ) ;
s . tab ( 'advanced' , _ ( 'Advanced Settings' ) ) ;
s . filter = function ( section _id ) {
return ( uci . get ( 'firewall' , section _id , 'target' ) != 'SNAT' ) ;
} ;
s . sectiontitle = function ( section _id ) {
return uci . get ( 'firewall' , section _id , 'name' ) || _ ( 'Unnamed forward' ) ;
} ;
s . handleAdd = function ( ev ) {
var config _name = this . uciconfig || this . map . config ,
section _id = uci . add ( config _name , this . sectiontype ) ;
uci . set ( config _name , section _id , 'dest' , 'lan' ) ;
uci . set ( config _name , section _id , 'target' , 'DNAT' ) ;
m . addedSection = section _id ;
this . renderMoreOptionsModal ( section _id ) ;
} ;
o = s . taboption ( 'general' , form . Value , 'name' , _ ( 'Name' ) ) ;
o . placeholder = _ ( 'Unnamed forward' ) ;
o . modalonly = true ;
if ( fw4 ) {
o = s . taboption ( 'general' , form . ListValue , 'family' , _ ( 'Restrict to address family' ) ) ;
o . modalonly = true ;
o . rmempty = true ;
o . value ( 'any' , _ ( 'IPv4 and IPv6' ) ) ;
o . value ( 'ipv4' , _ ( 'IPv4 only' ) ) ;
o . value ( 'ipv6' , _ ( 'IPv6 only' ) ) ;
o . value ( '' , _ ( 'automatic' ) ) ; // infer from zone or used IP addresses
o . cfgvalue = function ( section _id ) {
var val = this . map . data . get ( this . map . config , section _id , 'family' ) ;
if ( ! val )
return '' ;
else if ( val == 'any' || val == 'all' || val == '*' )
return 'any' ;
else if ( val == 'inet' || String ( val ) . indexOf ( '4' ) != - 1 )
return 'ipv4' ;
else if ( String ( val ) . indexOf ( '6' ) != - 1 )
return 'ipv6' ;
} ;
o . validate = function ( section _id , value ) {
fwtool . updateHostHints ( this . map , section _id , 'dest_ip' , value , hosts ) ;
return ! fw4 ? true : validate _opt _family ( this , section _id , 'family' ) ;
} ;
}
o = s . option ( form . DummyValue , '_match' , _ ( 'Match' ) ) ;
o . modalonly = false ;
o . textvalue = function ( s ) {
return E ( 'small' , [
rule _proto _txt ( s , ctHelpers ) , E ( 'br' ) ,
rule _src _txt ( s , hosts ) , E ( 'br' ) ,
rule _dest _txt ( s ) , E ( 'br' ) ,
rule _limit _txt ( s )
] ) ;
} ;
o = s . option ( form . ListValue , '_dest' , _ ( 'Action' ) ) ;
o . modalonly = false ;
o . textvalue = function ( s ) {
return E ( 'small' , [
rule _target _txt ( s )
] ) ;
} ;
o = s . option ( form . Flag , 'enabled' , _ ( 'Enable' ) ) ;
o . modalonly = false ;
o . default = o . enabled ;
o . editable = true ;
o = s . taboption ( 'general' , fwtool . CBIProtocolSelect , 'proto' , _ ( 'Protocol' ) ) ;
o . modalonly = true ;
o . default = 'tcp udp' ;
o = s . taboption ( 'general' , widgets . ZoneSelect , 'src' , _ ( 'Source zone' ) ) ;
o . modalonly = true ;
o . rmempty = false ;
o . nocreate = true ;
o . default = 'wan' ;
o = s . taboption ( 'advanced' , form . Value , 'ipset' , _ ( 'Use ipset' ) ) ;
uci . sections ( 'firewall' , 'ipset' , function ( s ) {
if ( typeof ( s . name ) == 'string' )
o . value ( s . name , s . comment ? '%s (%s)' . format ( s . name , s . comment ) : s . name ) ;
} ) ;
o . modalonly = true ;
o . rmempty = true ;
o = fwtool . addMACOption ( s , 'advanced' , 'src_mac' , _ ( 'Source MAC address' ) ,
_ ( 'Only match incoming traffic from these MACs.' ) , hosts ) ;
o . rmempty = true ;
o . datatype = 'list(neg(macaddr))' ;
o = fwtool . addIPOption ( s , 'advanced' , 'src_ip' , _ ( 'Source IP address' ) ,
_ ( 'Only match incoming traffic from this IP or range.' ) , ! fw4 ? 'ipv4' : '' , hosts ) ;
o . rmempty = true ;
o . datatype = ! fw4 ? 'neg(ipmask4("true"))' : 'neg(ipmask("true"))' ;
o = s . taboption ( 'advanced' , form . Value , 'src_port' , _ ( 'Source port' ) ,
_ ( 'Only match incoming traffic originating from the given source port or port range on the client host' ) ) ;
o . modalonly = true ;
o . rmempty = true ;
o . datatype = 'neg(portrange)' ;
o . placeholder = _ ( 'any' ) ;
o . depends ( { proto : 'tcp' , '!contains' : true } ) ;
o . depends ( { proto : 'udp' , '!contains' : true } ) ;
o = fwtool . addLocalIPOption ( s , 'advanced' , 'src_dip' , _ ( 'External IP address' ) ,
_ ( 'Only match incoming traffic directed at the given IP address.' ) , devs ) ;
o . datatype = ! fw4 ? 'neg(ipmask4("true"))' : 'neg(ipmask("true"))' ;
o . rmempty = true ;
o = s . taboption ( 'general' , form . Value , 'src_dport' , _ ( 'External port' ) ,
_ ( 'Match incoming traffic directed at the given destination port or port range on this host' ) ) ;
o . modalonly = true ;
o . rmempty = false ;
o . datatype = 'neg(portrange)' ;
o . depends ( { proto : 'tcp' , '!contains' : true } ) ;
o . depends ( { proto : 'udp' , '!contains' : true } ) ;
o = s . taboption ( 'general' , widgets . ZoneSelect , 'dest' , _ ( 'Internal zone' ) ) ;
o . modalonly = true ;
o . rmempty = true ;
o . nocreate = true ;
o = fwtool . addIPOption ( s , 'general' , 'dest_ip' , _ ( 'Internal IP address' ) ,
_ ( 'Redirect matched incoming traffic to the specified internal host' ) , ! fw4 ? 'ipv4' : '' , hosts ) ;
o . rmempty = true ;
o . datatype = ! fw4 ? 'ipmask4' : 'ipmask' ;
o = s . taboption ( 'general' , form . Value , 'dest_port' , _ ( 'Internal port' ) ,
_ ( 'Redirect matched incoming traffic to the given port on the internal host' ) ) ;
o . modalonly = true ;
o . rmempty = true ;
o . placeholder = _ ( 'any' ) ;
o . datatype = 'portrange' ;
o . depends ( { proto : 'tcp' , '!contains' : true } ) ;
o . depends ( { proto : 'udp' , '!contains' : true } ) ;
o = s . taboption ( 'advanced' , form . Flag , 'reflection' , _ ( 'Enable NAT Loopback' ) ) ;
o . modalonly = true ;
o . rmempty = true ;
o . default = o . enabled ;
o = s . taboption ( 'advanced' , form . ListValue , 'reflection_src' , _ ( 'Loopback source IP' ) , _ ( 'Specifies whether to use the external or the internal IP address for reflected traffic.' ) ) ;
o . modalonly = true ;
o . depends ( 'reflection' , '1' ) ;
o . value ( 'internal' , _ ( 'Use internal IP address' ) ) ;
o . value ( 'external' , _ ( 'Use external IP address' ) ) ;
o . write = function ( section _id , value ) {
uci . set ( 'firewall' , section _id , 'reflection_src' , ( value != 'internal' ) ? value : null ) ;
} ;
o = s . taboption ( 'advanced' , widgets . ZoneSelect , 'reflection_zone' , _ ( 'Reflection zones' ) , _ ( 'Zones from which reflection rules shall be created. If unset, only the destination zone is used.' ) ) ;
o . nocreate = true ;
o . multiple = true ;
o . modalonly = true ;
o . depends ( 'reflection' , '1' ) ;
o = s . taboption ( 'advanced' , form . Value , 'helper' , _ ( 'Match helper' ) , _ ( 'Match traffic using the specified connection tracking helper.' ) ) ;
o . modalonly = true ;
o . placeholder = _ ( 'any' ) ;
for ( var i = 0 ; i < ctHelpers . length ; i ++ )
o . value ( ctHelpers [ i ] . name , '%s (%s)' . format ( ctHelpers [ i ] . description , ctHelpers [ i ] . name . toUpperCase ( ) ) ) ;
o . validate = function ( section _id , value ) {
if ( value == '' || value == null )
return true ;
value = value . replace ( /^!\s*/ , '' ) ;
for ( var i = 0 ; i < ctHelpers . length ; i ++ )
if ( value == ctHelpers [ i ] . name )
return true ;
return _ ( 'Unknown or not installed conntrack helper "%s"' ) . format ( value ) ;
} ;
fwtool . addMarkOption ( s , false ) ;
fwtool . addLimitOption ( s ) ;
fwtool . addLimitBurstOption ( s ) ;
2024-12-11 16:29:22 +00:00
2024-12-11 15:54:21 +00:00
o = s . taboption ( 'advanced' , form . Flag , 'v2ray' , _ ( 'Use V2Ray/XRay' ) ,
_ ( 'Forward a port (not a range) from server using V2Ray/XRay proxy (if enabled) instead of VPN' ) ) ;
2024-12-11 16:29:22 +00:00
o . modalonly = true ;
2024-12-11 15:53:25 +00:00
if ( ! L . hasSystemFeature ( 'firewall4' ) ) {
o = s . taboption ( 'advanced' , form . Value , 'extra' , _ ( 'Extra arguments' ) ,
_ ( 'Passes additional arguments to iptables. Use with care!' ) ) ;
o . modalonly = true ;
o . rmempty = true ;
}
return m . render ( ) ;
}
} ) ;