Last active
September 26, 2019 09:43
-
-
Save winguse/647c44f24ace9f65a287a8886a012631 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fetch = require('node-fetch'); | |
const { deepStrictEqual } = require('assert'); | |
function ipv4ToInt(ip) { | |
return ip.split('.').reduce((acc, cur) => acc << 8 | +cur, 0); | |
} | |
function intToIpv4(i) { | |
return `${i >>> 24}.${i >>> 16 & 0xff}.${i >>> 8 & 0xff}.${i & 0xff}`; | |
} | |
function compress(t) { | |
if (!t) return false; | |
const left = compress(t[0]); | |
const right = compress(t[1]); | |
if (left && right) { | |
delete t[0]; | |
delete t[1]; | |
t.mark = true; | |
} | |
return t.mark; | |
} | |
function merge(cidrs) { | |
const root = {}; | |
for (let i = 0; i < cidrs.length; i++) { | |
const [ipV4String, lengthString] = cidrs[i].split('/'); | |
const ipInt = ipv4ToInt(ipV4String); | |
const length = +lengthString; | |
const shiftEnd = 31 - length; | |
let t = root; | |
for (let shift = 31; shift > shiftEnd; shift--) { | |
const mask = 1 << shift; | |
const value = ipInt & mask ? 1 : 0; | |
let next = t[value]; | |
if (next && next.mark) { | |
t = undefined; | |
break; | |
} | |
if (!next) { | |
next = t[value] = {}; | |
} | |
t = next; | |
} | |
if (t) { // found | |
t.mark = true; | |
if (t[0]) { | |
delete t[0]; | |
} | |
if (t[1]) { | |
delete t[1]; | |
} | |
} else { | |
// duplicate | |
} | |
} | |
// console.log(JSON.stringify(root, undefined, 2)) | |
compress(root); | |
// console.log(JSON.stringify(root, undefined, 2)) | |
const result = []; | |
const inverse = []; | |
function dfs(t, v, l) { | |
if (!t) { | |
inverse.push(`${intToIpv4(v)}/${l}`); | |
return; | |
} | |
if (t.mark) { | |
result.push(`${intToIpv4(v)}/${l}`); | |
return; | |
} | |
dfs(t[0], v, l + 1); | |
dfs(t[1], v | 1 << (31 - l), l + 1); | |
} | |
dfs(root, 0, 0); | |
return { result, inverse }; | |
} | |
function test(input, expected) { | |
const { result } = merge(input); | |
deepStrictEqual(result, expected); | |
} | |
test(['1.0.0.0/8', '0.0.0.0/8'], ['0.0.0.0/7']) | |
test(['127.1.0.0/16', '127.0.0.0/16'], ['127.0.0.0/15']); | |
test(['127.0.0.0/24', '127.0.1.0/24'], ['127.0.0.0/23']); | |
test(['128.0.0.0/1', '0.0.0.0/1'], ['0.0.0.0/0']); | |
test([ | |
'127.0.0.0/23', | |
'0.0.0.0/2', | |
'64.0.0.0/3', | |
'96.0.0.0/4', | |
'112.0.0.0/5', | |
'120.0.0.0/6', | |
'124.0.0.0/7', | |
'126.0.0.0/8', | |
'127.0.2.0/23', | |
'127.0.4.0/22', | |
'127.0.8.0/21', | |
'127.0.16.0/20', | |
'127.0.32.0/19', | |
'127.0.64.0/18', | |
'127.0.128.0/17', | |
'127.1.0.0/16', | |
'127.2.0.0/15', | |
'127.4.0.0/14', | |
'127.8.0.0/13', | |
'127.16.0.0/12', | |
'127.32.0.0/11', | |
'127.64.0.0/10', | |
'127.128.0.0/9', | |
'128.0.0.0/1'], ['0.0.0.0/0']); | |
const ipListUrl = 'https://raw.githubusercontent.com/misakaio/chnroutes2/master/chnroutes.txt'; | |
// https://zh.wikipedia.org/wiki/%E4%BF%9D%E7%95%99IP%E5%9C%B0%E5%9D%80 | |
const reserved = [ | |
'0.0.0.0/8', | |
'10.0.0.0/8', | |
'100.64.0.0/10', | |
'127.0.0.0/8', | |
'169.254.0.0/16', | |
'172.16.0.0/12', | |
'192.0.0.0/24', | |
'192.0.2.0/24', | |
'192.88.99.0/24', | |
'192.168.0.0/16', | |
'198.18.0.0/15', | |
'198.51.100.0/24', | |
'203.0.113.0/24', | |
'224.0.0.0/4', | |
'240.0.0.0/4', | |
'255.255.255.255/32', | |
] | |
async function main() { | |
const res = await fetch(ipListUrl); | |
const body = await res.text(); | |
const cidrs = body.split('\n') | |
.map(l => l.trim()) | |
.filter(l => l && !l.startsWith('#')) | |
.map(l => { | |
return l; | |
// if we want roughly | |
const [ip, length] = l.split('/'); | |
if (+length < 16) { | |
return l; | |
} | |
return ip.split('.').slice(0, 2).join('.') + '.0.0/16' | |
}) | |
.concat(reserved); | |
const { result, inverse } = merge(cidrs); | |
// console.log(cidrs.length, result.length, inverse.length) | |
// console.log(inverse.join(',')) | |
console.log( | |
['#!/bin/sh'] | |
.concat( | |
result.map(r => `/sbin/route add ${r} -interface $6`) | |
) | |
.join('\n') | |
); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment