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>
14 'require tools.widgets as widgets';
16 var callInitList
= rpc
.declare({
18 method
: 'getInitList',
21 filter: function(res
) {
23 return +res
[k
].enabled
;
28 var callInitAction
= rpc
.declare({
30 method
: 'setInitAction',
31 params
: [ 'name', 'action' ],
32 expect
: { result
: false }
35 var usage
= _('See syntax <a %s>here</a>.').format('href=https://lldpd.github.io/usage.html target="_blank"');
37 const validateioentries = function(section_id
, value
) {
39 const emsg
= _('Cannot have both interface %s and its exclusion %s');
40 const a
= value
.split(' ');
41 const noex
= a
.filter(el
=> !el
.startsWith('!'));
42 const ex
= a
.filter(el
=> el
.startsWith('!') && !el
.startsWith('!!'));
46 return emsg
.format(i
, e
);
54 return L
.view
.extend({
55 __init__: function() {
56 this.super('__init__', arguments
);
59 var head
= document
.getElementsByTagName('head')[0];
60 var css
= E('link', { 'href':
61 L
.resource('lldpd/lldpd.css')
62 + '?v=#PKG_VERSION', 'rel': 'stylesheet' });
64 head
.appendChild(css
);
69 callInitList('lldpd'),
76 // -----------------------------------------------------------------------------------------
80 // -----------------------------------------------------------------------------------------
83 populateBasicOptions: function(s
, tab
, data
) {
85 var serviceEnabled
= data
[0];
86 var net_devices
= data
[3];
88 // Service enable/disable
89 o
= s
.taboption(tab
, form
.Flag
, 'enabled', _('Enable service'));
93 o
.cfgvalue = function() {
94 return serviceEnabled
? this.enabled
: this.disabled
;
97 o
.write = function(section_id
, value
) {
98 uci
.set('lldpd', section_id
, 'enabled', value
);
102 callInitAction('lldpd', 'enable').then(function() {
103 return callInitAction('lldpd', 'start');
108 callInitAction('lldpd', 'stop').then(function() {
109 return callInitAction('lldpd', 'disable');
114 // System description
115 o
= s
.taboption(tab
, form
.Value
, 'lldp_description',
116 _('System description'),
117 _('Override %s.').format('<code>system description</code>'));
119 o
.placeholder
= 'System description';
122 o
= s
.taboption(tab
, form
.Value
, 'lldp_hostname',
123 _('System hostname'),
124 _('Override %s.').format('<code>system hostname</code>'));
126 o
.placeholder
= 'System hostname';
129 o
= s
.taboption(tab
, form
.Value
, 'lldp_location',
131 _('Override the announced location of the host.') + '<br />' +
133 // multiple syntaxes alert for location parameter
134 o
.placeholder
= 'address country EU';
136 o
.validate = function(section_id
, value
) {
138 if (!value
.match(/^coordinate |^address |^elin /))
139 return _("Must start: 'coordinate ...', 'address ...' or 'elin ...'");
146 o
= s
.taboption(tab
, form
.Value
, 'lldp_platform',
147 _('System platform description'),
148 _('Override %s.').format('<code>system platform</code>') + '<br />' +
149 _('The default description is the kernel name (Linux).'));
151 o
.placeholder
= 'System platform description';
153 o
= s
.taboption(tab
, form
.Flag
, 'lldp_capability_advertisements', _('System capability advertisements'));
154 o
.default = '1'; //lldpd internal default
157 // Capabilities override
158 o
= s
.taboption(tab
, form
.MultiValue
, 'lldp_syscapabilities',
159 _('System capabilities'),
160 _('Override %s.').format('<code>system capabilities</code>') + '<br />' +
161 _('The default is derived from kernel information.'));
162 o
.depends({lldp_capability_advertisements
: '1'});
169 o
.value('telephone');
171 o
.cfgvalue = function(section_id
) {
172 return String(this.super('load', [section_id
]) || this.default).split(',');
174 o
.write = function(section_id
, value
) {
175 return this.super('write', [ section_id
, L
.toArray(value
).join(',') ]);
178 o
= s
.taboption(tab
, form
.Flag
, 'lldp_mgmt_addr_advertisements', _('System management IO advertisements'));
179 o
.default = '1'; //lldpd internal default
182 // Management addresses of this system
183 // This value: lldpd.init handles as a single value, and needs a CSV for lldpd.conf: 'configure system ip management pattern'
184 o
= s
.taboption(tab
, lldpd
.CBIMultiIOSelect
, 'lldp_mgmt_ip',
185 _('System management IO'),
186 _('Defaults to the first IPv4 and IPv6. ' +
187 'If an exact IP address is provided, it is used ' +
188 'as a management address without any check. To ' +
189 'blacklist IPv6 addresses, use <code>!*:*</code>.') + '<br />' +
191 o
.placeholder
= 'Addresses and interfaces';
192 o
.depends({lldp_mgmt_addr_advertisements
: '1'});
193 o
.cfgvalue = function(section_id
) {
194 const opt
= uci
.get(this.config
, section_id
, this.option
);
195 return opt
? opt
.split(',') : '';
197 net_devices
.forEach(nd
=> {
198 o
.value(nd
.getName());
199 o
.value('!'+nd
.getName());
200 nd
.getIPAddrs().forEach(addr
=> o
.value(addr
.split('/')[0], E([], [addr
.split('/')[0], ' (', E('strong', {}, nd
.getName()), ')'])));
201 nd
.getIP6Addrs().forEach(addr
=> o
.value(addr
.split('/')[0], E([], [addr
.split('/')[0], ' (', E('strong', {}, nd
.getName()), ')'])));
204 o
.validate
= validateioentries
;
205 o
.write = function(section_id
, value
, sep
) {
206 return this.super('write', [ section_id
, value
.join(',') ]);
210 o
= s
.taboption(tab
, form
.Value
, 'lldp_tx_interval',
212 _('The delay between ' +
213 'transmissions of LLDP PDU. The default value ' +
214 'is 30 seconds.') + '<br />' +
215 _('Suffix %s for millisecond values.').format('<code>ms</code>'));
220 o
.validate = function(section_id
, value
) {
221 const pattern
= /^(\d+)(?:ms)?$/;
222 if (!value
.match(pattern
) || parseInt(value
) <= 0)
223 return _('Must be a greater than zero number optionally suffixed with "ms"');
228 o
= s
.taboption(tab
, form
.Value
, 'lldp_tx_hold',
229 _('Transmit hold value'),
230 _('Determines the transmitted ' +
231 'packet TTL (== this value * transmit delay). ' +
232 'The default value is 4 ∴ ' +
233 'the default TTL is 120 seconds.'));
235 o
.datatype
= 'uinteger';
240 o
.validate = function(section_id
, value
) {
241 if (value
!= parseInt(value
))
242 return _('Must be a number');
244 return _('Transmit hold value must be greater than 0');
248 // Received-only mode (-r)
249 o
= s
.taboption(tab
, form
.Flag
, 'readonly_mode',
250 _('Receive-only mode'),
251 _("LLDPd won't send any frames; " +
252 'only listen to neighbors.'));
259 // -----------------------------------------------------------------------------------------
261 // Network Interfaces
263 // -----------------------------------------------------------------------------------------
266 populateIfacesOptions: function(s
, tab
, data
) {
268 var net_devices
= data
[3];
270 // Interfaces to listen on
271 // This value: lldpd.init handles as a list value, and produces a CSV for lldpd.conf: 'configure system interface pattern'
272 o
= s
.taboption(tab
, lldpd
.CBIMultiIOSelect
, 'interface',
274 _('Specify which interface (not) to listen upon and send LLDPDU from. ' +
275 'Absent any value, LLDPd uses all available physical interfaces.'));
278 net_devices
.forEach(nd
=> {
279 o
.value(nd
.getName());
280 o
.value('!'+nd
.getName());
281 o
.value('!!'+nd
.getName());
284 o
.validate
= validateioentries
;
286 // ChassisID interfaces
287 // This value: lldpd.init handles as a list value, and produces a CSV for the -C param
288 o
= s
.taboption(tab
, lldpd
.CBIMultiIOSelect
, 'cid_interface',
289 _('Network IO for chassis ID'),
290 _('Specify which interfaces (not) to use for computing chassis ID. ' +
291 'Absent any value, all interfaces are considered. ' +
292 'LLDPd takes the first MAC address from all the considered ' +
293 'interfaces to compute the chassis ID.'));
297 net_devices
.forEach(nd
=> {
298 o
.value(nd
.getName());
299 o
.value('!'+nd
.getName());
300 o
.value('!!'+nd
.getName());
303 o
.validate
= validateioentries
;
307 // -----------------------------------------------------------------------------------------
311 // -----------------------------------------------------------------------------------------
314 populateAdvancedOptions: function(s
, tab
, data
) {
317 // SNMP agentX socket
318 // **Note**: lldpd is compiled in OpenWrt without SNMP support by default. Setting this action will then cause the lldpd daemon to stop starting and thus lldpd will stop working. To fix this, the value must then be deleted and lldpd restarted.
319 // o = s.taboption(tab, form.Value, 'agentxsocket',
320 // _('SNMP agentX socket path'),
321 // _('If the path to the socket is set, then LLDPd will enable an ' +
322 // 'SNMP subagent using AgentX protocol. This allows you to get ' +
323 // 'information about local system and remote systems through SNMP.'));
326 // o.placeholder = '/var/run/agentx.sock';
330 o
= s
.taboption(tab
, form
.ListValue
, 'lldp_class',
331 _('LLDP-MED device class'));
333 o
.value('1', _('Generic Endpoint (Class I)'));
334 o
.value('2', _('Media Endpoint (Class II)'));
335 o
.value('3', _('Communication Device Endpoints (Class III)'));
336 o
.value('4', _('Network Connectivity Device (Class IV)'));
341 o
= s
.taboption(tab
, form
.Value
, 'lldp_policy',
342 _('LLDP-MED policy'));
343 o
.depends({lldp_class
: '2'});
344 o
.depends({lldp_class
: '3'});
347 o
.placeholder
= 'application streaming-video';
348 o
.value('application voice');
349 o
.value('application voice unknown');
350 o
.value('application voice-signaling');
351 o
.value('application voice-signaling unknown');
352 o
.value('application guest-voice');
353 o
.value('application guest-voice unknown');
354 o
.value('application guest-voice-signaling');
355 o
.value('application guest-voice-signaling unknown');
356 o
.value('application softphone-voice');
357 o
.value('application softphone-voice unknown');
358 o
.value('application video-conferencing');
359 o
.value('application video-conferencing unknown');
360 o
.value('application streaming-video');
361 o
.value('application streaming-video unknown');
362 o
.value('application video-signaling');
363 o
.value('application video-signaling unknown');
365 o
.validate = function(section_id
, value
) {
366 if (value
&& !value
.startsWith('application '))
367 return _('Must start: application ...');
371 // LLDP-MED fast-start
372 o
= s
.taboption(tab
, form
.Flag
, 'lldpmed_fast_start',
373 _('LLDP-MED fast-start'));
375 // LLDP-MED fast-start
376 o
= s
.taboption(tab
, form
.Value
, 'lldpmed_fast_start_tx_interval',
377 _('LLDP-MED fast-start tx-interval'));
378 o
.depends({lldpmed_fast_start
: '1'});
379 o
.datatype
= 'uinteger';
380 o
.placeholder
= '10';
383 // LLDP-MED inventory TLV transmission (-i)
384 o
= s
.taboption(tab
, form
.Flag
, 'lldpmed_no_inventory',
385 _('Disable LLDP-MED inventory TLV transmission'),
386 _('LLDPd will still receive (and publish using SNMP if enabled) ' +
387 'those LLDP-MED TLV but will not send them. Use this option ' +
388 'if you do not want to transmit sensitive information like serial numbers.'));
392 // Disable advertising of kernel release, version and machine. (-k)
393 o
= s
.taboption(tab
, form
.Flag
, 'lldp_no_version',
394 _('Disable advertising of kernel release, version and machine'),
395 _('Kernel name (ie: Linux) will still be shared, and Inventory ' +
396 'software version will be set to %s.').format('<code>Unknown</code>'));
400 // Filter neighbors (-H)
401 o
= s
.taboption(tab
, lldpd
.cbiFilterSelect
, 'filter',
402 _('Specify the behaviour when detecting multiple neighbors'),
403 _('The default filter is 15. Refer to "FILTERING NEIGHBORS".') + '<br />' +
408 // Force port ID subtype
409 o
= s
.taboption(tab
, form
.ListValue
, 'lldp_portidsubtype',
410 _('Force port ID subtype'),
411 _('With this option, you can force the port identifier ' +
412 'to be the interface name or the MAC address.'));
414 o
.value('macaddress', _('Interface MAC address'));
415 o
.value('ifname', _('Interface name'));
417 o
.default = 'macaddress';
419 // The destination MAC address used to send LLDPDU
420 o
= s
.taboption(tab
, form
.ListValue
, 'lldp_agenttype',
421 _('LLDPDU destination MAC'),
422 _('Allows an agent ' +
423 'to control the propagation of LLDPDUs. By default, the ' +
424 'MAC address %s is used and limits the propagation ' +
425 'of the LLDPDU to the nearest bridge.').format('<code>01:80:c2:00:00:0e</code>'));
427 o
.value('nearest-bridge', '01:80:c2:00:00:0e (nearest-bridge)');
428 o
.value('nearest-nontpmr-bridge', '01:80:c2:00:00:03 (nearest-nontpmr-bridge)');
429 o
.value('nearest-customer-bridge', '01:80:c2:00:00:00 (nearest-customer-bridge)');
431 o
.default = 'nearest-bridge';
434 // -----------------------------------------------------------------------------------------
438 // -----------------------------------------------------------------------------------------
441 populateProtocolsOptions: function(s
, tab
, data
) {
444 o
= s
.taboption(tab
, form
.SectionValue
, '_protocols', form
.TypedSection
, 'lldpd');
445 var ss
= o
.subsection
;
447 ss
.addremove
= false;
451 // Link Layer Discovery Protocol
453 ss
.tab('lldp', _('LLDP'));
454 o
= ss
.taboption('lldp', form
.Flag
, 'enable_lldp',
460 o
= ss
.taboption('lldp', form
.Flag
, 'force_lldp',
461 _('Force sending LLDP packets'),
462 _('Even when there is no LLDP peer ' +
463 'detected but there is a peer speaking another protocol detected.') + '<br />' +
464 _('By default, LLDP packets are sent when there is a peer speaking ' +
465 'LLDP detected or when there is no peer at all.'));
469 o
.depends('enable_lldp', '1');
473 // Cisco Discovery Protocol
475 ss
.tab('cdp', _('CDP'));
476 o
= ss
.taboption('cdp', form
.Flag
, 'enable_cdp',
478 _('Enable the support of CDP protocol to deal with Cisco routers ' +
479 'that do not speak LLDP'));
484 o
= ss
.taboption('cdp', form
.ListValue
, 'cdp_version',
487 o
.value('cdpv1v2', _('CDPv1 and CDPv2'));
488 o
.value('cdpv2', _('Only CDPv2'));
489 o
.depends('enable_cdp', '1');
491 o
.default = 'cdpv1v2';
493 o
= ss
.taboption('cdp', form
.Flag
, 'force_cdp',
494 _('Send CDP packets even if no CDP peer detected'));
498 o
.depends('enable_cdp', '1');
500 o
= ss
.taboption('cdp', form
.Flag
, 'force_cdpv2',
501 _('Force sending CDPv2 packets'));
508 cdp_version
: 'cdpv1v2'
513 // Foundry Discovery Protocol
515 ss
.tab('fdp', _('FDP'));
516 o
= ss
.taboption('fdp', form
.Flag
, 'enable_fdp',
518 _('Enable the support of FDP protocol to deal with Foundry routers ' +
519 'that do not speak LLDP'));
524 o
= ss
.taboption('fdp', form
.Flag
, 'force_fdp',
525 _('Send FDP packets even if no FDP peer detected'));
529 o
.depends('enable_fdp', '1');
533 // Extreme Discovery Protocol
535 ss
.tab('edp', _('EDP'));
536 o
= ss
.taboption('edp', form
.Flag
, 'enable_edp',
538 _('Enable the support of EDP protocol to deal with Extreme routers ' +
539 'and switches that do not speak LLDP.'));
544 o
= ss
.taboption('edp', form
.Flag
, 'force_edp',
545 _('Send EDP packets even if no EDP peer detected'));
549 o
.depends('enable_edp', '1');
553 // SynOptics Network Management Protocol
556 // Nortel Topology Discovery Protocol (NTDP)
557 // Nortel Discovery Protocol (NDP)
558 // Bay Network Management Protocol (BNMP)
559 // Bay Discovery Protocol (BDP)
561 ss
.tab('sonmp', _('SONMP (NTDP, NDP, BNMP, BDP)'));
562 o
= ss
.taboption('sonmp', form
.Flag
, 'enable_sonmp',
564 _('Enable the support of SONMP protocol to deal with Nortel ' +
565 'routers and switches that do not speak LLDP.'));
570 o
= ss
.taboption('sonmp', form
.Flag
, 'force_sonmp',
571 _('Send SONMP packets even if no SONMP peer detected'));
575 o
.depends('enable_sonmp', '1');
579 populateOptions: function(s
, data
) {
582 s
.tab('basic', _('Basic Settings'));
583 this.populateBasicOptions(s
, 'basic', data
);
585 s
.tab('ifaces', _('Network Interfaces'));
586 this.populateIfacesOptions(s
, 'ifaces', data
);
588 s
.tab('advanced', _('Advanced Settings'));
589 this.populateAdvancedOptions(s
, 'advanced', data
);
591 s
.tab('protocols', _('Protocols Support'));
592 this.populateProtocolsOptions(s
, 'protocols', data
);
595 render: function(data
) {
598 m
= new form
.Map('lldpd', _('LLDPd Settings'),
599 _('LLDPd is an implementation of IEEE 802.1ab') + ' ' +
600 '(<abbr title="Link Layer Discovery Protocol">LLDP</abbr>).' + ' ' +
601 _('On this page you may configure LLDPd parameters.'));
603 s
= m
.section(form
.TypedSection
, 'lldpd');
607 this.populateOptions(s
, data
);