6 var callHostHints, callDUIDHints, callDHCPLeases, CBILeaseStatus;
8 callHostHints = rpc.declare({
13 callDUIDHints = rpc.declare({
18 callDHCPLeases = rpc.declare({
22 expect: { dhcp_leases: [] }
25 CBILeaseStatus = form.DummyValue.extend({
26 renderWidget: function(section_id, option_id, cfgvalue) {
28 E('h4', _('Active DHCP Leases')),
29 E('div', { 'id': 'lease_status_table', 'class': 'table' }, [
30 E('div', { 'class': 'tr table-titles' }, [
31 E('div', { 'class': 'th' }, _('Hostname')),
32 E('div', { 'class': 'th' }, _('IPv4-Address')),
33 E('div', { 'class': 'th' }, _('MAC-Address')),
34 E('div', { 'class': 'th' }, _('Leasetime remaining'))
36 E('div', { 'class': 'tr placeholder' }, [
37 E('div', { 'class': 'td' }, E('em', _('Collecting data...')))
44 return L.view.extend({
54 render: function(hosts_duids) {
55 var hosts = hosts_duids[0],
56 duids = hosts_duids[1],
59 m = new form.Map('dhcp', _('DHCP and DNS'), _('Dnsmasq is a combined <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server and <abbr title="Domain Name System">DNS</abbr>-Forwarder for <abbr title="Network Address Translation">NAT</abbr> firewalls'));
62 s = m.section(form.TypedSection, 'dnsmasq', _('Server Settings'));
66 s.tab('general', _('General Settings'));
67 s.tab('files', _('Resolv and Hosts Files'));
68 s.tab('tftp', _('TFTP Settings'));
69 s.tab('advanced', _('Advanced Settings'));
70 s.tab('leases', _('Static Leases'));
72 s.taboption('general', form.Flag, 'domainneeded',
74 _('Don\'t forward <abbr title="Domain Name System">DNS</abbr>-Requests without <abbr title="Domain Name System">DNS</abbr>-Name'));
76 s.taboption('general', form.Flag, 'authoritative',
78 _('This is the only <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> in the local network'));
81 s.taboption('files', form.Flag, 'readethers',
82 _('Use <code>/etc/ethers</code>'),
83 _('Read <code>/etc/ethers</code> to configure the <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server'));
85 s.taboption('files', form.Value, 'leasefile',
87 _('file where given <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-leases will be stored'));
89 s.taboption('files', form.Flag, 'noresolv',
90 _('Ignore resolve file')).optional = true;
92 o = s.taboption('files', form.Value, 'resolvfile',
94 _('local <abbr title="Domain Name System">DNS</abbr> file'));
96 o.depends('noresolv', '');
100 s.taboption('files', form.Flag, 'nohosts',
101 _('Ignore <code>/etc/hosts</code>')).optional = true;
103 s.taboption('files', form.DynamicList, 'addnhosts',
104 _('Additional Hosts files')).optional = true;
106 o = s.taboption('advanced', form.Flag, 'quietdhcp',
107 _('Suppress logging'),
108 _('Suppress logging of the routine operation of these protocols'));
111 o = s.taboption('advanced', form.Flag, 'sequential_ip',
112 _('Allocate IP sequentially'),
113 _('Allocate IP addresses sequentially, starting from the lowest available address'));
116 o = s.taboption('advanced', form.Flag, 'boguspriv',
118 _('Do not forward reverse lookups for local networks'));
119 o.default = o.enabled;
121 s.taboption('advanced', form.Flag, 'filterwin2k',
123 _('Do not forward requests that cannot be answered by public name servers'));
126 s.taboption('advanced', form.Flag, 'localise_queries',
127 _('Localise queries'),
128 _('Localise hostname depending on the requesting subnet if multiple IPs are available'));
130 //local have_dnssec_support = luci.util.checklib('/usr/sbin/dnsmasq', 'libhogweed.so');
131 var have_dnssec_support = true;
133 if (have_dnssec_support) {
134 o = s.taboption('advanced', form.Flag, 'dnssec',
138 o = s.taboption('advanced', form.Flag, 'dnsseccheckunsigned',
139 _('DNSSEC check unsigned'),
140 _('Requires upstream supports DNSSEC; verify unsigned domain responses really come from unsigned domains'));
144 s.taboption('general', form.Value, 'local',
146 _('Local domain specification. Names matching this domain are never forwarded and are resolved from DHCP or hosts files only'));
148 s.taboption('general', form.Value, 'domain',
150 _('Local domain suffix appended to DHCP names and hosts file entries'));
152 s.taboption('advanced', form.Flag, 'expandhosts',
154 _('Add local domain suffix to names served from hosts files'));
156 s.taboption('advanced', form.Flag, 'nonegcache',
157 _('No negative cache'),
158 _('Do not cache negative replies, e.g. for not existing domains'));
160 s.taboption('advanced', form.Value, 'serversfile',
161 _('Additional servers file'),
162 _('This file may contain lines like \'server=/domain/1.2.3.4\' or \'server=1.2.3.4\' for domain-specific or full upstream <abbr title="Domain Name System">DNS</abbr> servers.'));
164 s.taboption('advanced', form.Flag, 'strictorder',
166 _('<abbr title="Domain Name System">DNS</abbr> servers will be queried in the order of the resolvfile')).optional = true;
168 s.taboption('advanced', form.Flag, 'allservers',
170 _('Query all available upstream <abbr title="Domain Name System">DNS</abbr> servers')).optional = true;
172 o = s.taboption('advanced', form.DynamicList, 'bogusnxdomain', _('Bogus NX Domain Override'),
173 _('List of hosts that supply bogus NX domain results'));
176 o.placeholder = '67.215.65.132';
179 s.taboption('general', form.Flag, 'logqueries',
181 _('Write received DNS requests to syslog')).optional = true;
183 o = s.taboption('general', form.DynamicList, 'server', _('DNS forwardings'),
184 _('List of <abbr title="Domain Name System">DNS</abbr> servers to forward requests to'));
187 o.placeholder = '/example.org/10.1.2.3';
190 o = s.taboption('general', form.Flag, 'rebind_protection',
191 _('Rebind protection'),
192 _('Discard upstream RFC1918 responses'));
197 o = s.taboption('general', form.Flag, 'rebind_localhost',
198 _('Allow localhost'),
199 _('Allow upstream responses in the 127.0.0.0/8 range, e.g. for RBL services'));
201 o.depends('rebind_protection', '1');
204 o = s.taboption('general', form.DynamicList, 'rebind_domain',
205 _('Domain whitelist'),
206 _('List of domains to allow RFC1918 responses for'));
209 o.depends('rebind_protection', '1');
210 o.datatype = 'host(1)';
211 o.placeholder = 'ihost.netflix.com';
214 o = s.taboption('advanced', form.Value, 'port',
215 _('<abbr title="Domain Name System">DNS</abbr> server port'),
216 _('Listening port for inbound DNS queries'));
223 o = s.taboption('advanced', form.Value, 'queryport',
224 _('<abbr title="Domain Name System">DNS</abbr> query port'),
225 _('Fixed source port for outbound DNS queries'));
229 o.placeholder = _('any');
232 o = s.taboption('advanced', form.Value, 'dhcpleasemax',
233 _('<abbr title="maximal">Max.</abbr> <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> leases'),
234 _('Maximum allowed number of active DHCP leases'));
237 o.datatype = 'uinteger';
238 o.placeholder = _('unlimited');
241 o = s.taboption('advanced', form.Value, 'ednspacket_max',
242 _('<abbr title="maximal">Max.</abbr> <abbr title="Extension Mechanisms for Domain Name System">EDNS0</abbr> packet size'),
243 _('Maximum allowed size of EDNS.0 UDP packets'));
246 o.datatype = 'uinteger';
247 o.placeholder = 1280;
250 o = s.taboption('advanced', form.Value, 'dnsforwardmax',
251 _('<abbr title="maximal">Max.</abbr> concurrent queries'),
252 _('Maximum allowed number of concurrent DNS queries'));
255 o.datatype = 'uinteger';
258 o = s.taboption('advanced', form.Value, 'cachesize',
259 _('Size of DNS query cache'),
260 _('Number of cached DNS entries (max is 10000, 0 is no caching)'));
262 o.datatype = 'range(0,10000)';
265 s.taboption('tftp', form.Flag, 'enable_tftp',
266 _('Enable TFTP server')).optional = true;
268 o = s.taboption('tftp', form.Value, 'tftp_root',
269 _('TFTP server root'),
270 _('Root directory for files served via TFTP'));
273 o.depends('enable_tftp', '1');
277 o = s.taboption('tftp', form.Value, 'dhcp_boot',
278 _('Network boot image'),
279 _('Filename of the boot image advertised to clients'));
282 o.depends('enable_tftp', '1');
283 o.placeholder = 'pxelinux.0';
285 o = s.taboption('general', form.Flag, 'localservice',
286 _('Local Service Only'),
287 _('Limit DNS service to subnets interfaces on which we are serving DNS.'));
291 o = s.taboption('general', form.Flag, 'nonwildcard',
293 _('Bind dynamically to interfaces rather than wildcard address (recommended as linux default)'));
297 o = s.taboption('general', form.DynamicList, 'interface',
298 _('Listen Interfaces'),
299 _('Limit listening to these interfaces, and loopback.'));
302 o = s.taboption('general', form.DynamicList, 'notinterface',
303 _('Exclude interfaces'),
304 _('Prevent listening on these interfaces.'));
307 o = s.taboption('leases', form.SectionValue, '__leases__', form.GridSection, 'host', null,
308 _('Static leases are used to assign fixed IP addresses and symbolic hostnames to DHCP clients. They are also required for non-dynamic interface configurations where only hosts with a corresponding lease are served.') + '<br />' +
309 _('Use the <em>Add</em> Button to add a new lease entry. The <em>MAC-Address</em> identifies the host, the <em>IPv4-Address</em> specifies the fixed address to use, and the <em>Hostname</em> is assigned as a symbolic name to the requesting host. The optional <em>Lease time</em> can be used to set non-standard host-specific lease time, e.g. 12h, 3d or infinite.'));
316 so = ss.option(form.Value, 'name', _('Hostname'));
317 so.datatype = 'hostname("strict")';
319 so.write = function(section, value) {
320 uci.set('dhcp', section, 'name', value);
321 uci.set('dhcp', section, 'dns', '1');
323 so.remove = function(section) {
324 uci.unset('dhcp', section, 'name');
325 uci.unset('dhcp', section, 'dns');
328 so = ss.option(form.Value, 'mac', _('<abbr title="Media Access Control">MAC</abbr>-Address'));
329 so.datatype = 'list(unique(macaddr))';
331 so.cfgvalue = function(section) {
332 var macs = uci.get('dhcp', section, 'mac'),
335 if (!Array.isArray(macs))
336 macs = (macs != null && macs != '') ? macs.split(/\ss+/) : [];
338 for (var i = 0, mac; (mac = macs[i]) != null; i++)
339 if (/^([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2})$/.test(mac))
340 result.push('%02X:%02X:%02X:%02X:%02X:%02X'.format(
341 parseInt(RegExp.$1, 16), parseInt(RegExp.$2, 16),
342 parseInt(RegExp.$3, 16), parseInt(RegExp.$4, 16),
343 parseInt(RegExp.$5, 16), parseInt(RegExp.$6, 16)));
345 return result.length ? result.join(' ') : null;
347 Object.keys(hosts).forEach(function(mac) {
351 so = ss.option(form.Value, 'ip', _('<abbr title="Internet Protocol Version 4">IPv4</abbr>-Address'));
352 so.datatype = 'or(ip4addr,"ignore")';
353 so.validate = function(section, value) {
354 var mac = this.map.lookupOption('mac', section),
355 name = this.map.lookupOption('name', section),
356 m = mac ? mac[0].formvalue(section) : null,
357 n = name ? name[0].formvalue(section) : null;
359 if ((m == null || m == '') && (n == null || n == ''))
360 return _('One of hostname or mac address must be specified!');
364 Object.keys(hosts).forEach(function(mac) {
366 so.value(hosts[mac].ipv4);
369 so = ss.option(form.Value, 'leasetime', _('Lease time'));
372 so = ss.option(form.Value, 'duid', _('<abbr title="The DHCP Unique Identifier">DUID</abbr>'));
373 so.datatype = 'and(rangelength(20,36),hexstring)';
374 Object.keys(duids).forEach(function(duid) {
375 so.value(duid, '%s (%s)'.format(duid, duids[duid].name || '?'));
378 so = ss.option(form.Value, 'hostid', _('<abbr title="Internet Protocol Version 6">IPv6</abbr>-Suffix (hex)'));
380 o = s.taboption('leases', CBILeaseStatus, '__status__');
382 return m.render().then(function(mapEl) {
383 L.Poll.add(function() {
384 return callDHCPLeases(4).then(function(leases) {
385 cbi_update_table(mapEl.querySelector('#lease_status_table'),
386 leases.map(function(lease) {
389 if (lease.expires === false)
390 exp = E('em', _('unlimited'));
391 else if (lease.expires <= 0)
392 exp = E('em', _('expired'));
394 exp = '%t'.format(lease.expires);
397 lease.hostname || '?',
403 E('em', _('There are no active leases')));