6 var callHostHints, callDUIDHints, callDHCPLeases, CBILeaseStatus;
8 callHostHints = rpc.declare({
10 method: 'getHostHints',
14 callDUIDHints = rpc.declare({
16 method: 'getDUIDHints',
20 callDHCPLeases = rpc.declare({
22 method: 'getDHCPLeases',
24 expect: { dhcp_leases: [] }
27 CBILeaseStatus = form.DummyValue.extend({
28 renderWidget: function(section_id, option_id, cfgvalue) {
30 E('h4', _('Active DHCP Leases')),
31 E('div', { 'id': 'lease_status_table', 'class': 'table' }, [
32 E('div', { 'class': 'tr table-titles' }, [
33 E('div', { 'class': 'th' }, _('Hostname')),
34 E('div', { 'class': 'th' }, _('IPv4-Address')),
35 E('div', { 'class': 'th' }, _('MAC-Address')),
36 E('div', { 'class': 'th' }, _('Leasetime remaining'))
38 E('div', { 'class': 'tr placeholder' }, [
39 E('div', { 'class': 'td' }, E('em', _('Collecting data...')))
46 return L.view.extend({
56 render: function(hosts_duids) {
57 var hosts = hosts_duids[0],
58 duids = hosts_duids[1],
61 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'));
63 s = m.section(form.TypedSection, 'dnsmasq', _('Server Settings'));
67 s.tab('general', _('General Settings'));
68 s.tab('files', _('Resolv and Hosts Files'));
69 s.tab('tftp', _('TFTP Settings'));
70 s.tab('advanced', _('Advanced Settings'));
71 s.tab('leases', _('Static Leases'));
73 s.taboption('general', form.Flag, 'domainneeded',
75 _('Don\'t forward <abbr title="Domain Name System">DNS</abbr>-Requests without <abbr title="Domain Name System">DNS</abbr>-Name'));
77 s.taboption('general', form.Flag, 'authoritative',
79 _('This is the only <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> in the local network'));
82 s.taboption('files', form.Flag, 'readethers',
83 _('Use <code>/etc/ethers</code>'),
84 _('Read <code>/etc/ethers</code> to configure the <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server'));
86 s.taboption('files', form.Value, 'leasefile',
88 _('file where given <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-leases will be stored'));
90 s.taboption('files', form.Flag, 'noresolv',
91 _('Ignore resolve file')).optional = true;
93 o = s.taboption('files', form.Value, 'resolvfile',
95 _('local <abbr title="Domain Name System">DNS</abbr> file'));
97 o.depends('noresolv', '');
101 s.taboption('files', form.Flag, 'nohosts',
102 _('Ignore <code>/etc/hosts</code>')).optional = true;
104 s.taboption('files', form.DynamicList, 'addnhosts',
105 _('Additional Hosts files')).optional = true;
107 o = s.taboption('advanced', form.Flag, 'quietdhcp',
108 _('Suppress logging'),
109 _('Suppress logging of the routine operation of these protocols'));
112 o = s.taboption('advanced', form.Flag, 'sequential_ip',
113 _('Allocate IP sequentially'),
114 _('Allocate IP addresses sequentially, starting from the lowest available address'));
117 o = s.taboption('advanced', form.Flag, 'boguspriv',
119 _('Do not forward reverse lookups for local networks'));
120 o.default = o.enabled;
122 s.taboption('advanced', form.Flag, 'filterwin2k',
124 _('Do not forward requests that cannot be answered by public name servers'));
127 s.taboption('advanced', form.Flag, 'localise_queries',
128 _('Localise queries'),
129 _('Localise hostname depending on the requesting subnet if multiple IPs are available'));
131 //local have_dnssec_support = luci.util.checklib('/usr/sbin/dnsmasq', 'libhogweed.so');
132 var have_dnssec_support = true;
134 if (have_dnssec_support) {
135 o = s.taboption('advanced', form.Flag, 'dnssec',
139 o = s.taboption('advanced', form.Flag, 'dnsseccheckunsigned',
140 _('DNSSEC check unsigned'),
141 _('Requires upstream supports DNSSEC; verify unsigned domain responses really come from unsigned domains'));
145 s.taboption('general', form.Value, 'local',
147 _('Local domain specification. Names matching this domain are never forwarded and are resolved from DHCP or hosts files only'));
149 s.taboption('general', form.Value, 'domain',
151 _('Local domain suffix appended to DHCP names and hosts file entries'));
153 s.taboption('advanced', form.Flag, 'expandhosts',
155 _('Add local domain suffix to names served from hosts files'));
157 s.taboption('advanced', form.Flag, 'nonegcache',
158 _('No negative cache'),
159 _('Do not cache negative replies, e.g. for not existing domains'));
161 s.taboption('advanced', form.Value, 'serversfile',
162 _('Additional servers file'),
163 _('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.'));
165 s.taboption('advanced', form.Flag, 'strictorder',
167 _('<abbr title="Domain Name System">DNS</abbr> servers will be queried in the order of the resolvfile')).optional = true;
169 s.taboption('advanced', form.Flag, 'allservers',
171 _('Query all available upstream <abbr title="Domain Name System">DNS</abbr> servers')).optional = true;
173 o = s.taboption('advanced', form.DynamicList, 'bogusnxdomain', _('Bogus NX Domain Override'),
174 _('List of hosts that supply bogus NX domain results'));
177 o.placeholder = '67.215.65.132';
180 s.taboption('general', form.Flag, 'logqueries',
182 _('Write received DNS requests to syslog')).optional = true;
184 o = s.taboption('general', form.DynamicList, 'server', _('DNS forwardings'),
185 _('List of <abbr title="Domain Name System">DNS</abbr> servers to forward requests to'));
188 o.placeholder = '/example.org/10.1.2.3';
191 o = s.taboption('general', form.Flag, 'rebind_protection',
192 _('Rebind protection'),
193 _('Discard upstream RFC1918 responses'));
198 o = s.taboption('general', form.Flag, 'rebind_localhost',
199 _('Allow localhost'),
200 _('Allow upstream responses in the 127.0.0.0/8 range, e.g. for RBL services'));
202 o.depends('rebind_protection', '1');
205 o = s.taboption('general', form.DynamicList, 'rebind_domain',
206 _('Domain whitelist'),
207 _('List of domains to allow RFC1918 responses for'));
210 o.depends('rebind_protection', '1');
211 o.datatype = 'host(1)';
212 o.placeholder = 'ihost.netflix.com';
215 o = s.taboption('advanced', form.Value, 'port',
216 _('<abbr title="Domain Name System">DNS</abbr> server port'),
217 _('Listening port for inbound DNS queries'));
224 o = s.taboption('advanced', form.Value, 'queryport',
225 _('<abbr title="Domain Name System">DNS</abbr> query port'),
226 _('Fixed source port for outbound DNS queries'));
230 o.placeholder = _('any');
233 o = s.taboption('advanced', form.Value, 'dhcpleasemax',
234 _('<abbr title="maximal">Max.</abbr> <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> leases'),
235 _('Maximum allowed number of active DHCP leases'));
238 o.datatype = 'uinteger';
239 o.placeholder = _('unlimited');
242 o = s.taboption('advanced', form.Value, 'ednspacket_max',
243 _('<abbr title="maximal">Max.</abbr> <abbr title="Extension Mechanisms for Domain Name System">EDNS0</abbr> packet size'),
244 _('Maximum allowed size of EDNS.0 UDP packets'));
247 o.datatype = 'uinteger';
248 o.placeholder = 1280;
251 o = s.taboption('advanced', form.Value, 'dnsforwardmax',
252 _('<abbr title="maximal">Max.</abbr> concurrent queries'),
253 _('Maximum allowed number of concurrent DNS queries'));
256 o.datatype = 'uinteger';
259 o = s.taboption('advanced', form.Value, 'cachesize',
260 _('Size of DNS query cache'),
261 _('Number of cached DNS entries (max is 10000, 0 is no caching)'));
263 o.datatype = 'range(0,10000)';
266 s.taboption('tftp', form.Flag, 'enable_tftp',
267 _('Enable TFTP server')).optional = true;
269 o = s.taboption('tftp', form.Value, 'tftp_root',
270 _('TFTP server root'),
271 _('Root directory for files served via TFTP'));
274 o.depends('enable_tftp', '1');
278 o = s.taboption('tftp', form.Value, 'dhcp_boot',
279 _('Network boot image'),
280 _('Filename of the boot image advertised to clients'));
283 o.depends('enable_tftp', '1');
284 o.placeholder = 'pxelinux.0';
286 o = s.taboption('general', form.Flag, 'localservice',
287 _('Local Service Only'),
288 _('Limit DNS service to subnets interfaces on which we are serving DNS.'));
292 o = s.taboption('general', form.Flag, 'nonwildcard',
294 _('Bind dynamically to interfaces rather than wildcard address (recommended as linux default)'));
298 o = s.taboption('general', form.DynamicList, 'interface',
299 _('Listen Interfaces'),
300 _('Limit listening to these interfaces, and loopback.'));
303 o = s.taboption('general', form.DynamicList, 'notinterface',
304 _('Exclude interfaces'),
305 _('Prevent listening on these interfaces.'));
308 o = s.taboption('leases', form.SectionValue, '__leases__', form.GridSection, 'host', null,
309 _('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 />' +
310 _('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.'));
317 so = ss.option(form.Value, 'name', _('Hostname'));
318 so.datatype = 'hostname("strict")';
320 so.write = function(section, value) {
321 uci.set('dhcp', section, 'name', value);
322 uci.set('dhcp', section, 'dns', '1');
324 so.remove = function(section) {
325 uci.unset('dhcp', section, 'name');
326 uci.unset('dhcp', section, 'dns');
329 so = ss.option(form.Value, 'mac', _('<abbr title="Media Access Control">MAC</abbr>-Address'));
330 so.datatype = 'list(unique(macaddr))';
332 so.cfgvalue = function(section) {
333 var macs = uci.get('dhcp', section, 'mac'),
336 if (!Array.isArray(macs))
337 macs = (macs != null && macs != '') ? macs.split(/\ss+/) : [];
339 for (var i = 0, mac; (mac = macs[i]) != null; i++)
340 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))
341 result.push('%02X:%02X:%02X:%02X:%02X:%02X'.format(
342 parseInt(RegExp.$1, 16), parseInt(RegExp.$2, 16),
343 parseInt(RegExp.$3, 16), parseInt(RegExp.$4, 16),
344 parseInt(RegExp.$5, 16), parseInt(RegExp.$6, 16)));
346 return result.length ? result.join(' ') : null;
348 Object.keys(hosts).forEach(function(mac) {
352 so = ss.option(form.Value, 'ip', _('<abbr title="Internet Protocol Version 4">IPv4</abbr>-Address'));
353 so.datatype = 'or(ip4addr,"ignore")';
354 so.validate = function(section, value) {
355 var mac = this.map.lookupOption('mac', section),
356 name = this.map.lookupOption('name', section),
357 m = mac ? mac[0].formvalue(section) : null,
358 n = name ? name[0].formvalue(section) : null;
360 if ((m == null || m == '') && (n == null || n == ''))
361 return _('One of hostname or mac address must be specified!');
365 Object.keys(hosts).forEach(function(mac) {
367 so.value(hosts[mac].ipv4);
370 so = ss.option(form.Value, 'leasetime', _('Lease time'));
373 so = ss.option(form.Value, 'duid', _('<abbr title="The DHCP Unique Identifier">DUID</abbr>'));
374 so.datatype = 'and(rangelength(20,36),hexstring)';
375 Object.keys(duids).forEach(function(duid) {
376 so.value(duid, '%s (%s)'.format(duid, duids[duid].name || '?'));
379 so = ss.option(form.Value, 'hostid', _('<abbr title="Internet Protocol Version 6">IPv6</abbr>-Suffix (hex)'));
381 o = s.taboption('leases', CBILeaseStatus, '__status__');
383 return m.render().then(function(mapEl) {
384 L.Poll.add(function() {
385 return callDHCPLeases(4).then(function(leases) {
386 cbi_update_table(mapEl.querySelector('#lease_status_table'),
387 leases.map(function(lease) {
390 if (lease.expires === false)
391 exp = E('em', _('unlimited'));
392 else if (lease.expires <= 0)
393 exp = E('em', _('expired'));
395 exp = '%t'.format(lease.expires);
398 lease.hostname || '?',
404 E('em', _('There are no active leases')));