config.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. /*
  2. * Copyright (c) 2020 Tano Systems LLC. All Rights Reserved.
  3. * Author: Anton Kikin <a.kikin@tano-systems.com>
  4. * Copyright (c) 2023-2024. All Rights Reserved.
  5. * Paul Donald <newtwen+github@gmail.com>
  6. */
  7. 'use strict';
  8. 'require rpc';
  9. 'require form';
  10. 'require lldpd';
  11. 'require network';
  12. 'require uci';
  13. 'require tools.widgets as widgets';
  14. const callInitList = rpc.declare({
  15. object: 'luci',
  16. method: 'getInitList',
  17. params: [ 'name' ],
  18. expect: { '': {} },
  19. filter: function(res) {
  20. for (var k in res)
  21. return +res[k].enabled;
  22. return null;
  23. }
  24. });
  25. const callInitAction = rpc.declare({
  26. object: 'luci',
  27. method: 'setInitAction',
  28. params: [ 'name', 'action' ],
  29. expect: { result: false }
  30. });
  31. var usage = _('See syntax <a %s>here</a>.').format('href=https://lldpd.github.io/usage.html target="_blank"');
  32. const validateioentries = function(section_id, value) {
  33. if (value) {
  34. const emsg = _('Cannot have both interface %s and its exclusion %s');
  35. const a = value.split(' ');
  36. const noex = a.filter(el=> !el.startsWith('!'));
  37. const ex = a.filter(el=> el.startsWith('!') && !el.startsWith('!!'));
  38. for (var i of noex) {
  39. for (var e of ex) {
  40. if ('!'+i == e){
  41. return emsg.format(i, e);
  42. }
  43. }
  44. }
  45. }
  46. return true;
  47. };
  48. return L.view.extend({
  49. __init__: function() {
  50. this.super('__init__', arguments);
  51. // Inject CSS
  52. var head = document.getElementsByTagName('head')[0];
  53. var css = E('link', { 'href':
  54. L.resource('lldpd/lldpd.css')
  55. + '?v=#PKG_VERSION', 'rel': 'stylesheet' });
  56. head.appendChild(css);
  57. },
  58. load: function() {
  59. return Promise.all([
  60. callInitList('lldpd'),
  61. lldpd.init(),
  62. uci.load('lldpd'),
  63. network.getDevices()
  64. ]);
  65. },
  66. // -----------------------------------------------------------------------------------------
  67. //
  68. // Basic Options
  69. //
  70. // -----------------------------------------------------------------------------------------
  71. /** @private */
  72. populateBasicOptions: function(s, tab, data) {
  73. var o;
  74. var serviceEnabled = data[0];
  75. var net_devices = data[3];
  76. // Service enable/disable
  77. o = s.taboption(tab, form.Flag, 'enabled', _('Enable service'));
  78. o.optional = false;
  79. o.rmempty = false;
  80. o.cfgvalue = function() {
  81. return serviceEnabled ? this.enabled : this.disabled;
  82. };
  83. o.write = function(section_id, value) {
  84. uci.set('lldpd', section_id, 'enabled', value);
  85. if (value == '1') {
  86. // Enable and start
  87. callInitAction('lldpd', 'enable').then(function() {
  88. return callInitAction('lldpd', 'start');
  89. });
  90. }
  91. else {
  92. // Stop and disable
  93. callInitAction('lldpd', 'stop').then(function() {
  94. return callInitAction('lldpd', 'disable');
  95. });
  96. }
  97. };
  98. // System description
  99. o = s.taboption(tab, form.Value, 'lldp_description',
  100. _('System description'),
  101. _('Override %s.').format('<code>system description</code>'));
  102. o.placeholder = 'System description';
  103. // System hostname
  104. o = s.taboption(tab, form.Value, 'lldp_hostname',
  105. _('System hostname'),
  106. _('Override %s.').format('<code>system hostname</code>'));
  107. o.placeholder = 'System hostname';
  108. /* This function returns the value for a specified key. Used to fill
  109. various location fields derived from an lldpd location config string */
  110. function getLocationValueFromConfString(_key) {
  111. var inStr = this ? this.section.formvalue('config', 'lldp_location'):
  112. uci.get('lldpd', 'config', 'lldp_location');
  113. inStr = inStr ? inStr: '';
  114. const words = inStr.trim().split(/\s+/);
  115. /* This function does not assume an order to the key:value parameters.
  116. Only that the value comes after its key, so optional keys can be in any order. */
  117. const ix = words.indexOf(_key);
  118. if (ix !== -1) {
  119. let value = words.slice(ix + 1).join(' ');
  120. if (value.startsWith('"')) {
  121. const quoteStart = value.indexOf('"');
  122. const quoteEnd = value.indexOf('"', quoteStart + 1);
  123. return value.substring(quoteStart + 1, quoteEnd);
  124. }
  125. return _key == 'altitude' ? words[ix + 1] + ' ' + words[ix + 2] : words[ix + 1];
  126. } else {
  127. return ''; // Element not found
  128. }
  129. };
  130. function write_lldp_location() {
  131. var _input = this ? this.section.formvalue('config', '_lldp_location_type'):
  132. '1';
  133. if(_input){
  134. if (_input == '1') {
  135. /* location coordinate latitude
  136. 48.85667N longitude 2.2014E altitude 117.47 m datum WGS84 */
  137. var lat = this.section.formvalue('config', '_coordinate_lat'),
  138. lon = this.section.formvalue('config', '_coordinate_lon'),
  139. alt = this.section.formvalue('config', '_coordinate_alt'),
  140. dat = this.section.formvalue('config', '_coordinate_dat');
  141. if(lat && lon && dat) {
  142. uci.set('lldpd', 'config', 'lldp_location',
  143. 'coordinate latitude ' + lat +
  144. ' longitude ' + lon +
  145. ' altitude ' + (alt ? alt:'0 m') +
  146. ' datum ' + dat );
  147. }
  148. }
  149. else if (_input == '2') {
  150. /* location address country US
  151. street "Commercial Road" city "Roseville" */
  152. var cc = this.section.formvalue('config', '_civic_cc'),
  153. city = this.section.formvalue('config', '_civic_city'),
  154. str = this.section.formvalue('config', '_civic_str'),
  155. bldg = this.section.formvalue('config', '_civic_bldg'),
  156. nmbr = this.section.formvalue('config', '_civic_nmbr'),
  157. zip = this.section.formvalue('config', '_civic_zip');
  158. uci.set('lldpd', 'config', 'lldp_location',
  159. 'address country ' + cc.toUpperCase()
  160. + (city ? ' city "' + city + '"': '')
  161. + (str ? ' street "' + str + '"': '')
  162. + (bldg ? ' building "' + bldg + '"': '')
  163. + (nmbr ? ' number "' + nmbr + '"': '')
  164. + (zip ? ' zip "' + zip + '"': ''));
  165. }
  166. else if (_input == '3') {
  167. /* location elin 12345 */
  168. var elin = this.section.formvalue('config', '_elin');
  169. if(elin)
  170. uci.set('lldpd', 'config', 'lldp_location', 'elin ' + elin);
  171. }
  172. }
  173. };
  174. // Host location
  175. o = s.taboption(tab, form.ListValue, '_lldp_location_type',
  176. _('Host location type'),
  177. _('Override the announced location of the host.'));
  178. o.value('1', _('Coordinate based'));
  179. o.value('2', _('Civic address'));
  180. o.value('3', _('ELIN'));
  181. o.rmempty = true;
  182. o.write = write_lldp_location;
  183. o.load = function(section_id, value) {
  184. const loc = uci.get(this.config, section_id, 'lldp_location');
  185. if (!loc) return '1';
  186. if (loc.toLowerCase().includes('coordinate')) {
  187. return '1';
  188. }
  189. else if (loc.toLowerCase().includes('address country')) {
  190. return '2';
  191. }
  192. else if (loc.toLowerCase().includes('elin')) {
  193. return '3';
  194. }
  195. };
  196. o = s.taboption(tab, form.Value, 'lldp_location',
  197. _('Raw location config'),
  198. _('Raw config string sent to lldpd, starting: [coordinate|address|elin]'));
  199. o.placeholder = 'address country EU';
  200. o.rmempty = true;
  201. o.write = function(section_id, value) {
  202. if (value) {
  203. const words = value.trim().split(/\s+/),
  204. regex = /^coordinate|^address|^elin/;
  205. var start;
  206. words.forEach(w=>{
  207. if (w.match(regex)) start = w;
  208. });
  209. // Retain string tail from one of the regex keywords
  210. return this.super('write', [ section_id,
  211. value.substring(value.indexOf(start)) ]);
  212. }
  213. };
  214. o.validate = function(section_id, value) {
  215. if (value) {
  216. const words = value.trim().split(/\s+/),
  217. regex = /^coordinate|^address|^elin/;
  218. var _eval = _("Must contain: 'coordinate ...', 'address ...' or 'elin ...'");
  219. words.forEach(w=>{
  220. if (w.match(regex)) _eval = true;
  221. });
  222. return _eval;
  223. }
  224. return true;
  225. };
  226. // Coordinate based
  227. o = s.taboption(tab, form.Value, '_coordinate_lat',
  228. _('Latitude'), '0 .. 90.000[N|S]');
  229. o.depends({ '_lldp_location_type' : '1'});
  230. o.datatype = "maxlength(20)";
  231. o.validate = function(section_id, value) {
  232. if (!value) return true;
  233. var valid = _('valid syntax: 0 .. 90.000[N|S]');
  234. valid = (parseFloat(value) >= 0 && parseFloat(value) <= 90) ?
  235. /^-?\d+(?:\.\d+)?[NnSs]$/.test(value) ? true : valid : valid;
  236. return valid;
  237. }
  238. o.load = function(section_id, value) {
  239. return getLocationValueFromConfString('latitude');
  240. }
  241. o.write = write_lldp_location;
  242. o = s.taboption(tab, form.Value, '_coordinate_lon',
  243. _('Longitude'), '0 .. 180.000[E|W]');
  244. o.depends({ '_lldp_location_type' : '1'});
  245. o.datatype = "maxlength(20)";
  246. o.validate = function(section_id, value) {
  247. if (!value) return true;
  248. var valid = _('valid syntax: 0 .. 180.000[E|W]');
  249. valid = (parseFloat(value) >= 0 && parseFloat(value) <= 180) ?
  250. /^-?\d+(?:\.\d+)?[WwEe]$/.test(value) ? true : valid : valid;
  251. return valid;
  252. }
  253. o.load = function(section_id, value) {
  254. return getLocationValueFromConfString('longitude');
  255. }
  256. o.write = write_lldp_location;
  257. const min_alt = -100000.00,
  258. max_alt = 42849672.95;
  259. o = s.taboption(tab, form.Value, '_coordinate_alt',
  260. _('Altitude'), '%f .. %f [m|f]'.format(min_alt, max_alt));
  261. o.depends({ '_lldp_location_type' : '1'});
  262. o.datatype = 'maxlength(20)';
  263. o.validate = function(section_id, value) {
  264. if (!value) return true;
  265. var valid = _('valid syntax: %f .. %f [mf]').format(min_alt, max_alt);
  266. valid = (parseFloat(value) >= min_alt && parseFloat(value) <= max_alt) ?
  267. /^-?\d+(?:\.\d+)?\ [mf]$/.test(value) ? true : valid : valid;
  268. return valid;
  269. }
  270. o.load = function(section_id, value) {
  271. return getLocationValueFromConfString('altitude');
  272. }
  273. o.write = write_lldp_location;
  274. o = s.taboption(tab, form.ListValue, '_coordinate_dat',
  275. _('Datum'));
  276. o.depends({ '_lldp_location_type' : '1'});
  277. o.value('WGS84');
  278. o.value('NAD83');
  279. o.value('NAD83/MLLW');
  280. o.load = function(section_id, value) {
  281. return getLocationValueFromConfString('datum');
  282. }
  283. o.write = write_lldp_location;
  284. // Civic address based
  285. /* ISO 3166-2 CC list officially assigned + exceptional:
  286. https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements */
  287. o = s.taboption(tab, form.Value, '_civic_cc',
  288. _('Country'), '%s'.format('<a href=%s>ISO 3166-2 CC</a>').format('https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements'));
  289. o.depends({ '_lldp_location_type' : '2'});
  290. o.default = 'EU';
  291. o.placeholder = 'EU';
  292. o.validate = function(section_id, value) {
  293. if(!value) return true;
  294. var valid = _('Two character CC required');
  295. valid = (value.length == 2) ?
  296. /^[A-Z]{2}$/i.test(value) ? true : valid : valid;
  297. return valid;
  298. }
  299. o.load = function(section_id, value) {
  300. return getLocationValueFromConfString('country').toUpperCase();
  301. }
  302. o.write = write_lldp_location;
  303. o = s.taboption(tab, form.Value, '_civic_city',
  304. _('City'));
  305. o.depends({ '_lldp_location_type' : '2'});
  306. o.datatype = "maxlength(100)";
  307. o.load = function(section_id, value) {
  308. return getLocationValueFromConfString('city');
  309. }
  310. o.placeholder = _('Gotham');
  311. o.write = write_lldp_location;
  312. o = s.taboption(tab, form.Value, '_civic_str',
  313. _('Street'));
  314. o.depends({ '_lldp_location_type' : '2'});
  315. o.datatype = "maxlength(100)";
  316. o.load = function(section_id, value) {
  317. return getLocationValueFromConfString('street');
  318. }
  319. o.placeholder = _('Main Street');
  320. o.write = write_lldp_location;
  321. o = s.taboption(tab, form.Value, '_civic_bldg',
  322. _('Building'));
  323. o.depends({ '_lldp_location_type' : '2'});
  324. o.datatype = "maxlength(250)";
  325. o.load = function(section_id, value) {
  326. return getLocationValueFromConfString('building');
  327. }
  328. o.placeholder = _('Empire State Bldg');
  329. o.write = write_lldp_location;
  330. o = s.taboption(tab, form.Value, '_civic_nmbr',
  331. _('Number'));
  332. o.depends({ '_lldp_location_type' : '2'});
  333. o.datatype = "maxlength(25)";
  334. o.load = function(section_id, value) {
  335. return getLocationValueFromConfString('number');
  336. }
  337. o.placeholder = _('1A');
  338. o.write = write_lldp_location;
  339. o = s.taboption(tab, form.Value, '_civic_zip',
  340. _('Post-code'));
  341. o.depends({ '_lldp_location_type' : '2'});
  342. o.datatype = "maxlength(25)";
  343. o.load = function(section_id, value) {
  344. return getLocationValueFromConfString('zip');
  345. }
  346. o.placeholder = '12345';
  347. o.write = write_lldp_location;
  348. // ELIN based
  349. o = s.taboption(tab, form.Value, '_elin',
  350. _('ELIN Address'));
  351. o.depends({ '_lldp_location_type' : '3'});
  352. o.datatype = 'and(uinteger,maxlength(25))';
  353. o.load = function(section_id, value) {
  354. return getLocationValueFromConfString('elin');
  355. }
  356. o.placeholder = '1911';
  357. o.write = write_lldp_location;
  358. // Platform
  359. o = s.taboption(tab, form.Value, 'lldp_platform',
  360. _('System platform description'),
  361. _('Override %s.').format('<code>system platform</code>') + '<br />' +
  362. _('The default description is the kernel name (Linux).'));
  363. o.placeholder = 'System platform description';
  364. o = s.taboption(tab, form.Flag, 'lldp_capability_advertisements', _('System capability advertisements'));
  365. o.default = '1'; //lldpd internal default
  366. o.rmempty = false;
  367. // Capabilities override
  368. o = s.taboption(tab, form.MultiValue, 'lldp_syscapabilities',
  369. _('System capabilities'),
  370. _('Override %s.').format('<code>system capabilities</code>') + '<br />' +
  371. _('The default is derived from kernel information.'));
  372. o.depends({lldp_capability_advertisements: '1'});
  373. o.value('bridge');
  374. o.value('docsis');
  375. o.value('other');
  376. o.value('repeater');
  377. o.value('router');
  378. o.value('station');
  379. o.value('telephone');
  380. o.value('wlan');
  381. o.cfgvalue = function(section_id) {
  382. return String(this.super('load', [section_id]) || this.default).split(',');
  383. };
  384. o.write = function(section_id, value) {
  385. return this.super('write', [ section_id, L.toArray(value).join(',') ]);
  386. };
  387. o = s.taboption(tab, form.Flag, 'lldp_mgmt_addr_advertisements', _('System management IO advertisements'));
  388. o.default = '1'; //lldpd internal default
  389. o.rmempty = false;
  390. // Management addresses of this system
  391. // This value: lldpd.init handles as a single value, and needs a CSV for lldpd.conf: 'configure system ip management pattern'
  392. o = s.taboption(tab, lldpd.CBIMultiIOSelect, 'lldp_mgmt_ip',
  393. _('System management IO'),
  394. _('Defaults to the first IPv4 and IPv6. ' +
  395. 'If an exact IP address is provided, it is used ' +
  396. 'as a management address without any check. To ' +
  397. 'blacklist IPv6 addresses, use <code>!*:*</code>.') + '<br />' +
  398. usage);
  399. o.placeholder = 'Addresses and interfaces';
  400. o.depends({lldp_mgmt_addr_advertisements: '1'});
  401. o.cfgvalue = function(section_id) {
  402. const opt = uci.get(this.config, section_id, this.option);
  403. return opt ? opt.split(',') : '';
  404. };
  405. net_devices.forEach(nd => {
  406. o.value(nd.getName());
  407. o.value('!'+nd.getName());
  408. nd.getIPAddrs().forEach(addr => o.value(addr.split('/')[0], E([], [addr.split('/')[0], ' (', E('strong', {}, nd.getName()), ')'])));
  409. nd.getIP6Addrs().forEach(addr => o.value(addr.split('/')[0], E([], [addr.split('/')[0], ' (', E('strong', {}, nd.getName()), ')'])));
  410. });
  411. o.value('!*:*');
  412. o.validate = validateioentries;
  413. o.write = function(section_id, value, sep) {
  414. return this.super('write', [ section_id, value.join(',') ]);
  415. }
  416. // LLDP tx interval
  417. o = s.taboption(tab, form.Value, 'lldp_tx_interval',
  418. _('Transmit delay'),
  419. _('The delay between ' +
  420. 'transmissions of LLDP PDU. The default value ' +
  421. 'is 30 seconds.') + '<br />' +
  422. _('Suffix %s for millisecond values.').format('<code>ms</code>'));
  423. o.default = 30;
  424. o.placeholder = 30;
  425. o.rmempty = false;
  426. o.validate = function(section_id, value) {
  427. const pattern = /^(\d+)(?:ms)?$/;
  428. if (!value.match(pattern) || parseInt(value) <= 0)
  429. return _('Must be a greater than zero number optionally suffixed with "ms"');
  430. return true;
  431. };
  432. // LLDP tx hold
  433. o = s.taboption(tab, form.Value, 'lldp_tx_hold',
  434. _('Transmit hold value'),
  435. _('Determines the transmitted ' +
  436. 'packet TTL (== this value * transmit delay). ' +
  437. 'The default value is 4 &therefore; ' +
  438. 'the default TTL is 120 seconds.'));
  439. o.datatype = 'uinteger';
  440. o.default = 4;
  441. o.placeholder = 4;
  442. o.rmempty = false;
  443. o.validate = function(section_id, value) {
  444. if (value != parseInt(value))
  445. return _('Must be a number');
  446. else if (value <= 0)
  447. return _('Transmit hold value must be greater than 0');
  448. return true;
  449. };
  450. // Received-only mode (-r)
  451. o = s.taboption(tab, form.Flag, 'readonly_mode',
  452. _('Receive-only mode'),
  453. _("LLDPd won't send any frames; " +
  454. 'only listen to neighbors.'));
  455. o.rmempty = false;
  456. o.optional = false;
  457. o.default = '0';
  458. },
  459. // -----------------------------------------------------------------------------------------
  460. //
  461. // Network Interfaces
  462. //
  463. // -----------------------------------------------------------------------------------------
  464. /** @private */
  465. populateIfacesOptions: function(s, tab, data) {
  466. var o;
  467. var net_devices = data[3];
  468. // Interfaces to listen on
  469. // This value: lldpd.init handles as a list value, and produces a CSV for lldpd.conf: 'configure system interface pattern'
  470. o = s.taboption(tab, lldpd.CBIMultiIOSelect, 'interface',
  471. _('Network IO'),
  472. _('Specify which interface (not) to listen upon and send LLDPDU from. ' +
  473. 'Absent any value, LLDPd uses all available physical interfaces.'));
  474. o.value('*');
  475. net_devices.forEach(nd => {
  476. o.value(nd.getName());
  477. o.value('!'+nd.getName());
  478. o.value('!!'+nd.getName());
  479. });
  480. o.value('!*:*');
  481. o.validate = validateioentries;
  482. // ChassisID interfaces
  483. // This value: lldpd.init handles as a list value, and produces a CSV for the -C param
  484. o = s.taboption(tab, lldpd.CBIMultiIOSelect, 'cid_interface',
  485. _('Network IO for chassis ID'),
  486. _('Specify which interfaces (not) to use for computing chassis ID. ' +
  487. 'Absent any value, all interfaces are considered. ' +
  488. 'LLDPd takes the first MAC address from all the considered ' +
  489. 'interfaces to compute the chassis ID.'));
  490. o.value('*');
  491. o.value('!*');
  492. net_devices.forEach(nd => {
  493. o.value(nd.getName());
  494. o.value('!'+nd.getName());
  495. o.value('!!'+nd.getName());
  496. });
  497. o.value('!*:*');
  498. o.validate = validateioentries;
  499. },
  500. // -----------------------------------------------------------------------------------------
  501. //
  502. // Advanced Options
  503. //
  504. // -----------------------------------------------------------------------------------------
  505. /** @private */
  506. populateAdvancedOptions: function(s, tab, data) {
  507. var o;
  508. // SNMP agentX socket
  509. /* **Note**: The init file tests for SNMP support, so agentxsocket is at worst inert. */
  510. o = s.taboption(tab, form.Value, 'agentxsocket',
  511. _('SNMP agentX socket path'),
  512. _('When set, LLDPd enables an ' +
  513. 'SNMP subagent using AgentX protocol. This enables ' +
  514. 'information about other systems through SNMP.'));
  515. o.rmempty = true;
  516. o.placeholder = '/var/run/agentx.sock';
  517. o.default = '';
  518. // LLDP-MED class
  519. o = s.taboption(tab, form.ListValue, 'lldp_class',
  520. _('LLDP-MED device class'));
  521. o.value('1', _('Generic Endpoint (Class I)'));
  522. o.value('2', _('Media Endpoint (Class II)'));
  523. o.value('3', _('Communication Device Endpoints (Class III)'));
  524. o.value('4', _('Network Connectivity Device (Class IV)'));
  525. o.default = '4';
  526. // LLDP-MED policy
  527. o = s.taboption(tab, form.Value, 'lldp_policy',
  528. _('LLDP-MED policy'));
  529. o.depends({lldp_class: '2'});
  530. o.depends({lldp_class: '3'});
  531. o.rmempty = true;
  532. o.placeholder = 'application streaming-video';
  533. o.value('application voice');
  534. o.value('application voice unknown');
  535. o.value('application voice-signaling');
  536. o.value('application voice-signaling unknown');
  537. o.value('application guest-voice');
  538. o.value('application guest-voice unknown');
  539. o.value('application guest-voice-signaling');
  540. o.value('application guest-voice-signaling unknown');
  541. o.value('application softphone-voice');
  542. o.value('application softphone-voice unknown');
  543. o.value('application video-conferencing');
  544. o.value('application video-conferencing unknown');
  545. o.value('application streaming-video');
  546. o.value('application streaming-video unknown');
  547. o.value('application video-signaling');
  548. o.value('application video-signaling unknown');
  549. o.validate = function(section_id, value) {
  550. if (value && !value.startsWith('application '))
  551. return _('Must start: application ...');
  552. return true;
  553. };
  554. // LLDP-MED fast-start
  555. o = s.taboption(tab, form.Flag, 'lldpmed_fast_start',
  556. _('LLDP-MED fast-start'));
  557. // LLDP-MED fast-start
  558. o = s.taboption(tab, form.Value, 'lldpmed_fast_start_tx_interval',
  559. _('LLDP-MED fast-start tx-interval'));
  560. o.depends({lldpmed_fast_start: '1'});
  561. o.datatype = 'uinteger';
  562. o.placeholder = '10';
  563. o.rmempty = true;
  564. // LLDP-MED inventory TLV transmission (-i)
  565. o = s.taboption(tab, form.Flag, 'lldpmed_no_inventory',
  566. _('Disable LLDP-MED inventory TLV transmission'),
  567. _('LLDPd will still receive (and publish using SNMP if enabled) ' +
  568. 'those LLDP-MED TLV but will not send them. Use this option ' +
  569. 'if you do not want to transmit sensitive information like serial numbers.'));
  570. o.default = '0';
  571. // Disable advertising of kernel release, version and machine. (-k)
  572. o = s.taboption(tab, form.Flag, 'lldp_no_version',
  573. _('Disable advertising of kernel release, version and machine'),
  574. _('Kernel name (ie: Linux) will still be shared, and Inventory ' +
  575. 'software version will be set to %s.').format('<code>Unknown</code>'));
  576. o.default = '0';
  577. // Filter neighbors (-H)
  578. o = s.taboption(tab, lldpd.cbiFilterSelect, 'filter',
  579. _('Specify the behaviour when detecting multiple neighbors'),
  580. _('The default filter is 15. Refer to &quot;FILTERING NEIGHBORS&quot;.') + '<br />' +
  581. usage);
  582. o.default = 15;
  583. // Force port ID subtype
  584. o = s.taboption(tab, form.ListValue, 'lldp_portidsubtype',
  585. _('Force port ID subtype'),
  586. _('With this option, you can force the port identifier ' +
  587. 'to be the interface name or the MAC address.'));
  588. o.value('macaddress', _('Interface MAC address'));
  589. o.value('ifname', _('Interface name'));
  590. o.default = 'macaddress';
  591. // The destination MAC address used to send LLDPDU
  592. o = s.taboption(tab, form.ListValue, 'lldp_agenttype',
  593. _('LLDPDU destination MAC'),
  594. _('Allows an agent ' +
  595. 'to control the propagation of LLDPDUs. By default, the ' +
  596. 'MAC address %s is used and limits the propagation ' +
  597. 'of the LLDPDU to the nearest bridge.').format('<code>01:80:c2:00:00:0e</code>'));
  598. o.value('nearest-bridge', '01:80:c2:00:00:0e (nearest-bridge)');
  599. o.value('nearest-nontpmr-bridge', '01:80:c2:00:00:03 (nearest-nontpmr-bridge)');
  600. o.value('nearest-customer-bridge', '01:80:c2:00:00:00 (nearest-customer-bridge)');
  601. o.default = 'nearest-bridge';
  602. },
  603. // -----------------------------------------------------------------------------------------
  604. //
  605. // Protocols Support
  606. //
  607. // -----------------------------------------------------------------------------------------
  608. /** @private */
  609. populateProtocolsOptions: function(s, tab, data) {
  610. var o;
  611. o = s.taboption(tab, form.SectionValue, '_protocols', form.TypedSection, 'lldpd');
  612. var ss = o.subsection;
  613. ss.anonymous = true;
  614. ss.addremove = false;
  615. //
  616. // LLDPD
  617. // Link Layer Discovery Protocol
  618. //
  619. ss.tab('lldp', _('LLDP'));
  620. o = ss.taboption('lldp', form.Flag, 'enable_lldp',
  621. _('Enable LLDP'));
  622. o.default = '1';
  623. o.rmempty = true;
  624. o = ss.taboption('lldp', form.Flag, 'force_lldp',
  625. _('Force sending LLDP packets'),
  626. _('Even when there is no LLDP peer ' +
  627. 'detected but there is a peer speaking another protocol detected.') + '<br />' +
  628. _('By default, LLDP packets are sent when there is a peer speaking ' +
  629. 'LLDP detected or when there is no peer at all.'));
  630. o.default = '0';
  631. o.rmempty = true;
  632. o.depends('enable_lldp', '1');
  633. //
  634. // CDP
  635. // Cisco Discovery Protocol
  636. //
  637. ss.tab('cdp', _('CDP'));
  638. o = ss.taboption('cdp', form.Flag, 'enable_cdp',
  639. _('Enable CDP'),
  640. _('Enable the support of CDP protocol to deal with Cisco routers ' +
  641. 'that do not speak LLDP'));
  642. o.default = '1';
  643. o.rmempty = false;
  644. o = ss.taboption('cdp', form.ListValue, 'cdp_version',
  645. _('CDP version'));
  646. o.value('cdpv1v2', _('CDPv1 and CDPv2'));
  647. o.value('cdpv2', _('Only CDPv2'));
  648. o.depends('enable_cdp', '1');
  649. o.default = 'cdpv1v2';
  650. o = ss.taboption('cdp', form.Flag, 'force_cdp',
  651. _('Send CDP packets even if no CDP peer detected'));
  652. o.default = '0';
  653. o.rmempty = true;
  654. o.depends('enable_cdp', '1');
  655. o = ss.taboption('cdp', form.Flag, 'force_cdpv2',
  656. _('Force sending CDPv2 packets'));
  657. o.default = '0';
  658. o.rmempty = true;
  659. o.depends({
  660. force_cdp: '1',
  661. enable_cdp: '1',
  662. cdp_version: 'cdpv1v2'
  663. });
  664. //
  665. // FDP
  666. // Foundry Discovery Protocol
  667. //
  668. ss.tab('fdp', _('FDP'));
  669. o = ss.taboption('fdp', form.Flag, 'enable_fdp',
  670. _('Enable FDP'),
  671. _('Enable the support of FDP protocol to deal with Foundry routers ' +
  672. 'that do not speak LLDP'));
  673. o.default = '1';
  674. o.rmempty = false;
  675. o = ss.taboption('fdp', form.Flag, 'force_fdp',
  676. _('Send FDP packets even if no FDP peer detected'));
  677. o.default = '0';
  678. o.rmempty = true;
  679. o.depends('enable_fdp', '1');
  680. //
  681. // EDP
  682. // Extreme Discovery Protocol
  683. //
  684. ss.tab('edp', _('EDP'));
  685. o = ss.taboption('edp', form.Flag, 'enable_edp',
  686. _('Enable EDP'),
  687. _('Enable the support of EDP protocol to deal with Extreme routers ' +
  688. 'and switches that do not speak LLDP.'));
  689. o.default = '1';
  690. o.rmempty = false;
  691. o = ss.taboption('edp', form.Flag, 'force_edp',
  692. _('Send EDP packets even if no EDP peer detected'));
  693. o.default = '0';
  694. o.rmempty = true;
  695. o.depends('enable_edp', '1');
  696. //
  697. // SONMP
  698. // SynOptics Network Management Protocol
  699. //
  700. // a.k.a.
  701. // Nortel Topology Discovery Protocol (NTDP)
  702. // Nortel Discovery Protocol (NDP)
  703. // Bay Network Management Protocol (BNMP)
  704. // Bay Discovery Protocol (BDP)
  705. //
  706. ss.tab('sonmp', _('SONMP (NTDP, NDP, BNMP, BDP)'));
  707. o = ss.taboption('sonmp', form.Flag, 'enable_sonmp',
  708. _('Enable SONMP'),
  709. _('Enable the support of SONMP protocol to deal with Nortel ' +
  710. 'routers and switches that do not speak LLDP.'));
  711. o.default = '1';
  712. o.rmempty = false;
  713. o = ss.taboption('sonmp', form.Flag, 'force_sonmp',
  714. _('Send SONMP packets even if no SONMP peer detected'));
  715. o.default = '0';
  716. o.rmempty = true;
  717. o.depends('enable_sonmp', '1');
  718. },
  719. /** @private */
  720. populateOptions: function(s, data) {
  721. var o;
  722. s.tab('basic', _('Basic Settings'));
  723. this.populateBasicOptions(s, 'basic', data);
  724. s.tab('ifaces', _('Network Interfaces'));
  725. this.populateIfacesOptions(s, 'ifaces', data);
  726. s.tab('advanced', _('Advanced Settings'));
  727. this.populateAdvancedOptions(s, 'advanced', data);
  728. s.tab('protocols', _('Protocols Support'));
  729. this.populateProtocolsOptions(s, 'protocols', data);
  730. },
  731. render: function(data) {
  732. var m, s;
  733. m = new form.Map('lldpd', _('LLDPd Settings'),
  734. _('LLDPd is an implementation of IEEE 802.1ab') + ' ' +
  735. '(<abbr title="Link Layer Discovery Protocol">LLDP</abbr>).' + ' ' +
  736. _('On this page you may configure LLDPd parameters.'));
  737. s = m.section(form.TypedSection, 'lldpd');
  738. s.addremove = false;
  739. s.anonymous = true;
  740. this.populateOptions(s, data);
  741. return m.render();
  742. },
  743. });