1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
'use strict';
'require view';
'require fs';
'require uci';
'require form';
'require network';
'require tools.widgets as widgets';
'require tools.network as nettools';
return view.extend({
load() {
return Promise.all([
network.getDevices(),
fs.lines('/etc/iproute2/rt_tables')
]);
},
render([netDevs, rtTables]) {
let m, s, o;
rtTables = rtTables.map(function(l) {
const m = l.trim().match(/^(\d+)\s+(\S+)$/);
return m ? [ +m[1], m[2] ] : null;
}).filter(function(e) {
return e && e[0] > 0;
});
m = new form.Map('network', _('Routing'), _('Routing defines over which interface and gateway a certain host or network can be reached.') +
'<br/>' + _('Routes go in routing tables and define the specific path to reach destinations.') +
'<br/>' + _('Rules determine which routing table to use, based on conditions like source address or interface.'));
m.tabbed = true;
for (let family = 4; family <= 6; family += 2) {
s = m.section(form.GridSection, (family == 6) ? 'route6' : 'route', (family == 6) ? _('Static IPv6 Routes') : _('Static IPv4 Routes'));
s.anonymous = true;
s.addremove = true;
s.sortable = true;
s.cloneable = true;
s.nodescriptions = true;
s.tab('general', _('General Settings'));
s.tab('advanced', _('Advanced Settings'));
o = s.taboption('general', widgets.NetworkSelect, 'interface', _('Interface'), _('Specifies the logical interface name of the parent (or master) interface this route belongs to'));
o.loopback = true;
o.nocreate = true;
o.rmempty = true;
o = s.taboption('general', form.ListValue, 'type', _('Route type'), _('Specifies the route type to be created'));
o.modalonly = true;
o.value('', 'unicast');
o.value('local');
o.value('broadcast');
o.value('multicast');
o.value('unreachable');
o.value('prohibit');
o.value('blackhole');
o.value('anycast');
o.value('throw');
o = s.taboption('general', form.Value, 'target', _('Target'), _('Network address'));
o.rmempty = false;
o.datatype = (family == 6) ? 'cidr6' : 'cidr4';
o.placeholder = (family == 6) ? '::/0' : '0.0.0.0/0';
o.cfgvalue = function(section_id) {
const section_type = uci.get('network', section_id, '.type');
const target = uci.get('network', section_id, 'target');
const mask = uci.get('network', section_id, 'netmask');
const v6 = (section_type == 'route6') ? true : false;
const bits = mask ? network.maskToPrefix(mask, v6) : (v6 ? 128 : 32);
if (target) {
return target.split('/')[1] ? target : target + '/' + bits;
}
}
o.write = function(section_id, formvalue) {
uci.set('network', section_id, 'target', formvalue);
uci.unset('network', section_id, 'netmask');
}
o = s.taboption('general', form.Value, 'gateway', _('Gateway'), _('Specifies the network gateway. If omitted, the gateway from the parent interface is taken if any, otherwise creates a link scope route. If set to 0.0.0.0 no gateway will be specified for the route'));
o.datatype = (family == 6) ? 'ip6addr("nomask")' : 'ip4addr("nomask")';
o.placeholder = (family == 6) ? 'fe80::1' : '192.168.0.1';
o = s.taboption('advanced', form.Value, 'metric', _('Metric'), _('Ordinal: routes with the lowest metric match first'));
o.datatype = 'uinteger';
o.placeholder = 0;
o.textvalue = function(section_id) {
return this.cfgvalue(section_id) || E('em', _('auto'));
};
o = s.taboption('advanced', form.Value, 'mtu', _('MTU'), _('Packets exceeding this value may be fragmented'));
o.modalonly = true;
o.datatype = 'and(uinteger,range(64,9000))';
o.placeholder = 1500;
o = s.taboption('advanced', form.Value, 'table', _('Table'), _('Routing table into which to insert this rule.') + '<br/>' +
_('A numeric table index, or symbol alias declared in %s. Special aliases local (255), main (254) and default (253) are also valid').format('<code>/etc/iproute2/rt_tables</code>')
+ '<br/>' + _('Only interfaces using this table (via override) will use this route.'));
o.datatype = 'or(uinteger, string)';
for (let rt of rtTables)
o.value(rt[1], '%s (%d)'.format(rt[1], rt[0]));
o.textvalue = function(section_id) {
return this.cfgvalue(section_id) || E('em', _('auto'));
};
o = s.taboption('advanced', form.Value, 'source', _('Source'), _('Specifies the preferred source address when sending to destinations covered by the target')
+ '<br/>' + _('This is only used if no default route matches the destination gateway'));
o.modalonly = true;
o.datatype = (family == 6) ? 'ip6addr' : 'ip4addr';
for (let nd of netDevs) {
const addrs = (family == 6) ? nd.getIP6Addrs() : nd.getIPAddrs();
for (let a of addrs)
o.value(a.split('/')[0]);
}
o = s.taboption('advanced', form.Flag, 'onlink', _('On-link'), _('When enabled, gateway is on-link even if the gateway does not match any interface prefix'));
o.modalonly = true;
o.default = o.disabled;
o = s.taboption('advanced', form.Flag, 'disabled', _('Disable'));
o.modalonly = false;
o.editable = true;
o.default = o.disabled;
}
for (let family = 4; family <= 6; family += 2) {
s = m.section(form.GridSection, (family == 6) ? 'rule6' : 'rule', (family == 6) ? _('IPv6 Rules') : _('IPv4 Rules'));
s.anonymous = true;
s.addremove = true;
s.sortable = true;
s.cloneable = true;
s.nodescriptions = true;
s.tab('general', _('General Settings'));
s.tab('advanced', _('Advanced Settings'));
o = s.taboption('general', form.Value, 'priority', _('Priority'), _('Execution order of this IP rule: lower numbers go first'));
o.datatype = 'uinteger';
o.placeholder = 30000;
o.textvalue = function(section_id) {
return this.cfgvalue(section_id) || E('em', _('auto'));
};
o = s.taboption('general', form.ListValue, 'action', _('Rule type'), _('Specifies the rule target routing action'));
o.modalonly = true;
o.value('', 'unicast');
o.value('unreachable');
o.value('prohibit');
o.value('blackhole');
o.value('throw');
o = s.taboption('general', widgets.NetworkSelect, 'in', _('Incoming interface'), _('Match traffic from this interface'));
o.loopback = true;
o.nocreate = true;
o = s.taboption('general', form.Value, 'src', _('Source'), _('Match traffic from this source subnet (CIDR notation)'));
o.datatype = (family == 6) ? 'cidr6' : 'cidr4';
o.placeholder = (family == 6) ? '::/0' : '0.0.0.0/0';
o.textvalue = function(section_id) {
return this.cfgvalue(section_id) || E('em', _('any'));
};
o = s.taboption('general', form.Value, 'ipproto', _('IP Protocol'), _('Match traffic IP protocol type'));
o.datatype = 'range(0,255)';
nettools.protocols.forEach(function(p) {
o.value(p.i, p.d);
});
o = s.taboption('general', widgets.NetworkSelect, 'out', _('Outgoing interface'), _('Match traffic destined to this interface'));
o.loopback = true;
o.nocreate = true;
o = s.taboption('general', form.Value, 'dest', _('Destination'), _('Match traffic destined to this subnet (CIDR notation)'));
o.datatype = (family == 6) ? 'cidr6' : 'cidr4';
o.placeholder = (family == 6) ? '::/0' : '0.0.0.0/0';
o.textvalue = function(section_id) {
return this.cfgvalue(section_id) || E('em', _('any'));
};
o = s.taboption('advanced', form.Value, 'lookup', _('Table'), _('Routing table to use for traffic matching this rule.') + '<br/>' +
_('A numeric table index, or symbol alias declared in %s. Special aliases local (255), main (254) and default (253) are also valid').format('<code>/etc/iproute2/rt_tables</code>')
+ '<br/>' + _('Matched traffic re-targets to an interface using this table.'));
o.datatype = 'or(uinteger, string)';
for (let rt of rtTables)
o.value(rt[1], '%s (%d)'.format(rt[1], rt[0]));
o = s.taboption('advanced', form.Value, 'goto', _('Jump to rule'), _('Jumps to another rule specified by its priority value'));
o.modalonly = true;
o.datatype = 'uinteger';
o.placeholder = 80000;
o = s.taboption('advanced', form.Value, 'mark', _('Firewall mark'), _('Specifies the fwmark and optionally its mask to match, e.g. 0xFF to match mark 255 or 0x0/0x1 to match any even mark value'));
o.modalonly = true;
o.datatype = 'string';
o.placeholder = '0x1/0xf';
o = s.taboption('advanced', form.Value, 'sport', _('Source port'), _('Match traffic from this source port (range)'));
o.modalonly = true;
o.datatype = 'portrange';
o.placeholder = '0-65535';
o = s.taboption('advanced', form.Value, 'dport', _('Destination port'), _('Match traffic from this destination port (range)'));
o.modalonly = true;
o.datatype = 'portrange';
o.placeholder = '0-65535';
o = s.taboption('advanced', form.Value, 'tos', _('Type of service'), _('Specifies the TOS value to match in IP headers'));
o.modalonly = true;
o.datatype = 'uinteger';
o.placeholder = 10;
o = s.taboption('advanced', form.Value, 'uidrange', _('User identifier'), _('Specifies an individual UID or range of UIDs to match, e.g. 1000 to match corresponding UID or 1000-1005 to inclusively match all UIDs within the corresponding range'));
o.modalonly = true;
o.datatype = 'string';
o.placeholder = '1000-1005';
o = s.taboption('advanced', form.Value, 'suppress_prefixlength', _('Prefix suppressor'), _('Reject routing decisions that have a prefix length less than or equal to the specified value')
+ '<br/>' + _('Prevents overly broad routes being considered. Setting 16 would consider /17, /24, /28 or more specific routes yet ignore /16, /8, /0 (default) routes'));
o.modalonly = true;
o.datatype = (family == 6) ? 'ip6prefix' : 'ip4prefix';
o.placeholder = (family == 6) ? 64 : 24;
o = s.taboption('advanced', form.Flag, 'invert', _('Invert match'), _('If set, the meaning of the match options is inverted'));
o.modalonly = true;
o.default = o.disabled;
o = s.taboption('advanced', form.Flag, 'disabled', _('Disable'));
o.modalonly = false;
o.editable = true;
o.default = o.disabled;
}
return m.render();
}
});
|