-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathipv4-structured.rsc
More file actions
336 lines (289 loc) · 10.5 KB
/
ipv4-structured.rsc
File metadata and controls
336 lines (289 loc) · 10.5 KB
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#!rsc by RouterOS
# Make an IPv4 prefix mask of a given length.
#
# $1 (integer): prefix length
#
# > :put [$MakeIPPrefixMask 24]
# 255.255.255.0
#
:global MakeIPPrefixMask do={
:if (($1 < 0) or ($1 > 32)) do={ :error "$1 is invalid IPv4 mask length" }
:return ((255.255.255.255 >> (32 - $1)) << (32 - $1))
}
# Make an IPv4 suffix mask of a given length.
#
# $1 (integer): prefix length
#
# > :put [$MakeIPSuffixMask 24]
# 0.255.255.255
#
:global MakeIPSuffixMask do={
:if (($1 < 0) or ($1 > 32)) do={ :error "$1 is invalid IPv4 mask length" }
:return ((255.255.255.255 << (32 - $1)) >> (32 - $1))
}
# Make an array of four 8bit integers that represent IPv4 address,
# one integer per field.
#
# $1 (ip, str): IPv4 address
#
# > :put [$MakeIPFieldsFromAddress 192.0.2.1]
# 192;0;2;1
#
:global MakeIPFieldsFromAddress do={
:local argAddr [:toip $1]
:if ([:typeof $argAddr] != "ip") do={ :error "\"$1\" is invalid IPv4 address"}
:set argAddr [:tostr $argAddr]
:local varLastDelimIdx -1
:local varDelimIdx [:find $argAddr "." $varLastDelimIdx]
:local varField1 [:tonum [:pick $argAddr ($varLastDelimIdx + 1) $varDelimIdx]]
:set varLastDelimIdx $varDelimIdx
:set varDelimIdx [:find $argAddr "." $varLastDelimIdx]
:local varField2 [:tonum [:pick $argAddr ($varLastDelimIdx + 1) $varDelimIdx]]
:set varLastDelimIdx $varDelimIdx
:set varDelimIdx [:find $argAddr "." $varLastDelimIdx]
:local varField3 [:tonum [:pick $argAddr ($varLastDelimIdx + 1) $varDelimIdx]]
:set varLastDelimIdx $varDelimIdx
:local varField4 [:tonum [:pick $argAddr ($varLastDelimIdx + 1) [:len $argAddr]]]
:return ({$varField1 ; $varField2 ; $varField3 ; $varField4})
}
# Make an IPv4 address from an array of four 8bit integers.
#
# $1 (array): IPv4 address fields
#
# > :put [$MakeIPAddressFromFields ({192;0;2;1})]
# 192.0.2.1
#
:global MakeIPAddressFromFields do={
:return [:toip "$($1->0).$($1->1).$($1->2).$($1->3)"]
}
# Expand an array of four 8bit integers into a IPv4 address string with all decimals present.
#
# $1 (array): IPv4 address fields
#
# > :put [$ExpandIPAddressFromFields ({192;0;2;1})]
# 192.000.002.001
#
:global ExpandIPAddressFromFields do={
:local varAddr ""
:for fieldIdx from=0 to=3 step=1 do={
:local varFieldNum ($1->$fieldIdx)
:if ($varFieldNum < 10) do={
:set varAddr ($varAddr . "00$varFieldNum")
} else={
:if ($varFieldNum < 100) do={
:set varAddr ("$varAddr" . "0$varFieldNum")
} else={
:set varAddr ("$varAddr" . "$varFieldNum")
}
}
:if ($fieldIdx != 3) do={ :set varAddr ($varAddr . ".") }
}
:return $varAddr
}
# Expand IPv4 address into a string with all decimals present.
#
# $1 (ip, str): IPv4 address
#
# > :put [$ExpandIPAddress 192.0.2.1]
# 192.000.002.001
#
:global ExpandIPAddress do={
:global ExpandIPAddressFromFields
:global MakeIPFieldsFromAddress
:return [$ExpandIPAddressFromFields [$MakeIPFieldsFromAddress $1]]
}
# Make a structure of common IPv4 atributes.
#
# - $1 (ip, str): IPv4 address
# [$2] (integer): subnet prefix length; defaults to 32
#
# - $1 (str, ip-prefix): IPv4 address-prefix
#
# Returns:
# address (ip): IPv4 address of the input
# addressPrefix (ip-prefix): IPv4 address-prefix of the input
# prefix (ip): Prefix of the input
# prefixMask (ip): Netmask of the input
# prefixLength (integer): Prefix length of the input
#
# > :put [$StructureIPAddressCommon 192.0.2.1/16]
# address=192.0.2.1;addressPrefix=192.0.0.0/16;prefix=192.0.0.0;prefixLength=16;prefixMask=255.255.0.0
#
:global StructureIPAddressCommon do={
:global MakeIPPrefixMask
:local varRawAddr [:toip $1]
:if ([:typeof $varRawAddr] = "nil") do={ :set varRawAddr [:tostr $1] }
:if ([:typeof $varRawAddr] = "nil") do={ :error "\"$1\" is invalid IPv4 address" }
:local varAddr
:local varPrefix
:local varPrefixLen
:if ([:typeof $varRawAddr] = "ip") do={
:set varAddr $varRawAddr
:if ([:len $2] != 0) do={
:set varPrefixLen [:tonum $2]
} else={
:set varPrefixLen 32
}
:if ([:typeof $varPrefixLen] = "nil") do={ :error "\"$2\" is invalid subnet prefix length" }
} else={
:local varDelimIdx [:find $varRawAddr "/"]
:if ([:typeof $varDelimIdx] != "nil") do={
:set varAddr [:toip [:pick $varRawAddr 0 $varDelimIdx]]
:set varPrefixLen [:tonum [:pick $varRawAddr ($varDelimIdx + 1) [:len $varRawAddr]]]
} else={
:set varAddr [:toip $varRawAddr]
:set varPrefixLen 32
}
:if ([:typeof $varAddr] = "nil") do={ :error "\"$1\" is invalid IPv4 address" }
:if ([:typeof $varPrefixLen] = "nil") do={ :error "\"$1\" is invalid IPv4 address" }
}
:local varPrefixMask [$MakeIPPrefixMask $varPrefixLen]
:local varPrefix ($varAddr & $varPrefixMask)
:local varAddrPrefix [[:parse ":return $varPrefix/$varPrefixLen"]]
:return {"address"=$varAddr ; "addressPrefix"=$varAddrPrefix ; "prefix"=$varPrefix ; "prefixLength"=$varPrefixLen ; "prefixMask"=$varPrefixMask}
}
# Make a structure from an IPv4 address.
#
# - $1 (ip4, str): IPv4 address
# [$2] (integer): subnet prefix length; defaults to 32
# [detail] (bool): Whether to include details; defaults to false
#
# - $1 (str, ip-prefix): IPv4 address-prefix
# [detail] (bool): Whether to include details; defaults to false
#
:global StructureIPAddress do={
:global StructureIPAddressCommon
:return [$StructureIPAddressCommon $1 $2]
}
# Make an RFC1035 domain from an IPv4 address.
#
# - $1 (ip, str): IPv4 address
#
# - $1 (ip-prefix, str): IPv4 network
# [rfc2317] (bool): Whether to follow RFC2317 recommendation for networks on non-octet boundaries
#
# - $1 (array): IPv4 address structure
# [rfc2317] (bool): Whether to follow RFC2317 recommendation for networks on non-octet boundaries
#
# > :put [$MakeIPDomain 192.0.2.1]
# 1.2.0.192.in-addr.arpa.
# > :put [$MakeIPDomain 192.0.2.0/24]
# 2.0.192.in-addr.arpa.
# > :put [$MakeIPDomain 192.0.2.0/25]
# 2.0.192.in-addr.arpa.
# > :put [$MakeIPDomain 192.0.2.0/25 rfc2317=yes]
# 0/25.2.0.192.in-addr.arpa.
#
:global MakeIPDomain do={
:global MakeIPFieldsFromAddress
:global StructureIPAddressCommon
:local argNetworkStruct
:if ([:typeof $1] = "array") do={
:set argNetworkStruct $1
} else={
:set argNetworkStruct [$StructureIPAddressCommon $1]
}
:local varAddr ($argNetworkStruct->"prefix")
:local varNetworkLen ($argNetworkStruct->"prefixLength")
:local varFields [$MakeIPFieldsFromAddress $varAddr]
:local varDomain "in-addr.arpa."
:for fieldIdx from=0 to=($varNetworkLen / 8 - 1) step=1 do={
:set varDomain "$($varFields->$fieldIdx).$varDomain"
}
:if ((($varNetworkLen % 8) != 0) and ([:len $rfc2317] != 0)) do={
:if ([[:parse "[:tobool $rfc2317]"]]) do={
:set varDomain "$($varFields->($varNetworkLen / 8))/$varNetworkLen.$varDomain"
}
}
:return $varDomain
}
# Deduplicate, coalesce and sort IPv4 addresses and prefixes.
#
# - $1 (array): An array of IPv4 addresses and/or prefixes
# [structure] (bool): Whether to structure the output; defaults to false
#
# - $1 (array): An array of IPv4 address structures
#
# > :put [$DeduplicateIPAddresses ({192.0.2.0/28;192.0.2.32/28;192.0.2.40/29;192.0.2.0/28})]
# 192.0.2.0/28;192.0.2.32/28
#
:global DeduplicateIPAddresses do={
:global ExpandIPAddress
:global GetArrayValues
:global StructureIPAddressCommon
# Dictionary will deduplicate and sort.
:local varDeduplicatedPrefixes ({})
:foreach prefix in=$1 do={
:local varPrefixStruct
:if ([:typeof $prefix] = "array") do={
:set varPrefixStruct $prefix
} else={
:set varPrefixStruct [$StructureIPAddressCommon $prefix]
}
:local varSortKey [$ExpandIPAddress ($varPrefixStruct->"prefix")]
# Maintain shorter prefix.
:if ([:typeof ($varDeduplicatedPrefixes->$varSortKey)] != "nothing") do={
:if ($varPrefixStruct->"prefixLength" < $varDeduplicatedPrefixes->$varSortKey->"prefixLength") do={
:set ($varDeduplicatedPrefixes->$varSortKey) $varPrefixStruct
}
} else={
:set ($varDeduplicatedPrefixes->$varSortKey) $varPrefixStruct
}
}
:set varDeduplicatedPrefixes [$GetArrayValues $varDeduplicatedPrefixes]
:local varCoalescedPrefixes ({$varDeduplicatedPrefixes->0})
:local varParentIdx 0
:local varCurIdx 1
:while ($varCurIdx < [:len $varDeduplicatedPrefixes]) do={
:if (($varDeduplicatedPrefixes->$varCurIdx->"prefix" in $varDeduplicatedPrefixes->$varParentIdx->"addressPrefix") = false) do={
:set varCoalescedPrefixes ($varCoalescedPrefixes , {$varDeduplicatedPrefixes->$varCurIdx})
:set varParentIdx $varCurIdx
}
:set varCurIdx ($varCurIdx + 1)
}
:if ([:len $structure] != 0) do={
:if ([[:parse "[:tobool $structure]"]]) do={
:return $varCoalescedPrefixes
}
}
:local varTmp ({})
:foreach prefixStruct in=$varCoalescedPrefixes do={ :set varTmp ($varTmp , $prefixStruct->"addressPrefix") }
:return $varTmp
}
# Subnet IPv4 address prefixes.
#
# - $1 (ip-prefix, ip, str): An IPv4 prefix
# $2 (integer): Relative prefix length
#
# - $1 (array): An IPv4 address structure
# $2 (integer): Relative prefix length
#
# > :put [$SubnetIPPrefix 192.0.2.0/24 1]
# 192.0.2.0/25;192.0.2.128/25
#
:global SubnetIPPrefix do={
:global StructureIPAddressCommon
:local varPrefixStruct
:if ([:typeof $1] = "array") do={
:set varPrefixStruct $1
} else={
:set varPrefixStruct [$StructureIPAddressCommon $1]
}
:local argPrefixLength [:tonum $2]
:if (($varPrefixStruct->"prefixLength" + $argPrefixLength) > 32) do={
:set argPrefixLength (32 - $varPrefixStruct->"prefixLength")
}
:local varSubnets ({})
:local varSubnetsCount (1 << $argPrefixLength)
:local varSubnetPrefixLen ($varPrefixStruct->"prefixLength" + $argPrefixLength)
:local varSubnetIDShift (32 - $varSubnetPrefixLen)
:local varSubnetID [:toip 0.0.0.0]
:local varSubnetInc (0.0.0.1 << $varSubnetIDShift)
:for subnetIdx from=0 to=($varSubnetsCount - 1) step=1 do={
:local varSubnetPrefix ($varPrefixStruct->"prefix" | $varSubnetID)
:local varSubnetAddrPrefix [[:parse ":return $varSubnetPrefix/$varSubnetPrefixLen"]]
:set varSubnets ($varSubnets , $varSubnetAddrPrefix)
:set varSubnetID ($varSubnetID + $varSubnetInc)
}
return $varSubnets
}