'use strict'; 'require form'; 'require fs'; 'require uci'; 'require ui'; 'require rpc'; 'require poll'; 'require view'; 'require network'; 'require tools.widgets as widgets'; const conf = 'einat'; const instance = 'einat'; const callServiceList = rpc.declare({ object: 'service', method: 'list', params: ['name'], expect: { '': {} } }); const callRcInit = rpc.declare({ object: 'rc', method: 'init', params: ['name', 'action'] }); const callGetFeatures = rpc.declare({ object: 'luci.einat', method: 'get_features', expect: { '': {} } }); function getServiceStatus() { return L.resolveDefault(callServiceList(conf), {}) .then((res) => { let isrunning = false; try { isrunning = res[conf]['instances'][instance]['running']; } catch (e) { } return isrunning; }); } function handleAction(action, ev) { return callRcInit("einat", action).then((ret) => { if (ret) throw _('Command failed'); return true; }).catch((e) => { ui.addNotification(null, E('p', _('Failed to execute "/etc/init.d/%s %s" action: %s').format("einat", action, e))); }); } return view.extend({ load() { return Promise.all([ getServiceStatus(), L.resolveDefault(fs.stat('/usr/bin/einat'), null), callGetFeatures(), uci.load('einat') ]); }, poll_status(nodes, stat) { const isRunning = stat[0]; let view = nodes.querySelector('#service_status'); if (isRunning) { view.innerHTML = "<span style=\"color:green;font-weight:bold\">" + instance + " - " + _("SERVER RUNNING") + "</span>"; } else { view.innerHTML = "<span style=\"color:red;font-weight:bold\">" + instance + " - " + _("SERVER NOT RUNNING") + "</span>"; } return; }, render(res) { const isRunning = res[0]; const has_einat = res[1] ? res[1].path : null; const features = res[2]; let m, s, o; m = new form.Map('einat', _('einat-ebpf'), _('eBPF-based Endpoint-Independent NAT')); s = m.section(form.NamedSection, '_status'); s.anonymous = true; s.render = function(section_id) { return E('div', { class: 'cbi-section' }, [ E('div', { id: 'service_status' }, _('Collecting data ...')) ]); }; s = m.section(form.NamedSection, 'config', instance); s.anonymous = true; o = s.option(form.Button, '_reload', _('Reload')); o.inputtitle = _('Reload'); o.inputstyle = 'apply'; o.onclick = function() { return handleAction('reload'); }; o = s.option(form.Flag, 'enabled', _('Enable')); o.default = o.disabled; o.rmempty = false; if (! has_einat) { o.description = _('To enable you need install <b>einat-ebpf</b> first'); o.readonly = true; } o = s.option(form.ListValue, 'bpf_log_level', _('BPF tracing log level')); o.default = '0'; o.value('0', 'disable - ' + _('Disable')); o.value('1', 'error - ' + _('Error')); o.value('2', 'warn - ' + _('Warn')); o.value('3', 'info - ' + _('Info')); o.value('4', 'debug - ' + _('Debug')); o.value('5', 'trace - ' + _('Trace')); o = s.option(form.ListValue, 'bpf_loader', _('BPF loading backend')); o.value('', _('Default')); if (features.features.includes('aya')) o.value('aya', _('aya')); if (features.features.includes('libbpf')) o.value('libbpf', _('libbpf')); o = s.option(form.Flag, 'nat44', _('NAT44')); o.default = o.disabled; o.rmempty = false; //o = s.option(form.Flag, 'nat66', _('NAT66')); //o.default = o.disabled; //o.rmempty = false; o = s.option(widgets.DeviceSelect, 'ifname', _('External interface')); o.multiple = false; o.noaliases = true; o.nobridges = true; o.nocreate = true; o = s.option(form.Value, 'ports', _('External TCP/UDP port ranges'), _('Please avoid conflicts with external ports used by other applications')); o.datatype = 'portrange'; o.placeholder = '20000-29999'; o.rmempty = true; o = s.option(widgets.NetworkSelect, 'internal_ifaces', _('Internal interfaces'), _('Perform source NAT for these internal networks only.')); o.multiple = true; o.nocreate = true; o = s.option(form.DynamicList, 'internal_subnets', _('Internal subnets'), _('Perform source NAT for these internal networks only.')); o.datatype = 'cidr'; o.placeholder = '192.168.0.0/16'; o = s.option(form.Flag, 'hairpin_enabled', _('Enable hairpin'), _('May conflict with other policy routing-based applications')); o.default = o.disabled; o.rmempty = false; o = s.option(widgets.DeviceSelect, 'hairpinif', _('Hairpin internal interfaces')); o.multiple = true; o.noaliases = true; o.nobridges = false; o.nocreate = true; o.depends('hairpin_enabled', '1'); o.rmempty = true; o.retain = true; return m.render() .then(L.bind(function(m, nodes) { poll.add(L.bind(function() { return Promise.all([ getServiceStatus() ]).then(L.bind(this.poll_status, this, nodes)); }, this), 3); return nodes; }, this, m)); } });