@@ -12,12 +12,6 @@ const callLuciRealtimeStats = rpc.declare({
1212 expect : { result : [ ] }
1313} ) ;
1414
15- const callLuciConntrackList = rpc . declare ( {
16- object : 'luci' ,
17- method : 'getConntrackList' ,
18- expect : { result : [ ] }
19- } ) ;
20-
2115const callNetworkRrdnsLookup = rpc . declare ( {
2216 object : 'network.rrdns' ,
2317 method : 'lookup' ,
@@ -56,10 +50,61 @@ const recheck_lookup_queue = {};
5650
5751Math . log2 = Math . log2 || function ( x ) { return Math . log ( x ) * Math . LOG2E ; } ;
5852
53+ /*
54+ * Convert an full IPv6 address string to a shortened format
55+ * Examples:
56+ [
57+ compressIpv6('2620:01ec:0029:0001:0000:0000:0000:0049') === '2620:1ec:29:1::49',
58+ compressIpv6('fe80:0000:0000:0000:d86d:2fff:fe24:f6ea') === 'fe80::d86d:2fff:fe24:f6ea',
59+ compressIpv6('fe80:0000:0000:0000:d86d:2fff:0000:f6ea') === 'fe80::d86d:2fff:0:f6ea',
60+ compressIpv6('fe80:0100:0010:0001:d86d:2fff:0000:f6ea') === 'fe80:100:10:1:d86d:2fff::f6ea',
61+ compressIpv6('fe80:0000:d86d:2fff:0000:0000:0000:f6ea') === 'fe80:0:d86d:2fff::f6ea',
62+ compressIpv6('ff02:0000:0000:0000:0000:0000:0001:0002') === 'ff02::1:2',
63+ compressIpv6('0000:0000:0000:0000:0000:0000:0000:0001') === '::1',
64+ compressIpv6('0000:0000:0000:0000:0000:0000:0000:0000') === '::',
65+ compressIpv6('0001:0000:0000:0000:0000:0000:0000:0000') === '1::',
66+ compressIpv6('0000:0001:0001:0001:0001:0001:0001:0001') === '::1:1:1:1:1:1:1',
67+ compressIpv6('0001:0000:0001:0001:0001:0001:0001:0001') === '1::1:1:1:1:1:1',
68+ compressIpv6('0001:0001:0001:0001:0001:0001:0001:0001') === '1:1:1:1:1:1:1:1',
69+ ]
70+ */
71+ const compressIpv6 = function ( addr ) {
72+ if ( addr . indexOf ( ':' ) === - 1 )
73+ return addr ;
74+
75+ const parts = addr . split ( ':' ) ;
76+ let best_start = 0 , best_len = 0 , cur_start = 0 , cur_len = 0 ;
77+ for ( let i = 0 ; i < parts . length ; i ++ ) {
78+ if ( parts [ i ] . startsWith ( '0' ) )
79+ parts [ i ] = parts [ i ] . replace ( / ^ 0 + / , '' ) . replace ( / ^ $ / , '0' ) ;
80+
81+ if ( parts [ i ] === '0' ) {
82+ if ( cur_len === 0 )
83+ cur_start = i ;
84+ cur_len ++ ;
85+ } else {
86+ if ( cur_len > best_len ) {
87+ best_start = cur_start ;
88+ best_len = cur_len ;
89+ }
90+ cur_len = 0 ;
91+ }
92+ }
93+ if ( cur_len > best_len ) {
94+ best_start = cur_start ;
95+ best_len = cur_len ;
96+ }
97+ if ( best_len > 0 ) {
98+ parts . splice ( best_start , best_len , '' ) ;
99+ }
100+ return ( best_start === 0 && best_len > 0 ?':' :'' ) + parts . join ( ':' ) + ( best_start + best_len === 8 ?':' :'' ) ;
101+ } ;
102+
59103return view . extend ( {
60104 load ( ) {
61105 return Promise . all ( [
62- this . loadSVG ( L . resource ( 'svg/connections.svg' ) )
106+ this . loadSVG ( L . resource ( 'svg/connections.svg' ) ) ,
107+ fs . lines ( '/etc/protocols' )
63108 ] ) ;
64109 } ,
65110
@@ -282,10 +327,79 @@ return view.extend({
282327 }
283328 } ,
284329
330+ /*
331+ * Replace the conntrack ubus call with a fs.exec_direct to prevent
332+ * procd from being kicked off by ubusd due to large amounts of data. See https://github.com/openwrt/openwrt/issues/9747
333+ *
334+ * Copy from modules/luci-base/ucode/sys.uc:conntrack_list with adjustments for js
335+ */
336+ conntrackList : function ( ) {
337+ const protos = this . protos ;
338+ return fs . exec_direct ( '/usr/libexec/luci-status-call' , [ 'conntrack' ] ) . then ( function ( data ) {
339+ const connt = [ ] ;
340+
341+ data . split ( '\n' ) . forEach ( function ( line ) {
342+ let m = line . match ( / ^ ( i p v [ 4 6 ] ) + ( [ 0 - 9 ] + ) + \S + + ( [ 0 - 9 ] + ) ( + .+ ) $ / ) ;
343+ if ( ! m )
344+ return ;
345+
346+ const fam = m [ 1 ] ;
347+ const l4 = m [ 3 ] ;
348+ let tuples = m [ 4 ] ;
349+ let timeout = null ;
350+
351+ m = tuples . match ( / ^ + ( [ 0 - 9 ] + ) ( .+ ) $ / ) ;
352+
353+ if ( m ) {
354+ timeout = m [ 1 ] ;
355+ tuples = m [ 2 ] ;
356+ }
357+
358+ const e = {
359+ bytes : 0 ,
360+ packets : 0 ,
361+ layer3 : fam ,
362+ layer4 : protos [ l4 ] || 'unknown' ,
363+ timeout : + timeout
364+ } ;
365+
366+ tuples . split ( ' ' ) . forEach ( function ( tuple ) {
367+ const kv = tuple . match ( / ^ ( \w + ) = ( \S + ) $ / ) ;
368+
369+ if ( ! kv )
370+ return ;
371+
372+ switch ( kv [ 1 ] ) {
373+ case 'bytes' :
374+ case 'packets' :
375+ e [ kv [ 1 ] ] += + kv [ 2 ] ;
376+ break ;
377+
378+ case 'src' :
379+ case 'dst' :
380+ if ( undefined === e [ kv [ 1 ] ] )
381+ e [ kv [ 1 ] ] = compressIpv6 ( kv [ 2 ] ) ;
382+ break ;
383+
384+ case 'sport' :
385+ case 'dport' :
386+ if ( undefined === e [ kv [ 1 ] ] )
387+ e [ kv [ 1 ] ] = + kv [ 2 ] ;
388+ break ;
389+ }
390+ } ) ;
391+
392+ connt . push ( e ) ;
393+ } ) ;
394+ return connt ;
395+
396+ } ) ;
397+ } ,
398+
285399 async pollData ( ) {
286400 poll . add ( L . bind ( async function ( ) {
287401 const tasks = [
288- L . resolveDefault ( callLuciConntrackList ( ) , [ ] )
402+ L . resolveDefault ( this . conntrackList ( ) , [ ] )
289403 ] ;
290404
291405 graphPolls . forEach ( ( ) => {
@@ -415,7 +529,15 @@ return view.extend({
415529 } ) ;
416530 } ,
417531
418- render ( [ svg ] ) {
532+ render ( [ svg , protocols ] ) {
533+ const protos = { } ;
534+ protocols . forEach ( function ( line ) {
535+ const m = line . match ( / ^ ( [ ^ # \t \n ] + ) \s + ( [ 0 - 9 ] + ) \s + / ) ;
536+
537+ if ( m )
538+ protos [ m [ 2 ] ] = m [ 1 ] ;
539+ } ) ;
540+ this . protos = protos ;
419541
420542 const v = E ( 'div' , { 'class' : 'cbi-map' , 'id' : 'map' } , [
421543 E ( 'h2' , _ ( 'Connections' ) ) ,
0 commit comments